• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_, 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     }
191     if (currentVariant_ && currentVariant_->m3u8_) {
192         currentVariant_->m3u8_->httpHeader_ = httpHeader_;
193         ret = currentVariant_->m3u8_->Update(playList_, true);
194     }
195     if (master_->isSimple_) {
196         master_->isParseSuccess_ = ret;
197     }
198     if (ret) {
199         if (!master_->isSimple_) {
200             master_->isSimple_ = true;
201         }
202         UpdateMasterInfo(isPreParse);
203         NotifyListChange();
204     }
205 }
206 
UpdateMasterInfo(bool isPreParse)207 void HlsPlayListDownloader::UpdateMasterInfo(bool isPreParse)
208 {
209     master_->bLive_ = currentVariant_->m3u8_->IsLive();
210     master_->duration_ = currentVariant_->m3u8_->GetDuration();
211     master_->segmentOffsets_ = currentVariant_->m3u8_->segmentOffsets_;
212     master_->hasDiscontinuity_ = currentVariant_->m3u8_->hasDiscontinuity_;
213     isParseFinished_ = isPreParse ? false : true;
214 }
215 
PreParseManifest(const std::string & location)216 void HlsPlayListDownloader::PreParseManifest(const std::string& location)
217 {
218     if (master_ && master_->isSimple_) {
219         return;
220     }
221     if (playList_.find(M3U8_START_TAG) == std::string::npos ||
222         playList_.find(M3U8_END_TAG) != std::string::npos) {
223         return;
224     }
225     int tsNum = 0;
226     int tsIndex = 0;
227     int firstTsTagIndex = 0;
228     int lastTsTagIndex = 0;
229     std::string tsTag = M3U8_TS_TAG;
230     int tsTagSize = static_cast<int>(tsTag.size());
231     while ((tsIndex = static_cast<int>(playList_.find(tsTag, tsIndex))) <
232             static_cast<int>(playList_.length()) && tsIndex != -1) { // -1
233         if (tsNum == 0) {
234             firstTsTagIndex = tsIndex;
235         }
236         tsNum++;
237         lastTsTagIndex = tsIndex;
238         if (tsNum >= MIN_PRE_PARSE_NUM) {
239             break;
240         }
241         tsIndex += tsTagSize;
242     }
243     if (tsNum < MIN_PRE_PARSE_NUM) {
244         return;
245     }
246     std::string backUpPlayList = playList_;
247     playList_ = playList_.substr(0, lastTsTagIndex).append(tsTag);
248     isParseFinished_ = false;
249     ParseManifest(location, true);
250     playList_ = backUpPlayList.erase(firstTsTagIndex, lastTsTagIndex - firstTsTagIndex);
251 }
252 
SelectBitRate(uint32_t bitRate)253 void HlsPlayListDownloader::SelectBitRate(uint32_t bitRate)
254 {
255     if (newVariant_ == nullptr) {
256         return;
257     }
258     currentVariant_ = newVariant_;
259     MEDIA_LOG_I("SelectBitRate currentVariant_ " PUBLIC_LOG_U64, currentVariant_->bandWidth_);
260 }
261 
IsBitrateSame(uint32_t bitRate)262 bool HlsPlayListDownloader::IsBitrateSame(uint32_t bitRate)
263 {
264     uint32_t maxGap = 0;
265     bool isFirstSelect = true;
266     for (const auto &item : master_->variants_) {
267         uint32_t tempGap = (item->bandWidth_ > bitRate) ? (item->bandWidth_ - bitRate) : (bitRate - item->bandWidth_);
268         if (isFirstSelect || (tempGap < maxGap)) {
269             isFirstSelect = false;
270             maxGap = tempGap;
271             newVariant_ = item;
272         }
273     }
274     if (currentVariant_ != nullptr && newVariant_->bandWidth_ == currentVariant_->bandWidth_) {
275         return true;
276     }
277     return false;
278 }
279 
GetBitRates()280 std::vector<uint32_t> HlsPlayListDownloader::GetBitRates()
281 {
282     std::vector<uint32_t> bitRates;
283     for (const auto &item : master_->variants_) {
284         if (item->bandWidth_) {
285             bitRates.push_back(item->bandWidth_);
286         }
287     }
288     return bitRates;
289 }
290 
GetCurBitrate()291 uint32_t HlsPlayListDownloader::GetCurBitrate()
292 {
293     if (currentVariant_==nullptr) {
294         return 0;
295     }
296     return currentVariant_->bandWidth_;
297 }
298 
GetCurrentBitRate()299 uint64_t HlsPlayListDownloader::GetCurrentBitRate()
300 {
301     if (currentVariant_==nullptr) {
302         return 0;
303     }
304     MEDIA_LOG_I("HlsPlayListDownloader currentBitrate: " PUBLIC_LOG_D64, currentVariant_->bandWidth_);
305     return currentVariant_->bandWidth_;
306 }
307 
GetVedioWidth()308 int HlsPlayListDownloader::GetVedioWidth()
309 {
310     if (currentVariant_==nullptr) {
311         return 0;
312     }
313     MEDIA_LOG_I("HlsPlayListDownloader currentWidth: " PUBLIC_LOG_D64, currentVariant_->bandWidth_);
314     return currentVariant_->width_;
315 }
316 
GetVedioHeight()317 int HlsPlayListDownloader::GetVedioHeight()
318 {
319     if (currentVariant_==nullptr) {
320         return 0;
321     }
322     MEDIA_LOG_I("HlsPlayListDownloader currentHeight: " PUBLIC_LOG_D64, currentVariant_->bandWidth_);
323     return currentVariant_->height_;
324 }
325 
IsLive() const326 bool HlsPlayListDownloader::IsLive() const
327 {
328     MEDIA_LOG_I("HlsPlayListDownloader IsLive enter.");
329     if (!master_) {
330         return false;
331     }
332     return master_->bLive_;
333 }
334 
GetUrl()335 std::string HlsPlayListDownloader::GetUrl()
336 {
337     return url_;
338 }
339 
GetMaster()340 std::shared_ptr<M3U8MasterPlaylist> HlsPlayListDownloader::GetMaster()
341 {
342     return master_;
343 }
344 
GetCurrentVariant()345 std::shared_ptr<M3U8VariantStream> HlsPlayListDownloader::GetCurrentVariant()
346 {
347     return currentVariant_;
348 }
349 
GetNewVariant()350 std::shared_ptr<M3U8VariantStream> HlsPlayListDownloader::GetNewVariant()
351 {
352     return newVariant_;
353 }
354 
SetMimeType(const std::string & mimeType)355 void HlsPlayListDownloader::SetMimeType(const std::string& mimeType)
356 {
357     mimeType_ = mimeType;
358 }
359 
GetSegmentOffset(uint32_t tsIndex)360 size_t HlsPlayListDownloader::GetSegmentOffset(uint32_t tsIndex)
361 {
362     if (master_ && master_->segmentOffsets_.size() > tsIndex) {
363         return master_->segmentOffsets_[tsIndex];
364     }
365     return 0;
366 }
367 
GetHLSDiscontinuity()368 bool HlsPlayListDownloader::GetHLSDiscontinuity()
369 {
370     if (master_) {
371         return master_->hasDiscontinuity_;
372     }
373     return false;
374 }
375 
376 }
377 }
378 }
379 }