#include #include "output_http_internal.h" #include #include "flashPlayer.h" #include "oldFlashPlayer.h" namespace Mist { OutHTTP::OutHTTP(Socket::Connection & conn) : HTTPOutput(conn){ if (myConn.getPureSocket() >= 0){ std::string host = getConnectedHost(); dup2(myConn.getSocket(), STDIN_FILENO); dup2(myConn.getSocket(), STDOUT_FILENO); myConn.drop(); myConn = Socket::Connection(fileno(stdout),fileno(stdin) ); myConn.setHost(host); } } OutHTTP::~OutHTTP() {} bool OutHTTP::listenMode(){ return !(config->getString("ip").size()); } void OutHTTP::onFail(){ INFO_MSG("Failing: %s", H.url.c_str()); if (H.url.size() >= 3 && H.url.substr(H.url.size() - 3) == ".js"){ if (H.url.size() >= 5 && H.url.substr(0, 5) == "/json"){ H.Clean(); H.SetBody("{\"error\":\"Could not retrieve stream. Sorry.\"}\n"); }else{ H.Clean(); H.SetBody("if (!mistvideo){var mistvideo = {};}\nmistvideo['" + streamName + "'] = {\"error\":\"Could not retrieve stream. Sorry.\"};\n"); } H.SendResponse("200", "Stream not found", myConn); }else{ HTTPOutput::onFail(); } Output::onFail(); } void OutHTTP::init(Util::Config * cfg){ HTTPOutput::init(cfg); capa.removeMember("deps"); capa["name"] = "HTTP"; capa["desc"] = "Generic HTTP handler, required for all other HTTP-based outputs."; capa["url_match"].append("/crossdomain.xml"); capa["url_match"].append("/clientaccesspolicy.xml"); capa["url_match"].append("/$.html"); capa["url_match"].append("/$.ico"); capa["url_match"].append("/$.smil"); capa["url_match"].append("/info_$.js"); capa["url_match"].append("/json_$.js"); capa["url_match"].append("/embed_$.js"); capa["url_match"].append("/flashplayer.swf"); capa["url_match"].append("/oldflashplayer.swf"); cfg->addConnectorOptions(8080, capa); } /// Sorts the JSON::Value objects that hold source information by preference. struct sourceCompare { bool operator() (const JSON::Value& lhs, const JSON::Value& rhs) const { //first compare simultaneous tracks if (lhs["simul_tracks"].asInt() > rhs["simul_tracks"].asInt()){ //more tracks = higher priority = true. return true; } if (lhs["simul_tracks"].asInt() < rhs["simul_tracks"].asInt()){ //less tracks = lower priority = false return false; } //same amount of tracks - compare "hardcoded" priorities if (lhs["priority"].asInt() > rhs["priority"].asInt()){ //higher priority = true. return true; } if (lhs["priority"].asInt() < rhs["priority"].asInt()){ //lower priority = false return false; } //same priority - compare total matches if (lhs["total_matches"].asInt() > rhs["total_matches"].asInt()){ //more matches = higher priority = true. return true; } if (lhs["total_matches"].asInt() < rhs["total_matches"].asInt()){ //less matches = lower priority = false return false; } //also same amount of matches? just compare the URL then. return lhs["url"].asStringRef() < rhs["url"].asStringRef(); } }; void addSource(const std::string & rel, std::set & sources, std::string & host, const std::string & port, JSON::Value & conncapa, unsigned int most_simul, unsigned int total_matches, const std::string & flvPlayerPrefix){ JSON::Value tmp; tmp["type"] = conncapa["type"]; tmp["relurl"] = rel; tmp["priority"] = conncapa["priority"]; if (conncapa.isMember("player_url")){ tmp["player_url"] = flvPlayerPrefix + conncapa["player_url"].asStringRef(); } tmp["simul_tracks"] = most_simul; tmp["total_matches"] = total_matches; tmp["url"] = conncapa["handler"].asStringRef() + "://" + host + ":" + port + rel; sources.insert(tmp); } void addSources(std::string & streamname, const std::string & rel, std::set & sources, std::string & host, const std::string & port, JSON::Value & conncapa, JSON::Value & strmMeta, const std::string httpHost){ unsigned int most_simul = 0; unsigned int total_matches = 0; if (conncapa.isMember("codecs") && conncapa["codecs"].size() > 0){ jsonForEach(conncapa["codecs"], it) { unsigned int simul = 0; if ((*it).size() > 0){ jsonForEach((*it), itb) { unsigned int matches = 0; if ((*itb).size() > 0){ jsonForEach((*itb), itc) { jsonForEach(strmMeta["tracks"], trit) { if ((*trit)["codec"].asStringRef() == (*itc).asStringRef()){ matches++; total_matches++; } } } } if (matches){ simul++; } } } if (simul > most_simul){ most_simul = simul; } } } if (conncapa.isMember("methods") && conncapa["methods"].size() > 0){ std::string relurl; size_t found = rel.find('$'); if (found != std::string::npos){ relurl = rel.substr(0, found) + streamname + rel.substr(found+1); }else{ relurl = "/"; } jsonForEach(conncapa["methods"], it) { if (!strmMeta.isMember("live") || !it->isMember("nolive")){ addSource(relurl, sources, host, port, *it, most_simul, total_matches, "http://" + httpHost); } } } } void OutHTTP::onHTTP(){ std::string method = H.method; if (H.url == "/crossdomain.xml"){ H.Clean(); H.SetHeader("Content-Type", "text/xml"); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.setCORSHeaders(); if(method == "OPTIONS" || method == "HEAD"){ H.SendResponse("200", "OK", myConn); H.Clean(); return; } H.SetBody(""); H.SendResponse("200", "OK", myConn); H.Clean(); return; } //crossdomain.xml if (H.url == "/clientaccesspolicy.xml"){ H.Clean(); H.SetHeader("Content-Type", "text/xml"); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.setCORSHeaders(); if(method == "OPTIONS" || method == "HEAD"){ H.SendResponse("200", "OK", myConn); H.Clean(); return; } H.SetBody(""); H.SendResponse("200", "OK", myConn); H.Clean(); return; } //clientaccesspolicy.xml if (H.url == "/flashplayer.swf"){ H.Clean(); H.SetHeader("Content-Type", "application/x-shockwave-flash"); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetBody((const char*)FlashMediaPlayback_101_swf, FlashMediaPlayback_101_swf_len); H.SendResponse("200", "OK", myConn); return; } if (H.url == "/oldflashplayer.swf"){ H.Clean(); H.SetHeader("Content-Type", "application/x-shockwave-flash"); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetBody((const char *)FlashMediaPlayback_swf, FlashMediaPlayback_swf_len); H.SendResponse("200", "OK", myConn); return; } // send logo icon if (H.url.length() > 4 && H.url.substr(H.url.length() - 4, 4) == ".ico"){ H.Clean(); #include "../icon.h" H.SetHeader("Content-Type", "image/x-icon"); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetHeader("Content-Length", icon_len); H.setCORSHeaders(); if(method == "OPTIONS" || method == "HEAD"){ H.SendResponse("200", "OK", myConn); H.Clean(); return; } H.SendResponse("200", "OK", myConn); myConn.SendNow((const char*)icon_data, icon_len); H.Clean(); return; } // send generic HTML page if (H.url.length() > 6 && H.url.substr(H.url.length() - 5, 5) == ".html"){ H.Clean(); H.SetHeader("Content-Type", "text/html"); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.setCORSHeaders(); if(method == "OPTIONS" || method == "HEAD"){ H.SendResponse("200", "OK", myConn); H.Clean(); return; } H.SetBody("Stream "+streamName+""); H.SendResponse("200", "OK", myConn); return; } // send smil MBR index if (H.url.length() > 6 && H.url.substr(H.url.length() - 5, 5) == ".smil"){ std::string host = H.GetHeader("Host"); if (host.find(':')){ host.resize(host.find(':')); } std::string port, url_rel; IPC::semaphore configLock(SEM_CONF, O_CREAT | O_RDWR, ACCESSPERMS, 1); configLock.wait(); IPC::sharedPage serverCfg(SHM_CONF, DEFAULT_CONF_PAGE_SIZE); DTSC::Scan prtcls = DTSC::Scan(serverCfg.mapped, serverCfg.len).getMember("config").getMember("protocols"); DTSC::Scan capa = DTSC::Scan(serverCfg.mapped, serverCfg.len).getMember("capabilities").getMember("connectors").getMember("RTMP"); unsigned int pro_cnt = prtcls.getSize(); for (unsigned int i = 0; i < pro_cnt; ++i){ if (prtcls.getIndice(i).getMember("connector").asString() != "RTMP"){ continue; } port = prtcls.getIndice(i).getMember("port").asString(); //get the default port if none is set if (!port.size()){ port = capa.getMember("optional").getMember("port").getMember("default").asString(); } //extract url url_rel = capa.getMember("url_rel").asString(); if (url_rel.find('$')){ url_rel.resize(url_rel.find('$')); } } std::string trackSources;//this string contains all track sources for MBR smil DTSC::Scan tracks = DTSC::Scan(serverCfg.mapped, serverCfg.len).getMember("streams").getMember(streamName).getMember("meta").getMember("tracks"); unsigned int track_ctr = tracks.getSize(); for (unsigned int i = 0; i < track_ctr; ++i){//for all video tracks DTSC::Scan trk = tracks.getIndice(i); if (trk.getMember("type").asString() == "video"){ trackSources += "