diff --git a/lib/json.cpp b/lib/json.cpp index e4175ebc..28cc543c 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -607,37 +607,51 @@ bool JSON::Value::operator!=(const JSON::Value & rhs) const { } bool JSON::Value::compareExcept(const Value & rhs, const std::set & skip) const { - if (myType != OBJECT) { - return ((*this) == rhs); - } - jsonForEachConst(*this, it){ - if (skip.count(it.key())){continue;} - if (!rhs.isMember(it.key()) || !(*it).compareExcept(rhs[it.key()], skip)) { - return false; + if (myType == OBJECT) { + jsonForEachConst(*this, it){ + if (skip.count(it.key())){continue;} + if (!rhs.isMember(it.key()) || !(*it).compareExcept(rhs[it.key()], skip)) { + return false; + } } + jsonForEachConst(rhs, it){ + if (skip.count(it.key())){continue;} + if (!(*this).isMember(it.key())){return false;} + } + return true; } - jsonForEachConst(rhs, it){ - if (skip.count(it.key())){continue;} - if (!(*this).isMember(it.key())){return false;} + if (myType == ARRAY) { + if (size() != rhs.size()){return false;} + jsonForEachConst(*this, it){ + if (!(*it).compareExcept(rhs[it.num()], skip)){return false;} + } + return true; } - return true; + return ((*this) == rhs); } bool JSON::Value::compareOnly(const Value & rhs, const std::set & check) const { - if (myType != OBJECT) { - return ((*this) == rhs); - } - jsonForEachConst(*this, it){ - if (!check.count(it.key())){continue;} - if (!rhs.isMember(it.key()) || !(*it).compareOnly(rhs[it.key()], check)) { - return false; + if (myType == OBJECT) { + jsonForEachConst(*this, it){ + if (!check.count(it.key())){continue;} + if (!rhs.isMember(it.key()) || !(*it).compareOnly(rhs[it.key()], check)) { + return false; + } } + jsonForEachConst(rhs, it){ + if (!check.count(it.key())){continue;} + if (!(*this).isMember(it.key())){return false;} + } + return true; } - jsonForEachConst(rhs, it){ - if (!check.count(it.key())){continue;} - if (!(*this).isMember(it.key())){return false;} + if (myType == ARRAY) { + if (size() != rhs.size()){return false;} + jsonForEachConst(*this, it){ + if (!(*it).compareOnly(rhs[it.num()], check)){return false;} + } + return true; } - return true; + return ((*this) == rhs); } /// Completely clears the contents of this value, diff --git a/src/analysers/analyser_dtsc.cpp b/src/analysers/analyser_dtsc.cpp index 9bc3aab5..06049469 100644 --- a/src/analysers/analyser_dtsc.cpp +++ b/src/analysers/analyser_dtsc.cpp @@ -4,14 +4,30 @@ void AnalyserDTSC::init(Util::Config &conf){ Analyser::init(conf); + JSON::Value opt; + opt["long"] = "headless"; + opt["short"] = "H"; + opt["help"] = "Parse entire file or streams as a single headless DTSC packet"; + conf.addOption("headless", opt); + opt.null(); } AnalyserDTSC::AnalyserDTSC(Util::Config &conf) : Analyser(conf){ conn = Socket::Connection(0, fileno(stdin)); totalBytes = 0; + headLess = conf.getBool("headless"); } bool AnalyserDTSC::parsePacket(){ + if (headLess){ + while (conn){ + if (!conn.spool()){Util::sleep(50);} + } + std::string dataBuf = conn.Received().remove(conn.Received().bytes(0xFFFFFFFFul)); + DTSC::Scan S((char*)dataBuf.data(), dataBuf.size()); + std::cout << S.toPrettyString() << std::endl; + return false; + } P.reInit(conn); if (conn && !P){ FAIL_MSG("Invalid DTSC packet @ byte %llu", totalBytes) diff --git a/src/analysers/analyser_dtsc.h b/src/analysers/analyser_dtsc.h index 53ead4c5..6ddd7200 100644 --- a/src/analysers/analyser_dtsc.h +++ b/src/analysers/analyser_dtsc.h @@ -8,6 +8,7 @@ public: static void init(Util::Config &conf); private: + bool headLess; DTSC::Packet P; Socket::Connection conn; uint64_t totalBytes; diff --git a/src/output/output.cpp b/src/output/output.cpp index 435b552f..14a9991e 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -173,8 +173,12 @@ namespace Mist{ /// Called when stream initialization has failed. /// The standard implementation will set isInitialized to false and close the client connection, /// thus causing the process to exit cleanly. - void Output::onFail(){ - MEDIUM_MSG("onFail"); + void Output::onFail(const std::string & msg, bool critical){ + if (critical){ + FAIL_MSG("onFail '%s': %s", streamName.c_str(), msg.c_str()); + }else{ + MEDIUM_MSG("onFail '%s': %s", streamName.c_str(), msg.c_str()); + } isInitialized = false; wantRequest = true; parseData= false; @@ -196,7 +200,7 @@ namespace Mist{ reconnect(); //if the connection failed, fail if (streamName.size() < 1){ - onFail(); + onFail("Could not connect to stream", true); return; } sought = false; @@ -365,14 +369,12 @@ namespace Mist{ if (config->hasOption("noinput") && config->getBool("noinput")){ Util::sanitizeName(streamName); if (!Util::streamAlive(streamName)){ - FAIL_MSG("Stream %s not already active - aborting initialization", streamName.c_str()); - onFail(); + onFail("Stream not active already, aborting"); return; } }else{ if (!Util::startInput(streamName, "", true, isPushing())){ - FAIL_MSG("Opening stream %s failed - aborting initialization", streamName.c_str()); - onFail(); + onFail("Stream open failed", true); return; } } @@ -385,16 +387,14 @@ namespace Mist{ nProxy.userClient = IPC::sharedClient(userPageName, PLAY_EX_SIZE, true); } if (!nProxy.userClient.isAlive()){ - FAIL_MSG("Could not register as client for %s", streamName.c_str()); - onFail(); + onFail("Could not register as client", true); return; } char pageId[NAME_BUFFER_SIZE]; snprintf(pageId, NAME_BUFFER_SIZE, SHM_STREAM_INDEX, streamName.c_str()); nProxy.metaPages[0].init(pageId, DEFAULT_STRM_PAGE_SIZE); if (!nProxy.metaPages[0].mapped){ - FAIL_MSG("Could not connect to data for %s", streamName.c_str()); - onFail(); + onFail("Could not connect to stream data", true); return; } isInitialized = true; @@ -1710,12 +1710,7 @@ namespace Mist{ snprintf(userPageName, NAME_BUFFER_SIZE, SHM_USERS, streamName.c_str()); nProxy.userClient = IPC::sharedClient(userPageName, PLAY_EX_SIZE, true); if (!nProxy.userClient.getData()){ - WARN_MSG("Player connection failure - aborting output"); - if (!onFinish()){ - myConn.close(); - }else{ - disconnect(); - } + onFail("Player connection failure - aborting output", true); return; } } @@ -1723,21 +1718,11 @@ namespace Mist{ if (isPushing() && !pushIsOngoing){ waitForStreamPushReady(); if (!nProxy.userClient.isAlive()){ - WARN_MSG("Failed to wait for buffer, aborting incoming push"); - if (!onFinish()){ - myConn.close(); - }else{ - disconnect(); - } + onFail("Failed to wait for buffer, aborting incoming push", true); return; } }else{ - INFO_MSG("Received disconnect request from input"); - if (!onFinish()){ - myConn.close(); - }else{ - disconnect(); - } + onFail("Received disconnect request from input"); return; } } diff --git a/src/output/output.h b/src/output/output.h index 3239ce90..b6f12d90 100644 --- a/src/output/output.h +++ b/src/output/output.h @@ -78,7 +78,7 @@ namespace Mist { void disconnect(); virtual void initialize(); virtual void sendHeader(); - virtual void onFail(); + virtual void onFail(const std::string & msg, bool critical = false); virtual void requestHandler(); static Util::Config * config; void playbackSleep(uint64_t millis); diff --git a/src/output/output_hls.cpp b/src/output/output_hls.cpp index 70553509..90a6b648 100644 --- a/src/output/output_hls.cpp +++ b/src/output/output_hls.cpp @@ -418,7 +418,7 @@ namespace Mist { } if (H.url.find("hls") == std::string::npos){ - onFail(); + onFail("HLS handler active, but this is not a HLS URL. Eh... What...?"); return; } @@ -434,11 +434,7 @@ namespace Mist { } initialize(); - - if (!keepGoing()){ - onFail(); - return; - } + if (!keepGoing()){return;} if (H.url.substr(5 + streamName.size(), 5) == "/push"){ std::string relPushUrl = H.url.substr(10 + streamName.size()); diff --git a/src/output/output_http.cpp b/src/output/output_http.cpp index 6a82e57a..944ccfc4 100644 --- a/src/output/output_http.cpp +++ b/src/output/output_http.cpp @@ -41,13 +41,14 @@ namespace Mist { config = cfg; } - void HTTPOutput::onFail(){ + void HTTPOutput::onFail(const std::string & msg, bool critical){ + INFO_MSG("Failing '%s': %s: %s", streamName.c_str(), H.url.c_str(), msg.c_str()); if (!webSock){ H.Clean(); //make sure no parts of old requests are left in any buffers - H.SetBody("Stream not found. Sorry, we tried."); - H.SendResponse("404", "Stream not found", myConn); + H.SetBody("Could not retrieve stream: "+msg); + H.SendResponse("404", "Error opening stream", myConn); } - Output::onFail(); + Output::onFail(msg, critical); } bool isMatch(const std::string & url, const std::string & m, std::string & streamname){ diff --git a/src/output/output_http.h b/src/output/output_http.h index ec704291..62c91ecf 100644 --- a/src/output/output_http.h +++ b/src/output/output_http.h @@ -12,7 +12,7 @@ namespace Mist { virtual ~HTTPOutput(); static void init(Util::Config * cfg); void onRequest(); - virtual void onFail(); + virtual void onFail(const std::string & msg, bool critical = false); virtual void onHTTP(){}; virtual void onIdle(){}; virtual void onWebsocketFrame(){}; diff --git a/src/output/output_http_internal.cpp b/src/output/output_http_internal.cpp index 846fbdbb..b42dabdd 100644 --- a/src/output/output_http_internal.cpp +++ b/src/output/output_http_internal.cpp @@ -66,7 +66,7 @@ namespace Mist { return !(config->getString("ip").size()); } - void OutHTTP::onFail(){ + void OutHTTP::onFail(const std::string & msg, bool critical){ std::string method = H.method; // send logo icon if (H.url.length() > 4 && H.url.substr(H.url.length() - 4, 4) == ".ico"){ @@ -81,6 +81,7 @@ namespace Mist { if (websocketHandler()){return;} JSON::Value json_resp; json_resp["error"] = "Could not retrieve stream. Sorry."; + json_resp["error_guru"] = msg; if (config->getString("nostreamtext") != ""){ json_resp["on_error"] = config->getString("nostreamtext"); } @@ -101,9 +102,7 @@ namespace Mist { H.Clean(); return; } - INFO_MSG("Failing: %s", H.url.c_str()); - HTTPOutput::onFail(); - Output::onFail(); + HTTPOutput::onFail(msg, critical); } void OutHTTP::init(Util::Config * cfg){ diff --git a/src/output/output_http_internal.h b/src/output/output_http_internal.h index 49f37d72..f0342f68 100644 --- a/src/output/output_http_internal.h +++ b/src/output/output_http_internal.h @@ -8,7 +8,7 @@ namespace Mist { ~OutHTTP(); static void init(Util::Config * cfg); static bool listenMode(); - virtual void onFail(); + virtual void onFail(const std::string & msg, bool critical = false); ///preHTTP is disabled in the internal HTTP output, since most don't need the stream alive to work virtual void preHTTP(){}; void HTMLResponse(); diff --git a/src/output/output_json.cpp b/src/output/output_json.cpp index 12345fca..d118da53 100644 --- a/src/output/output_json.cpp +++ b/src/output/output_json.cpp @@ -81,10 +81,10 @@ namespace Mist { sentHeader = true; } - void OutJSON::onFail(){ + void OutJSON::onFail(const std::string & msg, bool critical){ //Only run failure handle if we're not being persistent if (!keepReselecting){ - HTTPOutput::onFail(); + HTTPOutput::onFail(msg, critical); }else{ onFinish(); } diff --git a/src/output/output_json.h b/src/output/output_json.h index 6010d2c5..35fe3ae4 100644 --- a/src/output/output_json.h +++ b/src/output/output_json.h @@ -12,7 +12,7 @@ namespace Mist { virtual void onWebsocketConnect(); virtual void preWebsocketConnect(); bool onFinish(); - void onFail(); + void onFail(const std::string & msg, bool critical = false); void sendNext(); void sendHeader(); bool doesWebsockets(){return true;} diff --git a/src/output/output_rtmp.cpp b/src/output/output_rtmp.cpp index 3d8717a4..595fd661 100644 --- a/src/output/output_rtmp.cpp +++ b/src/output/output_rtmp.cpp @@ -583,7 +583,7 @@ namespace Mist{ myConn.SendNow(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) myConn.SendNow(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5) myConn.SendNow(RTMPStream::SendCTL(6, RTMPStream::rec_window_size)); //send rec window acknowledgement size (msg 6) - myConn.SendNow(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 + //myConn.SendNow(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 //send onBWDone packet - no clue what it is, but real server sends it... //amfReply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); //amfReply.addContent(AMF::Object("", "onBWDone"));//result @@ -600,7 +600,7 @@ namespace Mist{ amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info amfReply.addContent(AMF::Object("", (double)1)); //stream ID - we use 1 sendCommand(amfReply, messageType, streamId); - myConn.SendNow(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 + //myConn.SendNow(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 return; }//createStream if (amfData.getContentP(0)->StrValue() == "ping"){