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
16 #include "hls_tags.h"
17 #include <cstring>
18 #include <sstream>
19 #include <stack>
20 #include <utility>
21
22 namespace OHOS {
23 namespace Media {
24 namespace Plugin {
25 namespace HttpPlugin {
26 struct {
27 const char* name;
28 HlsTag type;
29 } const g_exttagmapping[] = {
30 {"EXT-X-BYTERANGE", HlsTag::EXTXBYTERANGE},
31 {"EXT-X-DISCONTINUITY", HlsTag::EXTXDISCONTINUITY},
32 {"EXT-X-KEY", HlsTag::EXTXKEY},
33 {"EXT-X-MAP", HlsTag::EXTXMAP},
34 {"EXT-X-VERSION", HlsTag::EXTXVERSION},
35 {"EXT-X-PROGRAM-DATE-TIME", HlsTag::EXTXPROGRAMDATETIME},
36 {"EXT-X-TARGETDURATION", HlsTag::EXTXTARGETDURATION},
37 {"EXT-X-MEDIA-SEQUENCE", HlsTag::EXTXMEDIASEQUENCE},
38 {"EXT-X-DISCONTINUITY-SEQUENCE", HlsTag::EXTXDISCONTINUITYSEQUENCE},
39 {"EXT-X-ENDLIST", HlsTag::EXTXENDLIST},
40 {"EXT-X-PLAYLIST-TYPE", HlsTag::EXTXPLAYLISTTYPE},
41 {"EXT-X-I-FRAMES-ONLY", HlsTag::EXTXIFRAMESONLY},
42 {"EXT-X-MEDIA", HlsTag::EXTXMEDIA},
43 {"EXT-X-START", HlsTag::EXTXSTART},
44 {"EXT-X-STREAM-INF", HlsTag::EXTXSTREAMINF},
45 {"EXT-X-I-FRAME-STREAM-INF", HlsTag::EXTXIFRAMESTREAMINF},
46 {"EXT-X-SESSION-KEY", HlsTag::EXTXSESSIONKEY},
47 {"EXTINF", HlsTag::EXTINF},
48 {"", HlsTag::URI}
49 };
50
Attribute(std::string name,std::string value)51 Attribute::Attribute(std::string name, std::string value)
52 : name_(std::move(name)), value_(std::move(value))
53 {
54 }
55
Decimal() const56 uint64_t Attribute::Decimal() const
57 {
58 std::istringstream is(value_);
59 is.imbue(std::locale("C"));
60 uint64_t ret;
61 is >> ret;
62 return ret;
63 }
64
FloatingPoint() const65 double Attribute::FloatingPoint() const
66 {
67 std::istringstream is(value_);
68 is.imbue(std::locale("C"));
69 double ret;
70 is >> ret;
71 return ret;
72 }
73
HexSequence() const74 std::vector<uint8_t> Attribute::HexSequence() const
75 {
76 std::vector<uint8_t> ret;
77 if (value_.length() > 2 && (value_.substr(0, 2) == "0X" || value_.substr(0, 2) == "0x")) { // 2
78 for (size_t i = 2; i <= (value_.length() - 2); i += 2) { // 2
79 unsigned val;
80 std::stringstream ss(value_.substr(i, 2)); // 2
81 ss.imbue(std::locale("C"));
82 ss >> std::hex >> val;
83 ret.push_back(val);
84 }
85 }
86 return ret;
87 }
88
GetByteRange() const89 std::pair<std::size_t, std::size_t> Attribute::GetByteRange() const
90 {
91 std::size_t length = 0;
92 std::size_t offset = 0;
93 std::istringstream is(value_);
94 is.imbue(std::locale("C"));
95 if (!is.eof()) {
96 is >> length;
97 if (!is.eof()) {
98 char c = static_cast<char>(is.get());
99 if (c == '@' && !is.eof()) {
100 is >> offset;
101 }
102 }
103 }
104 return std::make_pair(offset, length);
105 }
106
GetResolution() const107 std::pair<int, int> Attribute::GetResolution() const
108 {
109 int w = 0;
110 int h = 0;
111 std::istringstream is(value_);
112 is.imbue(std::locale("C"));
113 if (!is.eof()) {
114 is >> w;
115 if (!is.eof()) {
116 char c = is.get();
117 if (c == 'x' && !is.eof()) {
118 is >> h;
119 }
120 }
121 }
122 return std::make_pair(w, h);
123 }
GetName() const124 std::string Attribute::GetName() const
125 {
126 return name_;
127 }
128
UnescapeQuotes() const129 Attribute Attribute::UnescapeQuotes() const
130 {
131 return {name_, QuotedString()};
132 }
133
QuotedString() const134 std::string Attribute::QuotedString() const
135 {
136 if (!value_.empty() && value_.at(0) != '"') {
137 return value_;
138 }
139 if (value_.length() < 2) { // 2
140 return "";
141 }
142 std::istringstream is(value_.substr(1, value_.length() - 2)); // 2
143 std::ostringstream os;
144 char c;
145 while (is.get(c)) {
146 if (c == '\\') {
147 if (!is.get(c)) {
148 break;
149 }
150 }
151 os << c;
152 }
153 return os.str();
154 }
155
Tag(HlsTag type)156 Tag::Tag(HlsTag type)
157 {
158 type_ = type;
159 }
160
GetType() const161 HlsTag Tag::GetType() const
162 {
163 return type_;
164 }
165
SingleValueTag(HlsTag type,const std::string & v)166 SingleValueTag::SingleValueTag(HlsTag type, const std::string& v)
167 : Tag(type), attr_("", v)
168 {
169 }
170
GetValue() const171 const Attribute& SingleValueTag::GetValue() const
172 {
173 return attr_;
174 }
175
AttributesTag(HlsTag type,const std::string & v)176 AttributesTag::AttributesTag(HlsTag type, const std::string& v) : Tag(type)
177 {
178 ParseAttributes(v);
179 }
180
GetAttributeByName(const char * name) const181 std::shared_ptr<Attribute> AttributesTag::GetAttributeByName(const char* name) const
182 {
183 for (auto& attribute :attributes) {
184 if (attribute->GetName() == name) {
185 return attribute;
186 }
187 }
188 return nullptr;
189 }
190
AddAttribute(std::shared_ptr<Attribute> & attr)191 void AttributesTag::AddAttribute(std::shared_ptr<Attribute>& attr)
192 {
193 attributes.push_back(attr);
194 }
195
ParseAttributes(const std::string & field)196 void AttributesTag::ParseAttributes(const std::string& field)
197 {
198 std::istringstream iss(field);
199 std::ostringstream oss;
200 while (!iss.eof()) {
201 std::string attrName = ParseAttributeName(iss, oss);
202 oss.str("");
203 std::string attrValue = ParseAttributeValue(iss, oss);
204 oss.str("");
205 auto attribute = std::make_shared<Attribute>(attrName, attrValue);
206 if (attribute) {
207 attributes.push_back(attribute);
208 }
209 }
210 }
211
ParseAttributeValue(std::istringstream & iss,std::ostringstream & oss)212 std::string AttributesTag::ParseAttributeValue(std::istringstream &iss, std::ostringstream &oss)
213 {
214 bool bQuoted = false;
215 while (!iss.eof()) {
216 char c = static_cast<char>(iss.peek());
217 if (c == '\\' && bQuoted) {
218 iss.get();
219 } else if (c == ',' && !bQuoted) {
220 iss.get();
221 break;
222 } else if (c == '"') {
223 bQuoted = !bQuoted;
224 if (!bQuoted) {
225 oss.put(static_cast<char>(iss.get()));
226 break;
227 }
228 } else if (!bQuoted && (c < '-' || c > 'z')) { /* out of range */
229 iss.get();
230 continue;
231 }
232 if (!iss.eof()) {
233 oss.put(static_cast<char>(iss.get()));
234 }
235 }
236 return oss.str();
237 }
238
ParseAttributeName(std::istringstream & iss,std::ostringstream & oss) const239 std::string AttributesTag::ParseAttributeName(std::istringstream& iss, std::ostringstream& oss) const
240 {
241 while (!iss.eof()) {
242 char c = static_cast<char>(iss.peek());
243 if ((c >= 'A' && c <= 'Z') || c == '-') {
244 oss.put(static_cast<char>(iss.get()));
245 } else if (c == '=') {
246 iss.get();
247 break;
248 } else { /* out of range */
249 iss.get();
250 }
251 }
252 return oss.str();
253 }
254
ValuesListTag(HlsTag type,const std::string & v)255 ValuesListTag::ValuesListTag(HlsTag type, const std::string& v) : AttributesTag(type, v)
256 {
257 ParseValuesAttributes(v);
258 }
259
ParseValuesAttributes(const std::string & field)260 void ValuesListTag::ParseValuesAttributes(const std::string& field)
261 {
262 auto pos = field.find(',');
263 std::shared_ptr<Attribute> attr;
264 if (pos != std::string::npos) {
265 attr = std::make_shared<Attribute>("DURATION", field.substr(0, pos));
266 if (attr) {
267 AddAttribute(attr);
268 }
269 attr = std::make_shared<Attribute>("TITLE", field.substr(pos));
270 if (attr) {
271 AddAttribute(attr);
272 }
273 } else { /* broken EXTINF without mandatory comma */
274 attr = std::make_shared<Attribute>("DURATION", field);
275 if (attr) {
276 AddAttribute(attr);
277 }
278 }
279 }
280
CreateTagByName(const std::string & name,const std::string & value)281 std::shared_ptr<Tag> TagFactory::CreateTagByName(const std::string& name, const std::string& value)
282 {
283 auto size = sizeof(g_exttagmapping) / sizeof(g_exttagmapping[0]);
284 for (int i = 0; i < size; i++) {
285 if (name != g_exttagmapping[i].name) {
286 continue;
287 }
288 switch (g_exttagmapping[i].type) {
289 case HlsTag::EXTXDISCONTINUITY:
290 case HlsTag::EXTXENDLIST:
291 case HlsTag::EXTXIFRAMESONLY:
292 return std::make_shared<Tag>(g_exttagmapping[i].type);
293 case HlsTag::URI:
294 case HlsTag::EXTXVERSION:
295 case HlsTag::EXTXBYTERANGE:
296 case HlsTag::EXTXPROGRAMDATETIME:
297 case HlsTag::EXTXTARGETDURATION:
298 case HlsTag::EXTXMEDIASEQUENCE:
299 case HlsTag::EXTXDISCONTINUITYSEQUENCE:
300 case HlsTag::EXTXPLAYLISTTYPE:
301 return std::make_shared<SingleValueTag>(g_exttagmapping[i].type, value);
302 case HlsTag::EXTINF:
303 return std::make_shared<ValuesListTag>(g_exttagmapping[i].type, value);
304 case HlsTag::EXTXKEY:
305 case HlsTag::EXTXSESSIONKEY:
306 case HlsTag::EXTXMAP:
307 case HlsTag::EXTXMEDIA:
308 case HlsTag::EXTXSTART:
309 case HlsTag::EXTXSTREAMINF:
310 case HlsTag::EXTXIFRAMESTREAMINF:
311 return std::make_shared<AttributesTag>(g_exttagmapping[i].type, value);
312 default:
313 return nullptr;
314 }
315 }
316 return nullptr;
317 }
318
Split(const std::string & s,const char * delim)319 static std::vector<std::string> Split(const std::string& s, const char* delim)
320 {
321 std::vector<std::string> ret;
322 std::string::size_type last = 0;
323 auto index = s.find(delim, last);
324 while (index != std::string::npos) {
325 if (index - last > 0) {
326 ret.push_back(s.substr(last, index - last));
327 }
328 last = index + strlen(delim);
329 index = s.find(delim, last);
330 }
331 if (s.empty() || s.size() - last > 0) {
332 ret.push_back(s.substr(last));
333 }
334 return ret;
335 }
336
ParseTag(std::list<std::shared_ptr<Tag>> & entriesList,std::shared_ptr<Tag> & lastTag,std::string & line)337 static void ParseTag(std::list<std::shared_ptr<Tag>>& entriesList, std::shared_ptr<Tag>& lastTag, std::string& line)
338 {
339 if (line.find("#EXT") != std::string::npos) { // tag
340 line = line.substr(1);
341 std::string key;
342 std::string value;
343 auto keyValue = Split(line, ":");
344 key = keyValue[0];
345 if (keyValue.size() == 2) { // 2
346 value = keyValue[1];
347 }
348 if (!key.empty()) {
349 auto tag = TagFactory::CreateTagByName(key, value);
350 if (tag) {
351 entriesList.push_back(tag);
352 }
353 lastTag = tag;
354 }
355 }
356 }
357
ParseURI(std::list<std::shared_ptr<Tag>> & entriesList,std::shared_ptr<Tag> & lastTag,const std::string & line)358 static void ParseURI(std::list<std::shared_ptr<Tag>>& entriesList,
359 std::shared_ptr<Tag>& lastTag, const std::string& line)
360 {
361 if (lastTag && lastTag->GetType() == HlsTag::EXTXSTREAMINF) {
362 auto streaminftag = std::static_pointer_cast<AttributesTag>(lastTag);
363 /* master playlist uri, merge as attribute */
364 auto uriAttr = std::make_shared<Attribute>("URI", line);
365 if (uriAttr) {
366 streaminftag->AddAttribute(uriAttr);
367 }
368 } else { /* playlist tag, will take modifiers */
369 auto tag = TagFactory::CreateTagByName("", line);
370 if (tag) {
371 entriesList.push_back(tag);
372 }
373 }
374 lastTag = nullptr;
375 }
376
ParseEntries(std::string & s)377 std::list<std::shared_ptr<Tag>> ParseEntries(std::string& s)
378 {
379 std::list<std::shared_ptr<Tag>> list;
380 std::shared_ptr<Tag> lastTag = nullptr;
381 auto lines = Split(s, "\r\n");
382 if (lines.size() == 1) { // 1
383 lines = Split(s, "\n");
384 }
385 for (auto line : lines) {
386 if (line[0] == '#') {
387 ParseTag(list, lastTag, line);
388 } else if (!line.empty()) {
389 /* URI */
390 ParseURI(list, lastTag, line);
391 } else {
392 // drop
393 lastTag = nullptr;
394 }
395 }
396 return list;
397 }
398 }
399 }
400 }
401 }