From 2bb1bdab8d11f5445693c028335fb3ace631f636 Mon Sep 17 00:00:00 2001 From: "pbos@webrtc.org" Date: Mon, 7 Jul 2014 13:06:48 +0000 Subject: [PATCH] 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 --- webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h | 4 + .../rtp_rtcp/interface/rtp_rtcp_defines.h | 14 ++ webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h | 3 + .../modules/rtp_rtcp/source/rtcp_receiver.cc | 2 - .../modules/rtp_rtcp/source/rtp_rtcp_impl.cc | 36 ++++ .../modules/rtp_rtcp/source/rtp_rtcp_impl.h | 4 + webrtc/modules/rtp_rtcp/source/rtp_sender.cc | 142 +++++++++---- webrtc/modules/rtp_rtcp/source/rtp_sender.h | 95 +++++---- webrtc/test/call_test.cc | 10 +- webrtc/test/call_test.h | 4 +- webrtc/video/call.cc | 24 ++- webrtc/video/end_to_end_tests.cc | 187 +++++++++++++++++- webrtc/video/video_send_stream.cc | 43 +++- webrtc/video/video_send_stream.h | 8 + webrtc/video/video_send_stream_tests.cc | 2 +- webrtc/video_engine/include/vie_rtp_rtcp.h | 12 ++ webrtc/video_engine/vie_channel.cc | 15 ++ webrtc/video_engine/vie_channel.h | 3 + webrtc/video_engine/vie_rtp_rtcp_impl.cc | 24 +++ webrtc/video_engine/vie_rtp_rtcp_impl.h | 5 + webrtc/video_engine/vie_sender.cc | 1 + 21 files changed, 530 insertions(+), 108 deletions(-) diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h index 95c565f01b..da82b65e10 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/webrtc/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/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h index 6f99f938de..e1bec5fc82 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h @@ -192,6 +192,20 @@ struct RtcpReceiveTimeInfo { typedef std::list 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/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 03156c79df..fe20c6a36a 100644 --- a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/webrtc/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/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc index 896bd5f4d3..b38ae1f0d5 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/webrtc/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/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 70fe717438..aff4234259 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/webrtc/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/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h index 55826b6fe8..c1694078ca 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/webrtc/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/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc index c0383934f4..919eb029c9 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc +++ b/webrtc/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/webrtc/modules/rtp_rtcp/source/rtp_sender.h b/webrtc/modules/rtp_rtcp/source/rtp_sender.h index 291e619b43..78d3a2a8a0 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender.h +++ b/webrtc/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 statistics_crit_; - SendDelayMap send_delays_; - std::map 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 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/webrtc/test/call_test.cc b/webrtc/test/call_test.cc index ec2f815fb9..16dad71919 100644 --- a/webrtc/test/call_test.cc +++ b/webrtc/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/webrtc/test/call_test.h b/webrtc/test/call_test.h index 37a883d685..e24d835931 100644 --- a/webrtc/test/call_test.h +++ b/webrtc/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 sender_call_; VideoSendStream::Config send_config_; std::vector video_streams_; diff --git a/webrtc/video/call.cc b/webrtc/video/call.cc index 0e153e91ec..2ae0ae564c 100644 --- a/webrtc/video/call.cc +++ b/webrtc/video/call.cc @@ -106,6 +106,8 @@ class Call : public webrtc::Call, public PacketReceiver { scoped_ptr 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::iterator it = - send_ssrcs_.begin(); - it != send_ssrcs_.end(); - ++it) { + std::map::iterator it = send_ssrcs_.begin(); + while (it != send_ssrcs_.end()) { if (it->second == static_cast(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/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc index 71db199f28..748eb71528 100644 --- a/webrtc/video/end_to_end_tests.cc +++ b/webrtc/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(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(header.headerLength + header.paddingLength) == + length; + + EXPECT_TRUE(configured_ssrcs_[ssrc]) + << "Received SSRC that wasn't configured: " << ssrc; + + std::map::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(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 last_observed_sequence_number_; + std::map last_observed_timestamp_; + std::map configured_ssrcs_; + + scoped_ptr crit_; + size_t ssrcs_to_observe_ GUARDED_BY(crit_); + std::map 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 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/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc index 4d32ab4010..ff3fe50817 100644 --- a/webrtc/video/video_send_stream.cc +++ b/webrtc/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 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 video_streams, + const void* encoder_settings, + const std::map& 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(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(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 VideoSendStream::GetRtpStates() const { + std::map 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/webrtc/video/video_send_stream.h b/webrtc/video/video_send_stream.h index df65b74efc..e177062481 100644 --- a/webrtc/video/video_send_stream.h +++ b/webrtc/video/video_send_stream.h @@ -11,7 +11,10 @@ #ifndef WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_ #define WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_ +#include + #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 video_streams, const void* encoder_settings, + const std::map& 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 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 suspended_ssrcs_; ViEBase* video_engine_base_; ViECapture* capture_; diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc index b1197ef64e..513bed2c4c 100644 --- a/webrtc/video/video_send_stream_tests.cc +++ b/webrtc/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/webrtc/video_engine/include/vie_rtp_rtcp.h b/webrtc/video_engine/include/vie_rtp_rtcp.h index e71585167e..3a565610ea 100644 --- a/webrtc/video_engine/include/vie_rtp_rtcp.h +++ b/webrtc/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/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc index 3031376da1..5eb36076cd 100644 --- a/webrtc/video_engine/vie_channel.cc +++ b/webrtc/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/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h index c76d1298e3..0ec5043705 100644 --- a/webrtc/video_engine/vie_channel.h +++ b/webrtc/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/webrtc/video_engine/vie_rtp_rtcp_impl.cc b/webrtc/video_engine/vie_rtp_rtcp_impl.cc index 53610b4a32..04f5e9b0ea 100644 --- a/webrtc/video_engine/vie_rtp_rtcp_impl.cc +++ b/webrtc/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/webrtc/video_engine/vie_rtp_rtcp_impl.h b/webrtc/video_engine/vie_rtp_rtcp_impl.h index 5eec0efe14..4afe1c5841 100644 --- a/webrtc/video_engine/vie_rtp_rtcp_impl.h +++ b/webrtc/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/webrtc/video_engine/vie_sender.cc b/webrtc/video_engine/vie_sender.cc index 349bc72fb9..28bf390334 100644 --- a/webrtc/video_engine/vie_sender.cc +++ b/webrtc/video_engine/vie_sender.cc @@ -11,6 +11,7 @@ #include "webrtc/video_engine/vie_sender.h" #include +#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"