Adds support for sending redundant payloads over RTX.

TEST=trybots
BUG=1812
R=mflodman@webrtc.org, pbos@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5209 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
stefan@webrtc.org 2013-12-04 10:24:26 +00:00
parent 9523b55826
commit 7e9315b42e
23 changed files with 504 additions and 232 deletions

View File

@ -1,3 +1,3 @@
# Tests that are too slow.
RampUpTest/*
RampUpTest.*
CallTest.PlaysOutAudioAndVideoInSync

View File

@ -15,6 +15,7 @@
#include <vector>
#include "webrtc/call.h"
#include "webrtc/common.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h"
@ -148,7 +149,7 @@ namespace internal {
TraceDispatcher* global_trace_dispatcher = NULL;
} // internal
Call* Call::Create(const Call::Config& config) {
void CreateTraceDispatcher() {
if (internal::global_trace_dispatcher == NULL) {
TraceDispatcher* dispatcher = new TraceDispatcher();
// TODO(pbos): Atomic compare and exchange.
@ -158,8 +159,13 @@ Call* Call::Create(const Call::Config& config) {
delete dispatcher;
}
}
}
VideoEngine* video_engine = VideoEngine::Create();
Call* Call::Create(const Call::Config& config) {
CreateTraceDispatcher();
VideoEngine* video_engine = config.webrtc_config != NULL ?
VideoEngine::Create(*config.webrtc_config) : VideoEngine::Create();
assert(video_engine != NULL);
return new internal::Call(video_engine, config);

View File

@ -38,12 +38,15 @@ class Call {
public:
struct Config {
explicit Config(newapi::Transport* send_transport)
: send_transport(send_transport),
: webrtc_config(NULL),
send_transport(send_transport),
overuse_detection(false),
voice_engine(NULL),
trace_callback(NULL),
trace_filter(kTraceDefault) {}
webrtc::Config* webrtc_config;
newapi::Transport* send_transport;
bool overuse_detection;
@ -56,6 +59,9 @@ class Call {
static Call* Create(const Call::Config& config);
static Call* Create(const Call::Config& config,
const webrtc::Config& webrtc_config);
virtual std::vector<VideoCodec> GetVideoCodecs() = 0;
virtual VideoSendStream::Config GetDefaultSendConfig() = 0;

25
webrtc/experiments.h Normal file
View File

@ -0,0 +1,25 @@
/*
* 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_EXPERIMENTS_H_
#define WEBRTC_EXPERIMENTS_H_
namespace webrtc {
struct PaddingStrategy {
PaddingStrategy()
: redundant_payloads(false) {}
explicit PaddingStrategy(bool redundant_payloads)
: redundant_payloads(redundant_payloads) {}
virtual ~PaddingStrategy() {}
const bool redundant_payloads;
};
} // namespace webrtc
#endif // WEBRTC_EXPERIMENTS_H_

View File

@ -251,7 +251,7 @@ class RtpRtcp : public Module {
/*
* Turn on/off sending RTX (RFC 4588) on a specific SSRC.
*/
virtual int32_t SetRTXSendStatus(RtxMode mode, bool set_ssrc,
virtual int32_t SetRTXSendStatus(int modes, bool set_ssrc,
uint32_t ssrc) = 0;
// Sets the payload type to use when sending RTX packets. Note that this
@ -261,7 +261,7 @@ class RtpRtcp : public Module {
/*
* Get status of sending RTX (RFC 4588) on a specific SSRC.
*/
virtual int32_t RTXSendStatus(RtxMode* mode, uint32_t* ssrc,
virtual int32_t RTXSendStatus(int* modes, uint32_t* ssrc,
int* payloadType) const = 0;
/*

View File

@ -129,9 +129,10 @@ enum RetransmissionMode {
};
enum RtxMode {
kRtxOff = 0,
kRtxRetransmitted = 1, // Apply RTX only to retransmitted packets.
kRtxAll = 2 // Apply RTX to all packets (source + retransmissions).
kRtxOff = 0x0,
kRtxRetransmitted = 0x1, // Only send retransmissions over RTX.
kRtxRedundantPayloads = 0x2 // Preventively send redundant payloads
// instead of padding.
};
const int kRtxHeaderSize = 2;

View File

@ -85,9 +85,9 @@ class MockRtpRtcp : public RtpRtcp {
MOCK_METHOD1(SetCSRCStatus,
int32_t(const bool include));
MOCK_METHOD3(SetRTXSendStatus,
int32_t(RtxMode mode, bool setSSRC, uint32_t ssrc));
int32_t(int modes, bool setSSRC, uint32_t ssrc));
MOCK_CONST_METHOD3(RTXSendStatus,
int32_t(RtxMode* mode, uint32_t* ssrc, int* payload_type));
int32_t(int* modes, uint32_t* ssrc, int* payload_type));
MOCK_METHOD1(SetRtxSendPayloadType,
void(int));
MOCK_METHOD1(SetSendingStatus,

View File

@ -343,27 +343,3 @@ TEST_F(RtpRtcpRtxNackTest, RtxNack) {
EXPECT_EQ(kTestNumberOfRtxPackets, transport_.count_rtx_ssrc_);
EXPECT_TRUE(ExpectedPacketsReceived());
}
TEST_F(RtpRtcpRtxNackTest, RTXAllNoLoss) {
RunRtxTest(kRtxAll, 0);
EXPECT_EQ(kTestSequenceNumber, *(receiver_.sequence_numbers_.begin()));
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
*(receiver_.sequence_numbers_.rbegin()));
// We have transmitted all packets twice, and loss was set to 0.
EXPECT_EQ(kTestNumberOfPackets * 2u, receiver_.sequence_numbers_.size());
// Half of the packets should be via RTX.
EXPECT_EQ(static_cast<int>(kTestNumberOfPackets),
transport_.count_rtx_ssrc_);
}
TEST_F(RtpRtcpRtxNackTest, RTXAllWithLoss) {
int loss = 10;
RunRtxTest(kRtxAll, loss);
EXPECT_EQ(kTestSequenceNumber, *(receiver_.sequence_numbers_.begin()));
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
*(receiver_.sequence_numbers_.rbegin()));
// Got everything but lost packets.
EXPECT_EQ(2u * (kTestNumberOfPackets - kTestNumberOfPackets / loss),
receiver_.sequence_numbers_.size());
EXPECT_EQ(static_cast<int>(kTestNumberOfPackets), transport_.count_rtx_ssrc_);
}

View File

@ -11,7 +11,10 @@
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h> // memset
#include <limits>
#include <set>
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
@ -19,6 +22,8 @@
namespace webrtc {
enum { kMinPacketRequestBytes = 50 };
RTPPacketHistory::RTPPacketHistory(Clock* clock)
: clock_(clock),
critsect_(CriticalSectionWrapper::CreateCriticalSection()),
@ -55,7 +60,7 @@ void RTPPacketHistory::Allocate(uint16_t number_to_store) {
stored_seq_nums_.resize(number_to_store);
stored_lengths_.resize(number_to_store);
stored_times_.resize(number_to_store);
stored_resend_times_.resize(number_to_store);
stored_send_times_.resize(number_to_store);
stored_types_.resize(number_to_store);
}
@ -74,7 +79,7 @@ void RTPPacketHistory::Free() {
stored_seq_nums_.clear();
stored_lengths_.clear();
stored_times_.clear();
stored_resend_times_.clear();
stored_send_times_.clear();
stored_types_.clear();
store_ = false;
@ -139,9 +144,9 @@ int32_t RTPPacketHistory::PutRTPPacket(const uint8_t* packet,
stored_seq_nums_[prev_index_] = seq_num;
stored_lengths_[prev_index_] = packet_length;
stored_times_[prev_index_] =
(capture_time_ms > 0) ? capture_time_ms : clock_->TimeInMilliseconds();
stored_resend_times_[prev_index_] = 0; // packet not resent
stored_times_[prev_index_] = (capture_time_ms > 0) ? capture_time_ms :
clock_->TimeInMilliseconds();
stored_send_times_[prev_index_] = 0; // Packet not sent.
stored_types_[prev_index_] = type;
++prev_index_;
@ -211,12 +216,12 @@ bool RTPPacketHistory::HasRTPPacket(uint16_t sequence_number) const {
return true;
}
bool RTPPacketHistory::GetRTPPacket(uint16_t sequence_number,
uint32_t min_elapsed_time_ms,
uint8_t* packet,
uint16_t* packet_length,
int64_t* stored_time_ms,
StorageType* type) const {
bool RTPPacketHistory::GetPacketAndSetSendTime(uint16_t sequence_number,
uint32_t min_elapsed_time_ms,
bool retransmit,
uint8_t* packet,
uint16_t* packet_length,
int64_t* stored_time_ms) {
CriticalSectionScoped cs(critsect_);
if (!store_) {
return false;
@ -237,46 +242,58 @@ bool RTPPacketHistory::GetRTPPacket(uint16_t sequence_number,
return false;
}
if (length > *packet_length) {
if (length > *packet_length) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
"Input buffer too short for packet %u", sequence_number);
"Input buffer too short for packet %u", sequence_number);
return false;
}
}
// Verify elapsed time since last retrieve.
int64_t now = clock_->TimeInMilliseconds();
if (min_elapsed_time_ms > 0 &&
((now - stored_resend_times_.at(index)) < min_elapsed_time_ms)) {
((now - stored_send_times_.at(index)) < min_elapsed_time_ms)) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"Skip getting packet %u, packet recently resent.", sequence_number);
*packet_length = 0;
return true;
}
// Get packet.
std::vector<std::vector<uint8_t> >::const_iterator it_found_packet =
stored_packets_.begin() + index;
std::copy(it_found_packet->begin(), it_found_packet->begin() + length, packet);
*packet_length = stored_lengths_.at(index);
*stored_time_ms = stored_times_.at(index);
*type = stored_types_.at(index);
if (length == 0 ||
(retransmit && stored_types_.at(index) == kDontRetransmit)) {
// No bytes copied since this packet shouldn't be retransmitted or is
// of zero size.
return false;
}
stored_send_times_[index] = clock_->TimeInMilliseconds();
GetPacket(index, packet, packet_length, stored_time_ms);
return true;
}
void RTPPacketHistory::UpdateResendTime(uint16_t sequence_number) {
CriticalSectionScoped cs(critsect_);
if (!store_) {
return;
}
void RTPPacketHistory::GetPacket(int index,
uint8_t* packet,
uint16_t* packet_length,
int64_t* stored_time_ms) const {
// Get packet.
uint16_t length = stored_lengths_.at(index);
std::vector<std::vector<uint8_t> >::const_iterator it_found_packet =
stored_packets_.begin() + index;
std::copy(it_found_packet->begin(), it_found_packet->begin() + length,
packet);
*packet_length = length;
*stored_time_ms = stored_times_.at(index);
}
int32_t index = 0;
bool found = FindSeqNum(sequence_number, &index);
if (!found) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
"Failed to update resend time, seq num: %u.", sequence_number);
return;
}
stored_resend_times_[index] = clock_->TimeInMilliseconds();
bool RTPPacketHistory::GetBestFittingPacket(uint8_t* packet,
uint16_t* packet_length,
int64_t* stored_time_ms) {
CriticalSectionScoped cs(critsect_);
if (!store_)
return false;
int index = FindBestFittingPacket(*packet_length);
if (index < 0)
return false;
GetPacket(index, packet, packet_length, stored_time_ms);
return true;
}
// private, lock should already be taken
@ -313,4 +330,23 @@ bool RTPPacketHistory::FindSeqNum(uint16_t sequence_number,
}
return false;
}
int RTPPacketHistory::FindBestFittingPacket(uint16_t size) const {
if (size < kMinPacketRequestBytes || stored_lengths_.empty())
return -1;
int min_diff = -1;
size_t best_index = 0;
for (size_t i = 0; i < stored_lengths_.size(); ++i) {
if (stored_lengths_[i] == 0)
continue;
int diff = abs(stored_lengths_[i] - size);
if (min_diff < 0 || diff < min_diff) {
min_diff = diff;
best_index = i;
}
}
if (min_diff < 0)
return -1;
return best_index;
}
} // namespace webrtc

View File

@ -59,22 +59,26 @@ class RTPPacketHistory {
// copied.
// stored_time_ms: returns the time when the packet was stored.
// type: returns the storage type set in PutRTPPacket.
bool GetRTPPacket(uint16_t sequence_number,
uint32_t min_elapsed_time_ms,
uint8_t* packet,
uint16_t* packet_length,
int64_t* stored_time_ms,
StorageType* type) const;
bool GetPacketAndSetSendTime(uint16_t sequence_number,
uint32_t min_elapsed_time_ms,
bool retransmit,
uint8_t* packet,
uint16_t* packet_length,
int64_t* stored_time_ms);
bool GetBestFittingPacket(uint8_t* packet, uint16_t* packet_length,
int64_t* stored_time_ms);
bool HasRTPPacket(uint16_t sequence_number) const;
void UpdateResendTime(uint16_t sequence_number);
private:
void GetPacket(int index, uint8_t* packet, uint16_t* packet_length,
int64_t* stored_time_ms) const;
void Allocate(uint16_t number_to_store);
void Free();
void VerifyAndAllocatePacketLength(uint16_t packet_length);
bool FindSeqNum(uint16_t sequence_number, int32_t* index) const;
int FindBestFittingPacket(uint16_t size) const;
private:
Clock* clock_;
@ -87,7 +91,7 @@ class RTPPacketHistory {
std::vector<uint16_t> stored_seq_nums_;
std::vector<uint16_t> stored_lengths_;
std::vector<int64_t> stored_times_;
std::vector<int64_t> stored_resend_times_;
std::vector<int64_t> stored_send_times_;
std::vector<StorageType> stored_types_;
};
} // namespace webrtc

View File

@ -74,8 +74,8 @@ TEST_F(RtpPacketHistoryTest, NoStoreStatus) {
// Packet should not be stored.
len = kMaxPacketLength;
int64_t time;
StorageType type;
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len, &time, &type));
EXPECT_FALSE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_, &len,
&time));
}
TEST_F(RtpPacketHistoryTest, DontStore) {
@ -89,8 +89,8 @@ TEST_F(RtpPacketHistoryTest, DontStore) {
// Packet should not be stored.
len = kMaxPacketLength;
int64_t time;
StorageType type;
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len, &time, &type));
EXPECT_FALSE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_, &len,
&time));
}
TEST_F(RtpPacketHistoryTest, PutRtpPacket_TooLargePacketLength) {
@ -112,17 +112,16 @@ TEST_F(RtpPacketHistoryTest, GetRtpPacket_TooSmallBuffer) {
capture_time_ms, kAllowRetransmission));
uint16_t len_out = len - 1;
int64_t time;
StorageType type;
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len_out, &time,
&type));
EXPECT_FALSE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_,
&len_out, &time));
}
TEST_F(RtpPacketHistoryTest, GetRtpPacket_NotStored) {
hist_->SetStorePacketsStatus(true, 10);
uint16_t len = kMaxPacketLength;
int64_t time;
StorageType type;
EXPECT_FALSE(hist_->GetRTPPacket(0, 0, packet_, &len, &time, &type));
EXPECT_FALSE(hist_->GetPacketAndSetSendTime(0, 0, false, packet_, &len,
&time));
}
TEST_F(RtpPacketHistoryTest, PutRtpPacket) {
@ -147,11 +146,9 @@ TEST_F(RtpPacketHistoryTest, GetRtpPacket) {
uint16_t len_out = kMaxPacketLength;
int64_t time;
StorageType type;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
&type));
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_,
&len_out, &time));
EXPECT_EQ(len, len_out);
EXPECT_EQ(kAllowRetransmission, type);
EXPECT_EQ(capture_time_ms, time);
for (int i = 0; i < len; i++) {
EXPECT_EQ(packet_[i], packet_out_[i]);
@ -176,11 +173,9 @@ TEST_F(RtpPacketHistoryTest, ReplaceRtpHeader) {
uint16_t len_out = kMaxPacketLength;
int64_t time;
StorageType type;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
&type));
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_,
&len_out, &time));
EXPECT_EQ(len, len_out);
EXPECT_EQ(kAllowRetransmission, type);
EXPECT_EQ(capture_time_ms, time);
for (int i = 0; i < len; i++) {
EXPECT_EQ(packet_[i], packet_out_[i]);
@ -207,11 +202,9 @@ TEST_F(RtpPacketHistoryTest, NoCaptureTime) {
uint16_t len_out = kMaxPacketLength;
int64_t time;
StorageType type;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
&type));
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_,
&len_out, &time));
EXPECT_EQ(len, len_out);
EXPECT_EQ(kAllowRetransmission, type);
EXPECT_EQ(capture_time_ms, time);
for (int i = 0; i < len; i++) {
EXPECT_EQ(packet_[i], packet_out_[i]);
@ -228,11 +221,9 @@ TEST_F(RtpPacketHistoryTest, DontRetransmit) {
uint16_t len_out = kMaxPacketLength;
int64_t time;
StorageType type;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
&type));
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_,
&len_out, &time));
EXPECT_EQ(len, len_out);
EXPECT_EQ(kDontRetransmit, type);
EXPECT_EQ(capture_time_ms, time);
}
@ -244,20 +235,22 @@ TEST_F(RtpPacketHistoryTest, MinResendTime) {
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
capture_time_ms, kAllowRetransmission));
hist_->UpdateResendTime(kSeqNum);
int64_t time;
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 100, false, packet_, &len,
&time));
fake_clock_.AdvanceTimeMilliseconds(100);
// Time has elapsed.
len = kMaxPacketLength;
StorageType type;
int64_t time;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 100, packet_, &len, &time, &type));
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 100, false, packet_, &len,
&time));
EXPECT_GT(len, 0);
EXPECT_EQ(capture_time_ms, time);
// Time has not elapsed. Packet should be found, but no bytes copied.
len = kMaxPacketLength;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 101, packet_, &len, &time, &type));
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 101, false, packet_, &len,
&time));
EXPECT_EQ(0, len);
}
} // namespace webrtc

View File

@ -269,15 +269,13 @@ int32_t ModuleRtpRtcpImpl::Process() {
return 0;
}
int32_t ModuleRtpRtcpImpl::SetRTXSendStatus(RtxMode mode, bool set_ssrc,
int32_t ModuleRtpRtcpImpl::SetRTXSendStatus(int mode, bool set_ssrc,
uint32_t ssrc) {
rtp_sender_.SetRTXStatus(mode, set_ssrc, ssrc);
return 0;
}
int32_t ModuleRtpRtcpImpl::RTXSendStatus(RtxMode* mode, uint32_t* ssrc,
int32_t ModuleRtpRtcpImpl::RTXSendStatus(int* mode, uint32_t* ssrc,
int* payload_type) const {
rtp_sender_.RTXStatus(mode, ssrc, payload_type);
return 0;
@ -1628,7 +1626,7 @@ int64_t ModuleRtpRtcpImpl::RtcpReportInterval() {
void ModuleRtpRtcpImpl::SetRtcpReceiverSsrcs(uint32_t main_ssrc) {
std::set<uint32_t> ssrcs;
ssrcs.insert(main_ssrc);
RtxMode rtx_mode = kRtxOff;
int rtx_mode = kRtxOff;
uint32_t rtx_ssrc = 0;
int rtx_payload_type = 0;
rtp_sender_.RTXStatus(&rtx_mode, &rtx_ssrc, &rtx_payload_type);

View File

@ -95,11 +95,11 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
virtual uint32_t ByteCountSent() const;
virtual int32_t SetRTXSendStatus(const RtxMode mode,
virtual int32_t SetRTXSendStatus(const int mode,
const bool set_ssrc,
const uint32_t ssrc) OVERRIDE;
virtual int32_t RTXSendStatus(RtxMode* mode, uint32_t* ssrc,
virtual int32_t RTXSendStatus(int* mode, uint32_t* ssrc,
int* payloadType) const OVERRIDE;

View File

@ -12,7 +12,6 @@
#include <stdlib.h> // srand
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
@ -54,7 +53,7 @@ RTPSender::RTPSender(const int32_t id, const bool audio, Clock *clock,
transmission_time_offset_(0), absolute_send_time_(0),
// NACK.
nack_byte_count_times_(), nack_byte_count_(), nack_bitrate_(clock),
packet_history_(new RTPPacketHistory(clock)),
packet_history_(clock),
// Statistics
statistics_crit_(CriticalSectionWrapper::CreateCriticalSection()),
packets_sent_(0), payload_bytes_sent_(0), start_time_stamp_forced_(false),
@ -97,7 +96,6 @@ RTPSender::~RTPSender() {
delete it->second;
payload_type_map_.erase(it);
}
delete packet_history_;
delete audio_;
delete video_;
@ -271,7 +269,7 @@ uint16_t RTPSender::MaxPayloadLength() const {
uint16_t RTPSender::PacketOverHead() const { return packet_over_head_; }
void RTPSender::SetRTXStatus(RtxMode mode, bool set_ssrc, uint32_t ssrc) {
void RTPSender::SetRTXStatus(int mode, bool set_ssrc, uint32_t ssrc) {
CriticalSectionScoped cs(send_critsect_);
rtx_ = mode;
if (rtx_ != kRtxOff) {
@ -283,7 +281,7 @@ void RTPSender::SetRTXStatus(RtxMode mode, bool set_ssrc, uint32_t ssrc) {
}
}
void RTPSender::RTXStatus(RtxMode* mode, uint32_t* ssrc,
void RTPSender::RTXStatus(int* mode, uint32_t* ssrc,
int* payload_type) const {
CriticalSectionScoped cs(send_critsect_);
*mode = rtx_;
@ -389,6 +387,28 @@ int32_t RTPSender::SendOutgoingData(
}
}
int RTPSender::SendRedundantPayloads(int payload_type, int bytes_to_send) {
if (!(rtx_ & kRtxRedundantPayloads))
return 0;
uint8_t buffer[IP_PACKET_SIZE];
int bytes_left = bytes_to_send;
while (bytes_left > 0) {
uint16_t length = bytes_left;
int64_t capture_time_ms;
if (!packet_history_.GetBestFittingPacket(buffer, &length,
&capture_time_ms)) {
break;
}
if (!PrepareAndSendPacket(buffer, length, capture_time_ms, true))
return -1;
ModuleRTPUtility::RTPHeaderParser rtp_parser(buffer, length);
RTPHeader rtp_header;
rtp_parser.Parse(rtp_header);
bytes_left -= length - rtp_header.headerLength;
}
return bytes_to_send - bytes_left;
}
bool RTPSender::SendPaddingAccordingToBitrate(
int8_t payload_type, uint32_t capture_timestamp,
int64_t capture_time_ms) {
@ -509,11 +529,11 @@ int RTPSender::SendPadData(int payload_type, uint32_t timestamp,
void RTPSender::SetStorePacketsStatus(const bool enable,
const uint16_t number_to_store) {
packet_history_->SetStorePacketsStatus(enable, number_to_store);
packet_history_.SetStorePacketsStatus(enable, number_to_store);
}
bool RTPSender::StorePackets() const {
return packet_history_->StorePackets();
return packet_history_.StorePackets();
}
int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) {
@ -521,20 +541,12 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) {
uint8_t data_buffer[IP_PACKET_SIZE];
uint8_t *buffer_to_send_ptr = data_buffer;
int64_t capture_time_ms;
StorageType type;
if (!packet_history_->GetRTPPacket(packet_id, min_resend_time, data_buffer,
&length, &capture_time_ms, &type)) {
if (!packet_history_.GetPacketAndSetSendTime(packet_id, min_resend_time, true,
data_buffer, &length,
&capture_time_ms)) {
// Packet not found.
return 0;
}
if (length == 0 || type == kDontRetransmit) {
// No bytes copied (packet recently resent, skip resending) or
// packet should not be retransmitted.
return 0;
}
// Store the time when the packet was last sent or added to pacer.
packet_history_->UpdateResendTime(packet_id);
{
// Update send statistics prior to pacer.
@ -567,7 +579,7 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) {
}
uint8_t data_buffer_rtx[IP_PACKET_SIZE];
if (rtx_ != kRtxOff) {
if ((rtx_ & kRtxRetransmitted) > 0) {
BuildRtxPacket(data_buffer, &length, data_buffer_rtx);
buffer_to_send_ptr = data_buffer_rtx;
}
@ -714,33 +726,39 @@ void RTPSender::UpdateNACKBitRate(const uint32_t bytes,
bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
int64_t capture_time_ms,
bool retransmission) {
StorageType type;
uint16_t length = IP_PACKET_SIZE;
uint8_t data_buffer[IP_PACKET_SIZE];
int64_t stored_time_ms;
uint8_t *buffer_to_send_ptr = data_buffer;
if (packet_history_ == NULL) {
if (!packet_history_.GetPacketAndSetSendTime(sequence_number,
0,
retransmission,
data_buffer,
&length,
&stored_time_ms)) {
// Packet cannot be found. Allow sending to continue.
return true;
}
if (!packet_history_->GetRTPPacket(sequence_number, 0, data_buffer, &length,
&stored_time_ms, &type)) {
// Packet cannot be found. Allow sending to continue.
return true;
}
assert(length > 0);
return PrepareAndSendPacket(data_buffer, length, capture_time_ms,
retransmission && (rtx_ & kRtxRetransmitted) > 0);
}
ModuleRTPUtility::RTPHeaderParser rtp_parser(data_buffer, length);
bool RTPSender::PrepareAndSendPacket(uint8_t* buffer,
uint16_t length,
int64_t capture_time_ms,
bool send_over_rtx) {
uint8_t *buffer_to_send_ptr = buffer;
ModuleRTPUtility::RTPHeaderParser rtp_parser(buffer, length);
RTPHeader rtp_header;
rtp_parser.Parse(rtp_header);
TRACE_EVENT_INSTANT2("webrtc_rtp", "RTPSender::TimeToSendPacket",
"timestamp", rtp_header.timestamp,
"seqnum", sequence_number);
"seqnum", rtp_header.sequenceNumber);
uint8_t data_buffer_rtx[IP_PACKET_SIZE];
if (retransmission && rtx_ != kRtxOff) {
BuildRtxPacket(data_buffer, &length, data_buffer_rtx);
if (send_over_rtx) {
BuildRtxPacket(buffer, &length, data_buffer_rtx);
buffer_to_send_ptr = data_buffer_rtx;
}
@ -753,9 +771,9 @@ bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
UpdateAbsoluteSendTime(buffer_to_send_ptr, length, rtp_header, now_ms);
if (updated_transmission_time_offset || updated_abs_send_time) {
// Update stored packet in case of receiving a re-transmission request.
packet_history_->ReplaceRTPHeader(buffer_to_send_ptr,
rtp_header.sequenceNumber,
rtp_header.headerLength);
packet_history_.ReplaceRTPHeader(buffer_to_send_ptr,
rtp_header.sequenceNumber,
rtp_header.headerLength);
}
return SendPacketToNetwork(buffer_to_send_ptr, length);
}
@ -769,7 +787,8 @@ int RTPSender::TimeToSendPadding(int bytes) {
uint32_t timestamp;
{
CriticalSectionScoped cs(send_critsect_);
payload_type = (rtx_ == kRtxOff) ? payload_type_ : payload_type_rtx_;
payload_type = ((rtx_ & kRtxRedundantPayloads) > 0) ? payload_type_rtx_ :
payload_type_;
timestamp = timestamp_;
capture_time_ms = capture_time_ms_;
if (last_timestamp_time_ms_ > 0) {
@ -779,8 +798,14 @@ int RTPSender::TimeToSendPadding(int bytes) {
(clock_->TimeInMilliseconds() - last_timestamp_time_ms_);
}
}
return SendPadData(payload_type, timestamp, capture_time_ms, bytes,
kDontStore, true, true);
int bytes_sent = SendRedundantPayloads(payload_type, bytes);
bytes -= bytes_sent;
if (bytes > 0) {
int padding_sent = SendPadData(payload_type, timestamp, capture_time_ms,
bytes, kDontStore, true, true);
bytes_sent += padding_sent;
}
return bytes_sent;
}
// TODO(pwestin): send in the RTPHeaderParser to avoid parsing it again.
@ -807,33 +832,17 @@ int32_t RTPSender::SendToNetwork(
rtp_header, now_ms);
// Used for NACK and to spread out the transmission of packets.
if (packet_history_->PutRTPPacket(buffer, rtp_header_length + payload_length,
max_payload_length_, capture_time_ms,
storage) != 0) {
if (packet_history_.PutRTPPacket(buffer, rtp_header_length + payload_length,
max_payload_length_, capture_time_ms,
storage) != 0) {
return -1;
}
// Create and send RTX Packet.
// TODO(pwesin): This should be moved to its own code path triggered by pacer.
bool rtx_sent = false;
if (rtx_ == kRtxAll && storage == kAllowRetransmission) {
uint16_t length_rtx = payload_length + rtp_header_length;
uint8_t data_buffer_rtx[IP_PACKET_SIZE];
BuildRtxPacket(buffer, &length_rtx, data_buffer_rtx);
if (!SendPacketToNetwork(data_buffer_rtx, length_rtx)) return -1;
rtx_sent = true;
}
{
// Update send statistics prior to pacer.
CriticalSectionScoped lock(statistics_crit_.get());
Bitrate::Update(payload_length + rtp_header_length);
++packets_sent_;
payload_bytes_sent_ += payload_length;
if (rtx_sent) {
// The RTX packet.
++packets_sent_;
payload_bytes_sent_ += payload_length;
}
}
if (paced_sender_ && storage != kDontStore) {

View File

@ -21,6 +21,7 @@
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
#include "webrtc/modules/rtp_rtcp/source/ssrc_database.h"
#include "webrtc/modules/rtp_rtcp/source/video_codec_information.h"
@ -30,7 +31,6 @@
namespace webrtc {
class CriticalSectionWrapper;
class RTPPacketHistory;
class RTPSenderAudio;
class RTPSenderVideo;
@ -134,10 +134,6 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
VideoCodecInformation *codec_info = NULL,
const RTPVideoTypeHeader * rtp_type_hdr = NULL);
int BuildPaddingPacket(uint8_t* packet, int header_length, int32_t bytes);
int SendPadData(int payload_type, uint32_t timestamp, int64_t capture_time_ms,
int32_t bytes, StorageType store,
bool force_full_size_packets, bool only_pad_after_markerbit);
// RTP header extension
int32_t SetTransmissionTimeOffset(
const int32_t transmission_time_offset);
@ -187,9 +183,9 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
bool ProcessNACKBitRate(const uint32_t now);
// RTX.
void SetRTXStatus(RtxMode mode, bool set_ssrc, uint32_t ssrc);
void SetRTXStatus(int mode, bool set_ssrc, uint32_t ssrc);
void RTXStatus(RtxMode* mode, uint32_t* ssrc, int* payload_type) const;
void RTXStatus(int* mode, uint32_t* ssrc, int* payload_type) const;
void SetRtxPayloadType(int payloadType);
@ -276,9 +272,20 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
void UpdateNACKBitRate(const uint32_t bytes, const uint32_t now);
bool PrepareAndSendPacket(uint8_t* buffer,
uint16_t length,
int64_t capture_time_ms,
bool send_over_rtx);
int SendRedundantPayloads(int payload_type, int bytes);
bool SendPaddingAccordingToBitrate(int8_t payload_type,
uint32_t capture_timestamp,
int64_t capture_time_ms);
int BuildPaddingPacket(uint8_t* packet, int header_length, int32_t bytes);
int SendPadData(int payload_type, uint32_t timestamp, int64_t capture_time_ms,
int32_t bytes, StorageType store,
bool force_full_size_packets, bool only_pad_after_markerbit);
void BuildRtxPacket(uint8_t* buffer, uint16_t* length,
uint8_t* buffer_rtx);
@ -312,7 +319,7 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
int32_t nack_byte_count_[NACK_BYTECOUNT_SIZE];
Bitrate nack_bitrate_;
RTPPacketHistory *packet_history_;
RTPPacketHistory packet_history_;
// Statistics
scoped_ptr<CriticalSectionWrapper> statistics_crit_;
@ -336,7 +343,7 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
uint8_t num_csrcs_;
uint32_t csrcs_[kRtpCsrcSize];
bool include_csrcs_;
RtxMode rtx_;
int rtx_;
uint32_t ssrc_rtx_;
int payload_type_rtx_;
};

View File

@ -21,6 +21,7 @@
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/test/mock_transport.h"
#include "webrtc/typedefs.h"
namespace webrtc {
@ -113,6 +114,23 @@ class RtpSenderTest : public ::testing::Test {
EXPECT_EQ(0, rtp_header.numCSRCs);
EXPECT_EQ(0, rtp_header.paddingLength);
}
void SendPacket(int64_t capture_time_ms, int payload_length) {
uint32_t timestamp = capture_time_ms * 90;
int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_,
kPayload,
kMarkerBit,
timestamp,
capture_time_ms);
// Packet should be stored in a send bucket.
EXPECT_EQ(0, rtp_sender_->SendToNetwork(packet_,
payload_length,
rtp_length,
capture_time_ms,
kAllowRetransmission,
PacedSender::kNormalPriority));
}
};
TEST_F(RtpSenderTest, RegisterRtpTransmissionTimeOffsetHeaderExtension) {
@ -582,6 +600,69 @@ TEST_F(RtpSenderTest, SendPadding) {
EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
}
TEST_F(RtpSenderTest, SendRedundantPayloads) {
MockTransport transport;
rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport, NULL,
&mock_paced_sender_));
rtp_sender_->SetSequenceNumber(kSeqNum);
// Make all packets go through the pacer.
EXPECT_CALL(mock_paced_sender_,
SendPacket(PacedSender::kNormalPriority, _, _, _, _, _)).
WillRepeatedly(testing::Return(false));
uint16_t seq_num = kSeqNum;
rtp_sender_->SetStorePacketsStatus(true, 10);
int rtp_header_len = 12;
EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
rtp_header_len += 4; // 4 bytes extension.
rtp_header_len += 4; // 4 extra bytes common to all extension headers.
rtp_sender_->SetRTXStatus(kRtxRetransmitted | kRtxRedundantPayloads, true,
1234);
// Create and set up parser.
scoped_ptr<webrtc::RtpHeaderParser> rtp_parser(
webrtc::RtpHeaderParser::Create());
ASSERT_TRUE(rtp_parser.get() != NULL);
rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
kTransmissionTimeOffsetExtensionId);
rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
kAbsoluteSendTimeExtensionId);
rtp_sender_->SetTargetSendBitrate(300000);
const size_t kNumPayloadSizes = 10;
const int kPayloadSizes[kNumPayloadSizes] = {500, 550, 600, 650, 700, 750,
800, 850, 900, 950};
// Send 10 packets of increasing size.
for (size_t i = 0; i < kNumPayloadSizes; ++i) {
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
EXPECT_CALL(transport, SendPacket(_, _, _))
.WillOnce(testing::ReturnArg<2>());
SendPacket(capture_time_ms, kPayloadSizes[i]);
rtp_sender_->TimeToSendPacket(seq_num++, capture_time_ms, false);
fake_clock_.AdvanceTimeMilliseconds(33);
}
const int kPaddingPayloadSize = 224;
// The amount of padding to send it too small to send a payload packet.
EXPECT_CALL(transport, SendPacket(_, _, kPaddingPayloadSize + rtp_header_len))
.WillOnce(testing::ReturnArg<2>());
EXPECT_EQ(kPaddingPayloadSize, rtp_sender_->TimeToSendPadding(49));
const int kRtxHeaderSize = 2;
EXPECT_CALL(transport, SendPacket(_, _, kPayloadSizes[0] +
rtp_header_len + kRtxHeaderSize))
.WillOnce(testing::ReturnArg<2>());
EXPECT_EQ(kPayloadSizes[0], rtp_sender_->TimeToSendPadding(500));
EXPECT_CALL(transport, SendPacket(_, _, kPayloadSizes[kNumPayloadSizes - 1] +
rtp_header_len + kRtxHeaderSize))
.WillOnce(testing::ReturnArg<2>());
EXPECT_CALL(transport, SendPacket(_, _, kPaddingPayloadSize + rtp_header_len))
.WillOnce(testing::ReturnArg<2>());
EXPECT_EQ(kPayloadSizes[kNumPayloadSizes - 1] + kPaddingPayloadSize,
rtp_sender_->TimeToSendPadding(999));
}
TEST_F(RtpSenderTest, SendGenericVideo) {
char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
const uint8_t payload_type = 127;

View File

@ -116,7 +116,7 @@ TEST_F(RtpRtcpAPITest, RTCP) {
TEST_F(RtpRtcpAPITest, RtxSender) {
unsigned int ssrc = 0;
RtxMode rtx_mode = kRtxOff;
int rtx_mode = kRtxOff;
const int kRtxPayloadType = 119;
int payload_type = -1;
EXPECT_EQ(0, module->SetRTXSendStatus(kRtxRetransmitted, true, 1));

View File

@ -0,0 +1,27 @@
/*
* 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_TEST_MOCK_TRANSPORT_H_
#define WEBRTC_TEST_MOCK_TRANSPORT_H_
#include "testing/gmock/include/gmock/gmock.h"
#include "webrtc/transport.h"
namespace webrtc {
class MockTransport : public webrtc::Transport {
public:
MOCK_METHOD3(SendPacket,
int(int channel, const void* data, int len));
MOCK_METHOD3(SendRTCPPacket,
int(int channel, const void* data, int len));
};
} // namespace webrtc
#endif // WEBRTC_TEST_MOCK_TRANSPORT_H_

View File

@ -34,6 +34,7 @@
'mac/run_tests.mm',
'mac/video_renderer_mac.h',
'mac/video_renderer_mac.mm',
'mock_transport.h',
'null_platform_renderer.cc',
'null_transport.cc',
'null_transport.h',

View File

@ -10,13 +10,17 @@
#include <assert.h>
#include <map>
#include <vector>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/call.h"
#include "webrtc/common.h"
#include "webrtc/experiments.h"
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.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"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
@ -26,18 +30,22 @@
#include "webrtc/test/fake_decoder.h"
#include "webrtc/test/fake_encoder.h"
#include "webrtc/test/frame_generator_capturer.h"
#include "webrtc/test/testsupport/perf_test.h"
#include "webrtc/video/transport_adapter.h"
namespace webrtc {
namespace {
static const int kTOffsetExtensionId = 7;
static const int kMaxPacketSize = 1500;
}
class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
public:
typedef std::map<uint32_t, int> BytesSentMap;
typedef std::map<uint32_t, uint32_t> SsrcMap;
StreamObserver(int num_expected_ssrcs,
const SsrcMap& rtx_media_ssrcs,
newapi::Transport* feedback_transport,
Clock* clock)
: critical_section_(CriticalSectionWrapper::CreateCriticalSection()),
@ -45,8 +53,17 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
rtp_parser_(RtpHeaderParser::Create()),
feedback_transport_(feedback_transport),
receive_stats_(ReceiveStatistics::Create(clock)),
payload_registry_(new RTPPayloadRegistry(
-1, RTPPayloadStrategy::CreateStrategy(false))),
clock_(clock),
num_expected_ssrcs_(num_expected_ssrcs) {
num_expected_ssrcs_(num_expected_ssrcs),
rtx_media_ssrcs_(rtx_media_ssrcs),
total_sent_(0),
padding_sent_(0),
rtx_media_sent_(0),
total_packets_sent_(0),
padding_packets_sent_(0),
rtx_media_packets_sent_(0) {
// Ideally we would only have to instantiate an RtcpSender, an
// RtpHeaderParser and a RemoteBitrateEstimator here, but due to the current
// state of the RTP module we need a full module and receive statistics to
@ -66,8 +83,25 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
virtual void OnReceiveBitrateChanged(const std::vector<unsigned int>& ssrcs,
unsigned int bitrate) {
CriticalSectionScoped lock(critical_section_.get());
if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps)
all_ssrcs_sent_->Set();
if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps) {
if (rtx_media_ssrcs_.empty() || rtx_media_sent_ > 0) {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
webrtc::test::PrintResult("total-sent", "", test_info->name(),
total_sent_, "bytes", false);
webrtc::test::PrintResult("padding-sent", "", test_info->name(),
padding_sent_, "bytes", false);
webrtc::test::PrintResult("rtx-media-sent", "", test_info->name(),
rtx_media_sent_, "bytes", false);
webrtc::test::PrintResult("total-packets-sent", "", test_info->name(),
total_packets_sent_, "packets", false);
webrtc::test::PrintResult("padding-packets-sent", "", test_info->name(),
padding_packets_sent_, "packets", false);
webrtc::test::PrintResult("rtx-packets-sent", "", test_info->name(),
rtx_media_packets_sent_, "packets", false);
all_ssrcs_sent_->Set();
}
}
rtp_rtcp_->SetREMBData(
bitrate, static_cast<uint8_t>(ssrcs.size()), &ssrcs[0]);
rtp_rtcp_->Process();
@ -78,12 +112,34 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
RTPHeader header;
EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header));
receive_stats_->IncomingPacket(header, length, false);
rtp_rtcp_->SetRemoteSSRC(header.ssrc);
payload_registry_->SetIncomingPayloadType(header);
remote_bitrate_estimator_->IncomingPacket(
clock_->TimeInMilliseconds(), static_cast<int>(length - 12), header);
if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) {
remote_bitrate_estimator_->Process();
}
total_sent_ += length;
padding_sent_ += header.paddingLength;
++total_packets_sent_;
if (header.paddingLength > 0)
++padding_packets_sent_;
if (rtx_media_ssrcs_.find(header.ssrc) != rtx_media_ssrcs_.end()) {
rtx_media_sent_ += length - header.headerLength - header.paddingLength;
if (header.paddingLength == 0)
++rtx_media_packets_sent_;
uint8_t restored_packet[kMaxPacketSize];
uint8_t* restored_packet_ptr = restored_packet;
int restored_length = static_cast<int>(length);
payload_registry_->RestoreOriginalPacket(
&restored_packet_ptr, packet, &restored_length,
rtx_media_ssrcs_[header.ssrc],
header);
length = restored_length;
EXPECT_TRUE(rtp_parser_->Parse(restored_packet, static_cast<int>(length),
&header));
} else {
rtp_rtcp_->SetRemoteSSRC(header.ssrc);
}
return true;
}
@ -102,9 +158,17 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
scoped_ptr<RtpRtcp> rtp_rtcp_;
internal::TransportAdapter feedback_transport_;
scoped_ptr<ReceiveStatistics> receive_stats_;
scoped_ptr<RTPPayloadRegistry> payload_registry_;
scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_;
Clock* clock_;
const size_t num_expected_ssrcs_;
SsrcMap rtx_media_ssrcs_;
size_t total_sent_;
size_t padding_sent_;
size_t rtx_media_sent_;
int total_packets_sent_;
int padding_packets_sent_;
int rtx_media_packets_sent_;
};
class RampUpTest : public ::testing::TestWithParam<bool> {
@ -112,52 +176,82 @@ class RampUpTest : public ::testing::TestWithParam<bool> {
virtual void SetUp() { reserved_ssrcs_.clear(); }
protected:
void RunRampUpTest(bool pacing, bool rtx) {
const size_t kNumberOfStreams = 3;
std::vector<uint32_t> ssrcs;
for (size_t i = 0; i < kNumberOfStreams; ++i)
ssrcs.push_back(static_cast<uint32_t>(i + 1));
uint32_t kRtxSsrcs[kNumberOfStreams] = {111, 112, 113};
StreamObserver::SsrcMap rtx_ssrc_map;
if (rtx) {
for (size_t i = 0; i < ssrcs.size(); ++i)
rtx_ssrc_map[kRtxSsrcs[i]] = ssrcs[i];
}
test::DirectTransport receiver_transport;
int num_expected_ssrcs = kNumberOfStreams + (rtx ? 1 : 0);
StreamObserver stream_observer(
num_expected_ssrcs, rtx_ssrc_map, &receiver_transport,
Clock::GetRealTimeClock());
Call::Config call_config(&stream_observer);
webrtc::Config webrtc_config;
call_config.webrtc_config = &webrtc_config;
webrtc_config.Set<PaddingStrategy>(new PaddingStrategy(rtx));
scoped_ptr<Call> call(Call::Create(call_config));
VideoSendStream::Config send_config = call->GetDefaultSendConfig();
receiver_transport.SetReceiver(call->Receiver());
test::FakeEncoder encoder(Clock::GetRealTimeClock());
send_config.encoder = &encoder;
send_config.internal_source = false;
test::FakeEncoder::SetCodecSettings(&send_config.codec, kNumberOfStreams);
send_config.codec.plType = 125;
send_config.pacing = pacing;
send_config.rtp.nack.rtp_history_ms = 1000;
send_config.rtp.ssrcs.insert(send_config.rtp.ssrcs.begin(), ssrcs.begin(),
ssrcs.end());
if (rtx) {
send_config.rtp.rtx.rtx_payload_type = 96;
send_config.rtp.rtx.ssrcs.insert(send_config.rtp.rtx.ssrcs.begin(),
kRtxSsrcs,
kRtxSsrcs + kNumberOfStreams);
}
send_config.rtp.extensions.push_back(
RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId));
VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
test::FrameGeneratorCapturer::Create(send_stream->Input(),
send_config.codec.width,
send_config.codec.height,
30,
Clock::GetRealTimeClock()));
send_stream->StartSending();
frame_generator_capturer->Start();
EXPECT_EQ(kEventSignaled, stream_observer.Wait());
frame_generator_capturer->Stop();
send_stream->StopSending();
call->DestroyVideoSendStream(send_stream);
}
std::map<uint32_t, bool> reserved_ssrcs_;
};
TEST_P(RampUpTest, RampUpWithPadding) {
static const size_t kNumStreams = 3;
test::DirectTransport receiver_transport;
StreamObserver stream_observer(
kNumStreams, &receiver_transport, Clock::GetRealTimeClock());
Call::Config call_config(&stream_observer);
scoped_ptr<Call> call(Call::Create(call_config));
VideoSendStream::Config send_config = call->GetDefaultSendConfig();
receiver_transport.SetReceiver(call->Receiver());
test::FakeEncoder encoder(Clock::GetRealTimeClock());
send_config.encoder = &encoder;
send_config.internal_source = false;
test::FakeEncoder::SetCodecSettings(&send_config.codec, kNumStreams);
send_config.codec.plType = 125;
send_config.pacing = GetParam();
send_config.rtp.extensions.push_back(
RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId));
for (size_t i = 0; i < kNumStreams; ++i)
send_config.rtp.ssrcs.push_back(static_cast<uint32_t>(i + 1));
VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
test::FrameGeneratorCapturer::Create(send_stream->Input(),
send_config.codec.width,
send_config.codec.height,
30,
Clock::GetRealTimeClock()));
send_stream->StartSending();
frame_generator_capturer->Start();
EXPECT_EQ(kEventSignaled, stream_observer.Wait());
frame_generator_capturer->Stop();
send_stream->StopSending();
call->DestroyVideoSendStream(send_stream);
TEST_F(RampUpTest, WithoutPacing) {
RunRampUpTest(false, false);
}
INSTANTIATE_TEST_CASE_P(RampUpTest, RampUpTest, ::testing::Bool());
TEST_F(RampUpTest, WithPacing) {
RunRampUpTest(true, false);
}
TEST_F(RampUpTest, WithPacingAndRtx) {
RunRampUpTest(true, true);
}
} // namespace webrtc

View File

@ -13,7 +13,9 @@
#include <algorithm>
#include <vector>
#include "webrtc/common.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/experiments.h"
#include "webrtc/modules/pacing/include/paced_sender.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
@ -102,7 +104,8 @@ ViEChannel::ViEChannel(int32_t channel_id,
sender_(sender),
nack_history_size_sender_(kSendSidePacketHistorySize),
max_nack_reordering_threshold_(kMaxPacketAgeToNack),
pre_render_callback_(NULL) {
pre_render_callback_(NULL),
config_(config) {
WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id, channel_id),
"ViEChannel::ViEChannel(channel_id: %d, engine_id: %d)",
channel_id, engine_id);
@ -965,9 +968,12 @@ int32_t ViEChannel::SetSSRC(const uint32_t SSRC,
ViEId(engine_id_, channel_id_),
"%s(usage:%d, SSRC: 0x%x, idx:%u)",
__FUNCTION__, usage, SSRC, simulcast_idx);
int rtx_settings = kRtxRetransmitted;
if (config_.Get<PaddingStrategy>().redundant_payloads)
rtx_settings |= kRtxRedundantPayloads;
if (simulcast_idx == 0) {
if (usage == kViEStreamTypeRtx) {
return rtp_rtcp_->SetRTXSendStatus(kRtxRetransmitted, true, SSRC);
return rtp_rtcp_->SetRTXSendStatus(rtx_settings, true, SSRC);
}
return rtp_rtcp_->SetSSRC(SSRC);
}
@ -983,7 +989,7 @@ int32_t ViEChannel::SetSSRC(const uint32_t SSRC,
}
RtpRtcp* rtp_rtcp_module = *it;
if (usage == kViEStreamTypeRtx) {
return rtp_rtcp_module->SetRTXSendStatus(kRtxRetransmitted, true, SSRC);
return rtp_rtcp_module->SetRTXSendStatus(rtx_settings, true, SSRC);
}
return rtp_rtcp_module->SetSSRC(SSRC);
}

View File

@ -403,6 +403,7 @@ class ViEChannel
int nack_history_size_sender_;
int max_nack_reordering_threshold_;
I420FrameCallback* pre_render_callback_;
const Config& config_;
};
} // namespace webrtc

View File

@ -63,6 +63,7 @@
'call.h',
'config.cc',
'config.h',
'experiments.h',
'frame_callback.h',
'transport.h',
'video_receive_stream.h',