Implemented browser detection and handling of output-specific browser exceptions.
This commit is contained in:
parent
dc12c1ea78
commit
f65c759292
4 changed files with 77 additions and 14 deletions
|
@ -40,6 +40,21 @@ namespace Mist{
|
||||||
capa["methods"][0u]["handler"] = "http";
|
capa["methods"][0u]["handler"] = "http";
|
||||||
capa["methods"][0u]["type"] = "html5/video/webm";
|
capa["methods"][0u]["type"] = "html5/video/webm";
|
||||||
capa["methods"][0u]["priority"] = 8ll;
|
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.
|
/// Calculates the size of a Cluster (contents only) and returns it.
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Mist {
|
||||||
result << "#EXTM3U\r\n";
|
result << "#EXTM3U\r\n";
|
||||||
int audioId = -1;
|
int audioId = -1;
|
||||||
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
for (std::map<unsigned int,DTSC::Track>::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;
|
audioId = it->first;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,8 @@ namespace Mist {
|
||||||
capa["methods"][0u]["handler"] = "http";
|
capa["methods"][0u]["handler"] = "http";
|
||||||
capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl";
|
capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl";
|
||||||
capa["methods"][0u]["priority"] = 9ll;
|
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() {
|
void OutHLS::onHTTP() {
|
||||||
|
@ -247,11 +249,7 @@ namespace Mist {
|
||||||
initialize();
|
initialize();
|
||||||
std::string request = H.url.substr(H.url.find("/", 5) + 1);
|
std::string request = H.url.substr(H.url.find("/", 5) + 1);
|
||||||
H.Clean();
|
H.Clean();
|
||||||
if (H.url.find(".m3u8") != std::string::npos){
|
H.SetHeader("Content-Type", "application/vnd.apple.mpegurl");
|
||||||
H.SetHeader("Content-Type", "audio/x-mpegurl");
|
|
||||||
}else{
|
|
||||||
H.SetHeader("Content-Type", "audio/mpegurl");
|
|
||||||
}
|
|
||||||
H.SetHeader("Cache-Control", "no-cache");
|
H.SetHeader("Cache-Control", "no-cache");
|
||||||
H.setCORSHeaders();
|
H.setCORSHeaders();
|
||||||
if (!myMeta.tracks.size()){
|
if (!myMeta.tracks.size()){
|
||||||
|
|
|
@ -153,9 +153,40 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
sources.insert(tmp);
|
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();
|
const std::string & rel = conncapa["url_rel"].asStringRef();
|
||||||
unsigned int most_simul = 0;
|
unsigned int most_simul = 0;
|
||||||
unsigned int total_matches = 0;
|
unsigned int total_matches = 0;
|
||||||
|
@ -171,6 +202,17 @@ namespace Mist {
|
||||||
if ((*trit)["codec"].asStringRef() == (*itc).asStringRef()){
|
if ((*trit)["codec"].asStringRef() == (*itc).asStringRef()){
|
||||||
matches++;
|
matches++;
|
||||||
total_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);
|
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;
|
JSON::Value json_resp;
|
||||||
uint8_t streamStatus = Util::getStreamStatus(streamName);
|
uint8_t streamStatus = Util::getStreamStatus(streamName);
|
||||||
if (streamStatus != STRMSTAT_READY){
|
if (streamStatus != STRMSTAT_READY){
|
||||||
|
@ -344,7 +386,7 @@ namespace Mist {
|
||||||
//and a URL - then list the URL
|
//and a URL - then list the URL
|
||||||
JSON::Value capa_json = capa.asJSON();
|
JSON::Value capa_json = capa.asJSON();
|
||||||
if (capa.getMember("url_rel") || capa.getMember("methods")){
|
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
|
//Make note if this connector can be depended upon by other connectors
|
||||||
if (capa.getMember("provides")){
|
if (capa.getMember("provides")){
|
||||||
|
@ -357,7 +399,7 @@ namespace Mist {
|
||||||
//if it depends on this connector and has a URL, list it
|
//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")){
|
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();
|
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 ((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;}
|
if (websocketHandler()){return;}
|
||||||
std::string reqHost = HTTP::URL(H.GetHeader("Host")).host;
|
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 response;
|
||||||
std::string rURL = H.url;
|
std::string rURL = H.url;
|
||||||
H.Clean();
|
H.Clean();
|
||||||
|
@ -514,7 +560,7 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
initialize();
|
initialize();
|
||||||
response = "// Generating info code for stream " + streamName + "\n\nif (!mistvideo){var mistvideo = {};}\n";
|
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_"){
|
if (rURL.substr(0, 6) != "/json_"){
|
||||||
response += "mistvideo['" + streamName + "'] = " + json_resp.toString() + ";\n";
|
response += "mistvideo['" + streamName + "'] = " + json_resp.toString() + ";\n";
|
||||||
}else{
|
}else{
|
||||||
|
@ -700,6 +746,10 @@ namespace Mist {
|
||||||
bool OutHTTP::websocketHandler(){
|
bool OutHTTP::websocketHandler(){
|
||||||
stayConnected = true;
|
stayConnected = true;
|
||||||
std::string reqHost = HTTP::URL(H.GetHeader("Host")).host;
|
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;}
|
if (H.GetHeader("Upgrade") != "websocket"){return false;}
|
||||||
HTTP::Websocket ws(myConn, H);
|
HTTP::Websocket ws(myConn, H);
|
||||||
if (!ws){return false;}
|
if (!ws){return false;}
|
||||||
|
@ -724,7 +774,7 @@ namespace Mist {
|
||||||
}else{
|
}else{
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
JSON::Value resp = getStatusJSON(reqHost);
|
JSON::Value resp = getStatusJSON(reqHost, useragent);
|
||||||
ws.sendFrame(resp.toString());
|
ws.sendFrame(resp.toString());
|
||||||
prevState = newState;
|
prevState = newState;
|
||||||
}else{
|
}else{
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Mist {
|
||||||
void onHTTP();
|
void onHTTP();
|
||||||
void sendIcon();
|
void sendIcon();
|
||||||
bool websocketHandler();
|
bool websocketHandler();
|
||||||
JSON::Value getStatusJSON(std::string & reqHost);
|
JSON::Value getStatusJSON(std::string & reqHost, const std::string & useragent = "");
|
||||||
bool stayConnected;
|
bool stayConnected;
|
||||||
virtual bool onFinish(){
|
virtual bool onFinish(){
|
||||||
return stayConnected;
|
return stayConnected;
|
||||||
|
|
Loading…
Add table
Reference in a new issue