Implemented browser detection and handling of output-specific browser exceptions.

This commit is contained in:
Thulinma 2018-03-18 17:24:54 +01:00
parent 4b2e2aa889
commit 266ab3a654
5 changed files with 77 additions and 11 deletions

View file

@ -68,6 +68,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;
capa["push_urls"].append("/*.mkv");
capa["push_urls"].append("/*.webm");

View file

@ -306,6 +306,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\"]]]");
/*LTS-START*/
cfg->addOption("listlimit", JSON::fromString("{\"arg\":\"integer\",\"default\":0,\"short\":\"y\",\"long\":\"list-limit\",\"help\":\"Maximum number of parts in live playlists (0 = infinite).\"}"));
capa["optional"]["listlimit"]["name"] = "Live playlist limit";

View file

@ -171,8 +171,39 @@ 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<JSON::Value, sourceCompare> & sources, HTTP::URL url, JSON::Value & conncapa, JSON::Value & strmMeta){
void addSources(std::string & streamname, std::set<JSON::Value, sourceCompare> & 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;
@ -188,6 +219,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;
}
}
}
}
}
}
@ -262,7 +304,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;
if (config->getString("nostreamtext") != ""){
json_resp["on_error"] = config->getString("nostreamtext");
@ -383,7 +425,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")){
@ -396,7 +438,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);
}
}
}
@ -536,6 +578,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();
@ -553,7 +599,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{
@ -764,6 +810,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;}
@ -788,7 +838,7 @@ namespace Mist {
}else{
disconnect();
}
JSON::Value resp = getStatusJSON(reqHost);
JSON::Value resp = getStatusJSON(reqHost, useragent);
ws.sendFrame(resp.toString());
prevState = newState;
}else{

View file

@ -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;

View file

@ -26,9 +26,8 @@ namespace Mist{
capa["methods"][0u]["handler"] = "http";
capa["methods"][0u]["type"] = "html5/video/mp4";
capa["methods"][0u]["priority"] = 8ll;
///\todo uncomment when we actually start implementing mp4 recording
//capa["canRecord"].append("mp4");
//capa["canRecord"].append("m3u");
//MP4 live is broken on Apple
capa["exceptions"]["live"] = JSON::fromString("[[\"blacklist\",[\"iPad\",\"iPhone\",\"iPod\",\"Safari\"]], [\"whitelist\",[\"Chrome\",\"Chromium\"]]]");
}
uint64_t OutProgressiveMP4::estimateFileSize(){