• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2022 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 "foundation/log.h"
20 #include "m3u8.h"
21 
22 namespace OHOS {
23 namespace Media {
24 namespace Plugin {
25 namespace HttpPlugin {
26 namespace {
StrHasPrefix(std::string & str,const std::string & prefix)27 bool StrHasPrefix(std::string& str, const std::string& prefix)
28 {
29     return str.find(prefix) == 0;
30 }
31 
UriJoin(std::string & baseUrl,const std::string & uri)32 std::string UriJoin(std::string& baseUrl, const std::string& uri)
33 {
34     if ((uri.find("http://") != std::string::npos) || (uri.find("https://") != std::string::npos)) {
35         return uri;
36     } else if (uri.find("//") == 0) { // start with "//"
37         return baseUrl.substr(0, baseUrl.find('/')) + uri;
38     } else {
39         std::string::size_type pos = baseUrl.rfind('/');
40         return baseUrl.substr(0, pos + 1) + uri;
41     }
42 }
43 }
44 
M3U8Fragment(std::string uri,std::string title,double duration,int sequence,bool discont)45 M3U8Fragment::M3U8Fragment(std::string uri, std::string title, double duration, int sequence, bool discont)
46     : uri_(std::move(uri)), title_(std::move(title)), duration_(duration), sequence_(sequence), discont_(discont)
47 {
48 }
49 
M3U8(std::string uri,std::string name)50 M3U8::M3U8(std::string uri, std::string name) : uri_(std::move(uri)), name_(std::move(name))
51 {
52     InitTagUpdatersMap();
53 }
54 
Update(std::string & playList)55 bool M3U8::Update(std::string& playList)
56 {
57     if (playList_ == playList) {
58         MEDIA_LOG_I("playlist does not change ");
59         return true;
60     }
61     if (!StrHasPrefix(playList, "#EXTM3U")) {
62         MEDIA_LOG_I("playlist doesn't start with #EXTM3U " PUBLIC_LOG_S, playList.c_str());
63         return false;
64     }
65     if (playList.find("\n#EXT-X-STREAM-INF:") != std::string::npos) {
66         MEDIA_LOG_I("Not a media playlist, but a master playlist! " PUBLIC_LOG_S, playList.c_str());
67         return false;
68     }
69     files_.clear();
70     MEDIA_LOG_I("media playlist " PUBLIC_LOG_S, playList.c_str());
71     auto tags = ParseEntries(playList);
72     UpdateFromTags(tags);
73     tags.clear();
74     playList_ = playList;
75     return true;
76 }
77 
InitTagUpdatersMap()78 void M3U8::InitTagUpdatersMap()
79 {
80     tagUpdatersMap_[HlsTag::EXTXPLAYLISTTYPE] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
81         bLive_ = !info.bVod && (std::static_pointer_cast<SingleValueTag>(tag)->GetValue().QuotedString() != "VOD");
82     };
83 
84     tagUpdatersMap_[HlsTag::EXTXTARGETDURATION] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
85         std::ignore = info;
86         targetDuration_ = std::static_pointer_cast<SingleValueTag>(tag)->GetValue().FloatingPoint();
87     };
88 
89     tagUpdatersMap_[HlsTag::EXTXMEDIASEQUENCE] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
90         std::ignore = info;
91         sequence_ = std::static_pointer_cast<SingleValueTag>(tag)->GetValue().Decimal();
92     };
93 
94     tagUpdatersMap_[HlsTag::EXTXDISCONTINUITYSEQUENCE] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
95         discontSequence_ = static_cast<int>(std::static_pointer_cast<SingleValueTag>(tag)->GetValue().Decimal());
96         info.discontinuity = true;
97     };
98 
99     tagUpdatersMap_[HlsTag::EXTINF] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
100         GetExtInf(tag, info.duration, info.title);
101     };
102 
103     tagUpdatersMap_[HlsTag::URI] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
104         info.uri = UriJoin(uri_, std::static_pointer_cast<SingleValueTag>(tag)->GetValue().QuotedString());
105     };
106 
107     tagUpdatersMap_[HlsTag::EXTXBYTERANGE] = [](std::shared_ptr<Tag> &tag, M3U8Info &info) {
108         std::ignore = tag;
109         std::ignore = info;
110         MEDIA_LOG_I("need to parse EXTXBYTERANGE");
111     };
112 
113     tagUpdatersMap_[HlsTag::EXTXDISCONTINUITY] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
114         std::ignore = tag;
115         discontSequence_++;
116         info.discontinuity = true;
117     };
118 
119     tagUpdatersMap_[HlsTag::EXTXKEY] = [](std::shared_ptr<Tag> &tag, M3U8Info &info) {
120         std::ignore = tag;
121         std::ignore = info;
122         MEDIA_LOG_I("need to parse EXTXKEY");
123     };
124 
125     tagUpdatersMap_[HlsTag::EXTXMAP] = [](std::shared_ptr<Tag> &tag, M3U8Info &info) {
126         std::ignore = tag;
127         std::ignore = info;
128         MEDIA_LOG_I("need to parse EXTXMAP");
129     };
130 }
131 
UpdateFromTags(std::list<std::shared_ptr<Tag>> & tags)132 void M3U8::UpdateFromTags(std::list<std::shared_ptr<Tag>>& tags)
133 {
134     M3U8Info info;
135     info.bVod = !tags.empty() && tags.back()->GetType() == HlsTag::EXTXENDLIST;
136     bLive_ = !info.bVod;
137     for (auto& tag : tags) {
138         HlsTag hlsTag = tag->GetType();
139         auto iter = tagUpdatersMap_.find(hlsTag);
140         if (iter != tagUpdatersMap_.end()) {
141             auto updater = iter->second;
142             updater(tag, info);
143         }
144 
145         if (!info.uri.empty()) {
146             files_.emplace_back(std::make_shared<M3U8Fragment>(info.uri, info.title, info.duration, sequence_++,
147                                                                info.discontinuity));
148             info.uri = "", info.title = "", info.duration = 0, info.discontinuity = false;
149         }
150     }
151 }
152 
GetExtInf(const std::shared_ptr<Tag> & tag,double & duration,std::string & title) const153 void M3U8::GetExtInf(const std::shared_ptr<Tag>& tag, double& duration, std::string& title) const
154 {
155     auto item = std::static_pointer_cast<ValuesListTag>(tag);
156     duration =  item ->GetAttributeByName("DURATION")->FloatingPoint();
157     title = item ->GetAttributeByName("TITLE")->QuotedString();
158 }
159 
GetDuration() const160 double M3U8::GetDuration() const
161 {
162     double duration = 0;
163     for (auto file : files_) {
164         duration += file->duration_;
165     }
166     return duration;
167 }
168 
IsLive() const169 bool M3U8::IsLive() const
170 {
171     return bLive_;
172 }
173 
M3U8VariantStream(std::string name,std::string uri,std::shared_ptr<M3U8> m3u8)174 M3U8VariantStream::M3U8VariantStream(std::string name, std::string uri, std::shared_ptr<M3U8> m3u8)
175     : name_(std::move(name)), uri_(std::move(uri)), m3u8_(std::move(m3u8))
176 {
177 }
178 
M3U8MasterPlaylist(std::string & playList,const std::string & uri)179 M3U8MasterPlaylist::M3U8MasterPlaylist(std::string& playList, const std::string& uri) : uri_(uri),
180                                                                                         playList_(playList)
181 {
182     if (!StrHasPrefix(playList_, "#EXTM3U")) {
183         MEDIA_LOG_I("playlist doesn't start with #EXTM3U " PUBLIC_LOG_S, uri.c_str());
184     }
185     if (playList_.find("\n#EXTINF:") != std::string::npos) {
186         UpdateMediaPlaylist();
187     } else {
188         UpdateMasterPlaylist();
189     }
190 }
191 
UpdateMediaPlaylist()192 void M3U8MasterPlaylist::UpdateMediaPlaylist()
193 {
194     MEDIA_LOG_I("This is a simple media playlist, not a master playlist " PUBLIC_LOG_S, uri_.c_str());
195     isSimple_ = true;
196     auto m3u8 = std::make_shared<M3U8>(uri_, "");
197     auto stream = std::make_shared<M3U8VariantStream>(uri_, uri_, m3u8);
198     variants_.emplace_back(stream);
199     defaultVariant_ = stream;
200     m3u8->Update(playList_);
201     duration_ = m3u8->GetDuration();
202     bLive_ = m3u8->IsLive();
203     MEDIA_LOG_D("UpdateMediaPlaylist called, duration_ = " PUBLIC_LOG_F, duration_);
204 }
205 
UpdateMasterPlaylist()206 void M3U8MasterPlaylist::UpdateMasterPlaylist()
207 {
208     MEDIA_LOG_I("master playlist " PUBLIC_LOG_S, playList_.c_str());
209     auto tags = ParseEntries(playList_);
210     std::for_each(tags.begin(), tags.end(), [this] (std::shared_ptr<Tag>& tag) {
211         switch (tag->GetType()) {
212             case HlsTag::EXTXSTREAMINF:
213             case HlsTag::EXTXIFRAMESTREAMINF: {
214                 auto item = std::static_pointer_cast<AttributesTag>(tag);
215                 auto uriAttribute = item->GetAttributeByName("URI");
216                 if (uriAttribute) {
217                     auto name = uriAttribute->QuotedString();
218                     auto uri = UriJoin(uri_, name);
219                     auto stream = std::make_shared<M3U8VariantStream>(name, uri, std::make_shared<M3U8>(uri, name));
220                     if (tag->GetType() == HlsTag::EXTXIFRAMESTREAMINF) {
221                         stream->iframe_ = true;
222                     }
223                     auto bandWidthAttribute = item->GetAttributeByName("BANDWIDTH");
224                     if (bandWidthAttribute) {
225                         stream->bandWidth_ = bandWidthAttribute->Decimal();
226                     }
227                     auto resolutionAttribute = item->GetAttributeByName("RESOLUTION");
228                     if (resolutionAttribute) {
229                         stream->width_ = resolutionAttribute->GetResolution().first;
230                         stream->height_ = resolutionAttribute->GetResolution().second;
231                     }
232                     variants_.emplace_back(stream);
233                     if (defaultVariant_ == nullptr) {
234                         defaultVariant_ = stream;
235                     }
236                 }
237                 break;
238             }
239             default:
240                 break;
241         }
242     });
243     tags.clear();
244 }
245 }
246 }
247 }
248 }