diff --git a/gulpfile.js b/gulpfile.js index e4c2fc48b0..35ef21020b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -62,7 +62,8 @@ gulp.task('test:worker', shell.task( `cd worker && ./out/${process.env.MEDIASOUP_BUILDTYPE === 'Debug' ? 'Debug' : 'Release'}/mediasoup-worker-test --invisibles --use-colour=yes` ], { - verbose : true + verbose : true, + env : { DEBUG: '*ABORT* *WARN*' } } )); diff --git a/worker/include/Logger.hpp b/worker/include/Logger.hpp index ac8ca04a64..d005db036e 100644 --- a/worker/include/Logger.hpp +++ b/worker/include/Logger.hpp @@ -77,6 +77,27 @@ #define _MS_LOG_DEV_ENABLED false #endif +// Usage: +// MS_DEBUG_DEV("Leading text "MS_UINT16_TO_BINARY_PATTERN, MS_UINT16_TO_BINARY(value)); +#define MS_UINT16_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c" +#define MS_UINT16_TO_BINARY(value) \ + ((value & 0x8000) ? '1' : '0'), \ + ((value & 0x4000) ? '1' : '0'), \ + ((value & 0x2000) ? '1' : '0'), \ + ((value & 0x1000) ? '1' : '0'), \ + ((value & 0x800) ? '1' : '0'), \ + ((value & 0x400) ? '1' : '0'), \ + ((value & 0x200) ? '1' : '0'), \ + ((value & 0x100) ? '1' : '0'), \ + ((value & 0x80) ? '1' : '0'), \ + ((value & 0x40) ? '1' : '0'), \ + ((value & 0x20) ? '1' : '0'), \ + ((value & 0x10) ? '1' : '0'), \ + ((value & 0x08) ? '1' : '0'), \ + ((value & 0x04) ? '1' : '0'), \ + ((value & 0x02) ? '1' : '0'), \ + ((value & 0x01) ? '1' : '0') + class Logger { public: diff --git a/worker/include/RTC/RTCP/FeedbackRtpNack.hpp b/worker/include/RTC/RTCP/FeedbackRtpNack.hpp index 473b66a297..07b62f84cf 100644 --- a/worker/include/RTC/RTCP/FeedbackRtpNack.hpp +++ b/worker/include/RTC/RTCP/FeedbackRtpNack.hpp @@ -9,7 +9,7 @@ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -0 | PID | BPL | + | PID | BPL | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ diff --git a/worker/include/RTC/RtpReceiver.hpp b/worker/include/RTC/RtpReceiver.hpp index 3f4c6e96d3..09ba0a4459 100644 --- a/worker/include/RTC/RtpReceiver.hpp +++ b/worker/include/RTC/RtpReceiver.hpp @@ -20,7 +20,8 @@ namespace RTC // the corresponding header files. class Transport; - class RtpReceiver + class RtpReceiver : + public RtpStreamRecv::Listener { public: /** @@ -58,6 +59,10 @@ namespace RTC private: void ClearRtpStreams(); + /* Pure virtual methods inherited from RTC::RtpStreamRecv::Listener. */ + public: + virtual void onNackRequired(RTC::RtpStreamRecv* rtpStream, uint16_t seq, uint16_t bitmask) override; + public: // Passed by argument. uint32_t rtpReceiverId; diff --git a/worker/include/RTC/RtpStream.hpp b/worker/include/RTC/RtpStream.hpp index dd3a83ad41..69ecc63eab 100644 --- a/worker/include/RTC/RtpStream.hpp +++ b/worker/include/RTC/RtpStream.hpp @@ -9,17 +9,23 @@ namespace RTC class RtpStream { public: - explicit RtpStream(uint32_t clockRate); + explicit RtpStream(uint32_t ssrc, uint32_t clockRate); virtual ~RtpStream(); + uint32_t GetSsrc(); virtual bool ReceivePacket(RTC::RtpPacket* packet); private: void InitSeq(uint16_t seq); bool UpdateSeq(uint16_t seq); + /* Pure virtual methods that must be implemented by the subclass. */ + protected: + virtual void onInitSeq() = 0; + protected: // Given as argument. + uint32_t ssrc = 0; uint32_t clockRate = 0; bool started = false; // Whether at least a RTP packet has been received. // https://tools.ietf.org/html/rfc3550#appendix-A.1 stuff. @@ -34,6 +40,14 @@ namespace RTC // Others. uint32_t max_timestamp = 0; // Highest timestamp seen. }; + + /* Inline instance methods. */ + + inline + uint32_t RtpStream::GetSsrc() + { + return this->ssrc; + } } #endif diff --git a/worker/include/RTC/RtpStreamRecv.hpp b/worker/include/RTC/RtpStreamRecv.hpp index 14f565a86c..fb0a6edaad 100644 --- a/worker/include/RTC/RtpStreamRecv.hpp +++ b/worker/include/RTC/RtpStreamRecv.hpp @@ -12,22 +12,39 @@ namespace RTC public RtpStream { public: - explicit RtpStreamRecv(uint32_t clockRate); + class Listener + { + public: + virtual void onNackRequired(RTC::RtpStreamRecv* rtpStream, uint16_t seq, uint16_t bitmask) = 0; + }; + + public: + explicit RtpStreamRecv(Listener* listener, uint32_t ssrc, uint32_t clockRate, bool useNack); virtual ~RtpStreamRecv(); Json::Value toJson(); - virtual bool ReceivePacket(RTC::RtpPacket* packet); + virtual bool ReceivePacket(RTC::RtpPacket* packet) override; RTC::RTCP::ReceiverReport* GetRtcpReceiverReport(); void ReceiveRtcpSenderReport(RTC::RTCP::SenderReport* report); private: void CalculateJitter(uint32_t rtpTimestamp); + void MayTriggerNack(RTC::RtpPacket* packet); + void ResetNack(); + + /* Pure virtual methods inherited from RtpStream. */ + protected: + virtual void onInitSeq() override; private: + // Passed by argument. + Listener* listener = nullptr; + bool useNack = false; uint32_t last_sr_timestamp = 0; // The middle 32 bits out of 64 in the NTP timestamp received in the most recent sender report. uint64_t last_sr_received = 0; // Wallclock time representing the most recent sender report arrival. uint32_t transit = 0; // Relative trans time for prev pkt. uint32_t jitter = 0; // Estimated jitter. + uint32_t last_seq32 = 0; // Extended seq number of last valid packet. }; } diff --git a/worker/include/RTC/RtpStreamSend.hpp b/worker/include/RTC/RtpStreamSend.hpp index 65f3d6e25b..aa18e34dae 100644 --- a/worker/include/RTC/RtpStreamSend.hpp +++ b/worker/include/RTC/RtpStreamSend.hpp @@ -26,11 +26,11 @@ namespace RTC }; public: - RtpStreamSend(uint32_t clockRate, size_t bufferSize); + RtpStreamSend(uint32_t ssrc, uint32_t clockRate, size_t bufferSize); virtual ~RtpStreamSend(); Json::Value toJson(); - bool ReceivePacket(RTC::RtpPacket* packet); + bool ReceivePacket(RTC::RtpPacket* packet) override; void RequestRtpRetransmission(uint16_t seq, uint16_t bitmask, std::vector& container); RTC::RTCP::SenderReport* GetRtcpSenderReport(uint64_t now); @@ -38,6 +38,10 @@ namespace RTC void ClearBuffer(); void StorePacket(RTC::RtpPacket* packet); + /* Pure virtual methods inherited from RtpStream. */ + protected: + virtual void onInitSeq() override; + private: std::vector storage; typedef std::list Buffer; diff --git a/worker/include/common.hpp b/worker/include/common.hpp index b5cbeae5b0..984b944fa6 100644 --- a/worker/include/common.hpp +++ b/worker/include/common.hpp @@ -2,7 +2,7 @@ #define MS_COMMON_HPP #include // std::addressof() -#include // std::transform(), std::find() +#include // std::transform(), std::find(), std::min(), std::max() #include // size_t #include // uint8_t, etc #include // PRIu64, etc diff --git a/worker/mediasoup-worker.gyp b/worker/mediasoup-worker.gyp index ddc526b5ac..ed88d8c327 100644 --- a/worker/mediasoup-worker.gyp +++ b/worker/mediasoup-worker.gyp @@ -230,7 +230,7 @@ }, { 'target_name': 'mediasoup-worker-test', - 'defines': [ 'MS_LOG_STD' ], + 'defines': [ 'MS_TEST', 'MS_LOG_STD' ], 'sources': [ # C++ source files @@ -239,6 +239,7 @@ 'test/test-rtp.cpp', 'test/test-rtcp.cpp', 'test/test-bitrate.cpp', + 'test/test-rtpstreamrecv.cpp', # C++ include files 'test/catch.hpp', 'test/helpers.hpp' diff --git a/worker/src/RTC/RTCP/FeedbackRtpNack.cpp b/worker/src/RTC/RTCP/FeedbackRtpNack.cpp index 8bb327b340..760b61cb8c 100644 --- a/worker/src/RTC/RTCP/FeedbackRtpNack.cpp +++ b/worker/src/RTC/RTCP/FeedbackRtpNack.cpp @@ -4,6 +4,7 @@ #include "RTC/RTCP/FeedbackRtpNack.hpp" #include "Logger.hpp" #include +#include // std::bitset() namespace RTC { namespace RTCP { @@ -50,9 +51,11 @@ namespace RTC { namespace RTCP { MS_TRACE(); + std::bitset<16> nack_bitset(this->GetLostPacketBitmask()); + MS_DUMP(""); MS_DUMP(" pid : %" PRIu16, this->GetPacketId()); - MS_DUMP(" bpl : %" PRIu16, this->GetLostPacketBitmask()); + MS_DUMP(" bpl : %s", nack_bitset.to_string().c_str()); MS_DUMP(""); } }} diff --git a/worker/src/RTC/RtpReceiver.cpp b/worker/src/RTC/RtpReceiver.cpp index dc23192773..000547b404 100644 --- a/worker/src/RTC/RtpReceiver.cpp +++ b/worker/src/RTC/RtpReceiver.cpp @@ -3,7 +3,8 @@ #include "RTC/RtpReceiver.hpp" #include "RTC/Transport.hpp" -#include "Utils.hpp" +#include "RTC/RTCP/FeedbackRtp.hpp" +#include "RTC/RTCP/FeedbackRtpNack.hpp" #include "MediaSoupError.hpp" #include "Logger.hpp" @@ -69,10 +70,10 @@ namespace RTC static const Json::StaticString k_rtpRawEventEnabled("rtpRawEventEnabled"); static const Json::StaticString k_rtpObjectEventEnabled("rtpObjectEventEnabled"); static const Json::StaticString k_rtpStreams("rtpStreams"); - static const Json::StaticString k_ssrc("ssrc"); static const Json::StaticString k_rtpStream("rtpStream"); Json::Value json(Json::objectValue); + Json::Value json_rtpStreams(Json::arrayValue); json[k_rtpReceiverId] = (Json::UInt)this->rtpReceiverId; @@ -89,19 +90,13 @@ namespace RTC json[k_rtpObjectEventEnabled] = this->rtpObjectEventEnabled; - json[k_rtpStreams] = Json::arrayValue; - for (auto& kv : this->rtpStreams) { - auto ssrc = kv.first; auto rtpStream = kv.second; - Json::Value json_rtpStream(Json::objectValue); - - json_rtpStream[k_ssrc] = (Json::UInt)ssrc; - json_rtpStream[k_rtpStream] = rtpStream->toJson(); - json[k_rtpStreams].append(json_rtpStream); + json_rtpStreams.append(rtpStream->toJson()); } + json[k_rtpStreams] = json_rtpStreams; return json; } @@ -199,9 +194,11 @@ namespace RTC // Get the clock rate of the stream/encoding. uint32_t streamClockRate = this->rtpParameters->GetClockRateForEncoding(encoding); + // TODO: Let's assume that, if video, NACK is negotiated. Must do this better. + bool useNack = (this->kind != RTC::Media::Kind::AUDIO); // Create a RtpStreamRecv for receiving a media stream. - this->rtpStreams[ssrc] = new RTC::RtpStreamRecv(streamClockRate); + this->rtpStreams[ssrc] = new RTC::RtpStreamRecv(this, ssrc, streamClockRate, useNack); } break; @@ -324,11 +321,10 @@ namespace RTC for (auto& kv : this->rtpStreams) { - auto ssrc = kv.first; auto rtpStream = kv.second; RTC::RTCP::ReceiverReport* report = rtpStream->GetRtcpReceiverReport(); - report->SetSsrc(ssrc); + report->SetSsrc(rtpStream->GetSsrc()); packet->AddReceiverReport(report); } @@ -388,4 +384,17 @@ namespace RTC this->rtpStreams.clear(); } + + void RtpReceiver::onNackRequired(RTC::RtpStreamRecv* rtpStream, uint16_t seq, uint16_t bitmask) + { + if (!this->transport) + return; + + RTC::RTCP::FeedbackRtpNackPacket packet(0, rtpStream->GetSsrc()); + RTC::RTCP::NackItem* nackItem = new RTC::RTCP::NackItem(seq, bitmask); + + packet.AddItem(nackItem); + packet.Serialize(RtpReceiver::rtcpBuffer); + this->transport->SendRtcpPacket(&packet); + } } diff --git a/worker/src/RTC/RtpSender.cpp b/worker/src/RTC/RtpSender.cpp index 55e519a42c..941071a33d 100644 --- a/worker/src/RTC/RtpSender.cpp +++ b/worker/src/RTC/RtpSender.cpp @@ -258,8 +258,7 @@ namespace RTC // NOTE: We assume a single stream/encoding when sending to remote peers. auto encoding = this->rtpParameters->encodings[0]; - - // Set the RtpStreamSend. + uint32_t ssrc = encoding.ssrc; uint32_t streamClockRate = this->rtpParameters->GetClockRateForEncoding(encoding); // Create a RtpStreamSend for sending a single media stream. @@ -268,15 +267,15 @@ namespace RTC case RTC::Media::Kind::VIDEO: case RTC::Media::Kind::DEPTH: { - // Buffer up to 100 packets. - this->rtpStream = new RTC::RtpStreamSend(streamClockRate, 100); + // Buffer up to N packets. + this->rtpStream = new RTC::RtpStreamSend(ssrc, streamClockRate, 200); break; } case RTC::Media::Kind::AUDIO: { // No buffer for audio streams. - this->rtpStream = new RTC::RtpStreamSend(streamClockRate, 0); + this->rtpStream = new RTC::RtpStreamSend(ssrc, streamClockRate, 0); break; } diff --git a/worker/src/RTC/RtpStream.cpp b/worker/src/RTC/RtpStream.cpp index b3698d1acf..ccfae35ef3 100644 --- a/worker/src/RTC/RtpStream.cpp +++ b/worker/src/RTC/RtpStream.cpp @@ -15,7 +15,8 @@ namespace RTC { /* Instance methods. */ - RtpStream::RtpStream(uint32_t clockRate) : + RtpStream::RtpStream(uint32_t ssrc, uint32_t clockRate) : + ssrc(ssrc), clockRate(clockRate) { MS_TRACE(); @@ -45,7 +46,10 @@ namespace RTC // If not a valid packet ignore it. if (!UpdateSeq(seq)) { - MS_WARN_TAG(rtp, "invalid packet [seq:%" PRIu16 "]", packet->GetSequenceNumber()); + if (!this->probation) + { + MS_WARN_TAG(rtp, "invalid packet [seq:%" PRIu16 "]", packet->GetSequenceNumber()); + } return false; } @@ -71,6 +75,9 @@ namespace RTC this->expected_prior = 0; // Also reset the highest seen RTP timestamp. this->max_timestamp = 0; + + // Call the onInitSeq method of the child. + onInitSeq(); } bool RtpStream::UpdateSeq(uint16_t seq) diff --git a/worker/src/RTC/RtpStreamRecv.cpp b/worker/src/RTC/RtpStreamRecv.cpp index 615b3b64fc..8d8719f5ea 100644 --- a/worker/src/RTC/RtpStreamRecv.cpp +++ b/worker/src/RTC/RtpStreamRecv.cpp @@ -4,13 +4,16 @@ #include "RTC/RtpStreamRecv.hpp" #include "DepLibUV.hpp" #include "Logger.hpp" +#include // std::bitset() namespace RTC { /* Instance methods. */ - RtpStreamRecv::RtpStreamRecv(uint32_t clockRate) : - RtpStream::RtpStream(clockRate) + RtpStreamRecv::RtpStreamRecv(Listener* listener, uint32_t ssrc, uint32_t clockRate, bool useNack) : + RtpStream::RtpStream(ssrc, clockRate), + listener(listener), + useNack(useNack) { MS_TRACE(); } @@ -25,6 +28,7 @@ namespace RTC MS_TRACE(); static Json::Value null_data(Json::nullValue); + static const Json::StaticString k_ssrc("ssrc"); static const Json::StaticString k_clockRate("clockRate"); static const Json::StaticString k_received("received"); static const Json::StaticString k_maxTimestamp("maxTimestamp"); @@ -33,6 +37,7 @@ namespace RTC Json::Value json(Json::objectValue); + json[k_ssrc] = (Json::UInt)this->ssrc; json[k_clockRate] = (Json::UInt)this->clockRate; json[k_received] = (Json::UInt)this->received; json[k_maxTimestamp] = (Json::UInt)this->max_timestamp; @@ -54,6 +59,10 @@ namespace RTC // Calculate Jitter. CalculateJitter(packet->GetTimestamp()); + // May trigger a NACK to the sender. + if (this->useNack) + MayTriggerNack(packet); + return true; } @@ -130,4 +139,52 @@ namespace RTC if (d < 0) d = -d; this->jitter += (1./16.) * ((double)d - this->jitter); } + + void RtpStreamRecv::MayTriggerNack(RTC::RtpPacket* packet) + { + uint32_t seq32 = (uint32_t)packet->GetSequenceNumber() + this->cycles; + + // If this is the first packet, just update last seen extended seq number. + if (this->last_seq32 == 0) + { + this->last_seq32 = (seq32 != 0 ? seq32 : 1); + return; + } + + int32_t diff_seq32 = seq32 - this->last_seq32; + + // If the received seq is older than the last seen, ignore. + if (diff_seq32 < 1) + return; + // Otherwise, update the last seen seq. + else + this->last_seq32 = seq32; + + // Just received next expected seq, do nothing else. + if (diff_seq32 == 1) + return; + + // Some packet(s) is/are missing, trigger a NACK. + uint8_t nack_bitmask_count = std::min(diff_seq32 - 2, 16); + uint32_t nack_seq32 = this->last_seq32 - nack_bitmask_count - 1; + std::bitset<16> nack_bitset(0); + + for (uint8_t i = 0; i < nack_bitmask_count; ++i) + { + nack_bitset[i] = 1; + } + + uint16_t nack_seq = (uint16_t)nack_seq32; + uint16_t nack_bitmask = (uint16_t)nack_bitset.to_ulong(); + + MS_DEBUG_TAG(rtcp, "NACK triggered [ssrc:%" PRIu32 ", seq:%" PRIu16 ", bitmask:" MS_UINT16_TO_BINARY_PATTERN "]", + this->ssrc, nack_seq, MS_UINT16_TO_BINARY(nack_bitmask)); + + this->listener->onNackRequired(this, nack_seq, nack_bitmask); + } + + void RtpStreamRecv::onInitSeq() + { + this->last_seq32 = 0; + } } diff --git a/worker/src/RTC/RtpStreamSend.cpp b/worker/src/RTC/RtpStreamSend.cpp index 0952824a49..dbb63a4a05 100644 --- a/worker/src/RTC/RtpStreamSend.cpp +++ b/worker/src/RTC/RtpStreamSend.cpp @@ -9,33 +9,12 @@ #define RTP_SEQ_MOD (1<<16) #define MAX_RETRANSMISSION_AGE 200 // Don't retransmit packets older than this (ms). -// Usage: -// MS_DEBUG_DEV("Leading text "UINT16_TO_BINARY_PATTERN, UINT16_TO_BINARY(value)); -#define UINT16_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c" -#define UINT16_TO_BINARY(value) \ - ((value & 0x8000) ? '1' : '0'), \ - ((value & 0x4000) ? '1' : '0'), \ - ((value & 0x2000) ? '1' : '0'), \ - ((value & 0x1000) ? '1' : '0'), \ - ((value & 0x800) ? '1' : '0'), \ - ((value & 0x400) ? '1' : '0'), \ - ((value & 0x200) ? '1' : '0'), \ - ((value & 0x100) ? '1' : '0'), \ - ((value & 0x80) ? '1' : '0'), \ - ((value & 0x40) ? '1' : '0'), \ - ((value & 0x20) ? '1' : '0'), \ - ((value & 0x10) ? '1' : '0'), \ - ((value & 0x08) ? '1' : '0'), \ - ((value & 0x04) ? '1' : '0'), \ - ((value & 0x02) ? '1' : '0'), \ - ((value & 0x01) ? '1' : '0') - namespace RTC { /* Instance methods. */ - RtpStreamSend::RtpStreamSend(uint32_t clockRate, size_t bufferSize) : - RtpStream::RtpStream(clockRate), + RtpStreamSend::RtpStreamSend(uint32_t ssrc, uint32_t clockRate, size_t bufferSize) : + RtpStream::RtpStream(ssrc, clockRate), storage(bufferSize) { MS_TRACE(); @@ -54,6 +33,7 @@ namespace RTC MS_TRACE(); static Json::Value null_data(Json::nullValue); + static const Json::StaticString k_ssrc("ssrc"); static const Json::StaticString k_clockRate("clockRate"); static const Json::StaticString k_received("received"); static const Json::StaticString k_maxTimestamp("maxTimestamp"); @@ -61,6 +41,7 @@ namespace RTC Json::Value json(Json::objectValue); + json[k_ssrc] = (Json::UInt)this->ssrc; json[k_clockRate] = (Json::UInt)this->clockRate; json[k_received] = (Json::UInt)this->received; json[k_maxTimestamp] = (Json::UInt)this->max_timestamp; @@ -159,7 +140,7 @@ namespace RTC uint8_t bitmask_counter = 0; bool too_old_packet_found = false; - MS_DEBUG_DEV("loop [bitmask:" UINT16_TO_BINARY_PATTERN "]", UINT16_TO_BINARY(bitmask)); + MS_DEBUG_DEV("loop [bitmask:" MS_UINT16_TO_BINARY_PATTERN "]", MS_UINT16_TO_BINARY(bitmask)); while (requested || bitmask != 0) { @@ -226,8 +207,8 @@ namespace RTC // log it. if (first_packet_sent && orig_bitmask != sent_bitmask) { - MS_WARN_TAG(rtcp, "first packet sent but not all the bitmask packets [bitmask:" UINT16_TO_BINARY_PATTERN ", sent:" UINT16_TO_BINARY_PATTERN "]", - UINT16_TO_BINARY(orig_bitmask), UINT16_TO_BINARY(sent_bitmask)); + MS_WARN_TAG(rtcp, "first packet sent but not all the bitmask packets [bitmask:" MS_UINT16_TO_BINARY_PATTERN ", sent:" MS_UINT16_TO_BINARY_PATTERN "]", + MS_UINT16_TO_BINARY(orig_bitmask), MS_UINT16_TO_BINARY(sent_bitmask)); } // Set the next container element to null. @@ -351,4 +332,9 @@ namespace RTC // Update the new buffer item so it points to the cloned packed. (*new_buffer_it).packet = packet->Clone(store); } + + void RtpStreamSend::onInitSeq() + { + // Do nothing. + } } diff --git a/worker/src/RTC/SrtpSession.cpp b/worker/src/RTC/SrtpSession.cpp index f5ed514796..3465675da3 100644 --- a/worker/src/RTC/SrtpSession.cpp +++ b/worker/src/RTC/SrtpSession.cpp @@ -159,7 +159,7 @@ namespace RTC err = srtp_unprotect(this->session, (void*)data, (int*)len); if (DepLibSRTP::IsError(err)) { - MS_WARN_TAG(srtp, "srtp_unprotect() failed: %s", DepLibSRTP::GetErrorString(err)); + MS_DEBUG_TAG(srtp, "srtp_unprotect() failed: %s", DepLibSRTP::GetErrorString(err)); return false; } @@ -206,7 +206,7 @@ namespace RTC err = srtp_unprotect_rtcp(this->session, (void*)data, (int*)len); if (DepLibSRTP::IsError(err)) { - MS_WARN_TAG(srtp, "srtp_unprotect_rtcp() failed: %s", DepLibSRTP::GetErrorString(err)); + MS_DEBUG_TAG(srtp, "srtp_unprotect_rtcp() failed: %s", DepLibSRTP::GetErrorString(err)); return false; } diff --git a/worker/test/test-nack.cpp b/worker/test/test-nack.cpp index 02a77ebbdd..c065d15a4c 100644 --- a/worker/test/test-nack.cpp +++ b/worker/test/test-nack.cpp @@ -60,7 +60,7 @@ SCENARIO("NACK and RTP packets retransmission", "[rtp][rtcp]") REQUIRE(packet5->GetTimestamp() == 1533796931); // Create a RtpStreamSend. - RtpStreamSend* stream = new RtpStreamSend(90000, 200); + RtpStreamSend* stream = new RtpStreamSend(packet1->GetSsrc(), 90000, 200); // Receive all the packets in order into the stream. stream->ReceivePacket(packet1); diff --git a/worker/test/test-rtcp.cpp b/worker/test/test-rtcp.cpp index 3329c35b8c..ac5e3c6de2 100644 --- a/worker/test/test-rtcp.cpp +++ b/worker/test/test-rtcp.cpp @@ -134,14 +134,14 @@ SCENARIO("parse RTCP packets", "[parser][rtcp]") // Create sdes item. SdesItem* item = new SdesItem(type, len, value.c_str()); - // // Create sdes chunk. + // Create sdes chunk. SdesChunk chunk(ssrc); chunk.AddItem(item); - // // Check chunk content. + // Check chunk content. REQUIRE(chunk.GetSsrc() == ssrc); - // // Check item content. + // Check item content. item = *(chunk.Begin()); REQUIRE(item->GetType() == type); diff --git a/worker/test/test-rtpstreamrecv.cpp b/worker/test/test-rtpstreamrecv.cpp new file mode 100644 index 0000000000..3a10c17298 --- /dev/null +++ b/worker/test/test-rtpstreamrecv.cpp @@ -0,0 +1,146 @@ +#include "include/catch.hpp" +#include "include/helpers.hpp" +#include "common.hpp" +#include "RTC/RtpPacket.hpp" +#include "RTC/RtpStreamRecv.hpp" +#include "Logger.hpp" +#include // std::bitset() + +using namespace RTC; + +SCENARIO("receive RTP packets and trigger NACK", "[rtp][rtpstream]") +{ + class RtpStreamRecvListener : + public RtpStreamRecv::Listener + { + public: + virtual void onNackRequired(RtpStreamRecv* rtpStream, uint16_t seq, uint16_t bitmask) override + { + std::bitset<16> nack_bitset(bitmask); + + INFO("NACK required [seq:" << seq << ", bitmask:" << nack_bitset << "]"); + + REQUIRE(this->should_trigger == true); + REQUIRE(seq == this->expected_nack_seq); + REQUIRE(bitmask == this->expected_nack_bitmask); + + this->should_trigger = false; + } + + public: + bool should_trigger = false; + uint16_t expected_nack_seq = 0; + uint16_t expected_nack_bitmask = 0; + }; + + SECTION("loose packets newer than 16 seq units") + { + uint8_t buffer[] = + { + 0b10000000, 0b00000001, 0, 100, + 0, 0, 0, 4, + 0, 0, 0, 5 + }; + + RtpPacket* packet = RtpPacket::Parse(buffer, sizeof(buffer)); + + if (!packet) + FAIL("not a RTP packet"); + + REQUIRE(packet->GetSequenceNumber() == 100); + + RtpStreamRecvListener listener; + RtpStreamRecv rtpStream(&listener, packet->GetSsrc(), 90000, true); + + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(101); + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(98); + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(104); + listener.should_trigger = true; + listener.expected_nack_seq = 102; + listener.expected_nack_bitmask = 0b0000000000000001; + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(104); + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(102); + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(103); + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(108); + listener.should_trigger = true; + listener.expected_nack_seq = 105; + listener.expected_nack_bitmask = 0b0000000000000011; + rtpStream.ReceivePacket(packet); + + delete packet; + } + + SECTION("loose packets older than 16 seq units") + { + uint8_t buffer[] = + { + 0b10000000, 0b00000001, 0, 100, + 0, 0, 0, 4, + 0, 0, 0, 5 + }; + + RtpPacket* packet = RtpPacket::Parse(buffer, sizeof(buffer)); + + if (!packet) + FAIL("not a RTP packet"); + + REQUIRE(packet->GetSequenceNumber() == 100); + + RtpStreamRecvListener listener; + RtpStreamRecv rtpStream(&listener, packet->GetSsrc(), 90000, true); + + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(120); + listener.should_trigger = true; + listener.expected_nack_seq = 103; + listener.expected_nack_bitmask = 0b1111111111111111; + rtpStream.ReceivePacket(packet); + + delete packet; + } + + SECTION("increase seq cycles and loose packets older than 16 seq units") + { + uint8_t buffer[] = + { + 0b10000000, 0b00000001, 0b11111111, 0b11111111, + 0, 0, 0, 4, + 0, 0, 0, 5 + }; + + RtpPacket* packet = RtpPacket::Parse(buffer, sizeof(buffer)); + + if (!packet) + FAIL("not a RTP packet"); + + REQUIRE(packet->GetSequenceNumber() == 65535); + + RtpStreamRecvListener listener; + RtpStreamRecv rtpStream(&listener, packet->GetSsrc(), 90000, true); + + rtpStream.ReceivePacket(packet); + + packet->SetSequenceNumber(20); + listener.should_trigger = true; + listener.expected_nack_seq = 3; + listener.expected_nack_bitmask = 0b1111111111111111; + rtpStream.ReceivePacket(packet); + + delete packet; + } +}