5G-MAG Reference Tools - MBMS Middleware
HlsPrimaryPlaylist.cpp
Go to the documentation of this file.
1 // 5G-MAG Reference Tools
2 // MBMS Middleware Process
3 //
4 // Copyright (C) 2021 Klaus Kühnhammer (Österreichische Rundfunksender GmbH & Co KG)
5 //
6 // Licensed under the License terms and conditions for use, reproduction, and
7 // distribution of 5G-MAG software (the “License”). You may not use this file
8 // except in compliance with the License. You may obtain a copy of the License at
9 // https://www.5g-mag.com/reference-tools. Unless required by applicable law or
10 // agreed to in writing, software distributed under the License is distributed on
11 // an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12 // or implied.
13 //
14 // See the License for the specific language governing permissions and limitations
15 // under the License.
16 //
17 
18 #include "HlsPrimaryPlaylist.h"
19 
20 #include "spdlog/spdlog.h"
21 #include "boost/algorithm/string.hpp"
22 #include <iomanip>
23 #include <sstream>
24 
25 MBMS_RT::HlsPrimaryPlaylist::HlsPrimaryPlaylist(const std::string& content, const std::string& base_path)
26 {
27  spdlog::debug("Parsing HLS primary playlist: {}", content);
28 
29  std::istringstream iss(content);
30  int idx = 0;
31  std::string resolution = "";
32  std::string codecs = "";
33  unsigned long bandwidth = 0;
34  double frame_rate = 0;
35  for (std::string line; std::getline(iss, line); idx++ )
36  {
37 
38  boost::algorithm::trim(line);
39 
40  if (idx==0) {
41  if ( line != "#EXTM3U") {
42  throw("HLS playlist parsing failed: first line is not #EXTM3U");
43  } else {
44  continue;
45  }
46  }
47 
48  size_t cpos = line.rfind(':');
49 
50  if (line.rfind("#EXT-X-VERSION", 0) == 0) {
51  if (_version != -1) {
52  throw("HLS playlist parsing failed: duplicate #EXT-X-VERSION");
53  }
54  if (cpos != std::string::npos) {
55  _version = atoi(line.substr(cpos+1).c_str());
56  }
57  } else if (line.rfind("#EXT-X-STREAM-INF", 0) == 0) {
58  if (cpos != std::string::npos) {
59  std::string info = line.substr(cpos+1);
60  auto params = parse_parameters(info);
61  for (const auto& param : params) {
62  if (param.first == "BANDWIDTH") {
63  bandwidth = std::atoi(param.second.c_str());
64  }
65  else if (param.first == "FRAME-RATE") {
66  frame_rate = std::atof(param.second.c_str());
67  }
68  else if (param.first == "RESOLUTION") {
69  resolution = param.second;
70  }
71  else if (param.first == "CODECS") {
72  codecs = param.second;
73  }
74  }
75  }
76  } else if (line.rfind('#', 0) == 0) {
77  spdlog::debug("HLS playlist parser ignoring unhandled line {}", line);
78  } else if (line.size() > 0) {
79  std::string uri = base_path + line;
80  _streams.push_back({uri, resolution, codecs, bandwidth, frame_rate});
81  resolution = ""; codecs = ""; bandwidth = 0; frame_rate = 0;
82  }
83  }
84 }
85 
86 auto MBMS_RT::HlsPrimaryPlaylist::parse_parameters(const std::string& line) const -> std::vector<std::pair<std::string, std::string>>
87 {
88  std::vector<std::pair<std::string, std::string>> result;
89  bool quoted = false;
90  bool in_key = true;
91  std::stringstream key;
92  std::stringstream value;
93  for(const char& c : line) {
94  if (c == '"') {
95  quoted = !quoted;
96  } else if (c == ',' && !quoted) {
97  result.emplace_back(std::make_pair(key.str(), value.str()));
98  key.str(""); key.clear();
99  value.str(""); value.clear();
100  in_key = true;
101  } else if (c == '=') {
102  in_key = false;
103  } else {
104  if (in_key) {
105  key << c;
106  } else {
107  value << c;
108  }
109  }
110  }
111  result.emplace_back(std::make_pair(key.str(), value.str()));
112  return result;
113 }
114 
115 auto MBMS_RT::HlsPrimaryPlaylist::to_string() const -> std::string
116 {
117  std::stringstream pl;
118 
119  pl << "#EXTM3U" << std::endl;
120  pl << "#EXT-X-VERSION:3" << std::endl;
121 
122  for (const auto& s : _streams) {
123  pl << "#EXT-X-STREAM-INF:BANDWITH=" << s.bandwidth <<
124  ",RESOLUTION=" << s.resolution <<
125  ",FRAME-RATE=" << std::fixed << std::setprecision(3) << s.frame_rate <<
126  ",CODECS=\"" << s.codecs << "\"" << std::endl;
127  pl << s.uri << std::endl;
128  }
129  return std::move(pl.str());
130 }
std::vector< std::pair< std::string, std::string > > parse_parameters(const std::string &line) const
std::vector< Stream > _streams