Created
November 9, 2021 18:01
-
-
Save ITotalJustice/9f51329d57dee259217d9f68d7b00f8f to your computer and use it in GitHub Desktop.
crapi hls parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "hlsparse.hpp" | |
#include "../../utils/logger.hpp" | |
#ifndef NDEBUG | |
#include <iostream> | |
#endif | |
#include <cassert> | |
#include <ranges> | |
#include <algorithm> | |
namespace hls { | |
static constexpr std::string_view MASTER_MAGIC = "#EXTM3U"; | |
static constexpr std::string_view META_MAGIC = "#EXT-X-STREAM-INF:"; | |
// SOURCE: https://www.bfilipek.com/2018/07/string-view-perf-followup.html | |
static auto splitSV(std::string_view strv, char delim) -> std::vector<std::string_view> { | |
std::vector<std::string_view> output; | |
std::size_t first{}; | |
while (first < strv.size()) { | |
const auto second = strv.find_first_of(delim, first); | |
if (first != second) { | |
output.emplace_back(strv.substr(first, second - first)); | |
} | |
if (second == std::string_view::npos) { | |
break; | |
} | |
first = second + 1; | |
} | |
return output; | |
} | |
static constexpr auto CHAR_INT_MIX = 48; | |
static constexpr auto CHAR_INT_MAX = 57; | |
static constexpr inline auto IsInt(const char a) { | |
return (a >= CHAR_INT_MIX) && (a <= CHAR_INT_MAX); | |
} | |
static auto ToUint(std::string_view view) { | |
std::uint32_t result{}; | |
std::size_t max{}; | |
for (std::size_t i = 0; i < view.size(); ++i) { | |
if (!IsInt(view[i])) { | |
break; | |
} | |
++max; | |
} | |
for (std::size_t mul10 = 1; max > 0; --max) { | |
result += (view[max - 1] - CHAR_INT_MIX) * mul10; | |
mul10 *= 10; | |
} | |
return result; | |
} | |
// todo: | |
static auto ParseMeta(std::string_view meta, Info& info) -> void { | |
static constexpr std::string_view tag_res{"RESOLUTION="}; | |
// find res | |
if (auto pos = meta.find(tag_res); pos != meta.npos) { | |
auto sub = meta.substr(pos + tag_res.size(), meta.find(',')); | |
if (auto split = splitSV(sub, 'x'); split.size() == 2) { | |
info.w = ToUint(split[0]); | |
info.h = ToUint(split[1]); | |
} else { | |
LOG("[HLS] failed to split\n"); | |
} | |
} else { | |
LOG("[HLS] missing res\n"); | |
} | |
} | |
auto ParseMaster(std::string_view master) -> std::vector<Info> { | |
if (!master.starts_with(MASTER_MAGIC)) { | |
LOG("[HLS] master has invalid magic!\n"); | |
return {}; | |
} | |
// skip magic + newline | |
auto data = master.substr(MASTER_MAGIC.size() + 1); | |
// split data by lines | |
auto lines = splitSV(data, '\n'); | |
// some hls will have gaps between the first stream with more metadata | |
// or empty spaces, so seek to the first stream (if any). | |
auto it = std::ranges::find_if(lines, [](const auto& a){ | |
return a.starts_with(META_MAGIC); | |
}); | |
// if we reach the end, then no streams were found. | |
if (it == lines.end()) { | |
LOG("[HLS] no streams found with meta magic\n"); | |
return {}; | |
} | |
// check if we had to seek past the first entry, if so, erease the rest. | |
if (it > lines.begin()) { | |
lines.erase(lines.begin(), it); | |
} | |
std::vector<Info> entries; | |
// process 2 lines at a time because they are split into [META, URL] pairs. | |
for (std::size_t i = 0; (i + 1) < lines.size(); i += 2) { | |
const auto meta = lines[i]; | |
const auto url = lines[i + 1]; | |
// basic test to see if the data is valid-ish. | |
if (!meta.starts_with(META_MAGIC) || !url.starts_with("https://")) { | |
LOG("HLS-ERROR invalid data at entry %lu\n", i); | |
break; | |
} | |
// #ifndef NDEBUG | |
// std::cout << meta << "\nURL: " << url << "\n\n"; | |
// #endif | |
Info info; | |
ParseMeta(meta, info); | |
info.url = url; | |
assert(info.url.starts_with("https://")); | |
entries.emplace_back(std::move(info)); | |
} | |
std::ranges::sort(entries, std::greater_equal{}, &Info::h); | |
return entries; | |
} | |
} // namespace hls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// simple m3u8 master parser made for CR streams. | |
// i looked at the m3u8 spec a while ago and iirc CR does stuff | |
// that is not defined in the spec, like some of the field entries | |
// didn't exist in the spec. | |
// this was a lonnng time ago though however so i might be forgetting. | |
// anyway, because of this, this parser will only work with CR master | |
// hls stuff. | |
#pragma once | |
#include <vector> | |
#include <string> | |
#include <string_view> | |
namespace hls { | |
struct Info { | |
unsigned bandwidth; //req | |
unsigned average_bandwidth; //opt | |
std::vector<std::string> codecs; //req | |
unsigned w; | |
unsigned h; | |
unsigned program_id; | |
float frame_rate; | |
std::string url; | |
}; | |
auto ParseMaster(std::string_view master) -> std::vector<Info>; | |
} // namespace hls |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment