Preserve RTP states for restarted VideoSendStreams.

A restarted VideoSendStream would previously be completely reset,
causing gaps in sequence numbers and potentially RTP timestamps as well.
This broke SRTP which requires fairly sequential sequence numbers.
Presumably, were this sent without SRTP, we'd still have problems on the
receiving end as the corresponding receiver is unaware of this reset.

Also adding annotation to RTPSender and addressing some unlocked
access to ssrc_, ssrc_rtx_ and rtx_.

BUG=
R=mflodman@webrtc.org, stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6612 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
pbos@webrtc.org 2014-07-07 13:06:48 +00:00
parent 73823cafa4
commit 2bb1bdab8d
21 changed files with 530 additions and 108 deletions

View File

@ -203,6 +203,10 @@ class RtpRtcp : public Module {
*/ */
virtual int32_t SetSequenceNumber(const uint16_t seq) = 0; virtual int32_t SetSequenceNumber(const uint16_t seq) = 0;
virtual void SetRtpStateForSsrc(uint32_t ssrc,
const RtpState& rtp_state) = 0;
virtual bool GetRtpStateForSsrc(uint32_t ssrc, RtpState* rtp_state) = 0;
/* /*
* Get SSRC * Get SSRC
*/ */

View File

@ -192,6 +192,20 @@ struct RtcpReceiveTimeInfo {
typedef std::list<RTCPReportBlock> ReportBlockList; typedef std::list<RTCPReportBlock> ReportBlockList;
struct RtpState {
RtpState()
: sequence_number(0),
start_timestamp(0),
timestamp(0),
capture_time_ms(-1),
last_timestamp_time_ms(-1) {}
uint16_t sequence_number;
uint32_t start_timestamp;
uint32_t timestamp;
int64_t capture_time_ms;
int64_t last_timestamp_time_ms;
};
class RtpData class RtpData
{ {
public: public:

View File

@ -74,6 +74,9 @@ class MockRtpRtcp : public RtpRtcp {
uint16_t()); uint16_t());
MOCK_METHOD1(SetSequenceNumber, MOCK_METHOD1(SetSequenceNumber,
int32_t(const uint16_t seq)); int32_t(const uint16_t seq));
MOCK_METHOD2(SetRtpStateForSsrc,
void(uint32_t ssrc, const RtpState& rtp_state));
MOCK_METHOD2(GetRtpStateForSsrc, bool(uint32_t ssrc, RtpState* rtp_state));
MOCK_CONST_METHOD0(SSRC, MOCK_CONST_METHOD0(SSRC,
uint32_t()); uint32_t());
MOCK_METHOD1(SetSSRC, MOCK_METHOD1(SetSSRC,

View File

@ -1361,8 +1361,6 @@ int32_t RTCPReceiver::UpdateTMMBR() {
void RTCPReceiver::RegisterRtcpStatisticsCallback( void RTCPReceiver::RegisterRtcpStatisticsCallback(
RtcpStatisticsCallback* callback) { RtcpStatisticsCallback* callback) {
CriticalSectionScoped cs(_criticalSectionFeedbacks); CriticalSectionScoped cs(_criticalSectionFeedbacks);
if (callback != NULL)
assert(stats_callback_ == NULL);
stats_callback_ = callback; stats_callback_ = callback;
} }

View File

@ -333,6 +333,42 @@ int32_t ModuleRtpRtcpImpl::SetSequenceNumber(
return 0; // TODO(pwestin): change to void. return 0; // TODO(pwestin): change to void.
} }
void ModuleRtpRtcpImpl::SetRtpStateForSsrc(uint32_t ssrc,
const RtpState& rtp_state) {
if (rtp_sender_.SSRC() == ssrc) {
rtp_sender_.SetRtpState(rtp_state);
return;
}
if (rtp_sender_.RtxSsrc() == ssrc) {
rtp_sender_.SetRtxRtpState(rtp_state);
return;
}
CriticalSectionScoped lock(critical_section_module_ptrs_.get());
for (size_t i = 0; i < child_modules_.size(); ++i) {
child_modules_[i]->SetRtpStateForSsrc(ssrc, rtp_state);
}
}
bool ModuleRtpRtcpImpl::GetRtpStateForSsrc(uint32_t ssrc, RtpState* rtp_state) {
if (rtp_sender_.SSRC() == ssrc) {
*rtp_state = rtp_sender_.GetRtpState();
return true;
}
if (rtp_sender_.RtxSsrc() == ssrc) {
*rtp_state = rtp_sender_.GetRtxRtpState();
return true;
}
CriticalSectionScoped lock(critical_section_module_ptrs_.get());
for (size_t i = 0; i < child_modules_.size(); ++i) {
if (child_modules_[i]->GetRtpStateForSsrc(ssrc, rtp_state))
return true;
}
return false;
}
uint32_t ModuleRtpRtcpImpl::SSRC() const { uint32_t ModuleRtpRtcpImpl::SSRC() const {
return rtp_sender_.SSRC(); return rtp_sender_.SSRC();
} }

View File

@ -73,6 +73,10 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
// Set SequenceNumber, default is a random number. // Set SequenceNumber, default is a random number.
virtual int32_t SetSequenceNumber(const uint16_t seq) OVERRIDE; virtual int32_t SetSequenceNumber(const uint16_t seq) OVERRIDE;
virtual void SetRtpStateForSsrc(uint32_t ssrc,
const RtpState& rtp_state) OVERRIDE;
virtual bool GetRtpStateForSsrc(uint32_t ssrc, RtpState* rtp_state) OVERRIDE;
virtual uint32_t SSRC() const OVERRIDE; virtual uint32_t SSRC() const OVERRIDE;
// Configure SSRC, default is a random number. // Configure SSRC, default is a random number.

View File

@ -74,8 +74,8 @@ RTPSender::RTPSender(const int32_t id,
rtp_stats_callback_(NULL), rtp_stats_callback_(NULL),
bitrate_callback_(NULL), bitrate_callback_(NULL),
// RTP variables // RTP variables
start_time_stamp_forced_(false), start_timestamp_forced_(false),
start_time_stamp_(0), start_timestamp_(0),
ssrc_db_(*SSRCDatabase::GetSSRCDatabase()), ssrc_db_(*SSRCDatabase::GetSSRCDatabase()),
remote_ssrc_(0), remote_ssrc_(0),
sequence_number_forced_(false), sequence_number_forced_(false),
@ -304,12 +304,17 @@ int32_t RTPSender::SetMaxPayloadLength(
} }
uint16_t RTPSender::MaxDataPayloadLength() const { uint16_t RTPSender::MaxDataPayloadLength() const {
int rtx;
{
CriticalSectionScoped rtx_lock(send_critsect_);
rtx = rtx_;
}
if (audio_configured_) { if (audio_configured_) {
return max_payload_length_ - RTPHeaderLength(); return max_payload_length_ - RTPHeaderLength();
} else { } else {
return max_payload_length_ - RTPHeaderLength() // RTP overhead. return max_payload_length_ - RTPHeaderLength() // RTP overhead.
- video_->FECPacketOverhead() // FEC/ULP/RED overhead. - video_->FECPacketOverhead() // FEC/ULP/RED overhead.
- ((rtx_) ? 2 : 0); // RTX overhead. - ((rtx) ? 2 : 0); // RTX overhead.
} }
} }
@ -329,6 +334,11 @@ void RTPSender::SetRtxSsrc(uint32_t ssrc) {
ssrc_rtx_ = ssrc; ssrc_rtx_ = ssrc;
} }
uint32_t RTPSender::RtxSsrc() const {
CriticalSectionScoped cs(send_critsect_);
return ssrc_rtx_;
}
void RTPSender::RTXStatus(int* mode, uint32_t* ssrc, void RTPSender::RTXStatus(int* mode, uint32_t* ssrc,
int* payload_type) const { int* payload_type) const {
CriticalSectionScoped cs(send_critsect_); CriticalSectionScoped cs(send_critsect_);
@ -389,9 +399,11 @@ int32_t RTPSender::SendOutgoingData(
const uint8_t *payload_data, const uint32_t payload_size, const uint8_t *payload_data, const uint32_t payload_size,
const RTPFragmentationHeader *fragmentation, const RTPFragmentationHeader *fragmentation,
VideoCodecInformation *codec_info, const RTPVideoTypeHeader *rtp_type_hdr) { VideoCodecInformation *codec_info, const RTPVideoTypeHeader *rtp_type_hdr) {
uint32_t ssrc;
{ {
// Drop this packet if we're not sending media packets. // Drop this packet if we're not sending media packets.
CriticalSectionScoped cs(send_critsect_); CriticalSectionScoped cs(send_critsect_);
ssrc = ssrc_;
if (!sending_media_) { if (!sending_media_) {
return 0; return 0;
} }
@ -435,17 +447,13 @@ int32_t RTPSender::SendOutgoingData(
CriticalSectionScoped cs(statistics_crit_.get()); CriticalSectionScoped cs(statistics_crit_.get());
uint32_t frame_count = ++frame_counts_[frame_type]; uint32_t frame_count = ++frame_counts_[frame_type];
if (frame_count_observer_) { if (frame_count_observer_) {
frame_count_observer_->FrameCountUpdated(frame_type, frame_count_observer_->FrameCountUpdated(frame_type, frame_count, ssrc);
frame_count,
ssrc_);
} }
return ret_val; return ret_val;
} }
int RTPSender::SendRedundantPayloads(int payload_type, int bytes_to_send) { int RTPSender::SendRedundantPayloads(int payload_type, int bytes_to_send) {
if (!(rtx_ & kRtxRedundantPayloads))
return 0;
uint8_t buffer[IP_PACKET_SIZE]; uint8_t buffer[IP_PACKET_SIZE];
int bytes_left = bytes_to_send; int bytes_left = bytes_to_send;
while (bytes_left > 0) { while (bytes_left > 0) {
@ -493,7 +501,7 @@ bool RTPSender::SendPaddingAccordingToBitrate(
CriticalSectionScoped cs(send_critsect_); CriticalSectionScoped cs(send_critsect_);
// Add the random RTP timestamp offset and store the capture time for // Add the random RTP timestamp offset and store the capture time for
// later calculation of the send time offset. // later calculation of the send time offset.
timestamp = start_time_stamp_ + capture_timestamp; timestamp = start_timestamp_ + capture_timestamp;
timestamp_ = timestamp; timestamp_ = timestamp;
capture_time_ms_ = capture_time_ms; capture_time_ms_ = capture_time_ms;
last_timestamp_time_ms_ = clock_->TimeInMilliseconds(); last_timestamp_time_ms_ = clock_->TimeInMilliseconds();
@ -567,6 +575,7 @@ int RTPSender::SendPadData(int payload_type, uint32_t timestamp,
++sequence_number_rtx_; ++sequence_number_rtx_;
} }
} }
uint8_t padding_packet[IP_PACKET_SIZE]; uint8_t padding_packet[IP_PACKET_SIZE];
int header_length = CreateRTPHeader(padding_packet, payload_type, ssrc, int header_length = CreateRTPHeader(padding_packet, payload_type, ssrc,
false, timestamp, sequence_number, NULL, false, timestamp, sequence_number, NULL,
@ -628,6 +637,7 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) {
} }
} }
CriticalSectionScoped lock(send_critsect_);
return PrepareAndSendPacket(data_buffer, length, capture_time_ms, return PrepareAndSendPacket(data_buffer, length, capture_time_ms,
(rtx_ & kRtxRetransmitted) > 0, true) ? (rtx_ & kRtxRetransmitted) > 0, true) ?
length : -1; length : -1;
@ -784,8 +794,15 @@ bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
if (!retransmission && capture_time_ms > 0) { if (!retransmission && capture_time_ms > 0) {
UpdateDelayStatistics(capture_time_ms, clock_->TimeInMilliseconds()); UpdateDelayStatistics(capture_time_ms, clock_->TimeInMilliseconds());
} }
return PrepareAndSendPacket(data_buffer, length, capture_time_ms, int rtx;
retransmission && (rtx_ & kRtxRetransmitted) > 0, {
CriticalSectionScoped lock(send_critsect_);
rtx = rtx_;
}
return PrepareAndSendPacket(data_buffer,
length,
capture_time_ms,
retransmission && (rtx & kRtxRetransmitted) > 0,
retransmission); retransmission);
} }
@ -827,12 +844,11 @@ void RTPSender::UpdateRtpStats(const uint8_t* buffer,
bool is_retransmit) { bool is_retransmit) {
StreamDataCounters* counters; StreamDataCounters* counters;
// Get ssrc before taking statistics_crit_ to avoid possible deadlock. // Get ssrc before taking statistics_crit_ to avoid possible deadlock.
uint32_t ssrc = SSRC(); uint32_t ssrc = is_rtx ? RtxSsrc() : SSRC();
CriticalSectionScoped lock(statistics_crit_.get()); CriticalSectionScoped lock(statistics_crit_.get());
if (is_rtx) { if (is_rtx) {
counters = &rtx_rtp_stats_; counters = &rtx_rtp_stats_;
ssrc = ssrc_rtx_;
} else { } else {
counters = &rtp_stats_; counters = &rtp_stats_;
} }
@ -874,6 +890,7 @@ int RTPSender::TimeToSendPadding(int bytes) {
int payload_type; int payload_type;
int64_t capture_time_ms; int64_t capture_time_ms;
uint32_t timestamp; uint32_t timestamp;
int rtx;
{ {
CriticalSectionScoped cs(send_critsect_); CriticalSectionScoped cs(send_critsect_);
if (!sending_media_) { if (!sending_media_) {
@ -889,8 +906,11 @@ int RTPSender::TimeToSendPadding(int bytes) {
capture_time_ms += capture_time_ms +=
(clock_->TimeInMilliseconds() - last_timestamp_time_ms_); (clock_->TimeInMilliseconds() - last_timestamp_time_ms_);
} }
rtx = rtx_;
} }
int bytes_sent = SendRedundantPayloads(payload_type, bytes); int bytes_sent = 0;
if ((rtx & kRtxRedundantPayloads) != 0)
bytes_sent = SendRedundantPayloads(payload_type, bytes);
bytes -= bytes_sent; bytes -= bytes_sent;
if (bytes > 0) { if (bytes > 0) {
int padding_sent = SendPadData(payload_type, int padding_sent = SendPadData(payload_type,
@ -899,7 +919,7 @@ int RTPSender::TimeToSendPadding(int bytes) {
bytes, bytes,
kDontStore, kDontStore,
true, true,
rtx_ == kRtxOff); rtx == kRtxOff);
bytes_sent += padding_sent; bytes_sent += padding_sent;
} }
return bytes_sent; return bytes_sent;
@ -975,6 +995,7 @@ void RTPSender::ProcessBitrate() {
} }
uint16_t RTPSender::RTPHeaderLength() const { uint16_t RTPSender::RTPHeaderLength() const {
CriticalSectionScoped lock(send_critsect_);
uint16_t rtp_header_length = 12; uint16_t rtp_header_length = 12;
if (include_csrcs_) { if (include_csrcs_) {
rtp_header_length += sizeof(uint32_t) * num_csrcs_; rtp_header_length += sizeof(uint32_t) * num_csrcs_;
@ -989,12 +1010,19 @@ uint16_t RTPSender::IncrementSequenceNumber() {
} }
void RTPSender::ResetDataCounters() { void RTPSender::ResetDataCounters() {
uint32_t ssrc;
uint32_t ssrc_rtx;
{
CriticalSectionScoped ssrc_lock(send_critsect_);
ssrc = ssrc_;
ssrc_rtx = ssrc_rtx_;
}
CriticalSectionScoped lock(statistics_crit_.get()); CriticalSectionScoped lock(statistics_crit_.get());
rtp_stats_ = StreamDataCounters(); rtp_stats_ = StreamDataCounters();
rtx_rtp_stats_ = StreamDataCounters(); rtx_rtp_stats_ = StreamDataCounters();
if (rtp_stats_callback_) { if (rtp_stats_callback_) {
rtp_stats_callback_->DataCountersUpdated(rtp_stats_, ssrc_); rtp_stats_callback_->DataCountersUpdated(rtp_stats_, ssrc);
rtp_stats_callback_->DataCountersUpdated(rtx_rtp_stats_, ssrc_rtx_); rtp_stats_callback_->DataCountersUpdated(rtx_rtp_stats_, ssrc_rtx);
} }
} }
@ -1049,16 +1077,18 @@ int RTPSender::CreateRTPHeader(
return rtp_header_length; return rtp_header_length;
} }
int32_t RTPSender::BuildRTPheader( int32_t RTPSender::BuildRTPheader(uint8_t* data_buffer,
uint8_t *data_buffer, const int8_t payload_type, const int8_t payload_type,
const bool marker_bit, const uint32_t capture_timestamp, const bool marker_bit,
int64_t capture_time_ms, const bool time_stamp_provided, const uint32_t capture_timestamp,
int64_t capture_time_ms,
const bool timestamp_provided,
const bool inc_sequence_number) { const bool inc_sequence_number) {
assert(payload_type >= 0); assert(payload_type >= 0);
CriticalSectionScoped cs(send_critsect_); CriticalSectionScoped cs(send_critsect_);
if (time_stamp_provided) { if (timestamp_provided) {
timestamp_ = start_time_stamp_ + capture_timestamp; timestamp_ = start_timestamp_ + capture_timestamp;
} else { } else {
// Make a unique time stamp. // Make a unique time stamp.
// We can't inc by the actual time, since then we increase the risk of back // We can't inc by the actual time, since then we increase the risk of back
@ -1380,6 +1410,7 @@ void RTPSender::SetSendingStatus(bool enabled) {
// Will be ignored if it's already configured via API. // Will be ignored if it's already configured via API.
SetStartTimestamp(RTPtime, false); SetStartTimestamp(RTPtime, false);
} else { } else {
CriticalSectionScoped lock(send_critsect_);
if (!ssrc_forced_) { if (!ssrc_forced_) {
// Generate a new SSRC. // Generate a new SSRC.
ssrc_db_.ReturnSSRC(ssrc_); ssrc_db_.ReturnSSRC(ssrc_);
@ -1412,18 +1443,18 @@ uint32_t RTPSender::Timestamp() const {
void RTPSender::SetStartTimestamp(uint32_t timestamp, bool force) { void RTPSender::SetStartTimestamp(uint32_t timestamp, bool force) {
CriticalSectionScoped cs(send_critsect_); CriticalSectionScoped cs(send_critsect_);
if (force) { if (force) {
start_time_stamp_forced_ = force; start_timestamp_forced_ = true;
start_time_stamp_ = timestamp; start_timestamp_ = timestamp;
} else { } else {
if (!start_time_stamp_forced_) { if (!start_timestamp_forced_) {
start_time_stamp_ = timestamp; start_timestamp_ = timestamp;
} }
} }
} }
uint32_t RTPSender::StartTimestamp() const { uint32_t RTPSender::StartTimestamp() const {
CriticalSectionScoped cs(send_critsect_); CriticalSectionScoped cs(send_critsect_);
return start_time_stamp_; return start_timestamp_;
} }
uint32_t RTPSender::GenerateNewSSRC() { uint32_t RTPSender::GenerateNewSSRC() {
@ -1460,6 +1491,7 @@ uint32_t RTPSender::SSRC() const {
} }
void RTPSender::SetCSRCStatus(const bool include) { void RTPSender::SetCSRCStatus(const bool include) {
CriticalSectionScoped lock(send_critsect_);
include_csrcs_ = include; include_csrcs_ = include;
} }
@ -1635,8 +1667,6 @@ void RTPSender::BuildRtxPacket(uint8_t* buffer, uint16_t* length,
void RTPSender::RegisterFrameCountObserver(FrameCountObserver* observer) { void RTPSender::RegisterFrameCountObserver(FrameCountObserver* observer) {
CriticalSectionScoped cs(statistics_crit_.get()); CriticalSectionScoped cs(statistics_crit_.get());
if (observer != NULL)
assert(frame_count_observer_ == NULL);
frame_count_observer_ = observer; frame_count_observer_ = observer;
} }
@ -1648,8 +1678,6 @@ FrameCountObserver* RTPSender::GetFrameCountObserver() const {
void RTPSender::RegisterRtpStatisticsCallback( void RTPSender::RegisterRtpStatisticsCallback(
StreamDataCountersCallback* callback) { StreamDataCountersCallback* callback) {
CriticalSectionScoped cs(statistics_crit_.get()); CriticalSectionScoped cs(statistics_crit_.get());
if (callback != NULL)
assert(rtp_stats_callback_ == NULL);
rtp_stats_callback_ = callback; rtp_stats_callback_ = callback;
} }
@ -1660,8 +1688,6 @@ StreamDataCountersCallback* RTPSender::GetRtpStatisticsCallback() const {
void RTPSender::RegisterBitrateObserver(BitrateStatisticsObserver* observer) { void RTPSender::RegisterBitrateObserver(BitrateStatisticsObserver* observer) {
CriticalSectionScoped cs(statistics_crit_.get()); CriticalSectionScoped cs(statistics_crit_.get());
if (observer != NULL)
assert(bitrate_callback_ == NULL);
bitrate_callback_ = observer; bitrate_callback_ = observer;
} }
@ -1673,9 +1699,53 @@ BitrateStatisticsObserver* RTPSender::GetBitrateObserver() const {
uint32_t RTPSender::BitrateSent() const { return bitrate_sent_.BitrateLast(); } uint32_t RTPSender::BitrateSent() const { return bitrate_sent_.BitrateLast(); }
void RTPSender::BitrateUpdated(const BitrateStatistics& stats) { void RTPSender::BitrateUpdated(const BitrateStatistics& stats) {
uint32_t ssrc;
{
CriticalSectionScoped ssrc_lock(send_critsect_);
ssrc = ssrc_;
}
CriticalSectionScoped cs(statistics_crit_.get()); CriticalSectionScoped cs(statistics_crit_.get());
if (bitrate_callback_) { if (bitrate_callback_) {
bitrate_callback_->Notify(stats, ssrc_); bitrate_callback_->Notify(stats, ssrc);
} }
} }
void RTPSender::SetRtpState(const RtpState& rtp_state) {
SetStartTimestamp(rtp_state.start_timestamp, true);
CriticalSectionScoped lock(send_critsect_);
sequence_number_ = rtp_state.sequence_number;
sequence_number_forced_ = true;
timestamp_ = rtp_state.timestamp;
capture_time_ms_ = rtp_state.capture_time_ms;
last_timestamp_time_ms_ = rtp_state.last_timestamp_time_ms;
}
RtpState RTPSender::GetRtpState() const {
CriticalSectionScoped lock(send_critsect_);
RtpState state;
state.sequence_number = sequence_number_;
state.start_timestamp = start_timestamp_;
state.timestamp = timestamp_;
state.capture_time_ms = capture_time_ms_;
state.last_timestamp_time_ms = last_timestamp_time_ms_;
return state;
}
void RTPSender::SetRtxRtpState(const RtpState& rtp_state) {
CriticalSectionScoped lock(send_critsect_);
sequence_number_rtx_ = rtp_state.sequence_number;
}
RtpState RTPSender::GetRtxRtpState() const {
CriticalSectionScoped lock(send_critsect_);
RtpState state;
state.sequence_number = sequence_number_rtx_;
state.start_timestamp = start_timestamp_;
return state;
}
} // namespace webrtc } // namespace webrtc

View File

@ -43,11 +43,12 @@ class RTPSenderInterface {
virtual uint32_t SSRC() const = 0; virtual uint32_t SSRC() const = 0;
virtual uint32_t Timestamp() const = 0; virtual uint32_t Timestamp() const = 0;
virtual int32_t BuildRTPheader( virtual int32_t BuildRTPheader(uint8_t* data_buffer,
uint8_t *data_buffer, const int8_t payload_type, const int8_t payload_type,
const bool marker_bit, const uint32_t capture_time_stamp, const bool marker_bit,
const uint32_t capture_timestamp,
int64_t capture_time_ms, int64_t capture_time_ms,
const bool time_stamp_provided = true, const bool timestamp_provided = true,
const bool inc_sequence_number = true) = 0; const bool inc_sequence_number = true) = 0;
virtual uint16_t RTPHeaderLength() const = 0; virtual uint16_t RTPHeaderLength() const = 0;
@ -132,10 +133,12 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer {
int32_t SetMaxPayloadLength(const uint16_t length, int32_t SetMaxPayloadLength(const uint16_t length,
const uint16_t packet_over_head); const uint16_t packet_over_head);
int32_t SendOutgoingData( int32_t SendOutgoingData(const FrameType frame_type,
const FrameType frame_type, const int8_t payload_type, const int8_t payload_type,
const uint32_t time_stamp, int64_t capture_time_ms, const uint32_t timestamp,
const uint8_t *payload_data, const uint32_t payload_size, int64_t capture_time_ms,
const uint8_t* payload_data,
const uint32_t payload_size,
const RTPFragmentationHeader* fragmentation, const RTPFragmentationHeader* fragmentation,
VideoCodecInformation* codec_info = NULL, VideoCodecInformation* codec_info = NULL,
const RTPVideoTypeHeader* rtp_type_hdr = NULL); const RTPVideoTypeHeader* rtp_type_hdr = NULL);
@ -189,16 +192,19 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer {
void RTXStatus(int* mode, uint32_t* ssrc, int* payload_type) const; void RTXStatus(int* mode, uint32_t* ssrc, int* payload_type) const;
uint32_t RtxSsrc() const;
void SetRtxSsrc(uint32_t ssrc); void SetRtxSsrc(uint32_t ssrc);
void SetRtxPayloadType(int payloadType); void SetRtxPayloadType(int payloadType);
// Functions wrapping RTPSenderInterface. // Functions wrapping RTPSenderInterface.
virtual int32_t BuildRTPheader( virtual int32_t BuildRTPheader(
uint8_t *data_buffer, const int8_t payload_type, uint8_t* data_buffer,
const bool marker_bit, const uint32_t capture_time_stamp, const int8_t payload_type,
const bool marker_bit,
const uint32_t capture_timestamp,
int64_t capture_time_ms, int64_t capture_time_ms,
const bool time_stamp_provided = true, const bool timestamp_provided = true,
const bool inc_sequence_number = true) OVERRIDE; const bool inc_sequence_number = true) OVERRIDE;
virtual uint16_t RTPHeaderLength() const OVERRIDE; virtual uint16_t RTPHeaderLength() const OVERRIDE;
@ -277,6 +283,11 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer {
virtual void BitrateUpdated(const BitrateStatistics& stats) OVERRIDE; virtual void BitrateUpdated(const BitrateStatistics& stats) OVERRIDE;
void SetRtpState(const RtpState& rtp_state);
RtpState GetRtpState() const;
void SetRtxRtpState(const RtpState& rtp_state);
RtpState GetRtxRtpState() const;
protected: protected:
int32_t CheckPayloadType(const int8_t payload_type, int32_t CheckPayloadType(const int8_t payload_type,
RtpVideoCodecTypes *video_type); RtpVideoCodecTypes *video_type);
@ -363,34 +374,34 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer {
// Statistics // Statistics
scoped_ptr<CriticalSectionWrapper> statistics_crit_; scoped_ptr<CriticalSectionWrapper> statistics_crit_;
SendDelayMap send_delays_; SendDelayMap send_delays_ GUARDED_BY(statistics_crit_);
std::map<FrameType, uint32_t> frame_counts_; std::map<FrameType, uint32_t> frame_counts_ GUARDED_BY(statistics_crit_);
FrameCountObserver* frame_count_observer_; FrameCountObserver* frame_count_observer_ GUARDED_BY(statistics_crit_);
StreamDataCounters rtp_stats_; StreamDataCounters rtp_stats_ GUARDED_BY(statistics_crit_);
StreamDataCounters rtx_rtp_stats_; StreamDataCounters rtx_rtp_stats_ GUARDED_BY(statistics_crit_);
StreamDataCountersCallback* rtp_stats_callback_; StreamDataCountersCallback* rtp_stats_callback_ GUARDED_BY(statistics_crit_);
BitrateStatisticsObserver* bitrate_callback_; BitrateStatisticsObserver* bitrate_callback_ GUARDED_BY(statistics_crit_);
// RTP variables // RTP variables
bool start_time_stamp_forced_; bool start_timestamp_forced_ GUARDED_BY(send_critsect_);
uint32_t start_time_stamp_; uint32_t start_timestamp_ GUARDED_BY(send_critsect_);
SSRCDatabase &ssrc_db_; SSRCDatabase& ssrc_db_ GUARDED_BY(send_critsect_);
uint32_t remote_ssrc_; uint32_t remote_ssrc_ GUARDED_BY(send_critsect_);
bool sequence_number_forced_; bool sequence_number_forced_ GUARDED_BY(send_critsect_);
uint16_t sequence_number_; uint16_t sequence_number_ GUARDED_BY(send_critsect_);
uint16_t sequence_number_rtx_; uint16_t sequence_number_rtx_ GUARDED_BY(send_critsect_);
bool ssrc_forced_; bool ssrc_forced_ GUARDED_BY(send_critsect_);
uint32_t ssrc_; uint32_t ssrc_ GUARDED_BY(send_critsect_);
uint32_t timestamp_; uint32_t timestamp_ GUARDED_BY(send_critsect_);
int64_t capture_time_ms_; int64_t capture_time_ms_ GUARDED_BY(send_critsect_);
int64_t last_timestamp_time_ms_; int64_t last_timestamp_time_ms_ GUARDED_BY(send_critsect_);
bool last_packet_marker_bit_; bool last_packet_marker_bit_ GUARDED_BY(send_critsect_);
uint8_t num_csrcs_; uint8_t num_csrcs_ GUARDED_BY(send_critsect_);
uint32_t csrcs_[kRtpCsrcSize]; uint32_t csrcs_[kRtpCsrcSize] GUARDED_BY(send_critsect_);
bool include_csrcs_; bool include_csrcs_ GUARDED_BY(send_critsect_);
int rtx_; int rtx_ GUARDED_BY(send_critsect_);
uint32_t ssrc_rtx_; uint32_t ssrc_rtx_ GUARDED_BY(send_critsect_);
int payload_type_rtx_; int payload_type_rtx_ GUARDED_BY(send_critsect_);
// Note: Don't access this variable directly, always go through // Note: Don't access this variable directly, always go through
// SetTargetBitrateKbps or GetTargetBitrateKbps. Also remember // SetTargetBitrateKbps or GetTargetBitrateKbps. Also remember

View File

@ -15,8 +15,9 @@ namespace webrtc {
namespace test { namespace test {
CallTest::CallTest() CallTest::CallTest()
: send_stream_(NULL), : clock_(Clock::GetRealTimeClock()),
fake_encoder_(Clock::GetRealTimeClock()) { send_stream_(NULL),
fake_encoder_(clock_) {
} }
CallTest::~CallTest() { CallTest::~CallTest() {
} }
@ -121,7 +122,7 @@ void CallTest::CreateFrameGeneratorCapturer() {
stream.width, stream.width,
stream.height, stream.height,
stream.max_framerate, stream.max_framerate,
Clock::GetRealTimeClock())); clock_));
} }
void CallTest::CreateStreams() { void CallTest::CreateStreams() {
assert(send_stream_ == NULL); assert(send_stream_ == NULL);
@ -150,7 +151,8 @@ const unsigned int CallTest::kLongTimeoutMs = 120 * 1000;
const uint8_t CallTest::kSendPayloadType = 100; const uint8_t CallTest::kSendPayloadType = 100;
const uint8_t CallTest::kFakeSendPayloadType = 125; const uint8_t CallTest::kFakeSendPayloadType = 125;
const uint8_t CallTest::kSendRtxPayloadType = 98; const uint8_t CallTest::kSendRtxPayloadType = 98;
const uint32_t CallTest::kSendRtxSsrc = 0xBADCAFE; const uint32_t CallTest::kSendRtxSsrcs[kNumSsrcs] = {0xBADCAFD, 0xBADCAFE,
0xBADCAFF};
const uint32_t CallTest::kSendSsrcs[kNumSsrcs] = {0xC0FFED, 0xC0FFEE, 0xC0FFEF}; const uint32_t CallTest::kSendSsrcs[kNumSsrcs] = {0xC0FFED, 0xC0FFEE, 0xC0FFEF};
const uint32_t CallTest::kReceiverLocalSsrc = 0x123456; const uint32_t CallTest::kReceiverLocalSsrc = 0x123456;
const int CallTest::kNackRtpHistoryMs = 1000; const int CallTest::kNackRtpHistoryMs = 1000;

View File

@ -35,7 +35,7 @@ class CallTest : public ::testing::Test {
static const uint8_t kSendPayloadType; static const uint8_t kSendPayloadType;
static const uint8_t kSendRtxPayloadType; static const uint8_t kSendRtxPayloadType;
static const uint8_t kFakeSendPayloadType; static const uint8_t kFakeSendPayloadType;
static const uint32_t kSendRtxSsrc; static const uint32_t kSendRtxSsrcs[kNumSsrcs];
static const uint32_t kSendSsrcs[kNumSsrcs]; static const uint32_t kSendSsrcs[kNumSsrcs];
static const uint32_t kReceiverLocalSsrc; static const uint32_t kReceiverLocalSsrc;
static const int kNackRtpHistoryMs; static const int kNackRtpHistoryMs;
@ -58,6 +58,8 @@ class CallTest : public ::testing::Test {
void Stop(); void Stop();
void DestroyStreams(); void DestroyStreams();
Clock* const clock_;
scoped_ptr<Call> sender_call_; scoped_ptr<Call> sender_call_;
VideoSendStream::Config send_config_; VideoSendStream::Config send_config_;
std::vector<VideoStream> video_streams_; std::vector<VideoStream> video_streams_;

View File

@ -106,6 +106,8 @@ class Call : public webrtc::Call, public PacketReceiver {
scoped_ptr<CpuOveruseObserverProxy> overuse_observer_proxy_; scoped_ptr<CpuOveruseObserverProxy> overuse_observer_proxy_;
VideoSendStream::RtpStateMap suspended_send_ssrcs_;
VideoEngine* video_engine_; VideoEngine* video_engine_;
ViERTP_RTCP* rtp_rtcp_; ViERTP_RTCP* rtp_rtcp_;
ViECodec* codec_; ViECodec* codec_;
@ -184,6 +186,7 @@ VideoSendStream* Call::CreateVideoSendStream(
config, config,
video_streams, video_streams,
encoder_settings, encoder_settings,
suspended_send_ssrcs_,
base_channel_id_, base_channel_id_,
config_.start_bitrate_bps != -1 ? config_.start_bitrate_bps config_.start_bitrate_bps != -1 ? config_.start_bitrate_bps
: kDefaultVideoStreamBitrateBps); : kDefaultVideoStreamBitrateBps);
@ -199,21 +202,30 @@ VideoSendStream* Call::CreateVideoSendStream(
void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) {
assert(send_stream != NULL); assert(send_stream != NULL);
send_stream->Stop();
VideoSendStream* send_stream_impl = NULL; VideoSendStream* send_stream_impl = NULL;
{ {
WriteLockScoped write_lock(*send_lock_); WriteLockScoped write_lock(*send_lock_);
for (std::map<uint32_t, VideoSendStream*>::iterator it = std::map<uint32_t, VideoSendStream*>::iterator it = send_ssrcs_.begin();
send_ssrcs_.begin(); while (it != send_ssrcs_.end()) {
it != send_ssrcs_.end();
++it) {
if (it->second == static_cast<VideoSendStream*>(send_stream)) { if (it->second == static_cast<VideoSendStream*>(send_stream)) {
send_stream_impl = it->second; send_stream_impl = it->second;
send_ssrcs_.erase(it); send_ssrcs_.erase(it++);
break; } else {
++it;
} }
} }
} }
VideoSendStream::RtpStateMap rtp_state = send_stream_impl->GetRtpStates();
for (VideoSendStream::RtpStateMap::iterator it = rtp_state.begin();
it != rtp_state.end();
++it) {
suspended_send_ssrcs_[it->first] = it->second;
}
assert(send_stream_impl != NULL); assert(send_stream_impl != NULL);
delete send_stream_impl; delete send_stream_impl;
} }

View File

@ -58,6 +58,7 @@ class EndToEndTest : public test::CallTest {
void RespectsRtcpMode(newapi::RtcpMode rtcp_mode); void RespectsRtcpMode(newapi::RtcpMode rtcp_mode);
void TestXrReceiverReferenceTimeReport(bool enable_rrtr); void TestXrReceiverReferenceTimeReport(bool enable_rrtr);
void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first); void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first);
void TestRtpStatePreservation(bool use_rtx);
}; };
TEST_F(EndToEndTest, ReceiverCanBeStartedTwice) { TEST_F(EndToEndTest, ReceiverCanBeStartedTwice) {
@ -434,7 +435,7 @@ void EndToEndTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) {
public: public:
explicit RetransmissionObserver(bool expect_rtx) explicit RetransmissionObserver(bool expect_rtx)
: EndToEndTest(kDefaultTimeoutMs), : EndToEndTest(kDefaultTimeoutMs),
retransmission_ssrc_(expect_rtx ? kSendRtxSsrc : kSendSsrcs[0]), retransmission_ssrc_(expect_rtx ? kSendRtxSsrcs[0] : kSendSsrcs[0]),
retransmission_payload_type_(expect_rtx ? kSendRtxPayloadType retransmission_payload_type_(expect_rtx ? kSendRtxPayloadType
: kFakeSendPayloadType), : kFakeSendPayloadType),
marker_bits_observed_(0), marker_bits_observed_(0),
@ -481,10 +482,11 @@ void EndToEndTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) {
send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs; send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
(*receive_configs)[0].pre_render_callback = this; (*receive_configs)[0].pre_render_callback = this;
(*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs; (*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
if (retransmission_ssrc_ == kSendRtxSsrc) { if (retransmission_ssrc_ == kSendRtxSsrcs[0]) {
send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrc); send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]);
send_config->rtp.rtx.payload_type = kSendRtxPayloadType; send_config->rtp.rtx.payload_type = kSendRtxPayloadType;
(*receive_configs)[0].rtp.rtx[kSendRtxPayloadType].ssrc = kSendRtxSsrc; (*receive_configs)[0].rtp.rtx[kSendRtxPayloadType].ssrc =
kSendRtxSsrcs[0];
(*receive_configs)[0].rtp.rtx[kSendRtxPayloadType].payload_type = (*receive_configs)[0].rtp.rtx[kSendRtxPayloadType].payload_type =
kSendRtxPayloadType; kSendRtxPayloadType;
} }
@ -1543,9 +1545,6 @@ TEST_F(EndToEndTest, CanSwitchToUseAllSsrcs) {
} }
TEST_F(EndToEndTest, RedundantPayloadsTransmittedOnAllSsrcs) { TEST_F(EndToEndTest, RedundantPayloadsTransmittedOnAllSsrcs) {
// TODO(pbos): Use CallTest::kSendRtxSsrcs when they're an array (pending CL).
static const uint32_t kSendRtxSsrcs[kNumSsrcs] = {
0xBADCAFD, 0xBADCAFE, 0xBADCAFF};
class ObserveRedundantPayloads: public test::EndToEndTest { class ObserveRedundantPayloads: public test::EndToEndTest {
public: public:
ObserveRedundantPayloads() ObserveRedundantPayloads()
@ -1618,4 +1617,178 @@ TEST_F(EndToEndTest, RedundantPayloadsTransmittedOnAllSsrcs) {
RunBaseTest(&test); RunBaseTest(&test);
} }
void EndToEndTest::TestRtpStatePreservation(bool use_rtx) {
static const uint32_t kMaxSequenceNumberGap = 100;
static const uint64_t kMaxTimestampGap = kDefaultTimeoutMs * 90;
class RtpSequenceObserver : public test::RtpRtcpObserver {
public:
RtpSequenceObserver(bool use_rtx)
: test::RtpRtcpObserver(kDefaultTimeoutMs),
crit_(CriticalSectionWrapper::CreateCriticalSection()),
ssrcs_to_observe_(kNumSsrcs) {
for (size_t i = 0; i < kNumSsrcs; ++i) {
configured_ssrcs_[kSendSsrcs[i]] = true;
if (use_rtx)
configured_ssrcs_[kSendRtxSsrcs[i]] = true;
}
}
void ResetExpectedSsrcs(size_t num_expected_ssrcs) {
CriticalSectionScoped lock(crit_.get());
ssrc_observed_.clear();
ssrcs_to_observe_ = num_expected_ssrcs;
}
private:
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header;
EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
const uint32_t ssrc = header.ssrc;
const uint16_t sequence_number = header.sequenceNumber;
const uint32_t timestamp = header.timestamp;
const bool only_padding =
static_cast<size_t>(header.headerLength + header.paddingLength) ==
length;
EXPECT_TRUE(configured_ssrcs_[ssrc])
<< "Received SSRC that wasn't configured: " << ssrc;
std::map<uint32_t, uint16_t>::iterator it =
last_observed_sequence_number_.find(header.ssrc);
if (it == last_observed_sequence_number_.end()) {
last_observed_sequence_number_[ssrc] = sequence_number;
last_observed_timestamp_[ssrc] = timestamp;
} else {
// Verify sequence numbers are reasonably close.
uint32_t extended_sequence_number = sequence_number;
// Check for roll-over.
if (sequence_number < last_observed_sequence_number_[ssrc])
extended_sequence_number += 0xFFFFu + 1;
EXPECT_LE(
extended_sequence_number - last_observed_sequence_number_[ssrc],
kMaxSequenceNumberGap)
<< "Gap in sequence numbers ("
<< last_observed_sequence_number_[ssrc] << " -> " << sequence_number
<< ") too large for SSRC: " << ssrc << ".";
last_observed_sequence_number_[ssrc] = sequence_number;
// TODO(pbos): Remove this check if we ever have monotonically
// increasing timestamps. Right now padding packets add a delta which
// can cause reordering between padding packets and regular packets,
// hence we drop padding-only packets to not flake.
if (only_padding) {
// Verify that timestamps are reasonably close.
uint64_t extended_timestamp = timestamp;
// Check for roll-over.
if (timestamp < last_observed_timestamp_[ssrc])
extended_timestamp += static_cast<uint64_t>(0xFFFFFFFFu) + 1;
EXPECT_LE(extended_timestamp - last_observed_timestamp_[ssrc],
kMaxTimestampGap)
<< "Gap in timestamps (" << last_observed_timestamp_[ssrc]
<< " -> " << timestamp << ") too large for SSRC: " << ssrc << ".";
}
last_observed_timestamp_[ssrc] = timestamp;
}
CriticalSectionScoped lock(crit_.get());
// Wait for media packets on all ssrcs.
if (!ssrc_observed_[ssrc] && !only_padding) {
ssrc_observed_[ssrc] = true;
if (--ssrcs_to_observe_ == 0)
observation_complete_->Set();
}
return SEND_PACKET;
}
std::map<uint32_t, uint16_t> last_observed_sequence_number_;
std::map<uint32_t, uint32_t> last_observed_timestamp_;
std::map<uint32_t, bool> configured_ssrcs_;
scoped_ptr<CriticalSectionWrapper> crit_;
size_t ssrcs_to_observe_ GUARDED_BY(crit_);
std::map<uint32_t, bool> ssrc_observed_ GUARDED_BY(crit_);
} observer(use_rtx);
CreateCalls(Call::Config(observer.SendTransport()),
Call::Config(observer.ReceiveTransport()));
observer.SetReceivers(sender_call_->Receiver(), NULL);
CreateSendConfig(kNumSsrcs);
if (use_rtx) {
for (size_t i = 0; i < kNumSsrcs; ++i) {
send_config_.rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[i]);
}
send_config_.rtp.rtx.payload_type = kSendRtxPayloadType;
}
// Lower bitrates so that all streams send initially.
for (size_t i = 0; i < video_streams_.size(); ++i) {
video_streams_[i].min_bitrate_bps = 10000;
video_streams_[i].target_bitrate_bps = 15000;
video_streams_[i].max_bitrate_bps = 20000;
}
CreateMatchingReceiveConfigs();
CreateStreams();
CreateFrameGeneratorCapturer();
Start();
EXPECT_EQ(kEventSignaled, observer.Wait())
<< "Timed out waiting for all SSRCs to send packets.";
// Test stream resetting more than once to make sure that the state doesn't
// get set once (this could be due to using std::map::insert for instance).
for (size_t i = 0; i < 3; ++i) {
frame_generator_capturer_->Stop();
sender_call_->DestroyVideoSendStream(send_stream_);
// Re-create VideoSendStream with only one stream.
std::vector<VideoStream> one_stream = video_streams_;
one_stream.resize(1);
send_stream_ =
sender_call_->CreateVideoSendStream(send_config_, one_stream, NULL);
send_stream_->Start();
CreateFrameGeneratorCapturer();
frame_generator_capturer_->Start();
observer.ResetExpectedSsrcs(1);
EXPECT_EQ(kEventSignaled, observer.Wait())
<< "Timed out waiting for single RTP packet.";
// Reconfigure back to use all streams.
send_stream_->ReconfigureVideoEncoder(video_streams_, NULL);
observer.ResetExpectedSsrcs(kNumSsrcs);
EXPECT_EQ(kEventSignaled, observer.Wait())
<< "Timed out waiting for all SSRCs to send packets.";
// Reconfigure down to one stream.
send_stream_->ReconfigureVideoEncoder(one_stream, NULL);
observer.ResetExpectedSsrcs(1);
EXPECT_EQ(kEventSignaled, observer.Wait())
<< "Timed out waiting for single RTP packet.";
// Reconfigure back to use all streams.
send_stream_->ReconfigureVideoEncoder(video_streams_, NULL);
observer.ResetExpectedSsrcs(kNumSsrcs);
EXPECT_EQ(kEventSignaled, observer.Wait())
<< "Timed out waiting for all SSRCs to send packets.";
}
observer.StopSending();
Stop();
DestroyStreams();
}
TEST_F(EndToEndTest, RestartingSendStreamPreservesRtpState) {
TestRtpStatePreservation(false);
}
TEST_F(EndToEndTest, RestartingSendStreamPreservesRtpStatesWithRtx) {
TestRtpStatePreservation(true);
}
} // namespace webrtc } // namespace webrtc

View File

@ -108,18 +108,21 @@ std::string VideoSendStream::Config::ToString() const {
} }
namespace internal { namespace internal {
VideoSendStream::VideoSendStream(newapi::Transport* transport, VideoSendStream::VideoSendStream(
newapi::Transport* transport,
CpuOveruseObserver* overuse_observer, CpuOveruseObserver* overuse_observer,
webrtc::VideoEngine* video_engine, webrtc::VideoEngine* video_engine,
const VideoSendStream::Config& config, const VideoSendStream::Config& config,
const std::vector<VideoStream> video_streams, const std::vector<VideoStream> video_streams,
const void* encoder_settings, const void* encoder_settings,
const std::map<uint32_t, RtpState>& suspended_ssrcs,
int base_channel, int base_channel,
int start_bitrate_bps) int start_bitrate_bps)
: transport_adapter_(transport), : transport_adapter_(transport),
encoded_frame_proxy_(config.post_encode_callback), encoded_frame_proxy_(config.post_encode_callback),
config_(config), config_(config),
start_bitrate_bps_(start_bitrate_bps), start_bitrate_bps_(start_bitrate_bps),
suspended_ssrcs_(suspended_ssrcs),
external_codec_(NULL), external_codec_(NULL),
channel_(-1), channel_(-1),
stats_proxy_(new SendStatisticsProxy(config, this)) { stats_proxy_(new SendStatisticsProxy(config, this)) {
@ -403,6 +406,9 @@ void VideoSendStream::ConfigureSsrcs() {
uint32_t ssrc = config_.rtp.ssrcs[i]; uint32_t ssrc = config_.rtp.ssrcs[i];
rtp_rtcp_->SetLocalSSRC( rtp_rtcp_->SetLocalSSRC(
channel_, ssrc, kViEStreamTypeNormal, static_cast<unsigned char>(i)); channel_, ssrc, kViEStreamTypeNormal, static_cast<unsigned char>(i));
RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
if (it != suspended_ssrcs_.end())
rtp_rtcp_->SetRtpStateForSsrc(channel_, ssrc, it->second);
} }
if (config_.rtp.rtx.ssrcs.empty()) { if (config_.rtp.rtx.ssrcs.empty()) {
@ -412,11 +418,15 @@ void VideoSendStream::ConfigureSsrcs() {
// Set up RTX. // Set up RTX.
assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size()); assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) { for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
rtp_rtcp_->SetLocalSSRC(channel_, rtp_rtcp_->SetLocalSSRC(channel_,
config_.rtp.rtx.ssrcs[i], config_.rtp.rtx.ssrcs[i],
kViEStreamTypeRtx, kViEStreamTypeRtx,
static_cast<unsigned char>(i)); static_cast<unsigned char>(i));
RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
if (it != suspended_ssrcs_.end())
rtp_rtcp_->SetRtpStateForSsrc(channel_, ssrc, it->second);
} }
if (config_.rtp.rtx.pad_with_redundant_payloads) { if (config_.rtp.rtx.pad_with_redundant_payloads) {
@ -427,5 +437,20 @@ void VideoSendStream::ConfigureSsrcs() {
rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type); rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type);
} }
std::map<uint32_t, RtpState> VideoSendStream::GetRtpStates() const {
std::map<uint32_t, RtpState> rtp_states;
for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
uint32_t ssrc = config_.rtp.ssrcs[i];
rtp_states[ssrc] = rtp_rtcp_->GetRtpStateForSsrc(channel_, ssrc);
}
for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
rtp_states[ssrc] = rtp_rtcp_->GetRtpStateForSsrc(channel_, ssrc);
}
return rtp_states;
}
} // namespace internal } // namespace internal
} // namespace webrtc } // namespace webrtc

View File

@ -11,7 +11,10 @@
#ifndef WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_ #ifndef WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_
#define WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_ #define WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_
#include <map>
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
#include "webrtc/video/encoded_frame_callback_adapter.h" #include "webrtc/video/encoded_frame_callback_adapter.h"
#include "webrtc/video/send_statistics_proxy.h" #include "webrtc/video/send_statistics_proxy.h"
#include "webrtc/video/transport_adapter.h" #include "webrtc/video/transport_adapter.h"
@ -44,6 +47,7 @@ class VideoSendStream : public webrtc::VideoSendStream,
const VideoSendStream::Config& config, const VideoSendStream::Config& config,
const std::vector<VideoStream> video_streams, const std::vector<VideoStream> video_streams,
const void* encoder_settings, const void* encoder_settings,
const std::map<uint32_t, RtpState>& suspended_ssrcs,
int base_channel, int base_channel,
int start_bitrate); int start_bitrate);
@ -65,6 +69,9 @@ class VideoSendStream : public webrtc::VideoSendStream,
// From webrtc::VideoSendStream. // From webrtc::VideoSendStream.
virtual VideoSendStreamInput* Input() OVERRIDE; virtual VideoSendStreamInput* Input() OVERRIDE;
typedef std::map<uint32_t, RtpState> RtpStateMap;
RtpStateMap GetRtpStates() const;
protected: protected:
// From SendStatisticsProxy::StreamStatsProvider. // From SendStatisticsProxy::StreamStatsProvider.
virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) OVERRIDE; virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) OVERRIDE;
@ -76,6 +83,7 @@ class VideoSendStream : public webrtc::VideoSendStream,
EncodedFrameCallbackAdapter encoded_frame_proxy_; EncodedFrameCallbackAdapter encoded_frame_proxy_;
const VideoSendStream::Config config_; const VideoSendStream::Config config_;
const int start_bitrate_bps_; const int start_bitrate_bps_;
std::map<uint32_t, RtpState> suspended_ssrcs_;
ViEBase* video_engine_base_; ViEBase* video_engine_base_;
ViECapture* capture_; ViECapture* capture_;

View File

@ -470,7 +470,7 @@ TEST_F(VideoSendStreamTest, RetransmitsNack) {
TEST_F(VideoSendStreamTest, RetransmitsNackOverRtx) { TEST_F(VideoSendStreamTest, RetransmitsNackOverRtx) {
// NACKs over RTX should use a separate SSRC. // NACKs over RTX should use a separate SSRC.
TestNackRetransmission(kSendRtxSsrc, kSendRtxPayloadType); TestNackRetransmission(kSendRtxSsrcs[0], kSendRtxPayloadType);
} }
void VideoSendStreamTest::TestPacketFragmentationSize(VideoFormat format, void VideoSendStreamTest::TestPacketFragmentationSize(VideoFormat format,

View File

@ -23,6 +23,7 @@
#define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_RTP_RTCP_H_ #define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_RTP_RTCP_H_
#include "webrtc/common_types.h" #include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
namespace webrtc { namespace webrtc {
@ -151,6 +152,17 @@ class WEBRTC_DLLEXPORT ViERTP_RTCP {
virtual int SetStartSequenceNumber(const int video_channel, virtual int SetStartSequenceNumber(const int video_channel,
unsigned short sequence_number) = 0; unsigned short sequence_number) = 0;
// TODO(pbos): Remove default implementation once this has been implemented
// in libjingle.
virtual void SetRtpStateForSsrc(int video_channel,
uint32_t ssrc,
const RtpState& rtp_state) {}
// TODO(pbos): Remove default implementation once this has been implemented
// in libjingle.
virtual RtpState GetRtpStateForSsrc(int video_channel, uint32_t ssrc) {
return RtpState();
}
// This function sets the RTCP status for the specified channel. // This function sets the RTCP status for the specified channel.
// Default mode is kRtcpCompound_RFC4585. // Default mode is kRtcpCompound_RFC4585.
virtual int SetRTCPStatus(const int video_channel, virtual int SetRTCPStatus(const int video_channel,

View File

@ -893,6 +893,21 @@ int32_t ViEChannel::SetStartSequenceNumber(uint16_t sequence_number) {
return rtp_rtcp_->SetSequenceNumber(sequence_number); return rtp_rtcp_->SetSequenceNumber(sequence_number);
} }
void ViEChannel::SetRtpStateForSsrc(uint32_t ssrc, const RtpState& rtp_state) {
assert(!rtp_rtcp_->Sending());
default_rtp_rtcp_->SetRtpStateForSsrc(ssrc, rtp_state);
}
RtpState ViEChannel::GetRtpStateForSsrc(uint32_t ssrc) {
assert(!rtp_rtcp_->Sending());
RtpState rtp_state;
if (!default_rtp_rtcp_->GetRtpStateForSsrc(ssrc, &rtp_state)) {
LOG(LS_ERROR) << "Couldn't get RTP state for ssrc: " << ssrc;
}
return rtp_state;
}
int32_t ViEChannel::SetRTCPCName(const char rtcp_cname[]) { int32_t ViEChannel::SetRTCPCName(const char rtcp_cname[]) {
if (rtp_rtcp_->Sending()) { if (rtp_rtcp_->Sending()) {
return -1; return -1;

View File

@ -152,6 +152,9 @@ class ViEChannel
// Sets the starting sequence number, must be called before StartSend. // Sets the starting sequence number, must be called before StartSend.
int32_t SetStartSequenceNumber(uint16_t sequence_number); int32_t SetStartSequenceNumber(uint16_t sequence_number);
void SetRtpStateForSsrc(uint32_t ssrc, const RtpState& rtp_state);
RtpState GetRtpStateForSsrc(uint32_t ssrc);
// Sets the CName for the outgoing stream on the channel. // Sets the CName for the outgoing stream on the channel.
int32_t SetRTCPCName(const char rtcp_cname[]); int32_t SetRTCPCName(const char rtcp_cname[]);

View File

@ -256,6 +256,30 @@ int ViERTP_RTCPImpl::SetStartSequenceNumber(const int video_channel,
return 0; return 0;
} }
void ViERTP_RTCPImpl::SetRtpStateForSsrc(int video_channel,
uint32_t ssrc,
const RtpState& rtp_state) {
ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
ViEChannel* vie_channel = cs.Channel(video_channel);
if (!vie_channel)
return;
if (vie_channel->Sending()) {
LOG_F(LS_ERROR) << "channel " << video_channel << " is already sending.";
return;
}
vie_channel->SetRtpStateForSsrc(ssrc, rtp_state);
}
RtpState ViERTP_RTCPImpl::GetRtpStateForSsrc(int video_channel, uint32_t ssrc) {
ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
ViEChannel* vie_channel = cs.Channel(video_channel);
if (!vie_channel)
return RtpState();
return vie_channel->GetRtpStateForSsrc(ssrc);
}
int ViERTP_RTCPImpl::SetRTCPStatus(const int video_channel, int ViERTP_RTCPImpl::SetRTCPStatus(const int video_channel,
const ViERTCPMode rtcp_mode) { const ViERTCPMode rtcp_mode) {
LOG_F(LS_INFO) << "channel: " << video_channel LOG_F(LS_INFO) << "channel: " << video_channel

View File

@ -46,6 +46,11 @@ class ViERTP_RTCPImpl
const uint8_t payload_type); const uint8_t payload_type);
virtual int SetStartSequenceNumber(const int video_channel, virtual int SetStartSequenceNumber(const int video_channel,
uint16_t sequence_number); uint16_t sequence_number);
virtual void SetRtpStateForSsrc(int video_channel,
uint32_t ssrc,
const RtpState& rtp_state) OVERRIDE;
virtual RtpState GetRtpStateForSsrc(int video_channel,
uint32_t ssrc) OVERRIDE;
virtual int SetRTCPStatus(const int video_channel, virtual int SetRTCPStatus(const int video_channel,
const ViERTCPMode rtcp_mode); const ViERTCPMode rtcp_mode);
virtual int GetRTCPStatus(const int video_channel, virtual int GetRTCPStatus(const int video_channel,

View File

@ -11,6 +11,7 @@
#include "webrtc/video_engine/vie_sender.h" #include "webrtc/video_engine/vie_sender.h"
#include <assert.h> #include <assert.h>
#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
#include "webrtc/modules/utility/interface/rtp_dump.h" #include "webrtc/modules/utility/interface/rtp_dump.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"