Support handling multiple RTX but only generate SDP with RTX associated with VP8.

This implementation registers RTX-APT map inside RTP sender and receiver.
While it only generates SDP with RTX associated with VP8 to make it
compatible with previous Chrome versions.

Should add following changes after reaches stable,
* Use RTX-APT map for building and restoring RTP packets.
* Add RTX support for RED or VP9 in Video engine.
* Set RTX payload type for RED inside FecConfig in EndToEndTest.

BUG=4024
R=mflodman@webrtc.org, pbos@webrtc.org, pthatcher@webrtc.org, stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/36889004

Cr-Commit-Position: refs/heads/master@{#9040}
This commit is contained in:
Shao Changbin
2015-04-21 20:24:50 +08:00
parent 6cff9cf8a3
commit e62202fedf
40 changed files with 461 additions and 251 deletions

View File

@ -32,6 +32,8 @@ const uint16_t kTestSequenceNumber = 2345;
const uint32_t kTestNumberOfPackets = 1350;
const int kTestNumberOfRtxPackets = 149;
const int kNumFrames = 30;
const int kPayloadType = 123;
const int kRtxPayloadType = 98;
class VerifyingRtxReceiver : public NullRtpData
{
@ -129,7 +131,10 @@ class RtxLoopBackTransport : public webrtc::Transport {
if (!parser->Parse(restored_packet_ptr, packet_length, &header)) {
return -1;
}
} else {
rtp_payload_registry_->SetIncomingPayloadType(header);
}
restored_packet_ptr += header.headerLength;
packet_length -= header.headerLength;
PayloadUnion payload_specific;
@ -203,15 +208,17 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
VideoCodec video_codec;
memset(&video_codec, 0, sizeof(video_codec));
video_codec.plType = 123;
video_codec.plType = kPayloadType;
memcpy(video_codec.plName, "I420", 5);
EXPECT_EQ(0, rtp_rtcp_module_->RegisterSendPayload(video_codec));
rtp_rtcp_module_->SetRtxSendPayloadType(kRtxPayloadType, kPayloadType);
EXPECT_EQ(0, rtp_receiver_->RegisterReceivePayload(video_codec.plName,
video_codec.plType,
90000,
0,
video_codec.maxBitrate));
rtp_payload_registry_.SetRtxPayloadType(kRtxPayloadType, kPayloadType);
for (size_t n = 0; n < payload_data_length; n++) {
payload_data[n] = n % 10;
@ -262,12 +269,9 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
uint32_t timestamp = 3000;
uint16_t nack_list[kVideoNackListSize];
for (int frame = 0; frame < kNumFrames; ++frame) {
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
123,
timestamp,
timestamp / 90,
payload_data,
payload_data_length));
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(
webrtc::kVideoFrameDelta, kPayloadType, timestamp,
timestamp / 90, payload_data, payload_data_length));
// Min required delay until retransmit = 5 + RTT ms (RTT = 0).
fake_clock.AdvanceTimeMilliseconds(5);
int length = BuildNackList(nack_list);
@ -310,12 +314,9 @@ TEST_F(RtpRtcpRtxNackTest, LongNackList) {
// Send 30 frames which at the default size is roughly what we need to get
// enough packets.
for (int frame = 0; frame < kNumFrames; ++frame) {
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
123,
timestamp,
timestamp / 90,
payload_data,
payload_data_length));
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(
webrtc::kVideoFrameDelta, kPayloadType, timestamp,
timestamp / 90, payload_data, payload_data_length));
// Prepare next frame.
timestamp += 3000;
fake_clock.AdvanceTimeMilliseconds(33);

View File

