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 }