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> ¤tStream)
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