@ -15,8 +15,7 @@
namespace webrtc {
RTPPayloadRegistry::RTPPayloadRegistry(
RTPPayloadStrategy* rtp_payload_strategy)
RTPPayloadRegistry::RTPPayloadRegistry(RTPPayloadStrategy* rtp_payload_strategy)
: crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
rtp_payload_strategy_(rtp_payload_strategy),
red_payload_type_(-1),
@ -25,8 +24,9 @@ RTPPayloadRegistry::RTPPayloadRegistry(
last_received_payload_type_(-1),
last_received_media_payload_type_(-1),
rtx_(false),
payload_type_rtx_(-1),
ssrc_rtx_(0) {}
rtx_payload_type_(-1),
ssrc_rtx_(0) {
}
RTPPayloadRegistry::~RTPPayloadRegistry() {
while (!payload_type_map_.empty()) {
@ -256,18 +256,18 @@ bool RTPPayloadRegistry::RestoreOriginalPacket(uint8_t** restored_packet,
ByteWriter<uint32_t>::WriteBigEndian(*restored_packet + 8, original_ssrc);
CriticalSectionScoped cs(crit_sect_.get());
if (!rtx_)
return true;
if (payload_type_rtx_ != -1) {
if (header.payloadType == payload_type_rtx_ &&
incoming_payload_type_ != -1) {
(*restored_packet)[1] = static_cast<uint8_t>(incoming_payload_type_);
if (header.markerBit) {
(*restored_packet)[1] |= kRtpMarkerBitMask; // Marker bit is set.
}
} else {
LOG(LS_WARNING) << "Incorrect RTX configuration, dropping packet.";
return false;
}
if (rtx_payload_type_ == -1 || incoming_payload_type_ == -1) {
LOG(LS_WARNING) << "Incorrect RTX configuration, dropping packet.";
return false;
}
// TODO(changbin): Will use RTX APT map for restoring packets,
// thus incoming_payload_type_ should be removed in future.
(*restored_packet)[1] = static_cast<uint8_t>(incoming_payload_type_);
if (header.markerBit) {
(*restored_packet)[1] |= kRtpMarkerBitMask; // Marker bit is set.
}
return true;
}
@ -284,11 +284,17 @@ bool RTPPayloadRegistry::GetRtxSsrc(uint32_t* ssrc) const {
return rtx_;
}
void RTPPayloadRegistry::SetRtxPayloadType(int payload_type) {
void RTPPayloadRegistry::SetRtxPayloadType(int payload_type,
int associated_payload_type) {
CriticalSectionScoped cs(crit_sect_.get());
assert(payload_type >= 0);
payload_type_rtx_ = payload_type;
if (payload_type < 0) {
LOG(LS_ERROR) << "Invalid RTX payload type: " << payload_type;
return;
}
rtx_payload_type_map_[payload_type] = associated_payload_type;
rtx_ = true;
rtx_payload_type_ = payload_type;
}
bool RTPPayloadRegistry::IsRed(const RTPHeader& header) const {

View File

@ -206,11 +206,12 @@ void ModuleRtpRtcpImpl::SetRtxSsrc(uint32_t ssrc) {
rtp_sender_.SetRtxSsrc(ssrc);
}
void ModuleRtpRtcpImpl::SetRtxSendPayloadType(int payload_type) {
rtp_sender_.SetRtxPayloadType(payload_type);
void ModuleRtpRtcpImpl::SetRtxSendPayloadType(int payload_type,
int associated_payload_type) {
rtp_sender_.SetRtxPayloadType(payload_type, associated_payload_type);
}
int ModuleRtpRtcpImpl::RtxSendPayloadType() const {
std::pair<int, int> ModuleRtpRtcpImpl::RtxSendPayloadType() const {
return rtp_sender_.RtxPayloadType();
}

View File

@ -88,8 +88,9 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
void SetRtxSsrc(uint32_t ssrc) override;
void SetRtxSendPayloadType(int payload_type) override;
int RtxSendPayloadType() const override;
void SetRtxSendPayloadType(int payload_type,
int associated_payload_type) override;
std::pair<int, int> RtxSendPayloadType() const override;
// Sends kRtcpByeCode when going from true to false.
int32_t SetSendingStatus(bool sending) override;

View File

@ -11,7 +11,9 @@
#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
#include <stdlib.h> // srand
#include <utility>
#include "webrtc/base/checks.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_cvo.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h"
@ -155,7 +157,7 @@ RTPSender::RTPSender(int32_t id,
last_packet_marker_bit_(false),
csrcs_(),
rtx_(kRtxOff),
payload_type_rtx_(-1),
rtx_payload_type_(-1),
target_bitrate_critsect_(CriticalSectionWrapper::CreateCriticalSection()),
target_bitrate_(0) {
memset(nack_byte_count_times_, 0, sizeof(nack_byte_count_times_));
@ -421,14 +423,28 @@ uint32_t RTPSender::RtxSsrc() const {
return ssrc_rtx_;
}
void RTPSender::SetRtxPayloadType(int payload_type) {
void RTPSender::SetRtxPayloadType(int payload_type,
int associated_payload_type) {
CriticalSectionScoped cs(send_critsect_.get());
payload_type_rtx_ = payload_type;
DCHECK_LE(payload_type, 127);
DCHECK_LE(associated_payload_type, 127);
if (payload_type < 0) {
LOG(LS_ERROR) << "Invalid RTX payload type: " << payload_type;
return;
}
rtx_payload_type_map_[associated_payload_type] = payload_type;
rtx_payload_type_ = payload_type;
}
int RTPSender::RtxPayloadType() const {
std::pair<int, int> RTPSender::RtxPayloadType() const {
CriticalSectionScoped cs(send_critsect_.get());
return payload_type_rtx_;
for (const auto& kv : rtx_payload_type_map_) {
if (kv.second == rtx_payload_type_) {
return std::make_pair(rtx_payload_type_, kv.first);
}
}
return std::make_pair(-1, -1);
}
int32_t RTPSender::CheckPayloadType(int8_t payload_type,
@ -637,8 +653,7 @@ size_t RTPSender::SendPadData(uint32_t timestamp,
ssrc = ssrc_rtx_;
sequence_number = sequence_number_rtx_;
++sequence_number_rtx_;
payload_type = ((rtx_ & kRtxRedundantPayloads) > 0) ? payload_type_rtx_
: payload_type_;
payload_type = rtx_payload_type_;
over_rtx = true;
}
}
@ -1795,8 +1810,8 @@ void RTPSender::BuildRtxPacket(uint8_t* buffer, size_t* length,
memcpy(data_buffer_rtx, buffer, rtp_header.headerLength);
// Replace payload type, if a specific type is set for RTX.
if (payload_type_rtx_ != -1) {
data_buffer_rtx[1] = static_cast<uint8_t>(payload_type_rtx_);
if (rtx_payload_type_ != -1) {
data_buffer_rtx[1] = static_cast<uint8_t>(rtx_payload_type_);
if (rtp_header.markerBit)
data_buffer_rtx[1] |= kRtpMarkerBitMask;
}

View File

@ -10,7 +10,6 @@
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_
#include <assert.h>
#include <math.h>
@ -216,8 +215,8 @@ class RTPSender : public RTPSenderInterface {
uint32_t RtxSsrc() const;
void SetRtxSsrc(uint32_t ssrc);
void SetRtxPayloadType(int payloadType);
int RtxPayloadType() const;
void SetRtxPayloadType(int payload_type, int associated_payload_type);
std::pair<int, int> RtxPayloadType() const;
// Functions wrapping RTPSenderInterface.
int32_t BuildRTPheader(uint8_t* data_buffer,
@ -426,7 +425,11 @@ class RTPSender : public RTPSenderInterface {
std::vector<uint32_t> csrcs_ GUARDED_BY(send_critsect_);
int rtx_ GUARDED_BY(send_critsect_);
uint32_t ssrc_rtx_ GUARDED_BY(send_critsect_);
int payload_type_rtx_ GUARDED_BY(send_critsect_);
// TODO(changbin): Remove rtx_payload_type_ once interop with old clients that
// only understand one RTX PT is no longer needed.
int rtx_payload_type_ GUARDED_BY(send_critsect_);
// Mapping rtx_payload_type_map_[associated] = rtx.
std::map<int8_t, int8_t> rtx_payload_type_map_ GUARDED_BY(send_critsect_);
// Note: Don't access this variable directly, always go through
// SetTargetBitrateKbps or GetTargetBitrateKbps. Also remember

View File

@ -36,6 +36,7 @@ const int kTransmissionTimeOffsetExtensionId = 1;
const int kAbsoluteSendTimeExtensionId = 14;
const int kTransportSequenceNumberExtensionId = 13;
const int kPayload = 100;
const int kRtxPayload = 98;
const uint32_t kTimestamp = 10;
const uint16_t kSeqNum = 33;
const int kTimeOffset = 22222;
@ -822,6 +823,7 @@ TEST_F(RtpSenderTest, SendRedundantPayloads) {
rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport, NULL,
&mock_paced_sender_, NULL, NULL, NULL));
rtp_sender_->SetSequenceNumber(kSeqNum);
rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
// Make all packets go through the pacer.
EXPECT_CALL(mock_paced_sender_,
SendPacket(PacedSender::kNormalPriority, _, _, _, _, _)).
@ -1291,7 +1293,7 @@ TEST_F(RtpSenderTest, BytesReportedCorrectly) {
const uint8_t kPayloadType = 127;
rtp_sender_->SetSSRC(1234);
rtp_sender_->SetRtxSsrc(4321);
rtp_sender_->SetRtxPayloadType(kPayloadType - 1);
rtp_sender_->SetRtxPayloadType(kPayloadType - 1, kPayloadType);
rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads);
ASSERT_EQ(