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 "M3U8"
16 
17 #include <algorithm>
18 #include <utility>
19 #include <sstream>
20 #include <iomanip>
21 #include "m3u8.h"
22 #include "base64_utils.h"
23 
24 namespace OHOS {
25 namespace Media {
26 namespace Plugins {
27 namespace HttpPlugin {
28 namespace {
29 constexpr uint32_t MAX_LOOP = 16;
30 constexpr uint32_t DRM_UUID_OFFSET = 12;
31 constexpr uint32_t DRM_PSSH_TITLE_LEN = 16;
32 constexpr uint32_t WAIT_KEY_SLEEP_TIME = 10;
33 constexpr uint32_t MAX_DOWNLOAD_TIME = 500;
34 constexpr uint64_t BAND_WIDTH_LIMIT = 3 * 1024 * 1024;
35 constexpr double SECOND_TO_MICROSECOND = 1000.0 * 1000.0;
36 const char DRM_PSSH_TITLE[] = "data:text/plain;";
37 
StrHasPrefix(const std::string & str,const std::string & prefix)38 bool StrHasPrefix(const std::string &str, const std::string &prefix)
39 {
40     return str.find(prefix) == 0;
41 }
42 
UriJoin(std::string & baseUrl,const std::string & uri)43 std::string UriJoin(std::string& baseUrl, const std::string& uri)
44 {
45     if ((uri.find("http://") != std::string::npos) || (uri.find("https://") != std::string::npos)) {
46         return uri;
47     } else if (uri.find("//") == 0) { // start with "//"
48         return baseUrl.substr(0, baseUrl.find('/')) + uri;
49     } else if (uri.find('/') == 0) {
50         auto pos = baseUrl.find('/', strlen("https://"));
51         return baseUrl.substr(0, pos) + uri;
52     } else {
53         std::string::size_type pos = baseUrl.rfind('/');
54         return baseUrl.substr(0, pos + 1) + uri;
55     }
56 }
57 }
58 
M3U8Fragment(const M3U8Fragment & m3u8,const uint8_t * key,const uint8_t * iv)59 M3U8Fragment::M3U8Fragment(const M3U8Fragment& m3u8, const uint8_t *key, const uint8_t *iv)
60     : uri_(std::move(m3u8.uri_)),
61       duration_(m3u8.duration_),
62       sequence_(m3u8.sequence_),
63       discont_(m3u8.discont_)
64 {
65     if (iv == nullptr || key == nullptr) {
66         return;
67     }
68 
69     for (int i = 0; i < static_cast<int>(MAX_LOOP); i++) {
70         iv_[i] = iv[i];
71         key_[i] = key[i];
72     }
73 }
74 
M3U8Fragment(std::string uri,double duration,int sequence,bool discont)75 M3U8Fragment::M3U8Fragment(std::string uri, double duration, int sequence, bool discont)
76     : uri_(std::move(uri)), duration_(duration), sequence_(sequence), discont_(discont)
77 {
78 }
79 
M3U8(std::string uri,std::string name)80 M3U8::M3U8(std::string uri, std::string name) : uri_(std::move(uri)), name_(std::move(name))
81 {
82     InitTagUpdatersMap();
83 }
84 
~M3U8()85 M3U8::~M3U8()
86 {
87     if (downloader_) {
88         downloader_ = nullptr;
89     }
90 }
91 
Update(const std::string & playList,bool isNeedCleanFiles)92 bool M3U8::Update(const std::string& playList, bool isNeedCleanFiles)
93 {
94     if (playList_ == playList) {
95         MEDIA_LOG_I("playlist does not change ");
96         return true;
97     }
98     if (!StrHasPrefix(playList, "#EXTM3U")) {
99         MEDIA_LOG_I("playlist doesn't start with #EXTM3U");
100         return false;
101     }
102     if (playList.find("\n#EXT-X-STREAM-INF:") != std::string::npos) {
103         MEDIA_LOG_I("Not a media playlist, but a master playlist!");
104         return false;
105     }
106     if (isNeedCleanFiles) {
107         files_.clear();
108         segmentOffsets_.clear();
109     }
110     MEDIA_LOG_I("media playlist");
111     std::list<std::shared_ptr<Tag>> tags = ParseEntries(playList);
112     UpdateFromTags(tags);
113     tags.clear();
114     playList_ = playList;
115     return true;
116 }
117 
InitTagUpdaters()118 void M3U8::InitTagUpdaters()
119 {
120     tagUpdatersMap_[HlsTag::EXTXPLAYLISTTYPE] = [this](std::shared_ptr<Tag> &tag, const M3U8Info &info) {
121         isPlayTypeFound_ = true;
122         bLive_ = std::static_pointer_cast<SingleValueTag>(tag)->GetValue().QuotedString() != "VOD";
123     };
124     tagUpdatersMap_[HlsTag::EXTXTARGETDURATION] = [this](std::shared_ptr<Tag> &tag, const M3U8Info &info) {
125         std::ignore = info;
126         targetDuration_ = std::static_pointer_cast<SingleValueTag>(tag)->GetValue().FloatingPoint();
127     };
128     tagUpdatersMap_[HlsTag::EXTXMEDIASEQUENCE] = [this](std::shared_ptr<Tag> &tag, const M3U8Info &info) {
129         std::ignore = info;
130         sequence_ = std::static_pointer_cast<SingleValueTag>(tag)->GetValue().Decimal();
131     };
132     tagUpdatersMap_[HlsTag::EXTXDISCONTINUITYSEQUENCE] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
133         discontSequence_ = static_cast<int>(std::static_pointer_cast<SingleValueTag>(tag)->GetValue().Decimal());
134         info.discontinuity = true;
135     };
136     tagUpdatersMap_[HlsTag::EXTINF] = [this](const std::shared_ptr<Tag> &tag, M3U8Info &info) {
137         GetExtInf(tag, info.duration);
138     };
139     tagUpdatersMap_[HlsTag::URI] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
140         info.uri = UriJoin(uri_, std::static_pointer_cast<SingleValueTag>(tag)->GetValue().QuotedString());
141     };
142     tagUpdatersMap_[HlsTag::EXTXBYTERANGE] = [](const std::shared_ptr<Tag> &tag, const M3U8Info &info) {
143         std::ignore = tag;
144         std::ignore = info;
145         MEDIA_LOG_I("need to parse EXTXBYTERANGE");
146     };
147 }
148 
InitTagUpdatersMap()149 void M3U8::InitTagUpdatersMap()
150 {
151     InitTagUpdaters();
152     tagUpdatersMap_[HlsTag::EXTXDISCONTINUITY] = [this](const std::shared_ptr<Tag> &tag, M3U8Info &info) {
153         std::ignore = tag;
154         discontSequence_++;
155         info.discontinuity = true;
156     };
157     tagUpdatersMap_[HlsTag::EXTXKEY] = [this](std::shared_ptr<Tag> &tag, const M3U8Info &info) {
158         if (!isDecryptAble_ && !isDecryptKeyReady_) {
159             isDecryptAble_ = true;
160             isDecryptKeyReady_ = false;
161             ParseKey(std::static_pointer_cast<AttributesTag>(tag));
162             if ((keyUri_ != nullptr) && (keyUri_->length() > DRM_PSSH_TITLE_LEN) &&
163                 (keyUri_->substr(0, DRM_PSSH_TITLE_LEN) == DRM_PSSH_TITLE)) {
164                 ProcessDrmInfos();
165             } else {
166                 DownloadKey();
167             }
168         }
169     };
170     tagUpdatersMap_[HlsTag::EXTXMAP] = [](const std::shared_ptr<Tag> &tag, const M3U8Info &info) {
171         std::ignore = tag;
172         std::ignore = info;
173         MEDIA_LOG_I("need to parse EXTXMAP");
174     };
175 }
176 
UpdateFromTags(std::list<std::shared_ptr<Tag>> & tags)177 void M3U8::UpdateFromTags(std::list<std::shared_ptr<Tag>>& tags)
178 {
179     M3U8Info info;
180     bLive_ = !info.bVod;
181     size_t segmentTimeOffset = 0;
182     size_t duration = 0;
183     for (const auto &frag : files_) {
184         duration += static_cast<size_t>(frag->duration_ * SECOND_TO_MICROSECOND);
185     }
186     for (auto& tag : tags) {
187         HlsTag hlsTag = tag->GetType();
188         if (hlsTag == HlsTag::EXTXENDLIST && !isPlayTypeFound_) {
189             info.bVod = true;
190             bLive_ = !info.bVod;
191             MEDIA_LOG_I("UpdateFromTags not live.");
192         }
193 
194         if (hlsTag == HlsTag::EXTXDISCONTINUITY) {
195             segmentTimeOffset = duration;
196             hasDiscontinuity_ = true;
197             MEDIA_LOG_I("segmentTimeOffset here is: " PUBLIC_LOG_ZU, segmentTimeOffset);
198             continue;
199         }
200 
201         auto iter = tagUpdatersMap_.find(hlsTag);
202         if (iter != tagUpdatersMap_.end()) {
203             auto updater = iter->second;
204             updater(tag, info);
205         }
206 
207         if (!info.uri.empty()) {
208             if (!isFirstFragmentReady_ && !isDecryptAble_) {
209                 firstFragment_ = info;
210                 isFirstFragmentReady_ = true;
211             }
212             // add key_ and iv_ to M3U8Fragment(file)
213             if (isDecryptAble_) {
214                 auto m3u8 = M3U8Fragment(info.uri, info.duration, sequence_++, info.discontinuity);
215                 auto fragment = std::make_shared<M3U8Fragment>(m3u8, key_, iv_);
216                 AddFile(fragment, duration);
217             } else {
218                 auto fragment = std::make_shared<M3U8Fragment>(info.uri, info.duration, sequence_++,
219                     info.discontinuity);
220                 AddFile(fragment, duration);
221             }
222             duration += static_cast<size_t>(info.duration * SECOND_TO_MICROSECOND);
223             info.uri = "", info.duration = 0, info.discontinuity = false;
224         }
225     }
226     isPlayTypeFound_ = false;
227 }
228 
AddFile(std::shared_ptr<M3U8Fragment> fragment,size_t duration)229 void M3U8::AddFile(std::shared_ptr<M3U8Fragment> fragment, size_t duration)
230 {
231     segmentOffsets_.emplace_back(duration);
232     files_.emplace_back(fragment);
233 }
234 
GetExtInf(const std::shared_ptr<Tag> & tag,double & duration) const235 void M3U8::GetExtInf(const std::shared_ptr<Tag>& tag, double& duration) const
236 {
237     auto item = std::static_pointer_cast<ValuesListTag>(tag);
238     if (item == nullptr) {
239         return;
240     }
241     if (item->GetAttributeByName("DURATION")) {
242         duration = item->GetAttributeByName("DURATION")->FloatingPoint();
243     }
244 }
245 
GetDuration() const246 double M3U8::GetDuration() const
247 {
248     double duration = 0;
249     for (auto file : files_) {
250         duration += file->duration_;
251     }
252 
253     return duration;
254 }
255 
IsLive() const256 bool M3U8::IsLive() const
257 {
258     return bLive_;
259 }
260 
ParseKey(const std::shared_ptr<AttributesTag> & tag)261 void M3U8::ParseKey(const std::shared_ptr<AttributesTag> &tag)
262 {
263     auto methodAttribute = tag->GetAttributeByName("METHOD");
264     if (methodAttribute) {
265         method_ = std::make_shared<std::string>(methodAttribute->QuotedString());
266     }
267     auto uriAttribute = tag->GetAttributeByName("URI");
268     if (uriAttribute) {
269         keyUri_ = std::make_shared<std::string>(uriAttribute->QuotedString());
270     }
271     auto ivAttribute = tag->GetAttributeByName("IV");
272     if (ivAttribute) {
273         std::vector<uint8_t> iv_buff = ivAttribute->HexSequence();
274         uint32_t size = iv_buff.size() > MAX_LOOP ? MAX_LOOP : iv_buff.size();
275         for (uint32_t i = 0; i < size; i++) {
276             iv_[i] = iv_buff[i];
277         }
278     }
279 }
280 
DownloadKey()281 void M3U8::DownloadKey()
282 {
283     if (keyUri_ == nullptr) {
284         return;
285     }
286 
287     downloader_ = std::make_shared<Downloader>("hlsSourceKey");
288     dataSave_ = [this](uint8_t *&&data, uint32_t &&len) {
289         return SaveData(std::forward<decltype(data)>(data), std::forward<decltype(len)>(len));
290     };
291     // this is default callback
292     statusCallback_ = [this](DownloadStatus &&status, std::shared_ptr<Downloader> d,
293         std::shared_ptr<DownloadRequest> &request) {
294         OnDownloadStatus(std::forward<decltype(status)>(status), downloader_, std::forward<decltype(request)>(request));
295     };
296     auto realStatusCallback = [this](DownloadStatus &&status, std::shared_ptr<Downloader> &downloader,
297         std::shared_ptr<DownloadRequest> &request) {
298         statusCallback_(status, downloader_, std::forward<decltype(request)>(request));
299     };
300     std::string realKeyUrl = UriJoin(uri_, *keyUri_);
301     RequestInfo requestInfo;
302     requestInfo.url = realKeyUrl;
303     requestInfo.httpHeader = httpHeader_;
304     // TO DO: If the fragment file is too large, should not requestWholeFile.
305     downloadRequest_ = std::make_shared<DownloadRequest>(dataSave_, realStatusCallback, requestInfo, true);
306     downloader_->Download(downloadRequest_, -1);
307     downloader_->Start();
308 }
309 
SaveData(uint8_t * data,uint32_t len)310 bool M3U8::SaveData(uint8_t *data, uint32_t len)
311 {
312     // 16 is a magic number
313     if (len <= MAX_LOOP && len != 0) {
314         NZERO_RETURN_V(memcpy_s(key_, MAX_LOOP, data, len), false);
315         keyLen_ = len;
316         isDecryptKeyReady_ = true;
317         MEDIA_LOG_I("DownloadKey hlsSourceKey end.\n");
318         return true;
319     }
320     return false;
321 }
322 
OnDownloadStatus(DownloadStatus status,std::shared_ptr<Downloader> &,std::shared_ptr<DownloadRequest> & request)323 void M3U8::OnDownloadStatus(DownloadStatus status, std::shared_ptr<Downloader> &,
324     std::shared_ptr<DownloadRequest> &request)
325 {
326     // This should not be called normally
327     if (request->GetClientError() != 0 || request->GetServerError() != 0) {
328         MEDIA_LOG_E("OnDownloadStatus " PUBLIC_LOG_D32, status);
329     }
330 }
331 
332 /**
333  * @brief Parse the data in the URI and obtain pssh data and uuid from it.
334  * @param drmInfo Map data of uuid and pssh.
335  * @return bool true: success false: invalid parameter
336  */
SetDrmInfo(std::multimap<std::string,std::vector<uint8_t>> & drmInfo)337 bool M3U8::SetDrmInfo(std::multimap<std::string, std::vector<uint8_t>>& drmInfo)
338 {
339     std::string::size_type n;
340     std::string psshString;
341     uint8_t pssh[2048]; // 2048: pssh len
342     uint32_t psshSize = 2048; // 2048: pssh len
343     if (keyUri_ == nullptr) {
344         return false;
345     }
346     n = keyUri_->find("base64,");
347     if (n != std::string::npos) {
348         psshString = keyUri_->substr(n + 7); // 7: len of "base64,"
349     }
350     if (psshString.length() == 0) {
351         return false;
352     }
353     bool ret = Base64Utils::Base64Decode(reinterpret_cast<const uint8_t *>(psshString.c_str()),
354         static_cast<uint32_t>(psshString.length()), pssh, &psshSize);
355     if (ret) {
356         uint32_t uuidSize = 16; // 16: uuid len
357         if (psshSize >= DRM_UUID_OFFSET + uuidSize) {
358             uint8_t uuid[16]; // 16: uuid len
359             NZERO_RETURN_V(memcpy_s(uuid, sizeof(uuid), pssh + DRM_UUID_OFFSET, uuidSize), false);
360             std::stringstream ssConverter;
361             std::string uuidString;
362             for (uint32_t i = 0; i < uuidSize; i++) {
363                 ssConverter << std::hex << std::setfill('0') << std::setw(2) << static_cast<int32_t>(uuid[i]); // 2:w
364                 uuidString = ssConverter.str();
365             }
366             drmInfo.insert({ uuidString, std::vector<uint8_t>(pssh, pssh + psshSize) });
367             return true;
368         }
369     }
370     return false;
371 }
372 
373 /**
374  * @brief Store uuid and pssh data.
375  * @param drmInfo Map data of uuid and pssh.
376  */
StoreDrmInfos(const std::multimap<std::string,std::vector<uint8_t>> & drmInfo)377 void M3U8::StoreDrmInfos(const std::multimap<std::string, std::vector<uint8_t>>& drmInfo)
378 {
379     MEDIA_LOG_I("StoreDrmInfos");
380     for (auto &newItem : drmInfo) {
381         auto pos = localDrmInfos_.equal_range(newItem.first);
382         if (pos.first == pos.second && pos.first == localDrmInfos_.end()) {
383             MEDIA_LOG_D("this uuid not exists and update");
384             localDrmInfos_.insert(newItem);
385             continue;
386         }
387         MEDIA_LOG_D("this uuid exists many times");
388         bool isSame = false;
389         for (; pos.first != pos.second; ++pos.first) {
390             if (newItem.second == pos.first->second) {
391                 MEDIA_LOG_D("this uuid exists and same pssh");
392                 isSame = true;
393                 break;
394             }
395         }
396         if (!isSame) {
397             MEDIA_LOG_D("this uuid exists but pssh not same");
398             localDrmInfos_.insert(newItem);
399         }
400     }
401     return;
402 }
403 
404 /**
405  * @brief Process uuid and pssh data.
406  */
ProcessDrmInfos(void)407 void M3U8::ProcessDrmInfos(void)
408 {
409     isDecryptAble_ = false;
410     std::multimap<std::string, std::vector<uint8_t>> drmInfo;
411     bool ret = SetDrmInfo(drmInfo);
412     if (ret) {
413         StoreDrmInfos(drmInfo);
414     }
415 }
416 
M3U8VariantStream(std::string name,std::string uri,std::shared_ptr<M3U8> m3u8)417 M3U8VariantStream::M3U8VariantStream(std::string name, std::string uri, std::shared_ptr<M3U8> m3u8)
418     : name_(std::move(name)), uri_(std::move(uri)), m3u8_(std::move(m3u8))
419 {
420 }
421 
M3U8MasterPlaylist(const std::string & playList,const std::string & uri,uint32_t initResolution,const std::map<std::string,std::string> & httpHeader)422 M3U8MasterPlaylist::M3U8MasterPlaylist(const std::string& playList, const std::string& uri, uint32_t initResolution,
423     const std::map<std::string, std::string>& httpHeader)
424 {
425     playList_ = playList;
426     uri_ = uri;
427     httpHeader_ = httpHeader;
428     initResolution_ = initResolution;
429     if (!StrHasPrefix(playList_, "#EXTM3U")) {
430         MEDIA_LOG_I("playlist doesn't start with #EXTM3U ");
431         isParseSuccess_ = false;
432     }
433     if (playList_.find("\n#EXTINF:") != std::string::npos) {
434         UpdateMediaPlaylist();
435     } else {
436         UpdateMasterPlaylist();
437     }
438 }
439 
UpdateMediaPlaylist()440 void M3U8MasterPlaylist::UpdateMediaPlaylist()
441 {
442     MEDIA_LOG_I("This is a simple media playlist, not a master playlist ");
443     auto m3u8 = std::make_shared<M3U8>(uri_, "");
444     auto stream = std::make_shared<M3U8VariantStream>(uri_, uri_, m3u8);
445     variants_.emplace_back(stream);
446     defaultVariant_ = stream;
447     if (isDecryptAble_) {
448         m3u8->isDecryptAble_ = isDecryptAble_;
449         std::copy(std::begin(key_), std::end(key_), std::begin(m3u8->key_));
450         m3u8->isDecryptKeyReady_ = isDecryptKeyReady_;
451         std::copy(std::begin(iv_), std::end(iv_), std::begin(m3u8->iv_));
452         m3u8->keyLen_ = keyLen_;
453     }
454     m3u8->httpHeader_ = httpHeader_;
455     isParseSuccess_ = m3u8->Update(playList_, false);
456     hasDiscontinuity_ = m3u8->hasDiscontinuity_;
457     segmentOffsets_ = m3u8->segmentOffsets_;
458     duration_ = m3u8->GetDuration();
459     bLive_ = m3u8->IsLive();
460     isSimple_ = true;
461     MEDIA_LOG_D("UpdateMediaPlaylist called, duration_ = " PUBLIC_LOG_F, duration_);
462 }
463 
DownloadSessionKey(std::shared_ptr<Tag> & tag)464 void M3U8MasterPlaylist::DownloadSessionKey(std::shared_ptr<Tag>& tag)
465 {
466     auto m3u8 = std::make_shared<M3U8>(uri_, "");
467     m3u8->isDecryptAble_ = true;
468     m3u8->isDecryptKeyReady_ = false;
469     m3u8->ParseKey(std::static_pointer_cast<AttributesTag>(tag));
470     m3u8->DownloadKey();
471     uint32_t downloadTime = 0;
472     while (!m3u8->isDecryptKeyReady_ && downloadTime < MAX_DOWNLOAD_TIME) {
473         Task::SleepInTask(WAIT_KEY_SLEEP_TIME);
474         downloadTime++;
475     }
476     std::copy(std::begin(m3u8->key_), std::end(m3u8->key_), std::begin(key_));
477     isDecryptKeyReady_ = m3u8->isDecryptKeyReady_;
478     std::copy(std::begin(m3u8->iv_), std::end(m3u8->iv_), std::begin(iv_));
479     keyLen_ = m3u8->keyLen_;
480 }
481 
UpdateMasterPlaylist()482 void M3U8MasterPlaylist::UpdateMasterPlaylist()
483 {
484     MEDIA_LOG_I("master playlist");
485     auto tags = ParseEntries(playList_);
486     std::for_each(tags.begin(), tags.end(), [this] (std::shared_ptr<Tag>& tag) {
487         switch (tag->GetType()) {
488             case HlsTag::EXTXSESSIONKEY:
489                 DownloadSessionKey(tag);
490                 break;
491             case HlsTag::EXTXSTREAMINF:
492             case HlsTag::EXTXIFRAMESTREAMINF: {
493                 auto item = std::static_pointer_cast<AttributesTag>(tag);
494                 auto uriAttribute = item->GetAttributeByName("URI");
495                 if (uriAttribute) {
496                     auto name = uriAttribute->QuotedString();
497                     auto uri = UriJoin(uri_, name);
498                     auto stream = std::make_shared<M3U8VariantStream>(name, uri, std::make_shared<M3U8>(uri, name));
499                     FALSE_RETURN_MSG(stream != nullptr, "UpdateMasterPlaylist memory not enough");
500                     if (tag->GetType() == HlsTag::EXTXIFRAMESTREAMINF) {
501                         stream->iframe_ = true;
502                     }
503                     auto bandWidthAttribute = item->GetAttributeByName("BANDWIDTH");
504                     if (bandWidthAttribute) {
505                         stream->bandWidth_ = bandWidthAttribute->Decimal();
506                     }
507                     auto resolutionAttribute = item->GetAttributeByName("RESOLUTION");
508                     if (resolutionAttribute) {
509                         stream->width_ = resolutionAttribute->GetResolution().first;
510                         stream->height_ = resolutionAttribute->GetResolution().second;
511                     }
512                     variants_.emplace_back(stream);
513                     if (stream->bandWidth_ <= BAND_WIDTH_LIMIT) {
514                         defaultVariant_ = stream; // play last stream
515                     }
516                 }
517                 break;
518             }
519             default:
520                 break;
521         }
522     });
523     if (defaultVariant_ == nullptr) {
524         defaultVariant_ = variants_.front();
525     }
526     tags.clear();
527     ChooseStreamByResolution();
528 }
529 
ChooseStreamByResolution()530 void M3U8MasterPlaylist::ChooseStreamByResolution()
531 {
532     if (initResolution_ == 0) {
533         return;
534     }
535     for (const auto &variant : variants_) {
536         if (variant == nullptr) {
537             continue;
538         }
539         if (IsNearToInitResolution(defaultVariant_, variant)) {
540             defaultVariant_ = variant;
541         }
542     }
543     MEDIA_LOG_I("resolution, width:" PUBLIC_LOG_U32 ", height:" PUBLIC_LOG_U32,
544                 defaultVariant_->width_, defaultVariant_->height_);
545 }
546 
IsNearToInitResolution(const std::shared_ptr<M3U8VariantStream> & choosedStream,const std::shared_ptr<M3U8VariantStream> & currentStream)547 bool M3U8MasterPlaylist::IsNearToInitResolution(const std::shared_ptr<M3U8VariantStream> &choosedStream,
548     const std::shared_ptr<M3U8VariantStream> &currentStream)
549 {
550     if (choosedStream == nullptr || currentStream == nullptr || initResolution_ == 0) {
551         return false;
552     }
553 
554     uint32_t choosedDelta = GetResolutionDelta(choosedStream->width_, choosedStream->height_);
555     uint32_t currentDelta = GetResolutionDelta(currentStream->width_, currentStream->height_);
556     return (currentDelta < choosedDelta)
557            || (currentDelta == choosedDelta && currentStream->bandWidth_ < choosedStream->bandWidth_);
558 }
559 
GetResolutionDelta(uint32_t width,uint32_t height)560 uint32_t M3U8MasterPlaylist::GetResolutionDelta(uint32_t width, uint32_t height)
561 {
562     uint32_t resolution = width * height;
563     if (resolution > initResolution_) {
564         return resolution - initResolution_;
565     } else {
566         return initResolution_ - resolution;
567     }
568 }
569 }
570 }
571 }
572 }
573