• 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 "foundation/log.h"
18 #include "m3u8.h"
19 
20 #include <utility>
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 }
53 
Update(std::string & playList)54 bool M3U8::Update(std::string& playList)
55 {
56     if (playList_ == playList) {
57         MEDIA_LOG_I("playlist does not change ");
58         return true;
59     }
60     if (!StrHasPrefix(playList, "#EXTM3U")) {
61         MEDIA_LOG_I("playlist doesn't start with #EXTM3U " PUBLIC_LOG_S, playList.c_str());
62         return false;
63     }
64     if (playList.find("\n#EXT-X-STREAM-INF:") != std::string::npos) {
65         MEDIA_LOG_I("Not a media playlist, but a master playlist! " PUBLIC_LOG_S, playList.c_str());
66         return false;
67     }
68     files_.clear();
69     MEDIA_LOG_I("media playlist " PUBLIC_LOG_S, playList.c_str());
70     auto tags = ParseEntries(playList);
71     UpdateFromTags(tags);
72     tags.clear();
73     playList_ = playList;
74     return true;
75 }
76 
UpdateFromTags(std::list<std::shared_ptr<Tag>> & tags)77 void M3U8::UpdateFromTags(std::list<std::shared_ptr<Tag>>& tags)
78 {
79     std::string uri;
80     std::string title;
81     double duration = 0;
82     bool discontinuity = false;
83     auto bVod = !tags.empty() && tags.back()->GetType() == HlsTag::EXTXENDLIST;
84     bLive_ = !bVod;
85     for (auto& tag : tags) {
86         switch (tag->GetType()) {
87             case HlsTag::EXTXPLAYLISTTYPE:
88                 bLive_ = !bVod && (std::static_pointer_cast<SingleValueTag>(tag)->GetValue().QuotedString() != "VOD");
89                 break;
90             case HlsTag::EXTXTARGETDURATION:
91                 targetDuration_ = std::static_pointer_cast<SingleValueTag>(tag)->GetValue().FloatingPoint();
92                 break;
93             case HlsTag::EXTXMEDIASEQUENCE:
94                 sequence_ = std::static_pointer_cast<SingleValueTag>(tag)->GetValue().Decimal();
95                 break;
96             case HlsTag::EXTXDISCONTINUITYSEQUENCE: {
97                 discontSequence_ =  std::static_pointer_cast<SingleValueTag>(tag)->GetValue().Decimal();
98                 discontinuity = true;
99                 break;
100             }
101             case HlsTag::EXTINF:
102                 GetExtInf(tag, duration, title);
103                 break;
104             case HlsTag::URI:
105                 uri = UriJoin(uri_, std::static_pointer_cast<SingleValueTag>(tag)->GetValue().QuotedString());
106                 break;
107             case HlsTag::EXTXBYTERANGE:
108                 MEDIA_LOG_I("need to parse EXTXBYTERANGE");
109                 break;
110             case HlsTag::EXTXDISCONTINUITY: {
111                 discontSequence_++;
112                 discontinuity = true;
113                 break;
114             }
115             case HlsTag::EXTXKEY:
116                 MEDIA_LOG_I("need to parse EXTXKEY");
117                 break;
118             case HlsTag::EXTXMAP:
119                 MEDIA_LOG_I("need to parse EXTXMAP");
120                 break;
121             default:
122                 break;
123         }
124         if (!uri.empty()) {
125             files_.emplace_back(std::make_shared<M3U8Fragment>(uri, title, duration, sequence_++, discontinuity));
126             uri = "", title = "", duration = 0, discontinuity = false;
127         }
128     }
129 }
130 
GetExtInf(const std::shared_ptr<Tag> & tag,double & duration,std::string & title) const131 void M3U8::GetExtInf(const std::shared_ptr<Tag>& tag, double& duration, std::string& title) const
132 {
133     auto item = std::static_pointer_cast<ValuesListTag>(tag);
134     duration =  item ->GetAttributeByName("DURATION")->FloatingPoint();
135     title = item ->GetAttributeByName("TITLE")->QuotedString();
136 }
137 
GetDuration() const138 double M3U8::GetDuration() const
139 {
140     double duration = 0;
141     for (auto file : files_) {
142         duration += file->duration_;
143     }
144     return duration;
145 }
146 
IsLive() const147 bool M3U8::IsLive() const
148 {
149     return bLive_;
150 }
151 
M3U8VariantStream(std::string name,std::string uri,std::shared_ptr<M3U8> m3u8)152 M3U8VariantStream::M3U8VariantStream(std::string name, std::string uri, std::shared_ptr<M3U8> m3u8)
153     : name_(std::move(name)), uri_(std::move(uri)), m3u8_(std::move(m3u8))
154 {
155 }
156 
M3U8MasterPlaylist(std::string & playList,const std::string & uri)157 M3U8MasterPlaylist::M3U8MasterPlaylist(std::string& playList, const std::string& uri)
158 {
159     playList_ = playList;
160     uri_ = uri;
161     if (!StrHasPrefix(playList_, "#EXTM3U")) {
162         MEDIA_LOG_I("playlist doesn't start with #EXTM3U " PUBLIC_LOG_S, uri.c_str());
163     }
164     if (playList_.find("\n#EXTINF:") != std::string::npos) {
165         UpdateMediaPlaylist();
166     } else {
167         UpdateMasterPlaylist();
168     }
169 }
170 
UpdateMediaPlaylist()171 void M3U8MasterPlaylist::UpdateMediaPlaylist()
172 {
173     MEDIA_LOG_I("This is a simple media playlist, not a master playlist " PUBLIC_LOG_S, uri_.c_str());
174     isSimple_ = true;
175     auto m3u8 = std::make_shared<M3U8>(uri_, "");
176     auto stream = std::make_shared<M3U8VariantStream>(uri_, uri_, m3u8);
177     variants_.emplace_back(stream);
178     defaultVariant_ = stream;
179     m3u8->Update(playList_);
180     duration_ = m3u8->GetDuration();
181     bLive_ = m3u8->IsLive();
182     MEDIA_LOG_D("UpdateMediaPlaylist called, duration_ = " PUBLIC_LOG_F, duration_);
183 }
184 
UpdateMasterPlaylist()185 void M3U8MasterPlaylist::UpdateMasterPlaylist()
186 {
187     MEDIA_LOG_I("master playlist " PUBLIC_LOG_S, playList_.c_str());
188     auto tags = ParseEntries(playList_);
189     for (auto& tag : tags) {
190         switch (tag->GetType()) {
191             case HlsTag::EXTXSTREAMINF:
192             case HlsTag::EXTXIFRAMESTREAMINF: {
193                 auto item = std::static_pointer_cast<AttributesTag>(tag);
194                 auto name = item->GetAttributeByName("URI")->QuotedString();
195                 auto uri = UriJoin(uri_, name);
196                 auto stream = std::make_shared<M3U8VariantStream>(name, uri, std::make_shared<M3U8>(uri, name));
197                 if (tag->GetType() == HlsTag::EXTXIFRAMESTREAMINF) {
198                     stream->iframe_ = true;
199                 }
200                 stream->bandWidth_ = item->GetAttributeByName("BANDWIDTH")->Decimal();
201                 auto resolution = item->GetAttributeByName("RESOLUTION");
202                 if (resolution) {
203                     stream->width_ = resolution->GetResolution().first;
204                     stream->height_ = resolution->GetResolution().second;
205                 }
206                 variants_.emplace_back(stream);
207                 if (defaultVariant_ == nullptr) {
208                     defaultVariant_ = stream;
209                 }
210                 break;
211             }
212             default:
213                 break;
214         }
215     }
216     tags.clear();
217 }
218 }
219 }
220 }
221 }