From 7d4fd248665de61a15ca3097cff576179ae75d61 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Fri, 22 Jul 2016 11:01:56 +0200 Subject: [PATCH 1/2] Resolved play/pause issue with JWPlayer --- src/output/output_rtmp.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/output/output_rtmp.cpp b/src/output/output_rtmp.cpp index 6d10e10b..bb921c4f 100644 --- a/src/output/output_rtmp.cpp +++ b/src/output/output_rtmp.cpp @@ -475,7 +475,22 @@ namespace Mist { sendCommand(amfReply, messageType, streamId); return; } //createStream - if ((amfData.getContentP(0)->StrValue() == "closeStream") || (amfData.getContentP(0)->StrValue() == "deleteStream")) { + if (amfData.getContentP(0)->StrValue() == "closeStream"){ + myConn.SendNow(RTMPStream::SendUSR(1, 1)); //send UCM StreamEOF (1), stream 1 + AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(AMF::Object("", (double)0)); //transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info + amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Stop")); + amfreply.getContentP(3)->addContent(AMF::Object("description", "Stream stopped")); + amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); + amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfreply, 20, 1); + return; + } + if (amfData.getContentP(0)->StrValue() == "deleteStream") { stop(); onFinish(); return; From dd612a26437988b03f78b59a553255e2a1aa6bda Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 22 Jul 2016 11:25:38 +0200 Subject: [PATCH 2/2] RTMP fixes for streams > 5h in duration. --- lib/rtmpchunks.cpp | 1 + src/output/output_rtmp.cpp | 24 ++++++++++++++++++++---- src/output/output_rtmp.h | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 25b2795b..ce83ccdc 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -465,6 +465,7 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer) { timestamp += indata[i++ ] * 256 * 256; timestamp += indata[i++ ] * 256 * 256 * 256; ts_delta = timestamp; + DEBUG_MSG(DLVL_DONTEVEN, "Extended timestamp: %u", timestamp); } //read data if length > 0, and allocate it diff --git a/src/output/output_rtmp.cpp b/src/output/output_rtmp.cpp index bb921c4f..d00fa204 100644 --- a/src/output/output_rtmp.cpp +++ b/src/output/output_rtmp.cpp @@ -277,7 +277,7 @@ namespace Mist { } data_len += dheader_len; - unsigned int timestamp = thisPacket.getTime(); + unsigned int timestamp = thisPacket.getTime() - rtmpOffset; bool allow_short = RTMPStream::lastsend.count(4); RTMPStream::Chunk & prev = RTMPStream::lastsend[4]; @@ -424,7 +424,7 @@ namespace Mist { } app_name = amfData.getContentP(2)->getContentP("tcUrl")->StrValue(); app_name = app_name.substr(app_name.find('/', 7) + 1); - RTMPStream::chunk_snd_max = 4096; + RTMPStream::chunk_snd_max = 10240000; 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) @@ -679,8 +679,16 @@ namespace Mist { amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing!")); amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + uint64_t earliestTime = 0xffffffffffffffff; + for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + if (myMeta.tracks.count(*it) && myMeta.tracks[*it].firstms < earliestTime){ + earliestTime = myMeta.tracks[*it].firstms; + } + } + rtmpOffset = earliestTime; + amfreply.getContentP(3)->addContent(AMF::Object("timecodeOffset", (double)rtmpOffset)); sendCommand(amfreply, playMessageType, playStreamId); - RTMPStream::chunk_snd_max = 102400; //100KiB + RTMPStream::chunk_snd_max = 10240000; //10000KiB myConn.SendNow(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) //send dunno? myConn.SendNow(RTMPStream::SendUSR(32, 1)); //send UCM no clue?, stream 1 @@ -736,8 +744,16 @@ namespace Mist { amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing!")); amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + uint64_t earliestTime = 0xffffffffffffffff; + for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + if (myMeta.tracks.count(*it) && myMeta.tracks[*it].firstms < earliestTime){ + earliestTime = myMeta.tracks[*it].firstms; + } + } + rtmpOffset = earliestTime; + amfreply.getContentP(3)->addContent(AMF::Object("timecodeOffset", (double)rtmpOffset)); sendCommand(amfreply, playMessageType, playStreamId); - RTMPStream::chunk_snd_max = 102400; //100KiB + RTMPStream::chunk_snd_max = 10240000; //10000KiB myConn.SendNow(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) //send dunno? myConn.SendNow(RTMPStream::SendUSR(32, 1)); //send UCM no clue?, stream 1 diff --git a/src/output/output_rtmp.h b/src/output/output_rtmp.h index 7441de2d..6f79a7da 100644 --- a/src/output/output_rtmp.h +++ b/src/output/output_rtmp.h @@ -16,6 +16,7 @@ namespace Mist { bool isReadyForPlay(); bool onFinish(); protected: + uint64_t rtmpOffset; bool isPushing; void parseVars(std::string data); std::string app_name;