• 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 
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 }