From dc12c1ea78dc8b0695e85afc5d9a79f9eb5de3c9 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 27 Mar 2018 15:43:41 +0200 Subject: [PATCH 1/3] EBML fix for ARMv7 --- lib/ebml.cpp | 14 +++++++------- lib/ebml_socketglue.cpp | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/ebml.cpp b/lib/ebml.cpp index 6e5e1652..e38ed2c2 100644 --- a/lib/ebml.cpp +++ b/lib/ebml.cpp @@ -55,13 +55,13 @@ namespace EBML{ void UniInt::writeInt(char *p, const uint64_t val){ switch (writeSize(val)){ case 1: p[0] = val | 0x80; break; - case 2: Bit::htobs(p, val | 0x4000); break; - case 3: Bit::htob24(p, val | 0x200000); break; - case 4: Bit::htobl(p, val | 0x10000000); break; - case 5: Bit::htob40(p, val | 0x800000000); break; - case 6: Bit::htob48(p, val | 0x40000000000); break; - case 7: Bit::htob56(p, val | 0x2000000000000); break; - case 8: Bit::htobll(p, val | 0x100000000000000); break; + case 2: Bit::htobs(p, val | 0x4000ull); break; + case 3: Bit::htob24(p, val | 0x200000ull); break; + case 4: Bit::htobl(p, val | 0x10000000ull); break; + case 5: Bit::htob40(p, val | 0x800000000ull); break; + case 6: Bit::htob48(p, val | 0x40000000000ull); break; + case 7: Bit::htob56(p, val | 0x2000000000000ull); break; + case 8: Bit::htobll(p, val | 0x100000000000000ull); break; } } diff --git a/lib/ebml_socketglue.cpp b/lib/ebml_socketglue.cpp index 3d40a0a5..3a9abb62 100644 --- a/lib/ebml_socketglue.cpp +++ b/lib/ebml_socketglue.cpp @@ -19,19 +19,19 @@ namespace EBML{ } uint8_t sizeUInt(const uint64_t val){ - if (val >= 0x100000000000000){ + if (val >= 0x100000000000000ull){ return 8; - }else if (val >= 0x1000000000000){ + }else if (val >= 0x1000000000000ull){ return 7; - }else if (val >= 0x10000000000){ + }else if (val >= 0x10000000000ull){ return 6; - }else if (val >= 0x100000000){ + }else if (val >= 0x100000000ull){ return 5; - }else if (val >= 0x1000000){ + }else if (val >= 0x1000000ull){ return 4; - }else if (val >= 0x10000){ + }else if (val >= 0x10000ull){ return 3; - }else if (val >= 0x100){ + }else if (val >= 0x100ull){ return 2; }else{ return 1; From f65c759292e25301520ab8fcc15e86e03c08ac64 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 27 Mar 2018 15:57:08 +0200 Subject: [PATCH 2/3] Implemented browser detection and handling of output-specific browser exceptions. --- src/output/output_ebml.cpp | 15 +++++++ src/output/output_hls.cpp | 10 ++--- src/output/output_http_internal.cpp | 64 +++++++++++++++++++++++++---- src/output/output_http_internal.h | 2 +- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/src/output/output_ebml.cpp b/src/output/output_ebml.cpp index 36b62c8c..99c13a3d 100644 --- a/src/output/output_ebml.cpp +++ b/src/output/output_ebml.cpp @@ -40,6 +40,21 @@ namespace Mist{ capa["methods"][0u]["handler"] = "http"; capa["methods"][0u]["type"] = "html5/video/webm"; capa["methods"][0u]["priority"] = 8ll; + //EBML will only work with VP8/VP9/Opus except on Chrome + JSON::Value blacklistNonChrome = JSON::fromString("[[\"blacklist\"], [\"whitelist\",[\"Chrome\",\"Chromium\"]], [\"blacklist\",[\"Edge\",\"OPR/\"]]]"); + capa["exceptions"]["codec:H264"] = blacklistNonChrome; + capa["exceptions"]["codec:HEVC"] = blacklistNonChrome; + capa["exceptions"]["codec:theora"] = blacklistNonChrome; + capa["exceptions"]["codec:MPEG2"] = blacklistNonChrome; + capa["exceptions"]["codec:AAC"] = blacklistNonChrome; + capa["exceptions"]["codec:vorbis"] = blacklistNonChrome; + capa["exceptions"]["codec:PCM"] = blacklistNonChrome; + capa["exceptions"]["codec:ALAW"] = blacklistNonChrome; + capa["exceptions"]["codec:ULAW"] = blacklistNonChrome; + capa["exceptions"]["codec:MP2"] = blacklistNonChrome; + capa["exceptions"]["codec:MP3"] = blacklistNonChrome; + capa["exceptions"]["codec:FLOAT"] = blacklistNonChrome; + capa["exceptions"]["codec:AC3"] = blacklistNonChrome; } /// Calculates the size of a Cluster (contents only) and returns it. diff --git a/src/output/output_hls.cpp b/src/output/output_hls.cpp index 143ea543..e0afa6fc 100644 --- a/src/output/output_hls.cpp +++ b/src/output/output_hls.cpp @@ -19,7 +19,7 @@ namespace Mist { result << "#EXTM3U\r\n"; int audioId = -1; for (std::map::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ - if (it->second.codec == "AAC"){ + if (it->second.codec == "AAC" || it->second.codec == "MP3"){ audioId = it->first; break; } @@ -125,6 +125,8 @@ namespace Mist { capa["methods"][0u]["handler"] = "http"; capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl"; capa["methods"][0u]["priority"] = 9ll; + //MP3 only works on Edge/Apple + capa["exceptions"]["codec:MP3"] = JSON::fromString("[[\"blacklist\"],[\"whitelist\",[\"iPad\",\"iPhone\",\"iPod\",\"MacIntel\",\"Edge\"]]]"); } void OutHLS::onHTTP() { @@ -247,11 +249,7 @@ namespace Mist { initialize(); std::string request = H.url.substr(H.url.find("/", 5) + 1); H.Clean(); - if (H.url.find(".m3u8") != std::string::npos){ - H.SetHeader("Content-Type", "audio/x-mpegurl"); - }else{ - H.SetHeader("Content-Type", "audio/mpegurl"); - } + H.SetHeader("Content-Type", "application/vnd.apple.mpegurl"); H.SetHeader("Cache-Control", "no-cache"); H.setCORSHeaders(); if (!myMeta.tracks.size()){ diff --git a/src/output/output_http_internal.cpp b/src/output/output_http_internal.cpp index 9dcdb120..1cedc385 100644 --- a/src/output/output_http_internal.cpp +++ b/src/output/output_http_internal.cpp @@ -153,9 +153,40 @@ namespace Mist { } sources.insert(tmp); } - + + /// Checks if a given user agent is allowed according to the given exception. + bool checkException(const JSON::Value & ex, const std::string & useragent){ + //No user agent? Always allow everything. + if (!useragent.size()){return true;} + if (!ex.isArray() || !ex.size()){return true;} + bool ret = true; + jsonForEachConst(ex, e){ + if (!e->isArray() || !e->size()){continue;} + bool setTo = ((*e)[0u].asStringRef() == "whitelist"); + if (e->size() == 1){ + ret = setTo; + continue; + } + if (!(*e)[1].isArray()){continue;} + jsonForEachConst((*e)[1u], i){ + if (useragent.find(i->asStringRef()) != std::string::npos){ + ret = setTo; + } + } + } + return ret; + } - void addSources(std::string & streamname, std::set & sources, HTTP::URL url, JSON::Value & conncapa, JSON::Value & strmMeta){ + void addSources(std::string & streamname, std::set & sources, HTTP::URL url, JSON::Value & conncapa, JSON::Value & strmMeta, const std::string & useragent){ + if (strmMeta.isMember("live") && conncapa.isMember("exceptions") && conncapa["exceptions"].isObject() && conncapa["exceptions"].size()){ + jsonForEach(conncapa["exceptions"], ex){ + if (ex.key() == "live"){ + if (!checkException(*ex, useragent)){ + return; + } + } + } + } const std::string & rel = conncapa["url_rel"].asStringRef(); unsigned int most_simul = 0; unsigned int total_matches = 0; @@ -171,6 +202,17 @@ namespace Mist { if ((*trit)["codec"].asStringRef() == (*itc).asStringRef()){ matches++; total_matches++; + if (conncapa.isMember("exceptions") && conncapa["exceptions"].isObject() && conncapa["exceptions"].size()){ + jsonForEach(conncapa["exceptions"], ex){ + if (ex.key() == "codec:"+(*trit)["codec"].asStringRef()){ + if (!checkException(*ex, useragent)){ + matches--; + total_matches--; + } + break; + } + } + } } } } @@ -236,7 +278,7 @@ namespace Mist { H.SendResponse("200", "OK", myConn); } - JSON::Value OutHTTP::getStatusJSON(std::string & reqHost){ + JSON::Value OutHTTP::getStatusJSON(std::string & reqHost, const std::string & useragent){ JSON::Value json_resp; uint8_t streamStatus = Util::getStreamStatus(streamName); if (streamStatus != STRMSTAT_READY){ @@ -344,7 +386,7 @@ namespace Mist { //and a URL - then list the URL JSON::Value capa_json = capa.asJSON(); if (capa.getMember("url_rel") || capa.getMember("methods")){ - addSources(streamName, sources, outURL, capa_json, json_resp["meta"]); + addSources(streamName, sources, outURL, capa_json, json_resp["meta"], useragent); } //Make note if this connector can be depended upon by other connectors if (capa.getMember("provides")){ @@ -357,7 +399,7 @@ namespace Mist { //if it depends on this connector and has a URL, list it if (conns.count(capa_lst.getIndiceName(j)) && capa_lst.getIndice(j).getMember("deps").asString() == cProv && capa_lst.getIndice(j).getMember("methods")){ JSON::Value subcapa_json = capa_lst.getIndice(j).asJSON(); - addSources(streamName, sources, outURL, subcapa_json, json_resp["meta"]); + addSources(streamName, sources, outURL, subcapa_json, json_resp["meta"], useragent); } } } @@ -497,6 +539,10 @@ namespace Mist { if ((H.url.length() > 9 && H.url.substr(0, 6) == "/info_" && H.url.substr(H.url.length() - 3, 3) == ".js") || (H.url.length() > 10 && H.url.substr(0, 7) == "/embed_" && H.url.substr(H.url.length() - 3, 3) == ".js") || (H.url.length() > 9 && H.url.substr(0, 6) == "/json_" && H.url.substr(H.url.length() - 3, 3) == ".js")){ if (websocketHandler()){return;} std::string reqHost = HTTP::URL(H.GetHeader("Host")).host; + std::string useragent = H.GetVar("ua"); + if (!useragent.size()){ + useragent = H.GetHeader("User-Agent"); + } std::string response; std::string rURL = H.url; H.Clean(); @@ -514,7 +560,7 @@ namespace Mist { } initialize(); response = "// Generating info code for stream " + streamName + "\n\nif (!mistvideo){var mistvideo = {};}\n"; - JSON::Value json_resp = getStatusJSON(reqHost); + JSON::Value json_resp = getStatusJSON(reqHost, useragent); if (rURL.substr(0, 6) != "/json_"){ response += "mistvideo['" + streamName + "'] = " + json_resp.toString() + ";\n"; }else{ @@ -700,6 +746,10 @@ namespace Mist { bool OutHTTP::websocketHandler(){ stayConnected = true; std::string reqHost = HTTP::URL(H.GetHeader("Host")).host; + std::string useragent = H.GetVar("ua"); + if (!useragent.size()){ + useragent = H.GetHeader("User-Agent"); + } if (H.GetHeader("Upgrade") != "websocket"){return false;} HTTP::Websocket ws(myConn, H); if (!ws){return false;} @@ -724,7 +774,7 @@ namespace Mist { }else{ disconnect(); } - JSON::Value resp = getStatusJSON(reqHost); + JSON::Value resp = getStatusJSON(reqHost, useragent); ws.sendFrame(resp.toString()); prevState = newState; }else{ diff --git a/src/output/output_http_internal.h b/src/output/output_http_internal.h index f2f9a0ba..49f37d72 100644 --- a/src/output/output_http_internal.h +++ b/src/output/output_http_internal.h @@ -15,7 +15,7 @@ namespace Mist { void onHTTP(); void sendIcon(); bool websocketHandler(); - JSON::Value getStatusJSON(std::string & reqHost); + JSON::Value getStatusJSON(std::string & reqHost, const std::string & useragent = ""); bool stayConnected; virtual bool onFinish(){ return stayConnected; From 3c67277d813fe15855a810fd0530289938ea8c65 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 27 Mar 2018 15:57:55 +0200 Subject: [PATCH 3/3] Changed EBML and MP4 priorities --- src/output/output_ebml.cpp | 2 +- src/output/output_progressive_mp4.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/output/output_ebml.cpp b/src/output/output_ebml.cpp index 99c13a3d..3f3aad96 100644 --- a/src/output/output_ebml.cpp +++ b/src/output/output_ebml.cpp @@ -39,7 +39,7 @@ namespace Mist{ capa["codecs"][0u][1u].append("AC3"); capa["methods"][0u]["handler"] = "http"; capa["methods"][0u]["type"] = "html5/video/webm"; - capa["methods"][0u]["priority"] = 8ll; + capa["methods"][0u]["priority"] = 11ll; //EBML will only work with VP8/VP9/Opus except on Chrome JSON::Value blacklistNonChrome = JSON::fromString("[[\"blacklist\"], [\"whitelist\",[\"Chrome\",\"Chromium\"]], [\"blacklist\",[\"Edge\",\"OPR/\"]]]"); capa["exceptions"]["codec:H264"] = blacklistNonChrome; diff --git a/src/output/output_progressive_mp4.cpp b/src/output/output_progressive_mp4.cpp index a424e245..699452bd 100644 --- a/src/output/output_progressive_mp4.cpp +++ b/src/output/output_progressive_mp4.cpp @@ -22,7 +22,7 @@ namespace Mist { capa["codecs"][0u][1u].append("MP3"); capa["methods"][0u]["handler"] = "http"; capa["methods"][0u]["type"] = "html5/video/mp4"; - capa["methods"][0u]["priority"] = 8ll; + capa["methods"][0u]["priority"] = 10ll; capa["methods"][0u]["nolive"] = 1; } uint64_t OutProgressiveMP4::estimateFileSize() {