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
This commit is contained in:
asapersson@webrtc.org
2015-01-28 13:58:27 +00:00
parent 22c2f0572b
commit 37c0559c1e
7 changed files with 148 additions and 4 deletions

View File

@@ -572,8 +572,6 @@ TEST(RtcpPacketTest, BuildWithTooSmallBuffer) {
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);
}

View File

@@ -127,7 +127,9 @@ VCMFrameBuffer::InsertPacket(const VCMPacket& packet,
_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,

View File

@@ -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 @@ RtcpPacketParser::~RtcpPacketParser() {}
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()) {

View File

@@ -64,6 +64,7 @@
'rtp_file_writer.h',
],
'dependencies': [
'<(DEPTH)/testing/gtest.gyp:gtest',
'<(webrtc_root)/modules/modules.gyp:rtp_rtcp',
],
},

View File

@@ -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 @@ class EndToEndTest : public test::CallTest {
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 @@ TEST_F(EndToEndTest, CanReceiveFec) {
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) {

View File

@@ -322,8 +322,11 @@ bool ViEReceiver::ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
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 @@ bool ViEReceiver::ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
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) {
{

View File

@@ -103,6 +103,7 @@ class ViEReceiver : public RtpData {
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;