summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/rtp_rtcp/interface/rtp_rtcp.h4
-rw-r--r--modules/rtp_rtcp/interface/rtp_rtcp_defines.h14
-rw-r--r--modules/rtp_rtcp/mocks/mock_rtp_rtcp.h3
-rw-r--r--modules/rtp_rtcp/source/rtcp_receiver.cc2
-rw-r--r--modules/rtp_rtcp/source/rtp_rtcp_impl.cc36
-rw-r--r--modules/rtp_rtcp/source/rtp_rtcp_impl.h4
-rw-r--r--modules/rtp_rtcp/source/rtp_sender.cc142
-rw-r--r--modules/rtp_rtcp/source/rtp_sender.h95
-rw-r--r--test/call_test.cc10
-rw-r--r--test/call_test.h4
-rw-r--r--video/call.cc24
-rw-r--r--video/end_to_end_tests.cc187
-rw-r--r--video/video_send_stream.cc43
-rw-r--r--video/video_send_stream.h8
-rw-r--r--video/video_send_stream_tests.cc2
-rw-r--r--video_engine/include/vie_rtp_rtcp.h12
-rw-r--r--video_engine/vie_channel.cc15
-rw-r--r--video_engine/vie_channel.h3
-rw-r--r--video_engine/vie_rtp_rtcp_impl.cc24
-rw-r--r--video_engine/vie_rtp_rtcp_impl.h5
-rw-r--r--video_engine/vie_sender.cc1
21 files changed, 530 insertions, 108 deletions
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp.h b/modules/rtp_rtcp/interface/rtp_rtcp.h
index 95c565f0..da82b65e 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -203,6 +203,10 @@ class RtpRtcp : public Module {
*/
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
*/
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
index 6f99f938..e1bec5fc 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -192,6 +192,20 @@ struct RtcpReceiveTimeInfo {
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
{
public:
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 03156c79..fe20c6a3 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -74,6 +74,9 @@ class MockRtpRtcp : public RtpRtcp {
uint16_t());
MOCK_METHOD1(SetSequenceNumber,
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,
uint32_t());
MOCK_METHOD1(SetSSRC,
diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc
index 896bd5f4..b38ae1f0 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver.cc
+++ b/modules/rtp_rtcp/source/rtcp_receiver.cc
@@ -1361,8 +1361,6 @@ int32_t RTCPReceiver::UpdateTMMBR() {
void RTCPReceiver::RegisterRtcpStatisticsCallback(
RtcpStatisticsCallback* callback) {
CriticalSectionScoped cs(_criticalSectionFeedbacks);
- if (callback != NULL)
- assert(stats_callback_ == NULL);
stats_callback_ = callback;
}
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index 70fe7174..aff42342 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -333,6 +333,42 @@ int32_t ModuleRtpRtcpImpl::SetSequenceNumber(
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 {
return rtp_sender_.SSRC();
}
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index 55826b6f..c1694078 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -73,6 +73,10 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
// Set SequenceNumber, default is a random number.
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;
// Configure SSRC, default is a random number.
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index c0383934..919eb029 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -74,8 +74,8 @@ RTPSender::RTPSender(const int32_t id,
rtp_stats_callback_(NULL),
bitrate_callback_(NULL),
// RTP variables
- start_time_stamp_forced_(false),
- start_time_stamp_(0),
+ start_timestamp_forced_(false),
+ start_timestamp_(0),
ssrc_db_(*SSRCDatabase::GetSSRCDatabase()),
remote_ssrc_(0),
sequence_number_forced_(false),
@@ -304,12 +304,17 @@ int32_t RTPSender::SetMaxPayloadLength(
}
uint16_t RTPSender::MaxDataPayloadLength() const {
+ int rtx;
+ {
+ CriticalSectionScoped rtx_lock(send_critsect_);
+ rtx = rtx_;
+ }
if (audio_configured_) {
return max_payload_length_ - RTPHeaderLength();
} else {
return max_payload_length_ - RTPHeaderLength() // RTP 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;
}
+uint32_t RTPSender::RtxSsrc() const {
+ CriticalSectionScoped cs(send_critsect_);
+ return ssrc_rtx_;
+}
+
void RTPSender::RTXStatus(int* mode, uint32_t* ssrc,
int* payload_type) const {
CriticalSectionScoped cs(send_critsect_);
@@ -389,9 +399,11 @@ int32_t RTPSender::SendOutgoingData(
const uint8_t *payload_data, const uint32_t payload_size,
const RTPFragmentationHeader *fragmentation,
VideoCodecInformation *codec_info, const RTPVideoTypeHeader *rtp_type_hdr) {
+ uint32_t ssrc;
{
// Drop this packet if we're not sending media packets.
CriticalSectionScoped cs(send_critsect_);
+ ssrc = ssrc_;
if (!sending_media_) {
return 0;
}
@@ -435,17 +447,13 @@ int32_t RTPSender::SendOutgoingData(
CriticalSectionScoped cs(statistics_crit_.get());
uint32_t frame_count = ++frame_counts_[frame_type];
if (frame_count_observer_) {
- frame_count_observer_->FrameCountUpdated(frame_type,
- frame_count,
- ssrc_);
+ frame_count_observer_->FrameCountUpdated(frame_type, frame_count, ssrc);
}
return ret_val;
}
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) {
@@ -493,7 +501,7 @@ bool RTPSender::SendPaddingAccordingToBitrate(
CriticalSectionScoped cs(send_critsect_);
// Add the random RTP timestamp offset and store the capture time for
// later calculation of the send time offset.
- timestamp = start_time_stamp_ + capture_timestamp;
+ timestamp = start_timestamp_ + capture_timestamp;
timestamp_ = timestamp;
capture_time_ms_ = capture_time_ms;
last_timestamp_time_ms_ = clock_->TimeInMilliseconds();
@@ -567,6 +575,7 @@ int RTPSender::SendPadData(int payload_type, uint32_t timestamp,
++sequence_number_rtx_;
}
}
+
uint8_t padding_packet[IP_PACKET_SIZE];
int header_length = CreateRTPHeader(padding_packet, payload_type, ssrc,
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,
(rtx_ & kRtxRetransmitted) > 0, true) ?
length : -1;
@@ -784,8 +794,15 @@ bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
if (!retransmission && capture_time_ms > 0) {
UpdateDelayStatistics(capture_time_ms, clock_->TimeInMilliseconds());
}
- return PrepareAndSendPacket(data_buffer, length, capture_time_ms,
- retransmission && (rtx_ & kRtxRetransmitted) > 0,
+ int rtx;
+ {
+ CriticalSectionScoped lock(send_critsect_);
+ rtx = rtx_;
+ }
+ return PrepareAndSendPacket(data_buffer,
+ length,
+ capture_time_ms,
+ retransmission && (rtx & kRtxRetransmitted) > 0,
retransmission);
}
@@ -827,12 +844,11 @@ void RTPSender::UpdateRtpStats(const uint8_t* buffer,
bool is_retransmit) {
StreamDataCounters* counters;
// 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());
if (is_rtx) {
counters = &rtx_rtp_stats_;
- ssrc = ssrc_rtx_;
} else {
counters = &rtp_stats_;
}
@@ -874,6 +890,7 @@ int RTPSender::TimeToSendPadding(int bytes) {
int payload_type;
int64_t capture_time_ms;
uint32_t timestamp;
+ int rtx;
{
CriticalSectionScoped cs(send_critsect_);
if (!sending_media_) {
@@ -889,8 +906,11 @@ int RTPSender::TimeToSendPadding(int bytes) {
capture_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;
if (bytes > 0) {
int padding_sent = SendPadData(payload_type,
@@ -899,7 +919,7 @@ int RTPSender::TimeToSendPadding(int bytes) {
bytes,
kDontStore,
true,
- rtx_ == kRtxOff);
+ rtx == kRtxOff);
bytes_sent += padding_sent;
}
return bytes_sent;
@@ -975,6 +995,7 @@ void RTPSender::ProcessBitrate() {
}
uint16_t RTPSender::RTPHeaderLength() const {
+ CriticalSectionScoped lock(send_critsect_);
uint16_t rtp_header_length = 12;
if (include_csrcs_) {
rtp_header_length += sizeof(uint32_t) * num_csrcs_;
@@ -989,12 +1010,19 @@ uint16_t RTPSender::IncrementSequenceNumber() {
}
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());
rtp_stats_ = StreamDataCounters();
rtx_rtp_stats_ = StreamDataCounters();
if (rtp_stats_callback_) {
- rtp_stats_callback_->DataCountersUpdated(rtp_stats_, ssrc_);
- rtp_stats_callback_->DataCountersUpdated(rtx_rtp_stats_, ssrc_rtx_);
+ rtp_stats_callback_->DataCountersUpdated(rtp_stats_, ssrc);
+ rtp_stats_callback_->DataCountersUpdated(rtx_rtp_stats_, ssrc_rtx);
}
}
@@ -1049,16 +1077,18 @@ int RTPSender::CreateRTPHeader(
return rtp_header_length;
}
-int32_t RTPSender::BuildRTPheader(
- uint8_t *data_buffer, const int8_t payload_type,
- const bool marker_bit, const uint32_t capture_timestamp,
- int64_t capture_time_ms, const bool time_stamp_provided,
- const bool inc_sequence_number) {
+int32_t RTPSender::BuildRTPheader(uint8_t* data_buffer,
+ const int8_t payload_type,
+ const bool marker_bit,
+ const uint32_t capture_timestamp,
+ int64_t capture_time_ms,
+ const bool timestamp_provided,
+ const bool inc_sequence_number) {
assert(payload_type >= 0);
CriticalSectionScoped cs(send_critsect_);
- if (time_stamp_provided) {
- timestamp_ = start_time_stamp_ + capture_timestamp;
+ if (timestamp_provided) {
+ timestamp_ = start_timestamp_ + capture_timestamp;
} else {
// Make a unique time stamp.
// 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.
SetStartTimestamp(RTPtime, false);
} else {
+ CriticalSectionScoped lock(send_critsect_);
if (!ssrc_forced_) {
// Generate a new SSRC.
ssrc_db_.ReturnSSRC(ssrc_);
@@ -1412,18 +1443,18 @@ uint32_t RTPSender::Timestamp() const {
void RTPSender::SetStartTimestamp(uint32_t timestamp, bool force) {
CriticalSectionScoped cs(send_critsect_);
if (force) {
- start_time_stamp_forced_ = force;
- start_time_stamp_ = timestamp;
+ start_timestamp_forced_ = true;
+ start_timestamp_ = timestamp;
} else {
- if (!start_time_stamp_forced_) {
- start_time_stamp_ = timestamp;
+ if (!start_timestamp_forced_) {
+ start_timestamp_ = timestamp;
}
}
}
uint32_t RTPSender::StartTimestamp() const {
CriticalSectionScoped cs(send_critsect_);
- return start_time_stamp_;
+ return start_timestamp_;
}
uint32_t RTPSender::GenerateNewSSRC() {
@@ -1460,6 +1491,7 @@ uint32_t RTPSender::SSRC() const {
}
void RTPSender::SetCSRCStatus(const bool include) {
+ CriticalSectionScoped lock(send_critsect_);
include_csrcs_ = include;
}
@@ -1635,8 +1667,6 @@ void RTPSender::BuildRtxPacket(uint8_t* buffer, uint16_t* length,
void RTPSender::RegisterFrameCountObserver(FrameCountObserver* observer) {
CriticalSectionScoped cs(statistics_crit_.get());
- if (observer != NULL)
- assert(frame_count_observer_ == NULL);
frame_count_observer_ = observer;
}
@@ -1648,8 +1678,6 @@ FrameCountObserver* RTPSender::GetFrameCountObserver() const {
void RTPSender::RegisterRtpStatisticsCallback(
StreamDataCountersCallback* callback) {
CriticalSectionScoped cs(statistics_crit_.get());
- if (callback != NULL)
- assert(rtp_stats_callback_ == NULL);
rtp_stats_callback_ = callback;
}
@@ -1660,8 +1688,6 @@ StreamDataCountersCallback* RTPSender::GetRtpStatisticsCallback() const {
void RTPSender::RegisterBitrateObserver(BitrateStatisticsObserver* observer) {
CriticalSectionScoped cs(statistics_crit_.get());
- if (observer != NULL)
- assert(bitrate_callback_ == NULL);
bitrate_callback_ = observer;
}
@@ -1673,9 +1699,53 @@ BitrateStatisticsObserver* RTPSender::GetBitrateObserver() const {
uint32_t RTPSender::BitrateSent() const { return bitrate_sent_.BitrateLast(); }
void RTPSender::BitrateUpdated(const BitrateStatistics& stats) {
+ uint32_t ssrc;
+ {
+ CriticalSectionScoped ssrc_lock(send_critsect_);
+ ssrc = ssrc_;
+ }
CriticalSectionScoped cs(statistics_crit_.get());
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
diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
index 291e619b..78d3a2a8 100644
--- a/modules/rtp_rtcp/source/rtp_sender.h
+++ b/modules/rtp_rtcp/source/rtp_sender.h
@@ -43,12 +43,13 @@ class RTPSenderInterface {
virtual uint32_t SSRC() const = 0;
virtual uint32_t Timestamp() const = 0;
- virtual int32_t BuildRTPheader(
- uint8_t *data_buffer, const int8_t payload_type,
- const bool marker_bit, const uint32_t capture_time_stamp,
- int64_t capture_time_ms,
- const bool time_stamp_provided = true,
- const bool inc_sequence_number = true) = 0;
+ virtual int32_t BuildRTPheader(uint8_t* data_buffer,
+ const int8_t payload_type,
+ const bool marker_bit,
+ const uint32_t capture_timestamp,
+ int64_t capture_time_ms,
+ const bool timestamp_provided = true,
+ const bool inc_sequence_number = true) = 0;
virtual uint16_t RTPHeaderLength() const = 0;
virtual uint16_t IncrementSequenceNumber() = 0;
@@ -132,13 +133,15 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer {
int32_t SetMaxPayloadLength(const uint16_t length,
const uint16_t packet_over_head);
- int32_t SendOutgoingData(
- const FrameType frame_type, const int8_t payload_type,
- const uint32_t time_stamp, int64_t capture_time_ms,
- const uint8_t *payload_data, const uint32_t payload_size,
- const RTPFragmentationHeader *fragmentation,
- VideoCodecInformation *codec_info = NULL,
- const RTPVideoTypeHeader * rtp_type_hdr = NULL);
+ int32_t SendOutgoingData(const FrameType frame_type,
+ const int8_t payload_type,
+ const uint32_t timestamp,
+ int64_t capture_time_ms,
+ const uint8_t* payload_data,
+ const uint32_t payload_size,
+ const RTPFragmentationHeader* fragmentation,
+ VideoCodecInformation* codec_info = NULL,
+ const RTPVideoTypeHeader* rtp_type_hdr = NULL);
// RTP header extension
int32_t SetTransmissionTimeOffset(
@@ -189,16 +192,19 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer {
void RTXStatus(int* mode, uint32_t* ssrc, int* payload_type) const;
+ uint32_t RtxSsrc() const;
void SetRtxSsrc(uint32_t ssrc);
void SetRtxPayloadType(int payloadType);
// Functions wrapping RTPSenderInterface.
virtual int32_t BuildRTPheader(
- uint8_t *data_buffer, const int8_t payload_type,
- const bool marker_bit, const uint32_t capture_time_stamp,
+ uint8_t* data_buffer,
+ const int8_t payload_type,
+ const bool marker_bit,
+ const uint32_t capture_timestamp,
int64_t capture_time_ms,
- const bool time_stamp_provided = true,
+ const bool timestamp_provided = true,
const bool inc_sequence_number = true) 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;
+ void SetRtpState(const RtpState& rtp_state);
+ RtpState GetRtpState() const;
+ void SetRtxRtpState(const RtpState& rtp_state);
+ RtpState GetRtxRtpState() const;
+
protected:
int32_t CheckPayloadType(const int8_t payload_type,
RtpVideoCodecTypes *video_type);
@@ -363,34 +374,34 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer {
// Statistics
scoped_ptr<CriticalSectionWrapper> statistics_crit_;
- SendDelayMap send_delays_;
- std::map<FrameType, uint32_t> frame_counts_;
- FrameCountObserver* frame_count_observer_;
- StreamDataCounters rtp_stats_;
- StreamDataCounters rtx_rtp_stats_;
- StreamDataCountersCallback* rtp_stats_callback_;
- BitrateStatisticsObserver* bitrate_callback_;
+ SendDelayMap send_delays_ GUARDED_BY(statistics_crit_);
+ std::map<FrameType, uint32_t> frame_counts_ GUARDED_BY(statistics_crit_);
+ FrameCountObserver* frame_count_observer_ GUARDED_BY(statistics_crit_);
+ StreamDataCounters rtp_stats_ GUARDED_BY(statistics_crit_);
+ StreamDataCounters rtx_rtp_stats_ GUARDED_BY(statistics_crit_);
+ StreamDataCountersCallback* rtp_stats_callback_ GUARDED_BY(statistics_crit_);
+ BitrateStatisticsObserver* bitrate_callback_ GUARDED_BY(statistics_crit_);
// RTP variables
- bool start_time_stamp_forced_;
- uint32_t start_time_stamp_;
- SSRCDatabase &ssrc_db_;
- uint32_t remote_ssrc_;
- bool sequence_number_forced_;
- uint16_t sequence_number_;
- uint16_t sequence_number_rtx_;
- bool ssrc_forced_;
- uint32_t ssrc_;
- uint32_t timestamp_;
- int64_t capture_time_ms_;
- int64_t last_timestamp_time_ms_;
- bool last_packet_marker_bit_;
- uint8_t num_csrcs_;
- uint32_t csrcs_[kRtpCsrcSize];
- bool include_csrcs_;
- int rtx_;
- uint32_t ssrc_rtx_;
- int payload_type_rtx_;
+ bool start_timestamp_forced_ GUARDED_BY(send_critsect_);
+ uint32_t start_timestamp_ GUARDED_BY(send_critsect_);
+ SSRCDatabase& ssrc_db_ GUARDED_BY(send_critsect_);
+ uint32_t remote_ssrc_ GUARDED_BY(send_critsect_);
+ bool sequence_number_forced_ GUARDED_BY(send_critsect_);
+ uint16_t sequence_number_ GUARDED_BY(send_critsect_);
+ uint16_t sequence_number_rtx_ GUARDED_BY(send_critsect_);
+ bool ssrc_forced_ GUARDED_BY(send_critsect_);
+ uint32_t ssrc_ GUARDED_BY(send_critsect_);
+ uint32_t timestamp_ GUARDED_BY(send_critsect_);
+ int64_t capture_time_ms_ GUARDED_BY(send_critsect_);
+ int64_t last_timestamp_time_ms_ GUARDED_BY(send_critsect_);
+ bool last_packet_marker_bit_ GUARDED_BY(send_critsect_);
+ uint8_t num_csrcs_ GUARDED_BY(send_critsect_);
+ uint32_t csrcs_[kRtpCsrcSize] GUARDED_BY(send_critsect_);
+ bool include_csrcs_ GUARDED_BY(send_critsect_);
+ int rtx_ GUARDED_BY(send_critsect_);
+ uint32_t ssrc_rtx_ GUARDED_BY(send_critsect_);
+ int payload_type_rtx_ GUARDED_BY(send_critsect_);
// Note: Don't access this variable directly, always go through
// SetTargetBitrateKbps or GetTargetBitrateKbps. Also remember
diff --git a/test/call_test.cc b/test/call_test.cc
index ec2f815f..16dad719 100644
--- a/test/call_test.cc
+++ b/test/call_test.cc
@@ -15,8 +15,9 @@ namespace webrtc {
namespace test {
CallTest::CallTest()
- : send_stream_(NULL),
- fake_encoder_(Clock::GetRealTimeClock()) {
+ : clock_(Clock::GetRealTimeClock()),
+ send_stream_(NULL),
+ fake_encoder_(clock_) {
}
CallTest::~CallTest() {
}
@@ -121,7 +122,7 @@ void CallTest::CreateFrameGeneratorCapturer() {
stream.width,
stream.height,
stream.max_framerate,
- Clock::GetRealTimeClock()));
+ clock_));
}
void CallTest::CreateStreams() {
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::kFakeSendPayloadType = 125;
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::kReceiverLocalSsrc = 0x123456;
const int CallTest::kNackRtpHistoryMs = 1000;
diff --git a/test/call_test.h b/test/call_test.h
index 37a883d6..e24d8359 100644
--- a/test/call_test.h
+++ b/test/call_test.h
@@ -35,7 +35,7 @@ class CallTest : public ::testing::Test {
static const uint8_t kSendPayloadType;
static const uint8_t kSendRtxPayloadType;
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 kReceiverLocalSsrc;
static const int kNackRtpHistoryMs;
@@ -58,6 +58,8 @@ class CallTest : public ::testing::Test {
void Stop();
void DestroyStreams();
+ Clock* const clock_;
+
scoped_ptr<Call> sender_call_;
VideoSendStream::Config send_config_;
std::vector<VideoStream> video_streams_;
diff --git a/video/call.cc b/video/call.cc
index 0e153e91..2ae0ae56 100644
--- a/video/call.cc
+++ b/video/call.cc
@@ -106,6 +106,8 @@ class Call : public webrtc::Call, public PacketReceiver {
scoped_ptr<CpuOveruseObserverProxy> overuse_observer_proxy_;
+ VideoSendStream::RtpStateMap suspended_send_ssrcs_;
+
VideoEngine* video_engine_;
ViERTP_RTCP* rtp_rtcp_;
ViECodec* codec_;
@@ -184,6 +186,7 @@ VideoSendStream* Call::CreateVideoSendStream(
config,
video_streams,
encoder_settings,
+ suspended_send_ssrcs_,
base_channel_id_,
config_.start_bitrate_bps != -1 ? config_.start_bitrate_bps
: kDefaultVideoStreamBitrateBps);
@@ -199,21 +202,30 @@ VideoSendStream* Call::CreateVideoSendStream(
void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) {
assert(send_stream != NULL);
+ send_stream->Stop();
+
VideoSendStream* send_stream_impl = NULL;
{
WriteLockScoped write_lock(*send_lock_);
- for (std::map<uint32_t, VideoSendStream*>::iterator it =
- send_ssrcs_.begin();
- it != send_ssrcs_.end();
- ++it) {
+ std::map<uint32_t, VideoSendStream*>::iterator it = send_ssrcs_.begin();
+ while (it != send_ssrcs_.end()) {
if (it->second == static_cast<VideoSendStream*>(send_stream)) {
send_stream_impl = it->second;
- send_ssrcs_.erase(it);
- break;
+ send_ssrcs_.erase(it++);
+ } 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);
delete send_stream_impl;
}
diff --git a/video/end_to_end_tests.cc b/video/end_to_end_tests.cc
index 71db199f..748eb715 100644
--- a/video/end_to_end_tests.cc
+++ b/video/end_to_end_tests.cc
@@ -58,6 +58,7 @@ class EndToEndTest : public test::CallTest {
void RespectsRtcpMode(newapi::RtcpMode rtcp_mode);
void TestXrReceiverReferenceTimeReport(bool enable_rrtr);
void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first);
+ void TestRtpStatePreservation(bool use_rtx);
};
TEST_F(EndToEndTest, ReceiverCanBeStartedTwice) {
@@ -434,7 +435,7 @@ void EndToEndTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) {
public:
explicit RetransmissionObserver(bool expect_rtx)
: EndToEndTest(kDefaultTimeoutMs),
- retransmission_ssrc_(expect_rtx ? kSendRtxSsrc : kSendSsrcs[0]),
+ retransmission_ssrc_(expect_rtx ? kSendRtxSsrcs[0] : kSendSsrcs[0]),
retransmission_payload_type_(expect_rtx ? kSendRtxPayloadType
: kFakeSendPayloadType),
marker_bits_observed_(0),
@@ -481,10 +482,11 @@ void EndToEndTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) {
send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
(*receive_configs)[0].pre_render_callback = this;
(*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
- if (retransmission_ssrc_ == kSendRtxSsrc) {
- send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrc);
+ if (retransmission_ssrc_ == kSendRtxSsrcs[0]) {
+ send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]);
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 =
kSendRtxPayloadType;
}
@@ -1543,9 +1545,6 @@ TEST_F(EndToEndTest, CanSwitchToUseAllSsrcs) {
}
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 {
public:
ObserveRedundantPayloads()
@@ -1618,4 +1617,178 @@ TEST_F(EndToEndTest, RedundantPayloadsTransmittedOnAllSsrcs) {
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
diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc
index 4d32ab40..ff3fe508 100644
--- a/video/video_send_stream.cc
+++ b/video/video_send_stream.cc
@@ -108,18 +108,21 @@ std::string VideoSendStream::Config::ToString() const {
}
namespace internal {
-VideoSendStream::VideoSendStream(newapi::Transport* transport,
- CpuOveruseObserver* overuse_observer,
- webrtc::VideoEngine* video_engine,
- const VideoSendStream::Config& config,
- const std::vector<VideoStream> video_streams,
- const void* encoder_settings,
- int base_channel,
- int start_bitrate_bps)
+VideoSendStream::VideoSendStream(
+ newapi::Transport* transport,
+ CpuOveruseObserver* overuse_observer,
+ webrtc::VideoEngine* video_engine,
+ const VideoSendStream::Config& config,
+ const std::vector<VideoStream> video_streams,
+ const void* encoder_settings,
+ const std::map<uint32_t, RtpState>& suspended_ssrcs,
+ int base_channel,
+ int start_bitrate_bps)
: transport_adapter_(transport),
encoded_frame_proxy_(config.post_encode_callback),
config_(config),
start_bitrate_bps_(start_bitrate_bps),
+ suspended_ssrcs_(suspended_ssrcs),
external_codec_(NULL),
channel_(-1),
stats_proxy_(new SendStatisticsProxy(config, this)) {
@@ -403,6 +406,9 @@ void VideoSendStream::ConfigureSsrcs() {
uint32_t ssrc = config_.rtp.ssrcs[i];
rtp_rtcp_->SetLocalSSRC(
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()) {
@@ -412,11 +418,15 @@ void VideoSendStream::ConfigureSsrcs() {
// Set up RTX.
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_,
config_.rtp.rtx.ssrcs[i],
kViEStreamTypeRtx,
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) {
@@ -427,5 +437,20 @@ void VideoSendStream::ConfigureSsrcs() {
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 webrtc
diff --git a/video/video_send_stream.h b/video/video_send_stream.h
index df65b74e..e1770624 100644
--- a/video/video_send_stream.h
+++ b/video/video_send_stream.h
@@ -11,7 +11,10 @@
#ifndef 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/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
#include "webrtc/video/encoded_frame_callback_adapter.h"
#include "webrtc/video/send_statistics_proxy.h"
#include "webrtc/video/transport_adapter.h"
@@ -44,6 +47,7 @@ class VideoSendStream : public webrtc::VideoSendStream,
const VideoSendStream::Config& config,
const std::vector<VideoStream> video_streams,
const void* encoder_settings,
+ const std::map<uint32_t, RtpState>& suspended_ssrcs,
int base_channel,
int start_bitrate);
@@ -65,6 +69,9 @@ class VideoSendStream : public webrtc::VideoSendStream,
// From webrtc::VideoSendStream.
virtual VideoSendStreamInput* Input() OVERRIDE;
+ typedef std::map<uint32_t, RtpState> RtpStateMap;
+ RtpStateMap GetRtpStates() const;
+
protected:
// From SendStatisticsProxy::StreamStatsProvider.
virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) OVERRIDE;
@@ -76,6 +83,7 @@ class VideoSendStream : public webrtc::VideoSendStream,
EncodedFrameCallbackAdapter encoded_frame_proxy_;
const VideoSendStream::Config config_;
const int start_bitrate_bps_;
+ std::map<uint32_t, RtpState> suspended_ssrcs_;
ViEBase* video_engine_base_;
ViECapture* capture_;
diff --git a/video/video_send_stream_tests.cc b/video/video_send_stream_tests.cc
index b1197ef6..513bed2c 100644
--- a/video/video_send_stream_tests.cc
+++ b/video/video_send_stream_tests.cc
@@ -470,7 +470,7 @@ TEST_F(VideoSendStreamTest, RetransmitsNack) {
TEST_F(VideoSendStreamTest, RetransmitsNackOverRtx) {
// NACKs over RTX should use a separate SSRC.
- TestNackRetransmission(kSendRtxSsrc, kSendRtxPayloadType);
+ TestNackRetransmission(kSendRtxSsrcs[0], kSendRtxPayloadType);
}
void VideoSendStreamTest::TestPacketFragmentationSize(VideoFormat format,
diff --git a/video_engine/include/vie_rtp_rtcp.h b/video_engine/include/vie_rtp_rtcp.h
index e7158516..3a565610 100644
--- a/video_engine/include/vie_rtp_rtcp.h
+++ b/video_engine/include/vie_rtp_rtcp.h
@@ -23,6 +23,7 @@
#define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_RTP_RTCP_H_
#include "webrtc/common_types.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
namespace webrtc {
@@ -151,6 +152,17 @@ class WEBRTC_DLLEXPORT ViERTP_RTCP {
virtual int SetStartSequenceNumber(const int video_channel,
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.
// Default mode is kRtcpCompound_RFC4585.
virtual int SetRTCPStatus(const int video_channel,
diff --git a/video_engine/vie_channel.cc b/video_engine/vie_channel.cc
index 3031376d..5eb36076 100644
--- a/video_engine/vie_channel.cc
+++ b/video_engine/vie_channel.cc
@@ -893,6 +893,21 @@ int32_t ViEChannel::SetStartSequenceNumber(uint16_t 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[]) {
if (rtp_rtcp_->Sending()) {
return -1;
diff --git a/video_engine/vie_channel.h b/video_engine/vie_channel.h
index c76d1298..0ec50437 100644
--- a/video_engine/vie_channel.h
+++ b/video_engine/vie_channel.h
@@ -152,6 +152,9 @@ class ViEChannel
// Sets the starting sequence number, must be called before StartSend.
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.
int32_t SetRTCPCName(const char rtcp_cname[]);
diff --git a/video_engine/vie_rtp_rtcp_impl.cc b/video_engine/vie_rtp_rtcp_impl.cc
index 53610b4a..04f5e9b0 100644
--- a/video_engine/vie_rtp_rtcp_impl.cc
+++ b/video_engine/vie_rtp_rtcp_impl.cc
@@ -256,6 +256,30 @@ int ViERTP_RTCPImpl::SetStartSequenceNumber(const int video_channel,
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,
const ViERTCPMode rtcp_mode) {
LOG_F(LS_INFO) << "channel: " << video_channel
diff --git a/video_engine/vie_rtp_rtcp_impl.h b/video_engine/vie_rtp_rtcp_impl.h
index 5eec0efe..4afe1c58 100644
--- a/video_engine/vie_rtp_rtcp_impl.h
+++ b/video_engine/vie_rtp_rtcp_impl.h
@@ -46,6 +46,11 @@ class ViERTP_RTCPImpl
const uint8_t payload_type);
virtual int SetStartSequenceNumber(const int video_channel,
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,
const ViERTCPMode rtcp_mode);
virtual int GetRTCPStatus(const int video_channel,
diff --git a/video_engine/vie_sender.cc b/video_engine/vie_sender.cc
index 349bc72f..28bf3903 100644
--- a/video_engine/vie_sender.cc
+++ b/video_engine/vie_sender.cc
@@ -11,6 +11,7 @@
#include "webrtc/video_engine/vie_sender.h"
#include <assert.h>
+#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
#include "webrtc/modules/utility/interface/rtp_dump.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"