diff --git a/webrtc/modules/interface/module_common_types.h b/webrtc/modules/interface/module_common_types.h index ec9b634b3a..bc32b226cf 100644 --- a/webrtc/modules/interface/module_common_types.h +++ b/webrtc/modules/interface/module_common_types.h @@ -98,9 +98,7 @@ enum RtpVideoCodecTypes { kRtpVideoNone, kRtpVideoGeneric, - kRtpVideoVp8, - kRtpVideoFec, - kRtpVideoI420 + kRtpVideoVp8 }; struct RTPVideoHeader { diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index bee2195953..e575fae7e5 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -166,11 +166,11 @@ 'remote_bitrate_estimator/bitrate_estimator_unittest.cc', 'remote_bitrate_estimator/rtp_to_ntp_unittest.cc', 'rtp_rtcp/source/mock/mock_rtp_payload_strategy.h', + 'rtp_rtcp/source/fec_receiver_unittest.cc', 'rtp_rtcp/source/fec_test_helper.cc', 'rtp_rtcp/source/fec_test_helper.h', 'rtp_rtcp/source/nack_rtx_unittest.cc', 'rtp_rtcp/source/producer_fec_unittest.cc', - 'rtp_rtcp/source/receiver_fec_unittest.cc', 'rtp_rtcp/source/receive_statistics_unittest.cc', 'rtp_rtcp/source/rtcp_format_remb_unittest.cc', 'rtp_rtcp/source/rtcp_sender_unittest.cc', diff --git a/webrtc/modules/rtp_rtcp/interface/fec_receiver.h b/webrtc/modules/rtp_rtcp/interface/fec_receiver.h new file mode 100644 index 0000000000..97b200f077 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/interface/fec_receiver.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_ +#define WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_ + +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class FecReceiver { + public: + static FecReceiver* Create(int32_t id, RtpData* callback); + + virtual ~FecReceiver() {} + + virtual int32_t AddReceivedRedPacket(const RTPHeader& rtp_header, + const uint8_t* incoming_rtp_packet, + int packet_length, + uint8_t ulpfec_payload_type) = 0; + + virtual int32_t ProcessReceivedFec() = 0; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_ diff --git a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h index 2ea155f9f4..707adaa0cd 100644 --- a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h +++ b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h @@ -44,8 +44,17 @@ class StreamStatistician { virtual void GetDataCounters(uint32_t* bytes_received, uint32_t* packets_received) const = 0; virtual uint32_t BitrateReceived() const = 0; + // Resets all statistics. virtual void ResetStatistics() = 0; + + // Returns true if the packet with RTP header |header| is likely to be a + // retransmitted packet, false otherwise. + virtual bool IsRetransmitOfOldPacket(const RTPHeader& header, + int min_rtt) const = 0; + + // Returns true if |sequence_number| is received in order, false otherwise. + virtual bool IsPacketInOrder(uint16_t sequence_number) const = 0; }; typedef std::map StatisticianMap; @@ -58,7 +67,7 @@ class ReceiveStatistics : public Module { // Updates the receive statistics with this packet. virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes, - bool retransmitted, bool in_order) = 0; + bool retransmitted) = 0; // Returns a map of all statisticians which have seen an incoming packet // during the last two seconds. @@ -66,16 +75,20 @@ class ReceiveStatistics : public Module { // Returns a pointer to the statistician of an ssrc. virtual StreamStatistician* GetStatistician(uint32_t ssrc) const = 0; + + // Sets the max reordering threshold in number of packets. + virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0; }; class NullReceiveStatistics : public ReceiveStatistics { public: virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes, - bool retransmitted, bool in_order) OVERRIDE; + bool retransmitted) OVERRIDE; virtual StatisticianMap GetActiveStatisticians() const OVERRIDE; virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE; virtual int32_t TimeUntilNextProcess() OVERRIDE; virtual int32_t Process() OVERRIDE; + virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE; }; } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h b/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h index f5aca81137..3ea4dcd1be 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h @@ -76,6 +76,26 @@ class RTPPayloadRegistry { const uint32_t rate, int8_t* payload_type) const; + void SetRtxStatus(bool enable, uint32_t ssrc); + + bool RtxEnabled() const; + + void SetRtxPayloadType(int payload_type); + + bool IsRtx(const RTPHeader& header) const; + + bool RestoreOriginalPacket(uint8_t** restored_packet, + const uint8_t* packet, + int* packet_length, + uint32_t original_ssrc, + const RTPHeader& header) const; + + bool IsRed(const RTPHeader& header) const; + + // Returns true if the media of this RTP packet is encapsulated within an + // extra header, such as RTX or RED. + bool IsEncapsulated(const RTPHeader& header) const; + bool GetPayloadSpecifics(uint8_t payload_type, PayloadUnion* payload) const; int GetPayloadTypeFrequency(uint8_t payload_type) const; @@ -85,22 +105,38 @@ class RTPPayloadRegistry { ModuleRTPUtility::Payload*& payload) const; void ResetLastReceivedPayloadTypes() { + CriticalSectionScoped cs(crit_sect_.get()); last_received_payload_type_ = -1; last_received_media_payload_type_ = -1; } + // This sets the payload type of the packets being received from the network + // on the media SSRC. For instance if packets are encapsulated with RED, this + // payload type will be the RED payload type. + void SetIncomingPayloadType(const RTPHeader& header); + // Returns true if the new media payload type has not changed. bool ReportMediaPayloadType(uint8_t media_payload_type); - int8_t red_payload_type() const { return red_payload_type_; } + int8_t red_payload_type() const { + CriticalSectionScoped cs(crit_sect_.get()); + return red_payload_type_; + } + int8_t ulpfec_payload_type() const { + CriticalSectionScoped cs(crit_sect_.get()); + return ulpfec_payload_type_; + } int8_t last_received_payload_type() const { + CriticalSectionScoped cs(crit_sect_.get()); return last_received_payload_type_; } void set_last_received_payload_type(int8_t last_received_payload_type) { + CriticalSectionScoped cs(crit_sect_.get()); last_received_payload_type_ = last_received_payload_type; } int8_t last_received_media_payload_type() const { + CriticalSectionScoped cs(crit_sect_.get()); return last_received_media_payload_type_; }; @@ -113,12 +149,20 @@ class RTPPayloadRegistry { const uint8_t channels, const uint32_t rate); + bool IsRtxInternal(const RTPHeader& header) const; + + scoped_ptr crit_sect_; ModuleRTPUtility::PayloadTypeMap payload_type_map_; int32_t id_; scoped_ptr rtp_payload_strategy_; int8_t red_payload_type_; + int8_t ulpfec_payload_type_; + int8_t incoming_payload_type_; int8_t last_received_payload_type_; int8_t last_received_media_payload_type_; + bool rtx_; + int8_t payload_type_rtx_; + uint32_t ssrc_rtx_; }; } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_receiver.h b/webrtc/modules/rtp_rtcp/interface/rtp_receiver.h index 40145e4978..d1f1a8182d 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_receiver.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_receiver.h @@ -70,9 +70,9 @@ class RtpReceiver { // Parses the media specific parts of an RTP packet and updates the receiver // state. This for instance means that any changes in SSRC and payload type is // detected and acted upon. - virtual bool IncomingRtpPacket(RTPHeader* rtp_header, - const uint8_t* incoming_rtp_packet, - int incoming_rtp_packet_length, + virtual bool IncomingRtpPacket(const RTPHeader& rtp_header, + const uint8_t* payload, + int payload_length, PayloadUnion payload_specific, bool in_order) = 0; @@ -80,8 +80,7 @@ class RtpReceiver { virtual NACKMethod NACK() const = 0; // Turn negative acknowledgement (NACK) requests on/off. - virtual int32_t SetNACKStatus(const NACKMethod method, - int max_reordering_threshold) = 0; + virtual void SetNACKStatus(const NACKMethod method) = 0; // Returns the last received timestamp. virtual uint32_t Timestamp() const = 0; @@ -96,24 +95,6 @@ class RtpReceiver { // Returns the current energy of the RTP stream received. virtual int32_t Energy(uint8_t array_of_energy[kRtpCsrcSize]) const = 0; - - // Enable/disable RTX and set the SSRC to be used. - virtual void SetRTXStatus(bool enable, uint32_t ssrc) = 0; - - // Returns the current RTX status and the SSRC and payload type used. - virtual void RTXStatus(bool* enable, uint32_t* ssrc, - int* payload_type) const = 0; - - // Sets the RTX payload type. - virtual void SetRtxPayloadType(int payload_type) = 0; - - // Returns true if the packet with RTP header |header| is likely to be a - // retransmitted packet, false otherwise. - virtual bool RetransmitOfOldPacket(const RTPHeader& header, int jitter, - int min_rtt) const = 0; - - // Returns true if |sequence_number| is received in order, false otherwise. - virtual bool InOrderPacket(const uint16_t sequence_number) const = 0; }; } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h index c56b71ffde..8ec9f0c8ac 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h @@ -277,17 +277,17 @@ class NullRtpFeedback : public RtpFeedback { const int frequency, const uint8_t channels, const uint32_t rate) OVERRIDE { - return 0; - } + return 0; + } virtual void OnIncomingSSRCChanged(const int32_t id, const uint32_t ssrc) OVERRIDE {} - virtual void OnIncomingCSRCChanged(const int32_t id, - const uint32_t CSRC, - const bool added) OVERRIDE {} + virtual void OnIncomingCSRCChanged(const int32_t id, + const uint32_t CSRC, + const bool added) OVERRIDE {} - virtual void ResetStatistics(uint32_t ssrc) OVERRIDE {} + virtual void ResetStatistics(uint32_t ssrc) OVERRIDE {} }; // Null object version of RtpData. diff --git a/webrtc/modules/rtp_rtcp/source/receiver_fec.cc b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc similarity index 74% rename from webrtc/modules/rtp_rtcp/source/receiver_fec.cc rename to webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc index e558fe7aa0..0dc142f867 100644 --- a/webrtc/modules/rtp_rtcp/source/receiver_fec.cc +++ b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h" +#include "webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h" #include @@ -20,33 +20,28 @@ // RFC 5109 namespace webrtc { -ReceiverFEC::ReceiverFEC(const int32_t id, RtpData* callback) + +FecReceiver* FecReceiver::Create(int32_t id, RtpData* callback) { + return new FecReceiverImpl(id, callback); +} + +FecReceiverImpl::FecReceiverImpl(const int32_t id, RtpData* callback) : id_(id), crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), recovered_packet_callback_(callback), - fec_(new ForwardErrorCorrection(id)), - payload_type_fec_(-1) {} + fec_(new ForwardErrorCorrection(id)) {} -ReceiverFEC::~ReceiverFEC() { - // Clean up DecodeFEC() +FecReceiverImpl::~FecReceiverImpl() { while (!received_packet_list_.empty()) { - ForwardErrorCorrection::ReceivedPacket* received_packet = - received_packet_list_.front(); - delete received_packet; + delete received_packet_list_.front(); received_packet_list_.pop_front(); } - assert(received_packet_list_.empty()); - if (fec_ != NULL) { fec_->ResetState(&recovered_packet_list_); delete fec_; } } -void ReceiverFEC::SetPayloadTypeFEC(const int8_t payload_type) { - CriticalSectionScoped cs(crit_sect_.get()); - payload_type_fec_ = payload_type; -} // 0 1 2 3 // 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -75,17 +70,12 @@ void ReceiverFEC::SetPayloadTypeFEC(const int8_t payload_type) { // block length: 10 bits Length in bytes of the corresponding data // block excluding header. -int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, - const uint8_t* incoming_rtp_packet, - const uint16_t payload_data_length, - bool& FECpacket) { +int32_t FecReceiverImpl::AddReceivedRedPacket( + const RTPHeader& header, const uint8_t* incoming_rtp_packet, + int packet_length, uint8_t ulpfec_payload_type) { CriticalSectionScoped cs(crit_sect_.get()); - - if (payload_type_fec_ == -1) { - return -1; - } - uint8_t REDHeaderLength = 1; + uint16_t payload_data_length = packet_length - header.headerLength; // Add to list without RED header, aka a virtual RTP packet // we remove the RED header @@ -96,26 +86,19 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, // get payload type from RED header uint8_t payload_type = - incoming_rtp_packet[rtp_header->header.headerLength] & 0x7f; + incoming_rtp_packet[header.headerLength] & 0x7f; - // use the payload_type to decide if it's FEC or coded data - if (payload_type_fec_ == payload_type) { - received_packet->is_fec = true; - FECpacket = true; - } else { - received_packet->is_fec = false; - FECpacket = false; - } - received_packet->seq_num = rtp_header->header.sequenceNumber; + received_packet->is_fec = payload_type == ulpfec_payload_type; + received_packet->seq_num = header.sequenceNumber; uint16_t blockLength = 0; - if (incoming_rtp_packet[rtp_header->header.headerLength] & 0x80) { + if (incoming_rtp_packet[header.headerLength] & 0x80) { // f bit set in RED header REDHeaderLength = 4; uint16_t timestamp_offset = - (incoming_rtp_packet[rtp_header->header.headerLength + 1]) << 8; + (incoming_rtp_packet[header.headerLength + 1]) << 8; timestamp_offset += - incoming_rtp_packet[rtp_header->header.headerLength + 2]; + incoming_rtp_packet[header.headerLength + 2]; timestamp_offset = timestamp_offset >> 2; if (timestamp_offset != 0) { // |timestampOffset| should be 0. However, it's possible this is the first @@ -127,11 +110,11 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, } blockLength = - (0x03 & incoming_rtp_packet[rtp_header->header.headerLength + 2]) << 8; - blockLength += (incoming_rtp_packet[rtp_header->header.headerLength + 3]); + (0x03 & incoming_rtp_packet[header.headerLength + 2]) << 8; + blockLength += (incoming_rtp_packet[header.headerLength + 3]); // check next RED header - if (incoming_rtp_packet[rtp_header->header.headerLength + 4] & 0x80) { + if (incoming_rtp_packet[header.headerLength + 4] & 0x80) { // more than 2 blocks in packet not supported delete received_packet; assert(false); @@ -152,7 +135,7 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, // copy the RTP header memcpy(received_packet->pkt->data, incoming_rtp_packet, - rtp_header->header.headerLength); + header.headerLength); // replace the RED payload type received_packet->pkt->data[1] &= 0x80; // reset the payload @@ -161,8 +144,8 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, // copy the payload data memcpy( - received_packet->pkt->data + rtp_header->header.headerLength, - incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength, + received_packet->pkt->data + header.headerLength, + incoming_rtp_packet + header.headerLength + REDHeaderLength, blockLength); received_packet->pkt->length = blockLength; @@ -171,11 +154,11 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, second_received_packet->pkt = new ForwardErrorCorrection::Packet; second_received_packet->is_fec = true; - second_received_packet->seq_num = rtp_header->header.sequenceNumber; + second_received_packet->seq_num = header.sequenceNumber; // copy the FEC payload data memcpy(second_received_packet->pkt->data, - incoming_rtp_packet + rtp_header->header.headerLength + + incoming_rtp_packet + header.headerLength + REDHeaderLength + blockLength, payload_data_length - REDHeaderLength - blockLength); @@ -186,7 +169,7 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, // everything behind the RED header memcpy( received_packet->pkt->data, - incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength, + incoming_rtp_packet + header.headerLength + REDHeaderLength, payload_data_length - REDHeaderLength); received_packet->pkt->length = payload_data_length - REDHeaderLength; received_packet->ssrc = @@ -195,7 +178,7 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, } else { // copy the RTP header memcpy(received_packet->pkt->data, incoming_rtp_packet, - rtp_header->header.headerLength); + header.headerLength); // replace the RED payload type received_packet->pkt->data[1] &= 0x80; // reset the payload @@ -204,12 +187,12 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, // copy the media payload data memcpy( - received_packet->pkt->data + rtp_header->header.headerLength, - incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength, + received_packet->pkt->data + header.headerLength, + incoming_rtp_packet + header.headerLength + REDHeaderLength, payload_data_length - REDHeaderLength); received_packet->pkt->length = - rtp_header->header.headerLength + payload_data_length - REDHeaderLength; + header.headerLength + payload_data_length - REDHeaderLength; } if (received_packet->pkt->length == 0) { @@ -225,7 +208,7 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, return 0; } -int32_t ReceiverFEC::ProcessReceivedFEC() { +int32_t FecReceiverImpl::ProcessReceivedFec() { crit_sect_->Enter(); if (!received_packet_list_.empty()) { // Send received media packet to VCM. diff --git a/webrtc/modules/rtp_rtcp/source/receiver_fec.h b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h similarity index 64% rename from webrtc/modules/rtp_rtcp/source/receiver_fec.h rename to webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h index 653a93e2fc..03421235c3 100644 --- a/webrtc/modules/rtp_rtcp/source/receiver_fec.h +++ b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h @@ -8,11 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_ +#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_ // This header is included to get the nested declaration of Packet structure. +#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -22,19 +23,17 @@ namespace webrtc { class CriticalSectionWrapper; -class ReceiverFEC { +class FecReceiverImpl : public FecReceiver { public: - ReceiverFEC(const int32_t id, RtpData* callback); - virtual ~ReceiverFEC(); + FecReceiverImpl(const int32_t id, RtpData* callback); + virtual ~FecReceiverImpl(); - int32_t AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header, - const uint8_t* incoming_rtp_packet, - const uint16_t payload_data_length, - bool& FECpacket); + virtual int32_t AddReceivedRedPacket(const RTPHeader& rtp_header, + const uint8_t* incoming_rtp_packet, + int packet_length, + uint8_t ulpfec_payload_type) OVERRIDE; - int32_t ProcessReceivedFEC(); - - void SetPayloadTypeFEC(const int8_t payload_type); + virtual int32_t ProcessReceivedFec() OVERRIDE; private: int id_; @@ -46,8 +45,7 @@ class ReceiverFEC { // arrives. We should remove the list. ForwardErrorCorrection::ReceivedPacketList received_packet_list_; ForwardErrorCorrection::RecoveredPacketList recovered_packet_list_; - int8_t payload_type_fec_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_ +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_ diff --git a/webrtc/modules/rtp_rtcp/source/receiver_fec_unittest.cc b/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc similarity index 88% rename from webrtc/modules/rtp_rtcp/source/receiver_fec_unittest.cc rename to webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc index 981f2370b8..2e8846c3b2 100644 --- a/webrtc/modules/rtp_rtcp/source/receiver_fec_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc @@ -14,9 +14,9 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h" #include "webrtc/modules/rtp_rtcp/source/fec_test_helper.h" #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" -#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h" using ::testing::_; using ::testing::Args; @@ -40,9 +40,8 @@ class ReceiverFecTest : public ::testing::Test { protected: virtual void SetUp() { fec_ = new ForwardErrorCorrection(0); - receiver_fec_ = new ReceiverFEC(0, &rtp_data_callback_); + receiver_fec_ = FecReceiver::Create(0, &rtp_data_callback_); generator_ = new FrameGenerator(); - receiver_fec_->SetPayloadTypeFEC(kFecPayloadType); } virtual void TearDown() { @@ -83,27 +82,23 @@ class ReceiverFecTest : public ::testing::Test { void BuildAndAddRedMediaPacket(RtpPacket* packet) { RtpPacket* red_packet = generator_->BuildMediaRedPacket(packet); - bool is_fec = false; - EXPECT_EQ(0, receiver_fec_->AddReceivedFECPacket( - &red_packet->header, red_packet->data, - red_packet->length - kRtpHeaderSize, is_fec)); + EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket( + red_packet->header.header, red_packet->data, + red_packet->length, kFecPayloadType)); delete red_packet; - EXPECT_FALSE(is_fec); } void BuildAndAddRedFecPacket(Packet* packet) { RtpPacket* red_packet = generator_->BuildFecRedPacket(packet); - bool is_fec = false; - EXPECT_EQ(0, receiver_fec_->AddReceivedFECPacket( - &red_packet->header, red_packet->data, - red_packet->length - kRtpHeaderSize, is_fec)); + EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket( + red_packet->header.header, red_packet->data, + red_packet->length, kFecPayloadType)); delete red_packet; - EXPECT_TRUE(is_fec); } ForwardErrorCorrection* fec_; MockRtpData rtp_data_callback_; - ReceiverFEC* receiver_fec_; + FecReceiver* receiver_fec_; FrameGenerator* generator_; }; @@ -127,13 +122,13 @@ TEST_F(ReceiverFecTest, TwoMediaOneFec) { std::list::iterator media_it = media_rtp_packets.begin(); BuildAndAddRedMediaPacket(*media_it); VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); // Drop one media packet. std::list::iterator fec_it = fec_packets.begin(); BuildAndAddRedFecPacket(*fec_it); ++it; VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); DeletePackets(&media_packets); } @@ -152,12 +147,12 @@ TEST_F(ReceiverFecTest, TwoMediaTwoFec) { std::list::iterator fec_it = fec_packets.begin(); BuildAndAddRedFecPacket(*fec_it); VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); ++fec_it; BuildAndAddRedFecPacket(*fec_it); ++it; VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); DeletePackets(&media_packets); } @@ -175,12 +170,12 @@ TEST_F(ReceiverFecTest, TwoFramesOneFec) { std::list::iterator it = media_rtp_packets.begin(); BuildAndAddRedMediaPacket(media_rtp_packets.front()); VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); // Drop one media packet. BuildAndAddRedFecPacket(fec_packets.front()); ++it; VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); DeletePackets(&media_packets); } @@ -199,11 +194,11 @@ TEST_F(ReceiverFecTest, OneCompleteOneUnrecoverableFrame) { std::list::iterator it = media_rtp_packets.begin(); BuildAndAddRedMediaPacket(*it); // First frame: one packet. VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); ++it; BuildAndAddRedMediaPacket(*it); // First packet of second frame. VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); DeletePackets(&media_packets); } @@ -225,12 +220,12 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) { for (; it != media_rtp_packets.end(); ++it) { BuildAndAddRedMediaPacket(*it); VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); } BuildAndAddRedFecPacket(fec_packets.front()); it = media_rtp_packets.begin(); VerifyReconstructedMediaPacket(*it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); DeletePackets(&media_packets); } @@ -267,7 +262,7 @@ TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) { BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front()); EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) .Times(1).WillRepeatedly(Return(true)); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); delayed_fec = fec_packets.front(); // Fill the FEC decoder. No packets should be dropped. @@ -282,14 +277,14 @@ TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) { BuildAndAddRedMediaPacket(*it); EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) .Times(1).WillRepeatedly(Return(true)); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); } // Add the delayed FEC packet. One packet should be reconstructed. BuildAndAddRedFecPacket(delayed_fec); EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) .Times(1).WillRepeatedly(Return(true)); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); DeletePackets(&media_packets_batch1); DeletePackets(&media_packets_batch2); @@ -311,7 +306,7 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) { BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front()); EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) .Times(1).WillRepeatedly(Return(true)); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); delayed_fec = fec_packets.front(); // Fill the FEC decoder and force the last packet to be dropped. @@ -326,7 +321,7 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) { BuildAndAddRedMediaPacket(*it); EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) .Times(1).WillRepeatedly(Return(true)); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); } // Add the delayed FEC packet. No packet should be reconstructed since the @@ -334,7 +329,7 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) { BuildAndAddRedFecPacket(delayed_fec); EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) .Times(0); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); DeletePackets(&media_packets_batch1); DeletePackets(&media_packets_batch2); @@ -358,7 +353,7 @@ TEST_F(ReceiverFecTest, OldFecPacketDropped) { BuildAndAddRedFecPacket(*it); EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) .Times(0); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); } media_packets.insert(media_packets.end(), frame_media_packets.begin(), frame_media_packets.end()); @@ -372,7 +367,7 @@ TEST_F(ReceiverFecTest, OldFecPacketDropped) { BuildAndAddRedMediaPacket(media_rtp_packets.front()); EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) .Times(1).WillRepeatedly(Return(true)); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); DeletePackets(&media_packets); } diff --git a/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc b/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc index 56b49cc5b8..3c87ebf91b 100644 --- a/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc @@ -75,8 +75,7 @@ class RtxLoopBackTransport : public webrtc::Transport { count_rtx_ssrc_(0), rtp_payload_registry_(NULL), rtp_receiver_(NULL), - module_(NULL) { - } + module_(NULL) {} void SetSendModule(RtpRtcp* rtpRtcpModule, RTPPayloadRegistry* rtp_payload_registry, @@ -112,19 +111,33 @@ class RtxLoopBackTransport : public webrtc::Transport { count_ < consecutive_drop_end_) { return len; } + int packet_length = len; + uint8_t restored_packet[1500]; + uint8_t* restored_packet_ptr = restored_packet; RTPHeader header; scoped_ptr parser(RtpHeaderParser::Create()); - if (!parser->Parse(static_cast(data), len, &header)) { + if (!parser->Parse(ptr, len, &header)) { return -1; } + if (rtp_payload_registry_->IsRtx(header)) { + // Remove the RTX header and parse the original RTP header. + EXPECT_TRUE(rtp_payload_registry_->RestoreOriginalPacket( + &restored_packet_ptr, ptr, &packet_length, rtp_receiver_->SSRC(), + header)); + if (!parser->Parse(restored_packet_ptr, packet_length, &header)) { + return -1; + } + } + restored_packet_ptr += header.headerLength; + packet_length -= header.headerLength; PayloadUnion payload_specific; if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType, - &payload_specific)) { + &payload_specific)) { return -1; } - if (!rtp_receiver_->IncomingRtpPacket(&header, - static_cast(data), - len, payload_specific, true)) { + if (!rtp_receiver_->IncomingRtpPacket(header, restored_packet_ptr, + packet_length, payload_specific, + true)) { return -1; } return len; @@ -177,7 +190,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test { EXPECT_EQ(0, rtp_rtcp_module_->SetSSRC(kTestSsrc)); EXPECT_EQ(0, rtp_rtcp_module_->SetRTCPStatus(kRtcpCompound)); - EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450)); + rtp_receiver_->SetNACKStatus(kNackRtcp); EXPECT_EQ(0, rtp_rtcp_module_->SetStorePacketsStatus(true, 600)); EXPECT_EQ(0, rtp_rtcp_module_->SetSendingStatus(true)); EXPECT_EQ(0, rtp_rtcp_module_->SetSequenceNumber(kTestSequenceNumber)); @@ -240,7 +253,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test { } void RunRtxTest(RtxMode rtx_method, int loss) { - rtp_receiver_->SetRTXStatus(true, kTestSsrc + 1); + rtp_payload_registry_.SetRtxStatus(true, kTestSsrc + 1); EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(rtx_method, true, kTestSsrc + 1)); transport_.DropEveryNthPacket(loss); diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc index c826938724..cf189903d1 100644 --- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc @@ -10,6 +10,8 @@ #include "webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h" +#include + #include "webrtc/modules/rtp_rtcp/source/bitrate.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" @@ -27,10 +29,12 @@ StreamStatisticianImpl::StreamStatisticianImpl(Clock* clock) crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), incoming_bitrate_(clock), ssrc_(0), + max_reordering_threshold_(kDefaultMaxReorderingThreshold), jitter_q4_(0), jitter_max_q4_(0), cumulative_loss_(0), jitter_q4_transmission_time_offset_(0), + last_receive_time_ms_(0), last_receive_time_secs_(0), last_receive_time_frac_(0), last_received_timestamp_(0), @@ -69,9 +73,9 @@ void StreamStatisticianImpl::ResetStatistics() { void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header, size_t bytes, - bool retransmitted, - bool in_order) { + bool retransmitted) { CriticalSectionScoped cs(crit_sect_.get()); + bool in_order = InOrderPacketInternal(header.sequenceNumber); ssrc_ = header.ssrc; incoming_bitrate_.Update(bytes); received_byte_count_ += bytes; @@ -83,6 +87,7 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header, received_seq_max_ = header.sequenceNumber; received_inorder_packet_count_ = 1; clock_->CurrentNtp(last_receive_time_secs_, last_receive_time_frac_); + last_receive_time_ms_ = clock_->TimeInMilliseconds(); return; } @@ -96,8 +101,7 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header, received_inorder_packet_count_++; // Wrong if we use RetransmitOfOldPacket. - int32_t seq_diff = - header.sequenceNumber - received_seq_max_; + int32_t seq_diff = header.sequenceNumber - received_seq_max_; if (seq_diff < 0) { // Wrap around detected. received_seq_wraps_++; @@ -147,6 +151,7 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header, last_received_timestamp_ = header.timestamp; last_receive_time_secs_ = receive_time_secs; last_receive_time_frac_ = receive_time_frac; + last_receive_time_ms_ = clock_->TimeInMilliseconds(); } else { if (retransmitted) { received_retransmitted_packets_++; @@ -162,6 +167,12 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header, received_packet_overhead_ = (15 * received_packet_overhead_ + packet_oh) >> 4; } +void StreamStatisticianImpl::SetMaxReorderingThreshold( + int max_reordering_threshold) { + CriticalSectionScoped cs(crit_sect_.get()); + max_reordering_threshold_ = max_reordering_threshold; +} + bool StreamStatisticianImpl::GetStatistics(Statistics* statistics, bool reset) { CriticalSectionScoped cs(crit_sect_.get()); if (received_seq_first_ == 0 && received_byte_count_ == 0) { @@ -275,6 +286,62 @@ void StreamStatisticianImpl::LastReceiveTimeNtp(uint32_t* secs, *frac = last_receive_time_frac_; } +bool StreamStatisticianImpl::IsRetransmitOfOldPacket( + const RTPHeader& header, int min_rtt) const { + CriticalSectionScoped cs(crit_sect_.get()); + if (InOrderPacketInternal(header.sequenceNumber)) { + return false; + } + uint32_t frequency_khz = header.payload_type_frequency / 1000; + assert(frequency_khz > 0); + + int64_t time_diff_ms = clock_->TimeInMilliseconds() - + last_receive_time_ms_; + + // Diff in time stamp since last received in order. + uint32_t timestamp_diff = header.timestamp - last_received_timestamp_; + int32_t rtp_time_stamp_diff_ms = static_cast(timestamp_diff) / + frequency_khz; + + int32_t max_delay_ms = 0; + if (min_rtt == 0) { + // Jitter standard deviation in samples. + float jitter_std = sqrt(static_cast(jitter_q4_ >> 4)); + + // 2 times the standard deviation => 95% confidence. + // And transform to milliseconds by dividing by the frequency in kHz. + max_delay_ms = static_cast((2 * jitter_std) / frequency_khz); + + // Min max_delay_ms is 1. + if (max_delay_ms == 0) { + max_delay_ms = 1; + } + } else { + max_delay_ms = (min_rtt / 3) + 1; + } + return time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms; +} + +bool StreamStatisticianImpl::IsPacketInOrder(uint16_t sequence_number) const { + CriticalSectionScoped cs(crit_sect_.get()); + return InOrderPacketInternal(sequence_number); +} + +bool StreamStatisticianImpl::InOrderPacketInternal( + uint16_t sequence_number) const { + // First packet is always in order. + if (last_receive_time_ms_ == 0) + return true; + + if (IsNewerSequenceNumber(sequence_number, received_seq_max_)) { + return true; + } else { + // If we have a restart of the remote side this packet is still in order. + return !IsNewerSequenceNumber(sequence_number, received_seq_max_ - + max_reordering_threshold_); + } +} + ReceiveStatistics* ReceiveStatistics::Create(Clock* clock) { return new ReceiveStatisticsImpl(clock); } @@ -292,8 +359,7 @@ ReceiveStatisticsImpl::~ReceiveStatisticsImpl() { } void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header, - size_t bytes, bool old_packet, - bool in_order) { + size_t bytes, bool old_packet) { CriticalSectionScoped cs(crit_sect_.get()); StatisticianImplMap::iterator it = statisticians_.find(header.ssrc); if (it == statisticians_.end()) { @@ -302,8 +368,7 @@ void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header, header.ssrc, new StreamStatisticianImpl(clock_))); it = insert_result.first; } - statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet, - in_order); + statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet); } void ReceiveStatisticsImpl::ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc) { @@ -342,6 +407,15 @@ StreamStatistician* ReceiveStatisticsImpl::GetStatistician( return it->second; } +void ReceiveStatisticsImpl::SetMaxReorderingThreshold( + int max_reordering_threshold) { + CriticalSectionScoped cs(crit_sect_.get()); + for (StatisticianImplMap::iterator it = statisticians_.begin(); + it != statisticians_.end(); ++it) { + it->second->SetMaxReorderingThreshold(max_reordering_threshold); + } +} + int32_t ReceiveStatisticsImpl::Process() { CriticalSectionScoped cs(crit_sect_.get()); for (StatisticianImplMap::iterator it = statisticians_.begin(); @@ -362,8 +436,7 @@ int32_t ReceiveStatisticsImpl::TimeUntilNextProcess() { void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header, size_t bytes, - bool retransmitted, - bool in_order) {} + bool retransmitted) {} StatisticianMap NullReceiveStatistics::GetActiveStatisticians() const { return StatisticianMap(); @@ -374,6 +447,9 @@ StreamStatistician* NullReceiveStatistics::GetStatistician( return NULL; } +void NullReceiveStatistics::SetMaxReorderingThreshold( + int max_reordering_threshold) {} + int32_t NullReceiveStatistics::TimeUntilNextProcess() { return 0; } int32_t NullReceiveStatistics::Process() { return 0; } diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h index f963123369..0af074c4a1 100644 --- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h +++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h @@ -34,17 +34,24 @@ class StreamStatisticianImpl : public StreamStatistician { uint32_t* packets_received) const OVERRIDE; virtual uint32_t BitrateReceived() const OVERRIDE; virtual void ResetStatistics() OVERRIDE; + virtual bool IsRetransmitOfOldPacket(const RTPHeader& header, + int min_rtt) const OVERRIDE; + virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE; void IncomingPacket(const RTPHeader& rtp_header, size_t bytes, - bool retransmitted, bool in_order); + bool retransmitted); + void SetMaxReorderingThreshold(int max_reordering_threshold); void ProcessBitrate(); virtual void LastReceiveTimeNtp(uint32_t* secs, uint32_t* frac) const; private: + bool InOrderPacketInternal(uint16_t sequence_number) const; + Clock* clock_; scoped_ptr crit_sect_; Bitrate incoming_bitrate_; uint32_t ssrc_; + int max_reordering_threshold_; // In number of packets or sequence numbers. // Stats on received RTP packets. uint32_t jitter_q4_; @@ -52,6 +59,7 @@ class StreamStatisticianImpl : public StreamStatistician { uint32_t cumulative_loss_; uint32_t jitter_q4_transmission_time_offset_; + int64_t last_receive_time_ms_; uint32_t last_receive_time_secs_; uint32_t last_receive_time_frac_; uint32_t last_received_timestamp_; @@ -82,9 +90,10 @@ class ReceiveStatisticsImpl : public ReceiveStatistics { // Implement ReceiveStatistics. virtual void IncomingPacket(const RTPHeader& header, size_t bytes, - bool old_packet, bool in_order) OVERRIDE; + bool old_packet) OVERRIDE; virtual StatisticianMap GetActiveStatisticians() const OVERRIDE; virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE; + virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE; // Implement Module. virtual int32_t Process() OVERRIDE; diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc index a69e408e89..39c5c6d748 100644 --- a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc @@ -43,14 +43,14 @@ class ReceiveStatisticsTest : public ::testing::Test { }; TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) { - receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true); + receive_statistics_->IncomingPacket(header1_, kPacketSize1, false); ++header1_.sequenceNumber; - receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true); + receive_statistics_->IncomingPacket(header2_, kPacketSize2, false); ++header2_.sequenceNumber; clock_.AdvanceTimeMilliseconds(100); - receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true); + receive_statistics_->IncomingPacket(header1_, kPacketSize1, false); ++header1_.sequenceNumber; - receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true); + receive_statistics_->IncomingPacket(header2_, kPacketSize2, false); ++header2_.sequenceNumber; StreamStatistician* statistician = @@ -75,9 +75,9 @@ TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) { EXPECT_EQ(2u, statisticians.size()); // Add more incoming packets and verify that they are registered in both // access methods. - receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true); + receive_statistics_->IncomingPacket(header1_, kPacketSize1, false); ++header1_.sequenceNumber; - receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true); + receive_statistics_->IncomingPacket(header2_, kPacketSize2, false); ++header2_.sequenceNumber; statisticians[kSsrc1]->GetDataCounters(&bytes_received, &packets_received); @@ -98,10 +98,10 @@ TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) { } TEST_F(ReceiveStatisticsTest, ActiveStatisticians) { - receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true); + receive_statistics_->IncomingPacket(header1_, kPacketSize1, false); ++header1_.sequenceNumber; clock_.AdvanceTimeMilliseconds(1000); - receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true); + receive_statistics_->IncomingPacket(header2_, kPacketSize2, false); ++header2_.sequenceNumber; StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians(); // Nothing should time out since only 1000 ms has passed since the first @@ -118,7 +118,7 @@ TEST_F(ReceiveStatisticsTest, ActiveStatisticians) { statisticians = receive_statistics_->GetActiveStatisticians(); EXPECT_EQ(0u, statisticians.size()); - receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true); + receive_statistics_->IncomingPacket(header1_, kPacketSize1, false); ++header1_.sequenceNumber; // kSsrc1 should be active again and the data counters should have survived. statisticians = receive_statistics_->GetActiveStatisticians(); diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc index 6e05fd1255..2f0dc7a434 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender_unittest.cc @@ -375,8 +375,8 @@ TEST_F(RtcpSenderTest, TestCompound) { PayloadUnion payload_specific; EXPECT_TRUE(rtp_payload_registry_->GetPayloadSpecifics(header.payloadType, &payload_specific)); - receive_statistics_->IncomingPacket(header, packet_length, false, true); - EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(&header, packet_, packet_length, + receive_statistics_->IncomingPacket(header, packet_length, false); + EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(header, packet_, packet_length, payload_specific, true)); EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true)); diff --git a/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc b/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc index b597b316d8..1c3b990c5d 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc @@ -17,11 +17,17 @@ namespace webrtc { RTPPayloadRegistry::RTPPayloadRegistry( const int32_t id, RTPPayloadStrategy* rtp_payload_strategy) - : id_(id), + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + id_(id), rtp_payload_strategy_(rtp_payload_strategy), red_payload_type_(-1), + ulpfec_payload_type_(-1), + incoming_payload_type_(-1), last_received_payload_type_(-1), - last_received_media_payload_type_(-1) {} + last_received_media_payload_type_(-1), + rtx_(false), + payload_type_rtx_(-1), + ssrc_rtx_(0) {} RTPPayloadRegistry::~RTPPayloadRegistry() { while (!payload_type_map_.empty()) { @@ -64,6 +70,8 @@ int32_t RTPPayloadRegistry::RegisterReceivePayload( size_t payload_name_length = strlen(payload_name); + CriticalSectionScoped cs(crit_sect_.get()); + ModuleRTPUtility::PayloadTypeMap::iterator it = payload_type_map_.find(payload_type); @@ -105,7 +113,12 @@ int32_t RTPPayloadRegistry::RegisterReceivePayload( payload = new ModuleRTPUtility::Payload; memset(payload, 0, sizeof(*payload)); payload->audio = false; - payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0; + strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1); + } else if (ModuleRTPUtility::StringCompare(payload_name, "ulpfec", 3)) { + ulpfec_payload_type_ = payload_type; + payload = new ModuleRTPUtility::Payload; + memset(payload, 0, sizeof(*payload)); + payload->audio = false; strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1); } else { *created_new_payload = true; @@ -123,6 +136,7 @@ int32_t RTPPayloadRegistry::RegisterReceivePayload( int32_t RTPPayloadRegistry::DeRegisterReceivePayload( const int8_t payload_type) { + CriticalSectionScoped cs(crit_sect_.get()); ModuleRTPUtility::PayloadTypeMap::iterator it = payload_type_map_.find(payload_type); @@ -139,6 +153,7 @@ int32_t RTPPayloadRegistry::DeRegisterReceivePayload( // There can't be several codecs with the same rate, frequency and channels // for audio codecs, but there can for video. +// Always called from within a critical section. void RTPPayloadRegistry::DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType( const char payload_name[RTP_PAYLOAD_NAME_SIZE], const size_t payload_name_length, @@ -186,6 +201,8 @@ int32_t RTPPayloadRegistry::ReceivePayloadType( } size_t payload_name_length = strlen(payload_name); + CriticalSectionScoped cs(crit_sect_.get()); + ModuleRTPUtility::PayloadTypeMap::const_iterator it = payload_type_map_.begin(); @@ -226,8 +243,84 @@ int32_t RTPPayloadRegistry::ReceivePayloadType( return -1; } +void RTPPayloadRegistry::SetRtxStatus(bool enable, uint32_t ssrc) { + CriticalSectionScoped cs(crit_sect_.get()); + rtx_ = enable; + ssrc_rtx_ = ssrc; +} + +bool RTPPayloadRegistry::RtxEnabled() const { + CriticalSectionScoped cs(crit_sect_.get()); + return rtx_; +} + +bool RTPPayloadRegistry::IsRtx(const RTPHeader& header) const { + CriticalSectionScoped cs(crit_sect_.get()); + return IsRtxInternal(header); +} + +bool RTPPayloadRegistry::IsRtxInternal(const RTPHeader& header) const { + return rtx_ && ssrc_rtx_ == header.ssrc; +} + +bool RTPPayloadRegistry::RestoreOriginalPacket(uint8_t** restored_packet, + const uint8_t* packet, + int* packet_length, + uint32_t original_ssrc, + const RTPHeader& header) const { + if (kRtxHeaderSize + header.headerLength > *packet_length) { + return false; + } + const uint8_t* rtx_header = packet + header.headerLength; + uint16_t original_sequence_number = (rtx_header[0] << 8) + rtx_header[1]; + + // Copy the packet into the restored packet, except for the RTX header. + memcpy(*restored_packet, packet, header.headerLength); + memcpy(*restored_packet + header.headerLength, + packet + header.headerLength + kRtxHeaderSize, + *packet_length - header.headerLength - kRtxHeaderSize); + *packet_length -= kRtxHeaderSize; + + // Replace the SSRC and the sequence number with the originals. + ModuleRTPUtility::AssignUWord16ToBuffer(*restored_packet + 2, + original_sequence_number); + ModuleRTPUtility::AssignUWord32ToBuffer(*restored_packet + 8, original_ssrc); + + CriticalSectionScoped cs(crit_sect_.get()); + + if (payload_type_rtx_ != -1) { + if (header.payloadType == payload_type_rtx_ && + incoming_payload_type_ != -1) { + (*restored_packet)[1] = static_cast(incoming_payload_type_); + if (header.markerBit) { + (*restored_packet)[1] |= kRtpMarkerBitMask; // Marker bit is set. + } + } else { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, + "Incorrect RTX configuration, dropping packet."); + return false; + } + } + return true; +} + +void RTPPayloadRegistry::SetRtxPayloadType(int payload_type) { + CriticalSectionScoped cs(crit_sect_.get()); + payload_type_rtx_ = payload_type; +} + +bool RTPPayloadRegistry::IsRed(const RTPHeader& header) const { + CriticalSectionScoped cs(crit_sect_.get()); + return red_payload_type_ == header.payloadType; +} + +bool RTPPayloadRegistry::IsEncapsulated(const RTPHeader& header) const { + return IsRed(header) || IsRtx(header); +} + bool RTPPayloadRegistry::GetPayloadSpecifics(uint8_t payload_type, PayloadUnion* payload) const { + CriticalSectionScoped cs(crit_sect_.get()); ModuleRTPUtility::PayloadTypeMap::const_iterator it = payload_type_map_.find(payload_type); @@ -245,12 +338,14 @@ int RTPPayloadRegistry::GetPayloadTypeFrequency( if (!PayloadTypeToPayload(payload_type, payload)) { return -1; } + CriticalSectionScoped cs(crit_sect_.get()); return rtp_payload_strategy_->GetPayloadTypeFrequency(*payload); } bool RTPPayloadRegistry::PayloadTypeToPayload( const uint8_t payload_type, ModuleRTPUtility::Payload*& payload) const { + CriticalSectionScoped cs(crit_sect_.get()); ModuleRTPUtility::PayloadTypeMap::const_iterator it = payload_type_map_.find(payload_type); @@ -264,8 +359,14 @@ bool RTPPayloadRegistry::PayloadTypeToPayload( return true; } -bool RTPPayloadRegistry::ReportMediaPayloadType( - uint8_t media_payload_type) { +void RTPPayloadRegistry::SetIncomingPayloadType(const RTPHeader& header) { + CriticalSectionScoped cs(crit_sect_.get()); + if (!IsRtxInternal(header)) + incoming_payload_type_ = header.payloadType; +} + +bool RTPPayloadRegistry::ReportMediaPayloadType(uint8_t media_payload_type) { + CriticalSectionScoped cs(crit_sect_.get()); if (last_received_media_payload_type_ == media_payload_type) { // Media type unchanged. return true; @@ -350,7 +451,7 @@ class RTPPayloadVideoStrategy : public RTPPayloadStrategy { } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) { videoType = kRtpVideoGeneric; } else if (ModuleRTPUtility::StringCompare(payloadName, "ULPFEC", 6)) { - videoType = kRtpVideoFec; + videoType = kRtpVideoNone; } else { videoType = kRtpVideoGeneric; } diff --git a/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.cc b/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.cc index 1596137927..04cfe14407 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_receiver_audio.cc @@ -183,8 +183,8 @@ int32_t RTPReceiverAudio::OnNewPayloadTypeCreated( int32_t RTPReceiverAudio::ParseRtpPacket(WebRtcRTPHeader* rtp_header, const PayloadUnion& specific_payload, bool is_red, - const uint8_t* packet, - uint16_t packet_length, + const uint8_t* payload, + uint16_t payload_length, int64_t timestamp_ms, bool is_first_packet) { TRACE_EVENT2("webrtc_rtp", "Audio::ParseRtp", @@ -198,13 +198,11 @@ int32_t RTPReceiverAudio::ParseRtpPacket(WebRtcRTPHeader* rtp_header, rtp_header->type.Audio.arrOfEnergy, rtp_header->type.Audio.numEnergy); } - const uint8_t* payload_data = - ModuleRTPUtility::GetPayloadData(rtp_header->header, packet); - const uint16_t payload_data_length = - ModuleRTPUtility::GetPayloadDataLength(rtp_header->header, packet_length); + const uint16_t payload_data_length = payload_length - + rtp_header->header.paddingLength; return ParseAudioCodecSpecific(rtp_header, - payload_data, + payload, payload_data_length, specific_payload.Audio, is_red); @@ -384,6 +382,7 @@ int32_t RTPReceiverAudio::ParseAudioCodecSpecific( } } } + // TODO(holmer): Break this out to have RED parsing handled generically. if (is_red && !(payload_data[0] & 0x80)) { // we recive only one frame packed in a RED packet remove the RED wrapper rtp_header->header.payloadType = payload_data[0]; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc index c3d1039fa3..00fa2a4892 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc @@ -82,11 +82,7 @@ RtpReceiverImpl::RtpReceiverImpl(int32_t id, last_received_timestamp_(0), last_received_frame_time_ms_(0), last_received_sequence_number_(0), - nack_method_(kNackOff), - max_reordering_threshold_(kDefaultMaxReorderingThreshold), - rtx_(false), - ssrc_rtx_(0), - payload_type_rtx_(-1) { + nack_method_(kNackOff) { assert(incoming_audio_messages_callback); assert(incoming_messages_callback); @@ -152,37 +148,9 @@ NACKMethod RtpReceiverImpl::NACK() const { } // Turn negative acknowledgment requests on/off. -int32_t RtpReceiverImpl::SetNACKStatus(const NACKMethod method, - int max_reordering_threshold) { +void RtpReceiverImpl::SetNACKStatus(const NACKMethod method) { CriticalSectionScoped lock(critical_section_rtp_receiver_.get()); - if (max_reordering_threshold < 0) { - return -1; - } else if (method == kNackRtcp) { - max_reordering_threshold_ = max_reordering_threshold; - } else { - max_reordering_threshold_ = kDefaultMaxReorderingThreshold; - } nack_method_ = method; - return 0; -} - -void RtpReceiverImpl::SetRTXStatus(bool enable, uint32_t ssrc) { - CriticalSectionScoped lock(critical_section_rtp_receiver_.get()); - rtx_ = enable; - ssrc_rtx_ = ssrc; -} - -void RtpReceiverImpl::RTXStatus(bool* enable, uint32_t* ssrc, - int* payload_type) const { - CriticalSectionScoped lock(critical_section_rtp_receiver_.get()); - *enable = rtx_; - *ssrc = ssrc_rtx_; - *payload_type = payload_type_rtx_; -} - -void RtpReceiverImpl::SetRtxPayloadType(int payload_type) { - CriticalSectionScoped cs(critical_section_rtp_receiver_.get()); - payload_type_rtx_ = payload_type; } uint32_t RtpReceiverImpl::SSRC() const { @@ -208,55 +176,21 @@ int32_t RtpReceiverImpl::Energy( } bool RtpReceiverImpl::IncomingRtpPacket( - RTPHeader* rtp_header, - const uint8_t* packet, - int packet_length, - PayloadUnion payload_specific, - bool in_order) { - // The rtp_header argument contains the parsed RTP header. - int length = packet_length - rtp_header->paddingLength; - + const RTPHeader& rtp_header, + const uint8_t* payload, + int payload_length, + PayloadUnion payload_specific, + bool in_order) { // Sanity check. - if ((length - rtp_header->headerLength) < 0) { + if (payload_length < 0) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, "%s invalid argument", __FUNCTION__); return false; } - { - CriticalSectionScoped cs(critical_section_rtp_receiver_.get()); - // TODO(holmer): Make rtp_header const after RTX has been broken out. - if (rtx_) { - if (ssrc_rtx_ == rtp_header->ssrc) { - // Sanity check, RTX packets has 2 extra header bytes. - if (rtp_header->headerLength + kRtxHeaderSize > packet_length) { - return false; - } - // If a specific RTX payload type is negotiated, set back to the media - // payload type and treat it like a media packet from here. - if (payload_type_rtx_ != -1) { - if (payload_type_rtx_ == rtp_header->payloadType && - rtp_payload_registry_->last_received_media_payload_type() != -1) { - rtp_header->payloadType = - rtp_payload_registry_->last_received_media_payload_type(); - } else { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_, - "Incorrect RTX configuration, dropping packet."); - return false; - } - } - rtp_header->ssrc = ssrc_; - rtp_header->sequenceNumber = - (packet[rtp_header->headerLength] << 8) + - packet[1 + rtp_header->headerLength]; - // Count the RTX header as part of the RTP - rtp_header->headerLength += 2; - } - } - } int8_t first_payload_byte = 0; - if (length > 0) { - first_payload_byte = packet[rtp_header->headerLength]; + if (payload_length > 0) { + first_payload_byte = payload[0]; } // Trigger our callbacks. CheckSSRCChanged(rtp_header); @@ -269,7 +203,7 @@ bool RtpReceiverImpl::IncomingRtpPacket( is_red, &payload_specific, &should_reset_statistics) == -1) { - if (length - rtp_header->headerLength == 0) { + if (payload_length == 0) { // OK, keep-alive packet. WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, "%s received keepalive", @@ -288,24 +222,23 @@ bool RtpReceiverImpl::IncomingRtpPacket( WebRtcRTPHeader webrtc_rtp_header; memset(&webrtc_rtp_header, 0, sizeof(webrtc_rtp_header)); - webrtc_rtp_header.header = *rtp_header; - CheckCSRC(&webrtc_rtp_header); + webrtc_rtp_header.header = rtp_header; + CheckCSRC(webrtc_rtp_header); - uint16_t payload_data_length = - ModuleRTPUtility::GetPayloadDataLength(*rtp_header, packet_length); + uint16_t payload_data_length = payload_length - rtp_header.paddingLength; bool is_first_packet_in_frame = false; bool is_first_packet = false; { CriticalSectionScoped lock(critical_section_rtp_receiver_.get()); is_first_packet_in_frame = - last_received_sequence_number_ + 1 == rtp_header->sequenceNumber && - Timestamp() != rtp_header->timestamp; + last_received_sequence_number_ + 1 == rtp_header.sequenceNumber && + Timestamp() != rtp_header.timestamp; is_first_packet = is_first_packet_in_frame || last_receive_time_ == 0; } int32_t ret_val = rtp_media_receiver_->ParseRtpPacket( - &webrtc_rtp_header, payload_specific, is_red, packet, packet_length, + &webrtc_rtp_header, payload_specific, is_red, payload, payload_length, clock_->TimeInMilliseconds(), is_first_packet); if (ret_val < 0) { @@ -319,73 +252,16 @@ bool RtpReceiverImpl::IncomingRtpPacket( last_received_payload_length_ = payload_data_length; if (in_order) { - if (last_received_timestamp_ != rtp_header->timestamp) { - last_received_timestamp_ = rtp_header->timestamp; + if (last_received_timestamp_ != rtp_header.timestamp) { + last_received_timestamp_ = rtp_header.timestamp; last_received_frame_time_ms_ = clock_->TimeInMilliseconds(); } - last_received_sequence_number_ = rtp_header->sequenceNumber; + last_received_sequence_number_ = rtp_header.sequenceNumber; } } return true; } -bool RtpReceiverImpl::RetransmitOfOldPacket(const RTPHeader& header, - int jitter, int min_rtt) const { - if (InOrderPacket(header.sequenceNumber)) { - return false; - } - - CriticalSectionScoped cs(critical_section_rtp_receiver_.get()); - uint32_t frequency_khz = header.payload_type_frequency / 1000; - assert(frequency_khz > 0); - - int64_t time_diff_ms = clock_->TimeInMilliseconds() - - last_receive_time_; - - // Diff in time stamp since last received in order. - uint32_t timestamp_diff = header.timestamp - last_received_timestamp_; - int32_t rtp_time_stamp_diff_ms = static_cast(timestamp_diff) / - frequency_khz; - - int32_t max_delay_ms = 0; - if (min_rtt == 0) { - // Jitter standard deviation in samples. - float jitter_std = sqrt(static_cast(jitter)); - - // 2 times the standard deviation => 95% confidence. - // And transform to milliseconds by dividing by the frequency in kHz. - max_delay_ms = static_cast((2 * jitter_std) / frequency_khz); - - // Min max_delay_ms is 1. - if (max_delay_ms == 0) { - max_delay_ms = 1; - } - } else { - max_delay_ms = (min_rtt / 3) + 1; - } - if (time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms) { - return true; - } - return false; -} - -bool RtpReceiverImpl::InOrderPacket(const uint16_t sequence_number) const { - CriticalSectionScoped cs(critical_section_rtp_receiver_.get()); - - // First packet is always in order. - if (last_receive_time_ == 0) - return true; - - if (IsNewerSequenceNumber(sequence_number, last_received_sequence_number_)) { - return true; - } else { - // If we have a restart of the remote side this packet is still in order. - return !IsNewerSequenceNumber(sequence_number, - last_received_sequence_number_ - - max_reordering_threshold_); - } -} - TelephoneEventHandler* RtpReceiverImpl::GetTelephoneEventHandler() { return rtp_media_receiver_->GetTelephoneEventHandler(); } @@ -401,7 +277,7 @@ int32_t RtpReceiverImpl::LastReceivedTimeMs() const { } // Implementation note: must not hold critsect when called. -void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) { +void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader& rtp_header) { bool new_ssrc = false; bool re_initialize_decoder = false; char payload_name[RTP_PAYLOAD_NAME_SIZE]; @@ -413,7 +289,7 @@ void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) { int8_t last_received_payload_type = rtp_payload_registry_->last_received_payload_type(); - if (ssrc_ != rtp_header->ssrc || + if (ssrc_ != rtp_header.ssrc || (last_received_payload_type == -1 && ssrc_ == 0)) { // We need the payload_type_ to make the call if the remote SSRC is 0. new_ssrc = true; @@ -427,12 +303,12 @@ void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) { // Do we have a SSRC? Then the stream is restarted. if (ssrc_ != 0) { // Do we have the same codec? Then re-initialize coder. - if (rtp_header->payloadType == last_received_payload_type) { + if (rtp_header.payloadType == last_received_payload_type) { re_initialize_decoder = true; Payload* payload; if (!rtp_payload_registry_->PayloadTypeToPayload( - rtp_header->payloadType, payload)) { + rtp_header.payloadType, payload)) { return; } assert(payload); @@ -444,24 +320,24 @@ void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) { } } } - ssrc_ = rtp_header->ssrc; + ssrc_ = rtp_header.ssrc; } } if (new_ssrc) { // We need to get this to our RTCP sender and receiver. // We need to do this outside critical section. - cb_rtp_feedback_->OnIncomingSSRCChanged(id_, rtp_header->ssrc); + cb_rtp_feedback_->OnIncomingSSRCChanged(id_, rtp_header.ssrc); } if (re_initialize_decoder) { if (-1 == cb_rtp_feedback_->OnInitializeDecoder( - id_, rtp_header->payloadType, payload_name, - rtp_header->payload_type_frequency, channels, rate)) { + id_, rtp_header.payloadType, payload_name, + rtp_header.payload_type_frequency, channels, rate)) { // New stream, same codec. WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_, "Failed to create decoder for payload type:%d", - rtp_header->payloadType); + rtp_header.payloadType); } } } @@ -474,7 +350,7 @@ void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) { // media_specific interface (such as CheckPayloadChange, possibly get/set // last known payload). int32_t RtpReceiverImpl::CheckPayloadChanged( - const RTPHeader* rtp_header, + const RTPHeader& rtp_header, const int8_t first_payload_byte, bool& is_red, PayloadUnion* specific_payload, @@ -482,13 +358,15 @@ int32_t RtpReceiverImpl::CheckPayloadChanged( bool re_initialize_decoder = false; char payload_name[RTP_PAYLOAD_NAME_SIZE]; - int8_t payload_type = rtp_header->payloadType; + int8_t payload_type = rtp_header.payloadType; { CriticalSectionScoped lock(critical_section_rtp_receiver_.get()); int8_t last_received_payload_type = rtp_payload_registry_->last_received_payload_type(); + // TODO(holmer): Remove this code when RED parsing has been broken out from + // RtpReceiverAudio. if (payload_type != last_received_payload_type) { if (rtp_payload_registry_->red_payload_type() == payload_type) { // Get the real codec payload type. @@ -537,16 +415,11 @@ int32_t RtpReceiverImpl::CheckPayloadChanged( rtp_media_receiver_->GetLastMediaSpecificPayload(specific_payload); if (!payload->audio) { - if (VideoCodecType() == kRtpVideoFec) { - // Only reset the decoder on media packets. + bool media_type_unchanged = + rtp_payload_registry_->ReportMediaPayloadType(payload_type); + if (media_type_unchanged) { + // Only reset the decoder if the media codec type has changed. re_initialize_decoder = false; - } else { - bool media_type_unchanged = - rtp_payload_registry_->ReportMediaPayloadType(payload_type); - if (media_type_unchanged) { - // Only reset the decoder if the media codec type has changed. - re_initialize_decoder = false; - } } } if (re_initialize_decoder) { @@ -569,7 +442,7 @@ int32_t RtpReceiverImpl::CheckPayloadChanged( } // Implementation note: must not hold critsect when called. -void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) { +void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader& rtp_header) { int32_t num_csrcs_diff = 0; uint32_t old_remote_csrc[kRtpCsrcSize]; uint8_t old_num_csrcs = 0; @@ -578,7 +451,7 @@ void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) { CriticalSectionScoped lock(critical_section_rtp_receiver_.get()); if (!rtp_media_receiver_->ShouldReportCsrcChanges( - rtp_header->header.payloadType)) { + rtp_header.header.payloadType)) { return; } old_num_csrcs = num_csrcs_; @@ -587,11 +460,11 @@ void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) { memcpy(old_remote_csrc, current_remote_csrc_, num_csrcs_ * sizeof(uint32_t)); } - const uint8_t num_csrcs = rtp_header->header.numCSRCs; + const uint8_t num_csrcs = rtp_header.header.numCSRCs; if ((num_csrcs > 0) && (num_csrcs <= kRtpCsrcSize)) { // Copy new. memcpy(current_remote_csrc_, - rtp_header->header.arrOfCSRCs, + rtp_header.header.arrOfCSRCs, num_csrcs * sizeof(uint32_t)); } if (num_csrcs > 0 || old_num_csrcs > 0) { @@ -605,8 +478,8 @@ void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) { bool have_called_callback = false; // Search for new CSRC in old array. - for (uint8_t i = 0; i < rtp_header->header.numCSRCs; ++i) { - const uint32_t csrc = rtp_header->header.arrOfCSRCs[i]; + for (uint8_t i = 0; i < rtp_header.header.numCSRCs; ++i) { + const uint32_t csrc = rtp_header.header.arrOfCSRCs[i]; bool found_match = false; for (uint8_t j = 0; j < old_num_csrcs; ++j) { @@ -626,8 +499,8 @@ void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) { const uint32_t csrc = old_remote_csrc[i]; bool found_match = false; - for (uint8_t j = 0; j < rtp_header->header.numCSRCs; ++j) { - if (csrc == rtp_header->header.arrOfCSRCs[j]) { + for (uint8_t j = 0; j < rtp_header.header.numCSRCs; ++j) { + if (csrc == rtp_header.header.arrOfCSRCs[j]) { found_match = true; break; } diff --git a/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h index bf3b925bb7..c3a2fa1b84 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h @@ -46,16 +46,16 @@ class RtpReceiverImpl : public RtpReceiver { int32_t DeRegisterReceivePayload(const int8_t payload_type); bool IncomingRtpPacket( - RTPHeader* rtp_header, - const uint8_t* incoming_rtp_packet, - int incoming_rtp_packet_length, + const RTPHeader& rtp_header, + const uint8_t* payload, + int payload_length, PayloadUnion payload_specific, bool in_order); NACKMethod NACK() const; // Turn negative acknowledgement requests on/off. - int32_t SetNACKStatus(const NACKMethod method, int max_reordering_threshold); + void SetNACKStatus(const NACKMethod method); // Returns the last received timestamp. virtual uint32_t Timestamp() const; @@ -74,19 +74,16 @@ class RtpReceiverImpl : public RtpReceiver { void SetRtxPayloadType(int payload_type); - virtual bool RetransmitOfOldPacket(const RTPHeader& header, - int jitter, int min_rtt) const; - bool InOrderPacket(const uint16_t sequence_number) const; TelephoneEventHandler* GetTelephoneEventHandler(); private: RtpVideoCodecTypes VideoCodecType() const; - void CheckSSRCChanged(const RTPHeader* rtp_header); - void CheckCSRC(const WebRtcRTPHeader* rtp_header); - int32_t CheckPayloadChanged(const RTPHeader* rtp_header, + void CheckSSRCChanged(const RTPHeader& rtp_header); + void CheckCSRC(const WebRtcRTPHeader& rtp_header); + int32_t CheckPayloadChanged(const RTPHeader& rtp_header, const int8_t first_payload_byte, - bool& isRED, + bool& is_red, PayloadUnion* payload, bool* should_reset_statistics); @@ -112,11 +109,6 @@ class RtpReceiverImpl : public RtpReceiver { uint16_t last_received_sequence_number_; NACKMethod nack_method_; - int max_reordering_threshold_; - - bool rtx_; - uint32_t ssrc_rtx_; - int payload_type_rtx_; }; } // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_IMPL_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h b/webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h index 0681ac9911..d8a2257962 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h @@ -43,8 +43,8 @@ class RTPReceiverStrategy { virtual int32_t ParseRtpPacket(WebRtcRTPHeader* rtp_header, const PayloadUnion& specific_payload, bool is_red, - const uint8_t* packet, - uint16_t packet_length, + const uint8_t* payload, + uint16_t payload_length, int64_t timestamp_ms, bool is_first_packet) = 0; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc b/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc index a47f7d3c71..65240806dc 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc @@ -10,24 +10,17 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h" -#include - -#include // assert -#include // memcpy() +#include +#include #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" -#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { -uint32_t BitRateBPS(uint16_t x) { - return (x & 0x3fff) * uint32_t(pow(10.0f, (2 + (x >> 14)))); -} RTPReceiverStrategy* RTPReceiverStrategy::CreateVideoStrategy( int32_t id, RtpData* data_callback) { @@ -36,12 +29,9 @@ RTPReceiverStrategy* RTPReceiverStrategy::CreateVideoStrategy( RTPReceiverVideo::RTPReceiverVideo(int32_t id, RtpData* data_callback) : RTPReceiverStrategy(data_callback), - id_(id), - receive_fec_(NULL) { -} + id_(id) {} RTPReceiverVideo::~RTPReceiverVideo() { - delete receive_fec_; } bool RTPReceiverVideo::ShouldReportCsrcChanges( @@ -54,13 +44,6 @@ int32_t RTPReceiverVideo::OnNewPayloadTypeCreated( const char payload_name[RTP_PAYLOAD_NAME_SIZE], int8_t payload_type, uint32_t frequency) { - if (ModuleRTPUtility::StringCompare(payload_name, "ULPFEC", 6)) { - // Enable FEC if not enabled. - if (receive_fec_ == NULL) { - receive_fec_ = new ReceiverFEC(id_, data_callback_); - } - receive_fec_->SetPayloadTypeFEC(payload_type); - } return 0; } @@ -68,25 +51,20 @@ int32_t RTPReceiverVideo::ParseRtpPacket( WebRtcRTPHeader* rtp_header, const PayloadUnion& specific_payload, bool is_red, - const uint8_t* packet, - uint16_t packet_length, + const uint8_t* payload, + uint16_t payload_length, int64_t timestamp_ms, bool is_first_packet) { TRACE_EVENT2("webrtc_rtp", "Video::ParseRtp", "seqnum", rtp_header->header.sequenceNumber, "timestamp", rtp_header->header.timestamp); rtp_header->type.Video.codec = specific_payload.Video.videoCodecType; - const uint8_t* payload_data = - ModuleRTPUtility::GetPayloadData(rtp_header->header, packet); - const uint16_t payload_data_length = - ModuleRTPUtility::GetPayloadDataLength(rtp_header->header, packet_length); + const uint16_t payload_data_length = payload_length - + rtp_header->header.paddingLength; return ParseVideoCodecSpecific(rtp_header, - payload_data, + payload, payload_data_length, specific_payload.Video.videoCodecType, - is_red, - packet, - packet_length, timestamp_ms, is_first_packet); } @@ -119,57 +97,31 @@ int32_t RTPReceiverVideo::InvokeOnInitializeDecoder( return 0; } -// we have no critext when calling this -// we are not allowed to have any critsects when calling -// CallbackOfReceivedPayloadData +// We are not allowed to hold a critical section when calling this function. int32_t RTPReceiverVideo::ParseVideoCodecSpecific( WebRtcRTPHeader* rtp_header, const uint8_t* payload_data, uint16_t payload_data_length, RtpVideoCodecTypes video_type, - bool is_red, - const uint8_t* incoming_rtp_packet, - uint16_t incoming_rtp_packet_size, int64_t now_ms, bool is_first_packet) { - int32_t ret_val = 0; + WEBRTC_TRACE(kTraceStream, + kTraceRtpRtcp, + id_, + "%s(timestamp:%u)", + __FUNCTION__, + rtp_header->header.timestamp); - crit_sect_->Enter(); - - if (is_red) { - if (receive_fec_ == NULL) { - crit_sect_->Leave(); - return -1; - } - crit_sect_->Leave(); - bool FECpacket = false; - ret_val = receive_fec_->AddReceivedFECPacket( - rtp_header, incoming_rtp_packet, payload_data_length, FECpacket); - if (ret_val != -1) { - ret_val = receive_fec_->ProcessReceivedFEC(); - } - - if (ret_val == 0 && FECpacket) { - // Callback with the received FEC packet. - // The normal packets are delivered after parsing. - // This contains the original RTP packet header but with - // empty payload and data length. - rtp_header->frameType = kFrameEmpty; - // We need this for the routing. - rtp_header->type.Video.codec = video_type; - // Pass the length of FEC packets so that they can be accounted for in - // the bandwidth estimator. - ret_val = data_callback_->OnReceivedPayloadData( - NULL, payload_data_length, rtp_header); - } - } else { - // will leave the crit_sect_ critsect - ret_val = ParseVideoCodecSpecificSwitch(rtp_header, - payload_data, - payload_data_length, - is_first_packet); + switch (rtp_header->type.Video.codec) { + case kRtpVideoGeneric: + rtp_header->type.Video.isFirstPacket = is_first_packet; + return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length); + case kRtpVideoVp8: + return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length); + case kRtpVideoNone: + break; } - return ret_val; + return -1; } int32_t RTPReceiverVideo::BuildRTPheader( @@ -208,35 +160,6 @@ int32_t RTPReceiverVideo::BuildRTPheader( return rtp_header_length; } -int32_t RTPReceiverVideo::ParseVideoCodecSpecificSwitch( - WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length, - bool is_first_packet) { - WEBRTC_TRACE(kTraceStream, - kTraceRtpRtcp, - id_, - "%s(timestamp:%u)", - __FUNCTION__, - rtp_header->header.timestamp); - - // Critical section has already been taken. - switch (rtp_header->type.Video.codec) { - case kRtpVideoGeneric: - rtp_header->type.Video.isFirstPacket = is_first_packet; - return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length); - case kRtpVideoVp8: - return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length); - case kRtpVideoFec: - break; - default: - assert(false); - } - // Releasing the already taken critical section here. - crit_sect_->Leave(); - return -1; -} - int32_t RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtp_header, const uint8_t* payload_data, uint16_t payload_data_length) { @@ -246,13 +169,16 @@ int32_t RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtp_header, success = true; parsed_packet.info.VP8.dataLength = 0; } else { + uint32_t id = 0; + { + CriticalSectionScoped cs(crit_sect_.get()); + id = id_; + } ModuleRTPUtility::RTPPayloadParser rtp_payload_parser( - kRtpVideoVp8, payload_data, payload_data_length, id_); + kRtpVideoVp8, payload_data, payload_data_length, id); success = rtp_payload_parser.Parse(parsed_packet); } - // from here down we only work on local data - crit_sect_->Leave(); if (!success) { return -1; @@ -315,8 +241,6 @@ int32_t RTPReceiverVideo::ReceiveGenericCodec( rtp_header->type.Video.isFirstPacket = (generic_header & RtpFormatVideoGeneric::kFirstPacketBit) != 0; - crit_sect_->Leave(); - if (data_callback_->OnReceivedPayloadData( payload_data, payload_data_length, rtp_header) != 0) { return -1; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h b/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h index 47639c832b..ab69b40ee6 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h @@ -19,10 +19,6 @@ #include "webrtc/typedefs.h" namespace webrtc { -class CriticalSectionWrapper; -class ModuleRtpRtcpImpl; -class ReceiverFEC; -class RtpReceiver; class RTPReceiverVideo : public RTPReceiverStrategy { public: @@ -65,12 +61,6 @@ class RTPReceiverVideo : public RTPReceiverStrategy { void SetPacketOverHead(uint16_t packet_over_head); protected: - int32_t ParseVideoCodecSpecificSwitch( - WebRtcRTPHeader* rtp_header, - const uint8_t* payload_data, - uint16_t payload_data_length, - bool is_first_packet); - int32_t ReceiveGenericCodec(WebRtcRTPHeader* rtp_header, const uint8_t* payload_data, uint16_t payload_data_length); @@ -88,16 +78,10 @@ class RTPReceiverVideo : public RTPReceiverStrategy { const uint8_t* payload_data, uint16_t payload_data_length, RtpVideoCodecTypes video_type, - bool is_red, - const uint8_t* incoming_rtp_packet, - uint16_t incoming_rtp_packet_size, int64_t now_ms, bool is_first_packet); int32_t id_; - - // FEC - ReceiverFEC* receive_fec_; }; } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi b/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi index 42b6580ca9..bb612b33df 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi @@ -28,6 +28,7 @@ }, 'sources': [ # Common + '../interface/fec_receiver.h', '../interface/receive_statistics.h', '../interface/rtp_header_parser.h', '../interface/rtp_payload_registry.h', @@ -36,6 +37,8 @@ '../interface/rtp_rtcp_defines.h', 'bitrate.cc', 'bitrate.h', + 'fec_receiver_impl.cc', + 'fec_receiver_impl.h', 'receive_statistics_impl.cc', 'receive_statistics_impl.h', 'rtp_header_parser.cc', @@ -87,8 +90,6 @@ 'rtp_receiver_video.h', 'rtp_sender_video.cc', 'rtp_sender_video.h', - 'receiver_fec.cc', - 'receiver_fec.h', 'video_codec_information.h', 'rtp_format_vp8.cc', 'rtp_format_vp8.h', diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc index 6c9131f3b9..83ee7f68df 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -19,7 +19,6 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h" #include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h" #include "webrtc/modules/rtp_rtcp/source/rtp_sender.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" @@ -41,6 +40,18 @@ const int kAudioPayload = 103; using testing::_; +const uint8_t* GetPayloadData(const RTPHeader& rtp_header, + const uint8_t* packet) { + return packet + rtp_header.headerLength; +} + +uint16_t GetPayloadDataLength(const RTPHeader& rtp_header, + const uint16_t packet_length) { + uint16_t length = packet_length - rtp_header.headerLength - + rtp_header.paddingLength; + return static_cast(length); +} + class LoopbackTransportTest : public webrtc::Transport { public: LoopbackTransportTest() @@ -464,13 +475,12 @@ TEST_F(RtpSenderTest, SendGenericVideo) { webrtc::RTPHeader rtp_header; ASSERT_TRUE(rtp_parser.Parse(rtp_header)); - const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header, + const uint8_t* payload_data = GetPayloadData(rtp_header, transport_.last_sent_packet_); uint8_t generic_header = *payload_data++; ASSERT_EQ(sizeof(payload) + sizeof(generic_header), - ModuleRTPUtility::GetPayloadDataLength(rtp_header, - transport_.last_sent_packet_len_)); + GetPayloadDataLength(rtp_header, transport_.last_sent_packet_len_)); EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit); EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit); @@ -490,16 +500,14 @@ TEST_F(RtpSenderTest, SendGenericVideo) { transport_.last_sent_packet_len_); ASSERT_TRUE(rtp_parser.Parse(rtp_header)); - payload_data = ModuleRTPUtility::GetPayloadData(rtp_header, - transport_.last_sent_packet_); + payload_data = GetPayloadData(rtp_header, transport_.last_sent_packet_); generic_header = *payload_data++; EXPECT_FALSE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit); EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit); ASSERT_EQ(sizeof(payload) + sizeof(generic_header), - ModuleRTPUtility::GetPayloadDataLength(rtp_header, - transport_.last_sent_packet_len_)); + GetPayloadDataLength(rtp_header, transport_.last_sent_packet_len_)); EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload))); } @@ -574,10 +582,10 @@ TEST_F(RtpSenderAudioTest, SendAudio) { webrtc::RTPHeader rtp_header; ASSERT_TRUE(rtp_parser.Parse(rtp_header)); - const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header, + const uint8_t* payload_data = GetPayloadData(rtp_header, transport_.last_sent_packet_); - ASSERT_EQ(sizeof(payload), ModuleRTPUtility::GetPayloadDataLength(rtp_header, + ASSERT_EQ(sizeof(payload), GetPayloadDataLength(rtp_header, transport_.last_sent_packet_len_)); EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload))); @@ -605,11 +613,11 @@ TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) { webrtc::RTPHeader rtp_header; ASSERT_TRUE(rtp_parser.Parse(rtp_header)); - const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header, - transport_.last_sent_packet_); + const uint8_t* payload_data = GetPayloadData(rtp_header, + transport_.last_sent_packet_); - ASSERT_EQ(sizeof(payload), ModuleRTPUtility::GetPayloadDataLength(rtp_header, - transport_.last_sent_packet_len_)); + ASSERT_EQ(sizeof(payload), GetPayloadDataLength( + rtp_header, transport_.last_sent_packet_len_)); EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload))); diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc index 409a1773d8..bc0be8b345 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc @@ -113,18 +113,6 @@ uint32_t ConvertNTPTimeToMS(uint32_t NTPsec, uint32_t NTPfrac) { * Misc utility routines */ -const uint8_t* GetPayloadData(const RTPHeader& rtp_header, - const uint8_t* packet) { - return packet + rtp_header.headerLength; -} - -uint16_t GetPayloadDataLength(const RTPHeader& rtp_header, - const uint16_t packet_length) { - uint16_t length = packet_length - rtp_header.paddingLength - - rtp_header.headerLength; - return static_cast(length); -} - #if defined(_WIN32) bool StringCompare(const char* str1, const char* str2, const uint32_t length) { diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.h b/webrtc/modules/rtp_rtcp/source/rtp_utility.h index 72bbfa0e59..8002273c37 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_utility.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.h @@ -56,14 +56,6 @@ namespace ModuleRTPUtility uint32_t pow2(uint8_t exp); - // Returns a pointer to the payload data given a packet. - const uint8_t* GetPayloadData(const RTPHeader& rtp_header, - const uint8_t* packet); - - // Returns payload length given a packet. - uint16_t GetPayloadDataLength(const RTPHeader& rtp_header, - const uint16_t packet_length); - // Returns true if |newTimestamp| is older than |existingTimestamp|. // |wrapped| will be set to true if there has been a wraparound between the // two timestamps. diff --git a/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc b/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc index 6789e041a0..78df647dab 100644 --- a/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc +++ b/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc @@ -110,11 +110,11 @@ TEST_F(RtpRtcpAPITest, RTCP) { EXPECT_FALSE(module->TMMBR()); EXPECT_EQ(kNackOff, rtp_receiver_->NACK()); - EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450)); + rtp_receiver_->SetNACKStatus(kNackRtcp); EXPECT_EQ(kNackRtcp, rtp_receiver_->NACK()); } -TEST_F(RtpRtcpAPITest, RTXSender) { +TEST_F(RtpRtcpAPITest, RtxSender) { unsigned int ssrc = 0; RtxMode rtx_mode = kRtxOff; const int kRtxPayloadType = 119; @@ -138,19 +138,20 @@ TEST_F(RtpRtcpAPITest, RTXSender) { EXPECT_EQ(kRtxPayloadType, payload_type); } -TEST_F(RtpRtcpAPITest, RTXReceiver) { - bool enable = false; - unsigned int ssrc = 0; +TEST_F(RtpRtcpAPITest, RtxReceiver) { + const uint32_t kRtxSsrc = 1; const int kRtxPayloadType = 119; - int payload_type = -1; - rtp_receiver_->SetRTXStatus(true, 1); - rtp_receiver_->SetRtxPayloadType(kRtxPayloadType); - rtp_receiver_->RTXStatus(&enable, &ssrc, &payload_type); - EXPECT_TRUE(enable); - EXPECT_EQ(1u, ssrc); - EXPECT_EQ(kRtxPayloadType ,payload_type); - rtp_receiver_->SetRTXStatus(false, 0); - rtp_receiver_->RTXStatus(&enable, &ssrc, &payload_type); - EXPECT_FALSE(enable); - EXPECT_EQ(kRtxPayloadType ,payload_type); + rtp_payload_registry_->SetRtxStatus(true, kRtxSsrc); + rtp_payload_registry_->SetRtxPayloadType(kRtxPayloadType); + EXPECT_TRUE(rtp_payload_registry_->RtxEnabled()); + RTPHeader rtx_header; + rtx_header.ssrc = kRtxSsrc; + rtx_header.payloadType = kRtxPayloadType; + EXPECT_TRUE(rtp_payload_registry_->IsRtx(rtx_header)); + rtx_header.ssrc = 0; + EXPECT_FALSE(rtp_payload_registry_->IsRtx(rtx_header)); + rtp_payload_registry_->SetRtxStatus(false, kRtxSsrc); + EXPECT_FALSE(rtp_payload_registry_->RtxEnabled()); + rtx_header.ssrc = kRtxSsrc; + EXPECT_FALSE(rtp_payload_registry_->IsRtx(rtx_header)); } diff --git a/webrtc/modules/rtp_rtcp/test/testAPI/test_api.h b/webrtc/modules/rtp_rtcp/test/testAPI/test_api.h index 1a13ab9cf3..8061ce018c 100644 --- a/webrtc/modules/rtp_rtcp/test/testAPI/test_api.h +++ b/webrtc/modules/rtp_rtcp/test/testAPI/test_api.h @@ -60,8 +60,8 @@ class LoopBackTransport : public webrtc::Transport { header.payloadType, &payload_specific)) { return -1; } - receive_statistics_->IncomingPacket(header, len, false, true); - if (!rtp_receiver_->IncomingRtpPacket(&header, + receive_statistics_->IncomingPacket(header, len, false); + if (!rtp_receiver_->IncomingRtpPacket(header, static_cast(data), len, payload_specific, true)) { return -1; diff --git a/webrtc/modules/rtp_rtcp/test/testAPI/test_api_video.cc b/webrtc/modules/rtp_rtcp/test/testAPI/test_api_video.cc index 6291a34df3..6f065d580f 100644 --- a/webrtc/modules/rtp_rtcp/test/testAPI/test_api_video.cc +++ b/webrtc/modules/rtp_rtcp/test/testAPI/test_api_video.cc @@ -52,7 +52,7 @@ class RtpRtcpVideoTest : public ::testing::Test { EXPECT_EQ(0, video_module_->SetRTCPStatus(kRtcpCompound)); EXPECT_EQ(0, video_module_->SetSSRC(test_ssrc_)); - EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450)); + rtp_receiver_->SetNACKStatus(kNackRtcp); EXPECT_EQ(0, video_module_->SetStorePacketsStatus(true, 600)); EXPECT_EQ(0, video_module_->SetSendingStatus(true)); @@ -176,11 +176,13 @@ TEST_F(RtpRtcpVideoTest, PaddingOnlyFrames) { PayloadUnion payload_specific; EXPECT_TRUE(rtp_payload_registry_.GetPayloadSpecifics(header.payloadType, &payload_specific)); - EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(&header, padding_packet, - packet_size, + const uint8_t* payload = padding_packet + header.headerLength; + const int payload_length = packet_size - header.headerLength; + EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(header, payload, + payload_length, payload_specific, true)); EXPECT_EQ(0, receiver_->payload_size()); - EXPECT_EQ(packet_size - 12, receiver_->rtp_header().header.paddingLength); + EXPECT_EQ(payload_length, receiver_->rtp_header().header.paddingLength); } timestamp += 3000; fake_clock.AdvanceTimeMilliseconds(33); diff --git a/webrtc/modules/video_coding/main/source/packet.cc b/webrtc/modules/video_coding/main/source/packet.cc index ad69418be0..61ef2ee859 100644 --- a/webrtc/modules/video_coding/main/source/packet.cc +++ b/webrtc/modules/video_coding/main/source/packet.cc @@ -111,11 +111,6 @@ void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader) codec = kVideoCodecVP8; break; } - case kRtpVideoI420: - { - codec = kVideoCodecI420; - break; - } default: { codec = kVideoCodecUnknown; diff --git a/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc b/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc index 2c04388ff4..5cad5d40c1 100644 --- a/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc +++ b/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc @@ -257,8 +257,7 @@ int MTRxTxTest(CmdArgs& args) FecProtectionParams delta_params = protectionCallback.DeltaFecParameters(); FecProtectionParams key_params = protectionCallback.KeyFecParameters(); rtp->SetFecParameters(&delta_params, &key_params); - rtp_receiver->SetNACKStatus(nackEnabled ? kNackRtcp : kNackOff, - kMaxPacketAgeToNack); + rtp_receiver->SetNACKStatus(nackEnabled ? kNackRtcp : kNackOff); vcm->SetChannelParameters(static_cast(1000 * bitRate), (uint8_t) lossRate, rttMS); diff --git a/webrtc/modules/video_coding/main/test/mt_test_common.cc b/webrtc/modules/video_coding/main/test/mt_test_common.cc index 0ec19afd7b..779ef7a78a 100644 --- a/webrtc/modules/video_coding/main/test/mt_test_common.cc +++ b/webrtc/modules/video_coding/main/test/mt_test_common.cc @@ -102,7 +102,7 @@ TransportCallback::TransportPackets() header.payloadType, &payload_specific)) { return -1; } - if (!rtp_receiver_->IncomingRtpPacket(&header, packet->data, + if (!rtp_receiver_->IncomingRtpPacket(header, packet->data, packet->length, payload_specific, true)) { diff --git a/webrtc/modules/video_coding/main/test/normal_test.cc b/webrtc/modules/video_coding/main/test/normal_test.cc index e0016bd896..3000a1a260 100644 --- a/webrtc/modules/video_coding/main/test/normal_test.cc +++ b/webrtc/modules/video_coding/main/test/normal_test.cc @@ -102,9 +102,6 @@ VCMNTEncodeCompleteCallback::SendData( rtpInfo.type.Video.codecHeader.VP8.pictureId = videoHdr->codecHeader.VP8.pictureId; break; - case kVideoCodecI420: - rtpInfo.type.Video.codec = kRtpVideoI420; - break; default: assert(false); return -1; diff --git a/webrtc/modules/video_coding/main/test/rtp_player.cc b/webrtc/modules/video_coding/main/test/rtp_player.cc index c3fc7cc782..6af4389341 100644 --- a/webrtc/modules/video_coding/main/test/rtp_player.cc +++ b/webrtc/modules/video_coding/main/test/rtp_player.cc @@ -226,10 +226,7 @@ class SsrcHandlers { return -1; } - if (handler->rtp_module_->SetNACKStatus(kNackOff, - kMaxPacketAgeToNack) < 0) { - return -1; - } + handler->rtp_module_->SetNACKStatus(kNackOff); handler->rtp_header_parser_->RegisterRtpHeaderExtension( kRtpExtensionTransmissionTimeOffset, kDefaultTransmissionTimeOffsetExtensionId); @@ -262,10 +259,8 @@ class SsrcHandlers { PayloadUnion payload_specific; it->second->rtp_payload_registry_->GetPayloadSpecifics( header.payloadType, &payload_specific); - bool in_order = - it->second->rtp_module_->InOrderPacket(header.sequenceNumber); - it->second->rtp_module_->IncomingRtpPacket(&header, data, length, - payload_specific, in_order); + it->second->rtp_module_->IncomingRtpPacket(header, data, length, + payload_specific, true); } } } diff --git a/webrtc/modules/video_coding/main/test/test_callbacks.cc b/webrtc/modules/video_coding/main/test/test_callbacks.cc index e9939d6a94..8e9677638d 100644 --- a/webrtc/modules/video_coding/main/test/test_callbacks.cc +++ b/webrtc/modules/video_coding/main/test/test_callbacks.cc @@ -82,8 +82,6 @@ VCMEncodeCompleteCallback::SendData( rtpInfo.type.Video.codecHeader.VP8.pictureId = videoHdr->codecHeader.VP8.pictureId; break; - case webrtc::kRtpVideoI420: - break; default: assert(false); return -1; @@ -308,7 +306,7 @@ RTPSendCompleteCallback::SendPacket(int channel, const void *data, int len) header.payloadType, &payload_specific)) { return -1; } - if (!rtp_receiver_->IncomingRtpPacket(&header, packet->data, + if (!rtp_receiver_->IncomingRtpPacket(header, packet->data, packet->length, payload_specific, true)) { diff --git a/webrtc/modules/video_coding/main/test/test_util.cc b/webrtc/modules/video_coding/main/test/test_util.cc index 6f694ab3ad..09ad991675 100644 --- a/webrtc/modules/video_coding/main/test/test_util.cc +++ b/webrtc/modules/video_coding/main/test/test_util.cc @@ -150,8 +150,6 @@ int32_t FileOutputFrameReceiver::FrameToRender( webrtc::RtpVideoCodecTypes ConvertCodecType(const char* plname) { if (strncmp(plname,"VP8" , 3) == 0) { return webrtc::kRtpVideoVp8; - } else if (strncmp(plname,"I420" , 5) == 0) { - return webrtc::kRtpVideoI420; } else { return webrtc::kRtpVideoNone; // Default value } diff --git a/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc b/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc index 00b50d1cb8..9b8040d925 100644 --- a/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc +++ b/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc @@ -37,6 +37,9 @@ #include "webrtc/video_engine/test/libvietest/include/tb_external_transport.h" #include "webrtc/voice_engine/include/voe_base.h" +const uint32_t kSsrc = 0x01234567; +const uint32_t kRtxSsrc = 0x01234568; +const int kRtxPayloadType = 98; #define VCM_RED_PAYLOAD_TYPE 96 #define VCM_ULPFEC_PAYLOAD_TYPE 97 @@ -219,21 +222,47 @@ int VideoEngineSampleCode(void* window1, void* window2) // Setting SSRC manually (arbitrary value), as otherwise we will get a clash // (loopback), and a new SSRC will be set, which will reset the receiver. - error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, 0x01234567); - if (error == -1) - { - printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n"); - return -1; + error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, kSsrc); + if (error == -1) { + printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n"); + return -1; } + error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, kRtxSsrc, + webrtc::kViEStreamTypeRtx, 0); + if (error == -1) { + printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n"); + return -1; + } + + error = ptrViERtpRtcp->SetRemoteSSRCType(videoChannel, + webrtc::kViEStreamTypeRtx, + kRtxSsrc); + + if (error == -1) { + printf("ERROR in ViERTP_RTCP::SetRtxReceivePayloadType\n"); + return -1; + } + + error = ptrViERtpRtcp->SetRtxSendPayloadType(videoChannel, kRtxPayloadType); + if (error == -1) { + printf("ERROR in ViERTP_RTCP::SetRtxSendPayloadType\n"); + return -1; + } + + error = ptrViERtpRtcp->SetRtxReceivePayloadType(videoChannel, + kRtxPayloadType); + if (error == -1) { + printf("ERROR in ViERTP_RTCP::SetRtxReceivePayloadType\n"); + return -1; + } // // Set up rendering // webrtc::ViERender* ptrViERender = webrtc::ViERender::GetInterface(ptrViE); - if (ptrViERender == NULL) - { - printf("ERROR in ViERender::GetInterface\n"); - return -1; + if (ptrViERender == NULL) { + printf("ERROR in ViERender::GetInterface\n"); + return -1; } error diff --git a/webrtc/video_engine/test/engine_tests.cc b/webrtc/video_engine/test/engine_tests.cc index 7a449affe7..3d6a81e158 100644 --- a/webrtc/video_engine/test/engine_tests.cc +++ b/webrtc/video_engine/test/engine_tests.cc @@ -74,7 +74,7 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver { RTPHeader header; EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast(length), &header)); - receive_stats_->IncomingPacket(header, length, false, true); + receive_stats_->IncomingPacket(header, length, false); rtp_rtcp_->SetRemoteSSRC(header.ssrc); remote_bitrate_estimator_->IncomingPacket(clock_->TimeInMilliseconds(), static_cast(length - 12), diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc index 4946f978ed..e80cb4b26c 100644 --- a/webrtc/video_engine/vie_channel.cc +++ b/webrtc/video_engine/vie_channel.cc @@ -662,13 +662,7 @@ int32_t ViEChannel::ProcessNACKRequest(const bool enable) { "%s: Could not enable NACK, RTPC not on ", __FUNCTION__); return -1; } - if (!vie_receiver_.SetNackStatus(true, - max_nack_reordering_threshold_)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not set NACK method %d", __FUNCTION__, - nackMethod); - return -1; - } + vie_receiver_.SetNackStatus(true, max_nack_reordering_threshold_); WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s: Using NACK method %d", __FUNCTION__, nackMethod); rtp_rtcp_->SetStorePacketsStatus(true, nack_history_size_sender_); @@ -699,12 +693,7 @@ int32_t ViEChannel::ProcessNACKRequest(const bool enable) { if (paced_sender_ == NULL) { rtp_rtcp_->SetStorePacketsStatus(false, 0); } - if (!vie_receiver_.SetNackStatus(false, - max_nack_reordering_threshold_)) { - WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not turn off NACK", __FUNCTION__); - return -1; - } + vie_receiver_.SetNackStatus(false, max_nack_reordering_threshold_); // When NACK is off, allow decoding with errors. Otherwise, the video // will freeze, and will only recover with a complete key frame. vcm_.SetDecodeErrorMode(kWithErrors); @@ -1189,10 +1178,10 @@ int32_t ViEChannel::SendApplicationDefinedRTCPPacket( } int32_t ViEChannel::GetSendRtcpStatistics(uint16_t* fraction_lost, - uint32_t* cumulative_lost, - uint32_t* extended_max, - uint32_t* jitter_samples, - int32_t* rtt_ms) { + uint32_t* cumulative_lost, + uint32_t* extended_max, + uint32_t* jitter_samples, + int32_t* rtt_ms) { WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", __FUNCTION__); diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc index cc50b9ac74..d7ac3c06d4 100644 --- a/webrtc/video_engine/vie_receiver.cc +++ b/webrtc/video_engine/vie_receiver.cc @@ -13,6 +13,7 @@ #include #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h" #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" @@ -40,13 +41,15 @@ ViEReceiver::ViEReceiver(const int32_t channel_id, rtp_payload_registry_.get())), rtp_receive_statistics_(ReceiveStatistics::Create( Clock::GetRealTimeClock())), + fec_receiver_(FecReceiver::Create(channel_id, this)), rtp_rtcp_(NULL), vcm_(module_vcm), remote_bitrate_estimator_(remote_bitrate_estimator), external_decryption_(NULL), decryption_buffer_(NULL), rtp_dump_(NULL), - receiving_(false) { + receiving_(false), + restored_packet_in_use_(false) { assert(remote_bitrate_estimator); } @@ -83,18 +86,24 @@ bool ViEReceiver::RegisterPayload(const VideoCodec& video_codec) { video_codec.maxBitrate) == 0; } -bool ViEReceiver::SetNackStatus(bool enable, +void ViEReceiver::SetNackStatus(bool enable, int max_nack_reordering_threshold) { - return rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff, - max_nack_reordering_threshold) == 0; + if (!enable) { + // Reset the threshold back to the lower default threshold when NACK is + // disabled since we no longer will be receiving retransmissions. + max_nack_reordering_threshold = kDefaultMaxReorderingThreshold; + } + rtp_receive_statistics_->SetMaxReorderingThreshold( + max_nack_reordering_threshold); + rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff); } void ViEReceiver::SetRtxStatus(bool enable, uint32_t ssrc) { - rtp_receiver_->SetRTXStatus(true, ssrc); + rtp_payload_registry_->SetRtxStatus(enable, ssrc); } void ViEReceiver::SetRtxPayloadType(uint32_t payload_type) { - rtp_receiver_->SetRtxPayloadType(payload_type); + rtp_payload_registry_->SetRtxPayloadType(payload_type); } uint32_t ViEReceiver::GetRemoteSsrc() const { @@ -182,9 +191,6 @@ int ViEReceiver::ReceivedRTCPPacket(const void* rtcp_packet, int32_t ViEReceiver::OnReceivedPayloadData( const uint8_t* payload_data, const uint16_t payload_size, const WebRtcRTPHeader* rtp_header) { - if (rtp_header == NULL) { - return 0; - } if (vcm_->IncomingPacket(payload_data, payload_size, *rtp_header) != 0) { // Check this... return -1; @@ -201,14 +207,7 @@ bool ViEReceiver::OnRecoveredPacket(const uint8_t* rtp_packet, return false; } header.payload_type_frequency = kVideoPayloadTypeFrequency; - PayloadUnion payload_specific; - if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType, - &payload_specific)) { - return false; - } - return rtp_receiver_->IncomingRtpPacket(&header, rtp_packet, - rtp_packet_length, - payload_specific, false); + return ReceivePacket(rtp_packet, rtp_packet_length, header, false); } int ViEReceiver::InsertRTPPacket(const int8_t* rtp_packet, @@ -253,25 +252,78 @@ int ViEReceiver::InsertRTPPacket(const int8_t* rtp_packet, if (!rtp_header_parser_->Parse(received_packet, received_packet_length, &header)) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_, - "IncomingPacket invalid RTP header"); + "Incoming packet: Invalid RTP header"); return -1; } - const int payload_size = received_packet_length - header.headerLength; + int payload_length = received_packet_length - header.headerLength; remote_bitrate_estimator_->IncomingPacket(TickTime::MillisecondTimestamp(), - payload_size, header); + payload_length, header); header.payload_type_frequency = kVideoPayloadTypeFrequency; - bool in_order = rtp_receiver_->InOrderPacket(header.sequenceNumber); - bool retransmitted = !in_order && IsPacketRetransmitted(header); + rtp_receive_statistics_->IncomingPacket(header, received_packet_length, - retransmitted, in_order); + IsPacketRetransmitted(header)); + rtp_payload_registry_->SetIncomingPayloadType(header); + return ReceivePacket(received_packet, received_packet_length, header, + IsPacketInOrder(header)) ? 0 : -1; +} + +bool ViEReceiver::ReceivePacket(const uint8_t* packet, + int packet_length, + const RTPHeader& header, + bool in_order) { + if (rtp_payload_registry_->IsEncapsulated(header)) { + return ParseAndHandleEncapsulatingHeader(packet, packet_length, header); + } + const uint8_t* payload = packet + header.headerLength; + int payload_length = packet_length - header.headerLength; + assert(payload_length >= 0); PayloadUnion payload_specific; if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType, &payload_specific)) { - return -1; + return false; } - return rtp_receiver_->IncomingRtpPacket(&header, received_packet, - received_packet_length, - payload_specific, in_order) ? 0 : -1; + return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length, + payload_specific, in_order); +} + +bool ViEReceiver::ParseAndHandleEncapsulatingHeader(const uint8_t* packet, + int packet_length, + const RTPHeader& header) { + if (rtp_payload_registry_->IsRed(header)) { + if (fec_receiver_->AddReceivedRedPacket( + header, packet, packet_length, + rtp_payload_registry_->ulpfec_payload_type()) != 0) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_, + "Incoming RED packet error"); + return false; + } + return fec_receiver_->ProcessReceivedFec() == 0; + } else if (rtp_payload_registry_->IsRtx(header)) { + // Remove the RTX header and parse the original RTP header. + if (packet_length < header.headerLength) + return false; + if (packet_length > static_cast(sizeof(restored_packet_))) + return false; + CriticalSectionScoped cs(receive_cs_.get()); + if (restored_packet_in_use_) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_, + "Multiple RTX headers detected, dropping packet"); + return false; + } + uint8_t* restored_packet_ptr = restored_packet_; + if (!rtp_payload_registry_->RestoreOriginalPacket( + &restored_packet_ptr, packet, &packet_length, rtp_receiver_->SSRC(), + header)) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_, + "Incoming RTX packet: invalid RTP header"); + return false; + } + restored_packet_in_use_ = true; + bool ret = OnRecoveredPacket(restored_packet_ptr, packet_length); + restored_packet_in_use_ = false; + return ret; + } + return false; } int ViEReceiver::InsertRTCPPacket(const int8_t* rtcp_packet, @@ -397,23 +449,26 @@ ReceiveStatistics* ViEReceiver::GetReceiveStatistics() const { return rtp_receive_statistics_.get(); } +bool ViEReceiver::IsPacketInOrder(const RTPHeader& header) const { + StreamStatistician* statistician = + rtp_receive_statistics_->GetStatistician(header.ssrc); + if (!statistician) + return false; + return statistician->IsPacketInOrder(header.sequenceNumber); +} + bool ViEReceiver::IsPacketRetransmitted(const RTPHeader& header) const { - bool rtx_enabled = false; - uint32_t rtx_ssrc = 0; - int rtx_payload_type = 0; - rtp_receiver_->RTXStatus(&rtx_enabled, &rtx_ssrc, &rtx_payload_type); - if (!rtx_enabled) { - // Check if this is a retransmission. - StreamStatistician::Statistics stats; - StreamStatistician* statistician = - rtp_receive_statistics_->GetStatistician(header.ssrc); - if (statistician && statistician->GetStatistics(&stats, false)) { - uint16_t min_rtt = 0; - rtp_rtcp_->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL); - return rtp_receiver_->RetransmitOfOldPacket(header, stats.jitter, - min_rtt); - } - } - return false; + // Retransmissions are handled separately if RTX is enabled. + if (rtp_payload_registry_->RtxEnabled()) + return false; + StreamStatistician* statistician = + rtp_receive_statistics_->GetStatistician(header.ssrc); + if (!statistician) + return false; + // Check if this is a retransmission. + uint16_t min_rtt = 0; + rtp_rtcp_->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL); + return !IsPacketInOrder(header) && + statistician->IsRetransmitOfOldPacket(header, min_rtt); } } // namespace webrtc diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h index 63014370e7..0fac8ed6fc 100644 --- a/webrtc/video_engine/vie_receiver.h +++ b/webrtc/video_engine/vie_receiver.h @@ -24,6 +24,7 @@ namespace webrtc { class CriticalSectionWrapper; class Encryption; +class FecReceiver; class ReceiveStatistics; class RemoteBitrateEstimator; class RtpDump; @@ -43,7 +44,7 @@ class ViEReceiver : public RtpData { bool SetReceiveCodec(const VideoCodec& video_codec); bool RegisterPayload(const VideoCodec& video_codec); - bool SetNackStatus(bool enable, int max_nack_reordering_threshold); + void SetNackStatus(bool enable, int max_nack_reordering_threshold); void SetRtxStatus(bool enable, uint32_t ssrc); void SetRtxPayloadType(uint32_t payload_type); @@ -86,7 +87,15 @@ class ViEReceiver : public RtpData { private: int InsertRTPPacket(const int8_t* rtp_packet, int rtp_packet_length); + bool ReceivePacket(const uint8_t* packet, int packet_length, + const RTPHeader& header, bool in_order); + // Parses and handles for instance RTX and RED headers. + // This function assumes that it's being called from only one thread. + bool ParseAndHandleEncapsulatingHeader(const uint8_t* packet, + int packet_length, + const RTPHeader& header); int InsertRTCPPacket(const int8_t* rtcp_packet, int rtcp_packet_length); + bool IsPacketInOrder(const RTPHeader& header) const; bool IsPacketRetransmitted(const RTPHeader& header) const; scoped_ptr receive_cs_; @@ -95,6 +104,7 @@ class ViEReceiver : public RtpData { scoped_ptr rtp_payload_registry_; scoped_ptr rtp_receiver_; scoped_ptr rtp_receive_statistics_; + scoped_ptr fec_receiver_; RtpRtcp* rtp_rtcp_; std::list rtp_rtcp_simulcast_; VideoCodingModule* vcm_; @@ -104,6 +114,8 @@ class ViEReceiver : public RtpData { uint8_t* decryption_buffer_; RtpDump* rtp_dump_; bool receiving_; + uint8_t restored_packet_[kViEMaxMtu]; + bool restored_packet_in_use_; }; } // namespace webrt diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc index d857776a6e..96554711fa 100644 --- a/webrtc/voice_engine/channel.cc +++ b/webrtc/voice_engine/channel.cc @@ -653,6 +653,21 @@ Channel::OnReceivedPayloadData(const uint8_t* payloadData, return 0; } +bool Channel::OnRecoveredPacket(const uint8_t* rtp_packet, + int rtp_packet_length) { + RTPHeader header; + if (!rtp_header_parser_->Parse(rtp_packet, rtp_packet_length, &header)) { + WEBRTC_TRACE(kTraceDebug, webrtc::kTraceVoice, _channelId, + "IncomingPacket invalid RTP header"); + return false; + } + header.payload_type_frequency = + rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType); + if (header.payload_type_frequency < 0) + return false; + return ReceivePacket(rtp_packet, rtp_packet_length, header, false); +} + int32_t Channel::GetAudioFrame(int32_t id, AudioFrame& audioFrame) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId), @@ -989,7 +1004,8 @@ Channel::Channel(int32_t channelId, _RxVadDetection(false), _rxApmIsEnabled(false), _rxAgcIsEnabled(false), - _rxNsIsEnabled(false) + _rxNsIsEnabled(false), + restored_packet_in_use_(false) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::Channel() - ctor"); @@ -2190,60 +2206,94 @@ int32_t Channel::ReceivedRTPPacket(const int8_t* data, int32_t length) { VoEId(_instanceId,_channelId), "Channel::SendPacket() RTP dump to input file failed"); } + const uint8_t* received_packet = reinterpret_cast(data); RTPHeader header; - if (!rtp_header_parser_->Parse(reinterpret_cast(data), - static_cast(length), &header)) { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, - VoEId(_instanceId,_channelId), - "IncomingPacket invalid RTP header"); + if (!rtp_header_parser_->Parse(received_packet, length, &header)) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId, + "Incoming packet: invalid RTP header"); return -1; } header.payload_type_frequency = rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType); - if (header.payload_type_frequency < 0) { + if (header.payload_type_frequency < 0) return -1; + rtp_receive_statistics_->IncomingPacket(header, length, + IsPacketRetransmitted(header)); + rtp_payload_registry_->SetIncomingPayloadType(header); + return ReceivePacket(received_packet, length, header, + IsPacketInOrder(header)) ? 0 : -1; +} + +bool Channel::ReceivePacket(const uint8_t* packet, + int packet_length, + const RTPHeader& header, + bool in_order) { + if (rtp_payload_registry_->IsEncapsulated(header)) { + return HandleEncapsulation(packet, packet_length, header); } - bool retransmitted = IsPacketRetransmitted(header); - bool in_order = rtp_receiver_->InOrderPacket(header.sequenceNumber); - rtp_receive_statistics_->IncomingPacket(header, static_cast(length), - retransmitted, in_order); + const uint8_t* payload = packet + header.headerLength; + int payload_length = packet_length - header.headerLength; + assert(payload_length >= 0); PayloadUnion payload_specific; if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType, - &payload_specific)) { - return -1; + &payload_specific)) { + return false; } - // Deliver RTP packet to RTP/RTCP module for parsing - // The packet will be pushed back to the channel thru the - // OnReceivedPayloadData callback so we don't push it to the ACM here - if (!rtp_receiver_->IncomingRtpPacket(&header, - reinterpret_cast(data), - static_cast(length), - payload_specific, in_order)) { - _engineStatisticsPtr->SetLastError( - VE_SOCKET_TRANSPORT_MODULE_ERROR, kTraceWarning, - "Channel::IncomingRTPPacket() RTP packet is invalid"); + return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length, + payload_specific, in_order); +} + +bool Channel::HandleEncapsulation(const uint8_t* packet, + int packet_length, + const RTPHeader& header) { + if (!rtp_payload_registry_->IsRtx(header)) + return false; + + // Remove the RTX header and parse the original RTP header. + if (packet_length < header.headerLength) + return false; + if (packet_length > kVoiceEngineMaxIpPacketSizeBytes) + return false; + if (restored_packet_in_use_) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId, + "Multiple RTX headers detected, dropping packet"); + return false; } - return 0; + uint8_t* restored_packet_ptr = restored_packet_; + if (!rtp_payload_registry_->RestoreOriginalPacket( + &restored_packet_ptr, packet, &packet_length, rtp_receiver_->SSRC(), + header)) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId, + "Incoming RTX packet: invalid RTP header"); + return false; + } + restored_packet_in_use_ = true; + bool ret = OnRecoveredPacket(restored_packet_ptr, packet_length); + restored_packet_in_use_ = false; + return ret; +} + +bool Channel::IsPacketInOrder(const RTPHeader& header) const { + StreamStatistician* statistician = + rtp_receive_statistics_->GetStatistician(header.ssrc); + if (!statistician) + return false; + return statistician->IsPacketInOrder(header.sequenceNumber); } bool Channel::IsPacketRetransmitted(const RTPHeader& header) const { - bool rtx_enabled = false; - uint32_t rtx_ssrc = 0; - int rtx_payload_type = 0; - rtp_receiver_->RTXStatus(&rtx_enabled, &rtx_ssrc, &rtx_payload_type); - if (!rtx_enabled) { - // Check if this is a retransmission. - StreamStatistician::Statistics stats; - StreamStatistician* statistician = - rtp_receive_statistics_->GetStatistician(header.ssrc); - if (statistician && statistician->GetStatistics(&stats, false)) { - uint16_t min_rtt = 0; - _rtpRtcpModule->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL); - return rtp_receiver_->RetransmitOfOldPacket(header, stats.jitter, - min_rtt); - } - } - return false; + // Retransmissions are handled separately if RTX is enabled. + if (rtp_payload_registry_->RtxEnabled()) + return false; + StreamStatistician* statistician = + rtp_receive_statistics_->GetStatistician(header.ssrc); + if (!statistician) + return false; + // Check if this is a retransmission. + uint16_t min_rtt = 0; + _rtpRtcpModule->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL); + return !IsPacketInOrder(header) && + statistician->IsRetransmitOfOldPacket(header, min_rtt); } int32_t Channel::ReceivedRTCPPacket(const int8_t* data, int32_t length) { @@ -4182,8 +4232,8 @@ Channel::GetFECStatus(bool& enabled, int& redPayloadtype) void Channel::SetNACKStatus(bool enable, int maxNumberOfPackets) { // None of these functions can fail. _rtpRtcpModule->SetStorePacketsStatus(enable, maxNumberOfPackets); - rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff, - maxNumberOfPackets); + rtp_receive_statistics_->SetMaxReorderingThreshold(maxNumberOfPackets); + rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff); if (enable) _audioCodingModule.EnableNack(maxNumberOfPackets); else diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h index 34013069f6..a61470755f 100644 --- a/webrtc/voice_engine/channel.h +++ b/webrtc/voice_engine/channel.h @@ -306,10 +306,7 @@ public: uint16_t payloadSize, const WebRtcRTPHeader* rtpHeader); - bool OnRecoveredPacket(const uint8_t* packet, int packet_length) { - // Generic FEC not supported for audio. - return true; - } + bool OnRecoveredPacket(const uint8_t* packet, int packet_length); public: // From RtpFeedback in the RTP/RTCP module @@ -439,6 +436,12 @@ public: uint32_t EncodeAndSend(); private: + bool ReceivePacket(const uint8_t* packet, int packet_length, + const RTPHeader& header, bool in_order); + bool HandleEncapsulation(const uint8_t* packet, + int packet_length, + const RTPHeader& header); + bool IsPacketInOrder(const RTPHeader& header) const; bool IsPacketRetransmitted(const RTPHeader& header) const; int ResendPackets(const uint16_t* sequence_numbers, int length); int InsertInbandDtmfTone(); @@ -502,6 +505,7 @@ private: uint32_t playout_delay_ms_; uint32_t _numberOfDiscardedPackets; uint16_t send_sequence_number_; + uint8_t restored_packet_[kVoiceEngineMaxIpPacketSizeBytes]; private: // uses @@ -571,6 +575,7 @@ private: bool _rxApmIsEnabled; bool _rxAgcIsEnabled; bool _rxNsIsEnabled; + bool restored_packet_in_use_; }; } // namespace voe