Notify jitter buffer about received FEC packets (to avoid sending NACK request for these packets).
Don't copy codec specific header for empty packets in the jitter buffer.
BUG=3135
R=pbos@webrtc.org, stefan@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/37659004
Cr-Commit-Position: refs/heads/master@{#8184}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8184 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc
index 095022e..c0ba307 100644
--- a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc
@@ -572,8 +572,6 @@
size_t len = 0;
uint8_t packet[kRrLength + kReportBlockLength - 1];
rr.Build(packet, &len, kRrLength + kReportBlockLength - 1);
- RtcpPacketParser parser;
- parser.Parse(packet, len);
EXPECT_EQ(0U, len);
}
diff --git a/webrtc/modules/video_coding/main/source/frame_buffer.cc b/webrtc/modules/video_coding/main/source/frame_buffer.cc
index fd35300..94b06f1 100644
--- a/webrtc/modules/video_coding/main/source/frame_buffer.cc
+++ b/webrtc/modules/video_coding/main/source/frame_buffer.cc
@@ -127,7 +127,9 @@
_encodedHeight = packet.height;
}
- CopyCodecSpecific(&packet.codecSpecificHeader);
+ // Don't copy payload specific data for empty packets (e.g padding packets).
+ if (packet.sizeBytes > 0)
+ CopyCodecSpecific(&packet.codecSpecificHeader);
int retVal = _sessionInfo.InsertPacket(packet, _buffer,
decode_error_mode,
diff --git a/webrtc/test/rtcp_packet_parser.cc b/webrtc/test/rtcp_packet_parser.cc
index 391be85..b9e430f 100644
--- a/webrtc/test/rtcp_packet_parser.cc
+++ b/webrtc/test/rtcp_packet_parser.cc
@@ -10,6 +10,8 @@
#include "webrtc/test/rtcp_packet_parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
namespace webrtc {
namespace test {
@@ -20,6 +22,7 @@
void RtcpPacketParser::Parse(const void *data, size_t len) {
const uint8_t* packet = static_cast<const uint8_t*>(data);
RTCPUtility::RTCPParserV2 parser(packet, len, true);
+ EXPECT_TRUE(parser.IsValid());
for (RTCPUtility::RTCPPacketTypes type = parser.Begin();
type != RTCPUtility::kRtcpNotValidCode;
type = parser.Iterate()) {
diff --git a/webrtc/test/test.gyp b/webrtc/test/test.gyp
index 03e0529..83f2316 100644
--- a/webrtc/test/test.gyp
+++ b/webrtc/test/test.gyp
@@ -64,6 +64,7 @@
'rtp_file_writer.h',
],
'dependencies': [
+ '<(DEPTH)/testing/gtest.gyp:gtest',
'<(webrtc_root)/modules/modules.gyp:rtp_rtcp',
],
},
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index e888ed8..c01035d 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -35,6 +35,7 @@
#include "webrtc/test/frame_generator.h"
#include "webrtc/test/frame_generator_capturer.h"
#include "webrtc/test/null_transport.h"
+#include "webrtc/test/rtcp_packet_parser.h"
#include "webrtc/test/rtp_rtcp_observer.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/gtest_disable.h"
@@ -75,6 +76,7 @@
void TestXrReceiverReferenceTimeReport(bool enable_rrtr);
void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first);
void TestRtpStatePreservation(bool use_rtx);
+ void TestReceivedFecPacketsNotNacked(const FakeNetworkPipe::Config& config);
};
TEST_F(EndToEndTest, ReceiverCanBeStartedTwice) {
@@ -536,6 +538,118 @@
RunBaseTest(&test);
}
+TEST_F(EndToEndTest, ReceivedFecPacketsNotNacked) {
+ // At low RTT (< kLowRttNackMs) -> NACK only, no FEC.
+ // Configure some network delay.
+ const int kNetworkDelayMs = 50;
+ FakeNetworkPipe::Config config;
+ config.queue_delay_ms = kNetworkDelayMs;
+ TestReceivedFecPacketsNotNacked(config);
+}
+
+void EndToEndTest::TestReceivedFecPacketsNotNacked(
+ const FakeNetworkPipe::Config& config) {
+ class FecNackObserver : public test::EndToEndTest {
+ public:
+ explicit FecNackObserver(const FakeNetworkPipe::Config& config)
+ : EndToEndTest(kDefaultTimeoutMs, config),
+ state_(kDropEveryOtherPacketUntilFec),
+ fec_sequence_number_(0),
+ has_last_sequence_number_(false),
+ last_sequence_number_(0) {}
+
+ private:
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+ RTPHeader header;
+ EXPECT_TRUE(parser_->Parse(packet, length, &header));
+ EXPECT_EQ(kRedPayloadType, header.payloadType);
+
+ int encapsulated_payload_type =
+ static_cast<int>(packet[header.headerLength]);
+ if (encapsulated_payload_type != kFakeSendPayloadType)
+ EXPECT_EQ(kUlpfecPayloadType, encapsulated_payload_type);
+
+ if (has_last_sequence_number_ &&
+ !IsNewerSequenceNumber(header.sequenceNumber,
+ last_sequence_number_)) {
+ // Drop retransmitted packets.
+ return DROP_PACKET;
+ }
+ last_sequence_number_ = header.sequenceNumber;
+ has_last_sequence_number_ = true;
+
+ bool fec_packet = encapsulated_payload_type == kUlpfecPayloadType;
+ switch (state_) {
+ case kDropEveryOtherPacketUntilFec:
+ if (fec_packet) {
+ state_ = kDropAllMediaPacketsUntilFec;
+ } else if (header.sequenceNumber % 2 == 0) {
+ return DROP_PACKET;
+ }
+ break;
+ case kDropAllMediaPacketsUntilFec:
+ if (!fec_packet)
+ return DROP_PACKET;
+ fec_sequence_number_ = header.sequenceNumber;
+ state_ = kVerifyFecPacketNotInNackList;
+ break;
+ case kVerifyFecPacketNotInNackList:
+ // Continue to drop packets. Make sure no frame can be decoded.
+ if (fec_packet || header.sequenceNumber % 2 == 0)
+ return DROP_PACKET;
+ break;
+ }
+ return SEND_PACKET;
+ }
+
+ virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length)
+ OVERRIDE {
+ if (state_ == kVerifyFecPacketNotInNackList) {
+ test::RtcpPacketParser rtcp_parser;
+ rtcp_parser.Parse(packet, length);
+ std::vector<uint16_t> nacks = rtcp_parser.nack_item()->last_nack_list();
+ if (!nacks.empty() &&
+ IsNewerSequenceNumber(nacks.back(), fec_sequence_number_)) {
+ EXPECT_TRUE(std::find(
+ nacks.begin(), nacks.end(), fec_sequence_number_) == nacks.end());
+ observation_complete_->Set();
+ }
+ }
+ return SEND_PACKET;
+ }
+
+ virtual void ModifyConfigs(
+ VideoSendStream::Config* send_config,
+ std::vector<VideoReceiveStream::Config>* receive_configs,
+ VideoEncoderConfig* encoder_config) OVERRIDE {
+ // Configure hybrid NACK/FEC.
+ send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
+ send_config->rtp.fec.red_payload_type = kRedPayloadType;
+ send_config->rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
+ (*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
+ (*receive_configs)[0].rtp.fec.red_payload_type = kRedPayloadType;
+ (*receive_configs)[0].rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
+ }
+
+ virtual void PerformTest() OVERRIDE {
+ EXPECT_EQ(kEventSignaled, Wait())
+ << "Timed out while waiting for FEC packets to be received.";
+ }
+
+ enum {
+ kDropEveryOtherPacketUntilFec,
+ kDropAllMediaPacketsUntilFec,
+ kVerifyFecPacketNotInNackList,
+ } state_;
+
+ uint16_t fec_sequence_number_;
+ bool has_last_sequence_number_;
+ uint16_t last_sequence_number_;
+ } test(config);
+
+ RunBaseTest(&test);
+}
+
// This test drops second RTP packet with a marker bit set, makes sure it's
// retransmitted and renders. Retransmission SSRCs are also checked.
void EndToEndTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) {
diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc
index cdff860..1d2485e 100644
--- a/webrtc/video_engine/vie_receiver.cc
+++ b/webrtc/video_engine/vie_receiver.cc
@@ -322,8 +322,11 @@
const RTPHeader& header) {
if (rtp_payload_registry_->IsRed(header)) {
int8_t ulpfec_pt = rtp_payload_registry_->ulpfec_payload_type();
- if (packet[header.headerLength] == ulpfec_pt)
+ if (packet[header.headerLength] == ulpfec_pt) {
rtp_receive_statistics_->FecPacketReceived(header, packet_length);
+ // Notify vcm about received FEC packets to avoid NACKing these packets.
+ NotifyReceiverOfFecPacket(header);
+ }
if (fec_receiver_->AddReceivedRedPacket(
header, packet, packet_length, ulpfec_pt) != 0) {
return false;
@@ -360,6 +363,28 @@
return false;
}
+void ViEReceiver::NotifyReceiverOfFecPacket(const RTPHeader& header) {
+ int8_t last_media_payload_type =
+ rtp_payload_registry_->last_received_media_payload_type();
+ if (last_media_payload_type < 0) {
+ LOG(LS_WARNING) << "Failed to get last media payload type.";
+ return;
+ }
+ // Fake an empty media packet.
+ WebRtcRTPHeader rtp_header = {};
+ rtp_header.header = header;
+ rtp_header.header.payloadType = last_media_payload_type;
+ rtp_header.header.paddingLength = 0;
+ PayloadUnion payload_specific;
+ if (!rtp_payload_registry_->GetPayloadSpecifics(last_media_payload_type,
+ &payload_specific)) {
+ LOG(LS_WARNING) << "Failed to get payload specifics.";
+ return;
+ }
+ rtp_header.type.Video.codec = payload_specific.Video.videoCodecType;
+ OnReceivedPayloadData(NULL, 0, &rtp_header);
+}
+
int ViEReceiver::InsertRTCPPacket(const uint8_t* rtcp_packet,
size_t rtcp_packet_length) {
{
diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h
index b8e6569..2216df1 100644
--- a/webrtc/video_engine/vie_receiver.h
+++ b/webrtc/video_engine/vie_receiver.h
@@ -103,6 +103,7 @@
bool ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
size_t packet_length,
const RTPHeader& header);
+ void NotifyReceiverOfFecPacket(const RTPHeader& header);
int InsertRTCPPacket(const uint8_t* rtcp_packet, size_t rtcp_packet_length);
bool IsPacketInOrder(const RTPHeader& header) const;
bool IsPacketRetransmitted(const RTPHeader& header, bool in_order) const;