1 /*
2  * Copyright (c) 2024-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #define HST_LOG_TAG "HlsPlayListDownloader"
16 #include <mutex>
17 #include "plugin/plugin_time.h"
18 #include "hls_playlist_downloader.h"
19 #include "common/media_source.h"
20 #include <unistd.h>
21 
22 namespace OHOS {
23 namespace Media {
24 namespace Plugins {
25 namespace HttpPlugin {
26 namespace {
27 constexpr unsigned int SLEEP_TIME = 1;
28 constexpr size_t RETRY_TIMES = 1000;
29 constexpr int MIN_PRE_PARSE_NUM = 2; // at least 2 ts frag
30 const std::string M3U8_START_TAG = "#EXTM3U";
31 const std::string M3U8_END_TAG = "#EXT-X-ENDLIST";
32 const std::string M3U8_TS_TAG = "#EXTINF";
33 }
34 // StateMachine thread: call plugin SetSource -> call Open
35 // StateMachine thread: call plugin GetSeekable -> call GetSeekable
36 // PlayListDownload thread: call ParseManifest
37 // First call Open, then start PlayListDownload thread, it seems no lock is required.
38 // [In future] StateMachine thread: call plugin GetDuration -> call GetDuration
Open(const std::string & url,const std::map<std::string,std::string> & httpHeader)39 void HlsPlayListDownloader::Open(const std::string& url, const std::map<std::string, std::string>& httpHeader)
40 {
41     url_ = url;
42     master_ = nullptr;
43     SaveHttpHeader(httpHeader);
44     if (mimeType_ == AVMimeTypes::APPLICATION_M3U8) {
45         DoOpenNative(url_);
46     } else {
47         DoOpen(url_);
48     }
49 }
50 
~HlsPlayListDownloader()51 HlsPlayListDownloader::~HlsPlayListDownloader()
52 {
53     MEDIA_LOG_I("~HlsPlayListDownloader in");
54     if (updateTask_ != nullptr) {
55         updateTask_->Stop();
56     }
57     if (downloader_ != nullptr) {
58         downloader_->Stop(false);
59     }
60     MEDIA_LOG_I("~HlsPlayListDownloader out");
61 }
62 
UpdateManifest()63 void HlsPlayListDownloader::UpdateManifest()
64 {
65     if (currentVariant_ && currentVariant_->m3u8_ && !currentVariant_->m3u8_->uri_.empty()) {
66         isNotifyPlayListFinished_ = false;
67         DoOpen(currentVariant_->m3u8_->uri_);
68     } else {
69         MEDIA_LOG_E("UpdateManifest currentVariant_ not ready.");
70     }
71 }
72 
SetPlayListCallback(PlayListChangeCallback * callback)73 void HlsPlayListDownloader::SetPlayListCallback(PlayListChangeCallback* callback)
74 {
75     callback_ = callback;
76 }
77 
IsParseAndNotifyFinished()78 bool HlsPlayListDownloader::IsParseAndNotifyFinished()
79 {
80     return isParseFinished_ && isNotifyPlayListFinished_;
81 }
82 
IsParseFinished()83 bool HlsPlayListDownloader::IsParseFinished()
84 {
85     return isParseFinished_;
86 }
87 
GetDuration() const88 int64_t HlsPlayListDownloader::GetDuration() const
89 {
90     if (!master_) {
91         return 0;
92     }
93     return master_->bLive_ ? -1 : ((int64_t)(master_->duration_ * HST_SECOND) / HST_NSECOND); // -1
94 }
95 
GetSeekable() const96 Seekable HlsPlayListDownloader::GetSeekable() const
97 {
98     // need wait master_ not null
99     size_t times = 0;
100     while (times < RETRY_TIMES && !isInterruptNeeded_) {
101         if (master_ && master_->isSimple_ && isParseFinished_) {
102             break;
103         }
104         OSAL::SleepFor(SLEEP_TIME); // 1 ms
105         times++;
106     }
107     if (times >= RETRY_TIMES || isInterruptNeeded_) {
108         return Seekable::INVALID;
109     }
110     return master_->bLive_ ? Seekable::UNSEEKABLE : Seekable::SEEKABLE;
111 }
112 
NotifyListChange()113 void HlsPlayListDownloader::NotifyListChange()
114 {
115     if (currentVariant_ == nullptr || callback_ == nullptr) {
116         return;
117     }
118     if (currentVariant_->m3u8_ == nullptr) {
119         return;
120     }
121     auto files = currentVariant_->m3u8_->files_;
122     auto playList = std::vector<PlayInfo>();
123     if (currentVariant_->m3u8_->isDecryptAble_) {
124         while (!currentVariant_->m3u8_->isDecryptKeyReady_) {
125             Task::SleepInTask(10); // sleep 10ms
126         }
127         callback_->OnSourceKeyChange(currentVariant_->m3u8_->key_, currentVariant_->m3u8_->keyLen_,
128             currentVariant_->m3u8_->iv_);
129     } else {
130         MEDIA_LOG_E("Decrypkey is not needed.");
131         if (master_ != nullptr) {
132             callback_->OnSourceKeyChange(master_->key_, master_->keyLen_, master_->iv_);
133         } else {
134             callback_->OnSourceKeyChange(nullptr, 0, nullptr);
135         }
136     }
137     playList.reserve(files.size());
138     for (const auto &file: files) {
139         PlayInfo palyInfo;
140         palyInfo.url_ = file->uri_;
141         palyInfo.duration_ = file->duration_;
142         playList.push_back(palyInfo);
143     }
144     if (!currentVariant_->m3u8_->localDrmInfos_.empty()) {
145         callback_->OnDrmInfoChanged(currentVariant_->m3u8_->localDrmInfos_);
146     }
147     callback_->OnPlayListChanged(playList);
148     if (isParseFinished_) {
149         isNotifyPlayListFinished_ = true;
150         if (master_->bLive_ && !updateTask_->IsTaskRunning() && !isLiveUpdateTaskStarted_) {
151             isLiveUpdateTaskStarted_ = true;
152             updateTask_->Start();
153         }
154     }
155 }
156 
ParseManifest(const std::string & location,bool isPreParse)157 void HlsPlayListDownloader::ParseManifest(const std::string& location, bool isPreParse)
158 {
159     if (!location.empty()) {
160         url_ = location;
161     }
162     if (!master_) {
163         master_ = std::make_shared<M3U8MasterPlaylist>(playList_, url_, initResolution_, httpHeader_);
164         currentVariant_ = master_->defaultVariant_;
165         if (currentVariant_ && currentVariant_->m3u8_) {
166             currentVariant_->m3u8_->httpHeader_ = httpHeader_;
167         }
168         if (!master_->isSimple_) {
169             UpdateManifest();
170         } else {
171             // need notify , avoid delay 5s
172             isParseFinished_ = isPreParse ? false : true;
173             NotifyListChange();
174         }
175     } else {
176         UpdateMasterAndNotifyList(isPreParse);
177     }
178     if (!master_->isParseSuccess_ && eventCallback_ != nullptr) {
179         MEDIA_LOG_E("ParseManifest parse failed.");
180         eventCallback_->OnEvent({PluginEventType::CLIENT_ERROR,
181                                 {NetworkClientErrorCode::ERROR_TIME_OUT}, "parse m3u8"});
182     }
183 }
184 
UpdateMasterAndNotifyList(bool isPreParse)185 void HlsPlayListDownloader::UpdateMasterAndNotifyList(bool isPreParse)
186 {
187     bool ret = false;
188     if (!master_->isSimple_) {
189         currentVariant_ = master_->defaultVariant_;
190         if (currentVariant_ && currentVariant_->m3u8_) {
191             ret = currentVariant_->m3u8_->Update(playList_, true);
192         }
193     }
194     if (currentVariant_ && currentVariant_->m3u8_) {
195         currentVariant_->m3u8_->httpHeader_ = httpHeader_;
196     }
197     if (master_->isSimple_ && currentVariant_ && currentVariant_->m3u8_) {
198         ret = currentVariant_->m3u8_->Update(playList_, isParseFinished_);
199         master_->isParseSuccess_ = ret;
200     }
201     if (ret) {
202         UpdateMasterInfo(isPreParse);
203         if (!master_->isSimple_) {
204             master_->isSimple_ = true;
205         }
206         NotifyListChange();
207     }
208 }
209 
UpdateMasterInfo(bool isPreParse)210 void HlsPlayListDownloader::UpdateMasterInfo(bool isPreParse)
211 {
212     master_->bLive_ = currentVariant_->m3u8_->IsLive();
213     master_->duration_ = currentVariant_->m3u8_->GetDuration();
214     master_->segmentOffsets_ = currentVariant_->m3u8_->segmentOffsets_;
215     master_->hasDiscontinuity_ = currentVariant_->m3u8_->hasDiscontinuity_;
216     isParseFinished_ = isPreParse ? false : true;
217 }
218 
PreParseManifest(const std::string & location)219 void HlsPlayListDownloader::PreParseManifest(const std::string& location)
220 {
221     if (master_ && master_->isSimple_) {
222         return;
223     }
224     if (playList_.find(M3U8_START_TAG) == std::string::npos ||
225         playList_.find(M3U8_END_TAG) != std::string::npos) {
226         return;
227     }
228     int tsNum = 0;
229     int tsIndex = 0;
230     int firstTsTagIndex = 0;
231     int lastTsTagIndex = 0;
232     std::string tsTag = M3U8_TS_TAG;
233     int tsTagSize = static_cast<int>(tsTag.size());
234     while ((tsIndex = static_cast<int>(playList_.find(tsTag, tsIndex))) <
235             static_cast<int>(playList_.length()) && tsIndex != -1) { // -1
236         if (tsNum == 0) {
237             firstTsTagIndex = tsIndex;
238         }
239         tsNum++;
240         lastTsTagIndex = tsIndex;
241         if (tsNum >= MIN_PRE_PARSE_NUM) {
242             break;
243         }
244         tsIndex += tsTagSize;
245     }
246     if (tsNum < MIN_PRE_PARSE_NUM) {
247         return;
248     }
249     std::string backUpPlayList = playList_;
250     playList_ = playList_.substr(0, lastTsTagIndex).append(tsTag);
251     isParseFinished_ = false;
252     ParseManifest(location, true);
253     playList_ = backUpPlayList.erase(firstTsTagIndex, lastTsTagIndex - firstTsTagIndex);
254 }
255 
SelectBitRate(uint32_t bitRate)256 void HlsPlayListDownloader::SelectBitRate(uint32_t bitRate)
257 {
258     if (newVariant_ == nullptr) {
259         return;
260     }
261     currentVariant_ = newVariant_;
262     MEDIA_LOG_I("SelectBitRate currentVariant_ " PUBLIC_LOG_U64, currentVariant_->bandWidth_);
263 }
264 
IsBitrateSame(uint32_t bitRate)265 bool HlsPlayListDownloader::IsBitrateSame(uint32_t bitRate)
266 {
267     uint32_t maxGap = 0;
268     bool isFirstSelect = true;
269     for (const auto &item : master_->variants_) {
270         uint32_t tempGap = (item->bandWidth_ > bitRate) ? (item->bandWidth_ - bitRate) : (bitRate - item->bandWidth_);
271         if (isFirstSelect || (tempGap < maxGap)) {
272             isFirstSelect = false;
273             maxGap = tempGap;
274             newVariant_ = item;
275         }
276     }
277     if (currentVariant_ != nullptr && newVariant_->bandWidth_ == currentVariant_->bandWidth_) {
278         return true;
279     }
280     return false;
281 }
282 
GetBitRates()283 std::vector<uint32_t> HlsPlayListDownloader::GetBitRates()
284 {
285     std::vector<uint32_t> bitRates;
286     for (const auto &item : master_->variants_) {
287         if (item->bandWidth_) {
288             bitRates.push_back(item->bandWidth_);
289         }
290     }
291     return bitRates;
292 }
293 
GetCurBitrate()294 uint32_t HlsPlayListDownloader::GetCurBitrate()
295 {
296     if (currentVariant_==nullptr) {
297         return 0;
298     }
299     return currentVariant_->bandWidth_;
300 }
301 
GetCurrentBitRate()302 uint64_t HlsPlayListDownloader::GetCurrentBitRate()
303 {
304     if (currentVariant_==nullptr) {
305         return 0;
306     }
307     MEDIA_LOG_I("HlsPlayListDownloader currentBitrate: " PUBLIC_LOG_D64, currentVariant_->bandWidth_);
308     return currentVariant_->bandWidth_;
309 }
310 
GetVedioWidth()311 int HlsPlayListDownloader::GetVedioWidth()
312 {
313     if (currentVariant_==nullptr) {
314         return 0;
315     }
316     MEDIA_LOG_I("HlsPlayListDownloader currentWidth: " PUBLIC_LOG_D64, currentVariant_->bandWidth_);
317     return currentVariant_->width_;
318 }
319 
GetVedioHeight()320 int HlsPlayListDownloader::GetVedioHeight()
321 {
322     if (currentVariant_==nullptr) {
323         return 0;
324     }
325     MEDIA_LOG_I("HlsPlayListDownloader currentHeight: " PUBLIC_LOG_D64, currentVariant_->bandWidth_);
326     return currentVariant_->height_;
327 }
328 
IsLive() const329 bool HlsPlayListDownloader::IsLive() const
330 {
331     MEDIA_LOG_I("HlsPlayListDownloader IsLive enter.");
332     if (!master_) {
333         return false;
334     }
335     return master_->bLive_;
336 }
337 
GetUrl()338 std::string HlsPlayListDownloader::GetUrl()
339 {
340     return url_;
341 }
342 
GetMaster()343 std::shared_ptr<M3U8MasterPlaylist> HlsPlayListDownloader::GetMaster()
344 {
345     return master_;
346 }
347 
GetCurrentVariant()348 std::shared_ptr<M3U8VariantStream> HlsPlayListDownloader::GetCurrentVariant()
349 {
350     return currentVariant_;
351 }
352 
GetNewVariant()353 std::shared_ptr<M3U8VariantStream> HlsPlayListDownloader::GetNewVariant()
354 {
355     return newVariant_;
356 }
357 
SetMimeType(const std::string & mimeType)358 void HlsPlayListDownloader::SetMimeType(const std::string& mimeType)
359 {
360     mimeType_ = mimeType;
361 }
362 
GetSegmentOffset(uint32_t tsIndex)363 size_t HlsPlayListDownloader::GetSegmentOffset(uint32_t tsIndex)
364 {
365     if (master_ && master_->segmentOffsets_.size() > tsIndex) {
366         return master_->segmentOffsets_[tsIndex];
367     }
368     return 0;
369 }
370 
GetHLSDiscontinuity()371 bool HlsPlayListDownloader::GetHLSDiscontinuity()
372 {
373     if (master_) {
374         return master_->hasDiscontinuity_;
375     }
376     return false;
377 }
378 
SetInitResolution(uint32_t width,uint32_t height)379 void HlsPlayListDownloader::SetInitResolution(uint32_t width, uint32_t height)
380 {
381     MEDIA_LOG_I("SetInitResolution, width:" PUBLIC_LOG_U32 ", height:" PUBLIC_LOG_U32, width, height);
382     if (width > 0 && height > 0) {
383         initResolution_ = width * height;
384     }
385 }
386 }
387 }
388 }
389 }