diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index a195683663..a27516d32e 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -223,6 +223,7 @@ 'remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc', 'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc', 'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h', + 'remote_bitrate_estimator/remote_estimator_proxy_unittest.cc', 'remote_bitrate_estimator/send_time_history_unittest.cc', 'remote_bitrate_estimator/test/bwe_test_framework_unittest.cc', 'remote_bitrate_estimator/test/bwe_unittest.cc', diff --git a/webrtc/modules/pacing/include/packet_router.h b/webrtc/modules/pacing/include/packet_router.h index e7d630eb2f..c181ec094a 100644 --- a/webrtc/modules/pacing/include/packet_router.h +++ b/webrtc/modules/pacing/include/packet_router.h @@ -19,6 +19,7 @@ #include "webrtc/base/thread_annotations.h" #include "webrtc/common_types.h" #include "webrtc/modules/pacing/include/paced_sender.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" namespace webrtc { @@ -45,6 +46,9 @@ class PacketRouter : public PacedSender::Callback { void SetTransportWideSequenceNumber(uint16_t sequence_number); uint16_t AllocateSequenceNumber(); + // Send transport feedback packet to send-side. + virtual bool SendFeedback(rtcp::TransportFeedback* packet); + private: rtc::CriticalSection modules_lock_; // Map from ssrc to sending rtp module. diff --git a/webrtc/modules/pacing/packet_router.cc b/webrtc/modules/pacing/packet_router.cc index 1b124981a1..ac11903dd6 100644 --- a/webrtc/modules/pacing/packet_router.cc +++ b/webrtc/modules/pacing/packet_router.cc @@ -14,6 +14,7 @@ #include "webrtc/base/checks.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" namespace webrtc { @@ -89,4 +90,14 @@ uint16_t PacketRouter::AllocateSequenceNumber() { return new_seq; } +bool PacketRouter::SendFeedback(rtcp::TransportFeedback* packet) { + rtc::CritScope cs(&modules_lock_); + for (auto* rtp_module : rtp_modules_) { + packet->WithPacketSenderSsrc(rtp_module->SSRC()); + if (rtp_module->SendFeedbackPacket(*packet)) + return true; + } + return false; +} + } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi index 3bbd5039f1..5f4ed85bd6 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi @@ -36,6 +36,8 @@ 'remote_bitrate_estimator_abs_send_time.h', 'remote_bitrate_estimator_single_stream.cc', 'remote_bitrate_estimator_single_stream.h', + 'remote_estimator_proxy.cc', + 'remote_estimator_proxy.h', 'send_time_history.cc', 'test/bwe_test_logging.cc', 'test/bwe_test_logging.h', diff --git a/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc b/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc new file mode 100644 index 0000000000..3ded0df591 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/modules/pacing/include/packet_router.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" + +namespace webrtc { + +// TODO(sprang): Tune these! +const int RemoteEstimatorProxy::kDefaultProcessIntervalMs = 200; +const int RemoteEstimatorProxy::kBackWindowMs = 500; + +RemoteEstimatorProxy::RemoteEstimatorProxy(Clock* clock, + PacketRouter* packet_router) + : clock_(clock), + packet_router_(packet_router), + last_process_time_ms_(-1), + media_ssrc_(0), + feedback_sequence_(0), + window_start_seq_(-1) {} + +RemoteEstimatorProxy::~RemoteEstimatorProxy() {} + +void RemoteEstimatorProxy::IncomingPacketFeedbackVector( + const std::vector& packet_feedback_vector) { + rtc::CritScope cs(&lock_); + for (PacketInfo info : packet_feedback_vector) + OnPacketArrival(info.sequence_number, info.arrival_time_ms); +} + +void RemoteEstimatorProxy::IncomingPacket(int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header, + bool was_paced) { + DCHECK(header.extension.hasTransportSequenceNumber); + rtc::CritScope cs(&lock_); + media_ssrc_ = header.ssrc; + OnPacketArrival(header.extension.transportSequenceNumber, arrival_time_ms); +} + +void RemoteEstimatorProxy::RemoveStream(unsigned int ssrc) {} + +bool RemoteEstimatorProxy::LatestEstimate(std::vector* ssrcs, + unsigned int* bitrate_bps) const { + return false; +} + +bool RemoteEstimatorProxy::GetStats( + ReceiveBandwidthEstimatorStats* output) const { + return false; +} + +void RemoteEstimatorProxy::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) { +} + +int64_t RemoteEstimatorProxy::TimeUntilNextProcess() { + int64_t now = clock_->TimeInMilliseconds(); + int64_t time_until_next = 0; + if (last_process_time_ms_ != -1 && + now - last_process_time_ms_ < kDefaultProcessIntervalMs) { + time_until_next = (last_process_time_ms_ + kDefaultProcessIntervalMs - now); + } + return time_until_next; +} + +int32_t RemoteEstimatorProxy::Process() { + // TODO(sprang): Perhaps we need a dedicated thread here instead? + + if (TimeUntilNextProcess() > 0) + return 0; + last_process_time_ms_ = clock_->TimeInMilliseconds(); + + bool more_to_build = true; + while (more_to_build) { + rtcp::TransportFeedback feedback_packet; + if (BuildFeedbackPacket(&feedback_packet)) { + DCHECK(packet_router_ != nullptr); + packet_router_->SendFeedback(&feedback_packet); + } else { + more_to_build = false; + } + } + + return 0; +} + +void RemoteEstimatorProxy::OnPacketArrival(uint16_t sequence_number, + int64_t arrival_time) { + int64_t seq = unwrapper_.Unwrap(sequence_number); + + if (window_start_seq_ == -1) { + window_start_seq_ = seq; + // Start new feedback packet, cull old packets. + for (auto it = packet_arrival_times_.begin(); + it != packet_arrival_times_.end() && it->first < seq && + arrival_time - it->second >= kBackWindowMs;) { + auto delete_it = it; + ++it; + packet_arrival_times_.erase(delete_it); + } + } else if (seq < window_start_seq_) { + window_start_seq_ = seq; + } + + DCHECK(packet_arrival_times_.end() == packet_arrival_times_.find(seq)); + packet_arrival_times_[seq] = arrival_time; +} + +bool RemoteEstimatorProxy::BuildFeedbackPacket( + rtcp::TransportFeedback* feedback_packet) { + rtc::CritScope cs(&lock_); + if (window_start_seq_ == -1) + return false; + + // window_start_seq_ is the first sequence number to include in the current + // feedback packet. Some older may still be in the map, in case a reordering + // happens and we need to retransmit them. + auto it = packet_arrival_times_.find(window_start_seq_); + DCHECK(it != packet_arrival_times_.end()); + + // TODO(sprang): Measure receive times in microseconds and remove the + // conversions below. + feedback_packet->WithMediaSourceSsrc(media_ssrc_); + feedback_packet->WithBase(static_cast(it->first & 0xFFFF), + it->second * 1000); + feedback_packet->WithFeedbackSequenceNumber(feedback_sequence_++); + for (; it != packet_arrival_times_.end(); ++it) { + if (!feedback_packet->WithReceivedPacket( + static_cast(it->first & 0xFFFF), it->second * 1000)) { + // If we can't even add the first seq to the feedback packet, we won't be + // able to build it at all. + CHECK_NE(window_start_seq_, it->first); + + // Could not add timestamp, feedback packet might be full. Return and + // try again with a fresh packet. + window_start_seq_ = it->first; + break; + } + // Note: Don't erase items from packet_arrival_times_ after sending, in case + // they need to be re-sent after a reordering. Removal will be handled + // by OnPacketArrival once packets are too old. + } + if (it == packet_arrival_times_.end()) + window_start_seq_ = -1; + + return true; +} + +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h b/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h new file mode 100644 index 0000000000..6d7390ec2e --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_ +#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_ + +#include +#include + +#include "webrtc/base/criticalsection.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" + +namespace webrtc { + +class Clock; +class PacketRouter; +namespace rtcp { +class TransportFeedback; +} + +// Class used when send-side BWE is enabled: This proxy is instantiated on the +// receive side. It buffers a number of receive timestamps and then sends +// transport feedback messages back too the send side. + +class RemoteEstimatorProxy : public RemoteBitrateEstimator { + public: + RemoteEstimatorProxy(Clock* clock, PacketRouter* packet_router); + virtual ~RemoteEstimatorProxy(); + + void IncomingPacketFeedbackVector( + const std::vector& packet_feedback_vector) override; + void IncomingPacket(int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header, + bool was_paced) override; + void RemoveStream(unsigned int ssrc) override; + bool LatestEstimate(std::vector* ssrcs, + unsigned int* bitrate_bps) const override; + bool GetStats(ReceiveBandwidthEstimatorStats* output) const override; + void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override; + int64_t TimeUntilNextProcess() override; + int32_t Process() override; + + static const int kDefaultProcessIntervalMs; + static const int kBackWindowMs; + + private: + void OnPacketArrival(uint16_t sequence_number, int64_t arrival_time) + EXCLUSIVE_LOCKS_REQUIRED(&lock_); + bool BuildFeedbackPacket(rtcp::TransportFeedback* feedback_packetket); + + Clock* const clock_; + PacketRouter* const packet_router_; + int64_t last_process_time_ms_; + + rtc::CriticalSection lock_; + + uint32_t media_ssrc_ GUARDED_BY(&lock_); + uint8_t feedback_sequence_ GUARDED_BY(&lock_); + SequenceNumberUnwrapper unwrapper_ GUARDED_BY(&lock_); + int64_t window_start_seq_ GUARDED_BY(&lock_); + // Map unwrapped seq -> time. + std::map packet_arrival_times_ GUARDED_BY(&lock_); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_ diff --git a/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc b/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc new file mode 100644 index 0000000000..5ebd921e7a --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/modules/pacing/include/packet_router.h" +#include "webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "webrtc/system_wrappers/interface/clock.h" + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; + +namespace webrtc { + +class MockPacketRouter : public PacketRouter { + public: + MOCK_METHOD1(SendFeedback, bool(rtcp::TransportFeedback* packet)); +}; + +class RemoteEstimatorProxyTest : public ::testing::Test { + public: + RemoteEstimatorProxyTest() : clock_(0), proxy_(&clock_, &router_) {} + + protected: + void IncomingPacket(uint16_t seq, int64_t time_ms) { + RTPHeader header; + header.extension.hasTransportSequenceNumber = true; + header.extension.transportSequenceNumber = seq; + header.ssrc = kMediaSsrc; + proxy_.IncomingPacket(time_ms, kDefaultPacketSize, header, true); + } + + void Process() { + clock_.AdvanceTimeMilliseconds( + RemoteEstimatorProxy::kDefaultProcessIntervalMs); + proxy_.Process(); + } + + SimulatedClock clock_; + MockPacketRouter router_; + RemoteEstimatorProxy proxy_; + + const size_t kDefaultPacketSize = 100; + const uint32_t kMediaSsrc = 456; + const uint16_t kBaseSeq = 10; + const int64_t kBaseTimeMs = 123; + const int64_t kMaxSmallDeltaMs = + (rtcp::TransportFeedback::kDeltaScaleFactor * 0xFF) / 1000; +}; + +TEST_F(RemoteEstimatorProxyTest, SendsSinglePacketFeedback) { + IncomingPacket(kBaseSeq, kBaseTimeMs); + + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq, packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, packet->GetMediaSourceSsrc()); + + std::vector status_vec = + packet->GetStatusVector(); + EXPECT_EQ(1u, status_vec.size()); + EXPECT_EQ(rtcp::TransportFeedback::StatusSymbol::kReceivedSmallDelta, + status_vec[0]); + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(1u, delta_vec.size()); + EXPECT_EQ(kBaseTimeMs, (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + return true; + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, SendsFeedbackWithVaryingDeltas) { + IncomingPacket(kBaseSeq, kBaseTimeMs); + IncomingPacket(kBaseSeq + 1, kBaseTimeMs + kMaxSmallDeltaMs); + IncomingPacket(kBaseSeq + 2, kBaseTimeMs + (2 * kMaxSmallDeltaMs) + 1); + + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq, packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, packet->GetMediaSourceSsrc()); + + std::vector status_vec = + packet->GetStatusVector(); + EXPECT_EQ(3u, status_vec.size()); + EXPECT_EQ(rtcp::TransportFeedback::StatusSymbol::kReceivedSmallDelta, + status_vec[0]); + EXPECT_EQ(rtcp::TransportFeedback::StatusSymbol::kReceivedSmallDelta, + status_vec[1]); + EXPECT_EQ(rtcp::TransportFeedback::StatusSymbol::kReceivedLargeDelta, + status_vec[2]); + + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(3u, delta_vec.size()); + EXPECT_EQ(kBaseTimeMs, (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + EXPECT_EQ(kMaxSmallDeltaMs, delta_vec[1] / 1000); + EXPECT_EQ(kMaxSmallDeltaMs + 1, delta_vec[2] / 1000); + return true; + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, SendsFragmentedFeedback) { + const int64_t kTooLargeDelta = + rtcp::TransportFeedback::kDeltaScaleFactor * (1 << 16); + + IncomingPacket(kBaseSeq, kBaseTimeMs); + IncomingPacket(kBaseSeq + 1, kBaseTimeMs + kTooLargeDelta); + + InSequence s; + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([kTooLargeDelta, this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq, packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, packet->GetMediaSourceSsrc()); + + std::vector status_vec = + packet->GetStatusVector(); + EXPECT_EQ(1u, status_vec.size()); + EXPECT_EQ(rtcp::TransportFeedback::StatusSymbol::kReceivedSmallDelta, + status_vec[0]); + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(1u, delta_vec.size()); + EXPECT_EQ(kBaseTimeMs, (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + return true; + })) + .RetiresOnSaturation(); + + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([kTooLargeDelta, this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq + 1, packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, packet->GetMediaSourceSsrc()); + + std::vector status_vec = + packet->GetStatusVector(); + EXPECT_EQ(1u, status_vec.size()); + EXPECT_EQ(rtcp::TransportFeedback::StatusSymbol::kReceivedSmallDelta, + status_vec[0]); + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(1u, delta_vec.size()); + EXPECT_EQ(kBaseTimeMs + kTooLargeDelta, + (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + return true; + })) + .RetiresOnSaturation(); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, ResendsTimestampsOnReordering) { + IncomingPacket(kBaseSeq, kBaseTimeMs); + IncomingPacket(kBaseSeq + 2, kBaseTimeMs + 2); + + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq, packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, packet->GetMediaSourceSsrc()); + + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(2u, delta_vec.size()); + EXPECT_EQ(kBaseTimeMs, (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + EXPECT_EQ(2, delta_vec[1] / 1000); + return true; + })); + + Process(); + + IncomingPacket(kBaseSeq + 1, kBaseTimeMs + 1); + + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq + 1, packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, packet->GetMediaSourceSsrc()); + + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(2u, delta_vec.size()); + EXPECT_EQ(kBaseTimeMs + 1, + (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + EXPECT_EQ(1, delta_vec[1] / 1000); + return true; + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, RemovesTimestampsOutOfScope) { + const int64_t kTimeoutTimeMs = + kBaseTimeMs + RemoteEstimatorProxy::kBackWindowMs; + + IncomingPacket(kBaseSeq + 2, kBaseTimeMs); + + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([kTimeoutTimeMs, this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq + 2, packet->GetBaseSequence()); + + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(1u, delta_vec.size()); + EXPECT_EQ(kBaseTimeMs, (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + return true; + })); + + Process(); + + IncomingPacket(kBaseSeq + 3, kTimeoutTimeMs); // kBaseSeq + 2 times out here. + + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([kTimeoutTimeMs, this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq + 3, packet->GetBaseSequence()); + + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(1u, delta_vec.size()); + EXPECT_EQ(kTimeoutTimeMs, + (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + return true; + })); + + Process(); + + // New group, with sequence starting below the first so that they may be + // retransmitted. + IncomingPacket(kBaseSeq, kBaseTimeMs - 1); + IncomingPacket(kBaseSeq + 1, kTimeoutTimeMs - 1); + + EXPECT_CALL(router_, SendFeedback(_)) + .Times(1) + .WillOnce(Invoke([kTimeoutTimeMs, this](rtcp::TransportFeedback* packet) { + packet->Build(); + EXPECT_EQ(kBaseSeq, packet->GetBaseSequence()); + + // Four status entries (kBaseSeq + 3 missing). + EXPECT_EQ(4u, packet->GetStatusVector().size()); + + // Only three actual timestamps. + std::vector delta_vec = packet->GetReceiveDeltasUs(); + EXPECT_EQ(3u, delta_vec.size()); + EXPECT_EQ(kBaseTimeMs - 1, + (packet->GetBaseTimeUs() + delta_vec[0]) / 1000); + EXPECT_EQ(kTimeoutTimeMs - kBaseTimeMs, delta_vec[1] / 1000); + EXPECT_EQ(1, delta_vec[2] / 1000); + return true; + })); + + Process(); +} + +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h index d8f9a9477c..6aa4687f8e 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h @@ -25,6 +25,9 @@ class ReceiveStatistics; class RemoteBitrateEstimator; class RtpReceiver; class Transport; +namespace rtcp { +class TransportFeedback; +} class RtpRtcp : public Module { public: @@ -542,6 +545,8 @@ class RtpRtcp : public Module { RtcpStatisticsCallback* callback) = 0; virtual RtcpStatisticsCallback* GetRtcpStatisticsCallback() = 0; + // BWE feedback packets. + virtual bool SendFeedbackPacket(const rtcp::TransportFeedback& packet) = 0; /************************************************************************** * diff --git a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 13837d3ccd..fe63239d85 100644 --- a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -16,6 +16,7 @@ #include "webrtc/modules/interface/module.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" namespace webrtc { @@ -205,6 +206,7 @@ class MockRtpRtcp : public RtpRtcp { MOCK_CONST_METHOD0(StorePackets, bool()); MOCK_METHOD1(RegisterRtcpStatisticsCallback, void(RtcpStatisticsCallback*)); MOCK_METHOD0(GetRtcpStatisticsCallback, RtcpStatisticsCallback*()); + MOCK_METHOD1(SendFeedbackPacket, bool(const rtcp::TransportFeedback& packet)); MOCK_METHOD1(RegisterAudioCallback, int32_t(RtpAudioFeedback* messagesCallback)); MOCK_METHOD1(SetAudioPacketSize, diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc index fee634b383..975d3f30bc 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc @@ -304,6 +304,13 @@ void TransportFeedback::WithMediaSourceSsrc(uint32_t ssrc) { media_source_ssrc_ = ssrc; } +uint32_t TransportFeedback::GetPacketSenderSsrc() const { + return packet_sender_ssrc_; +} + +uint32_t TransportFeedback::GetMediaSourceSsrc() const { + return media_source_ssrc_; +} void TransportFeedback::WithBase(uint16_t base_sequence, int64_t ref_timestamp_us) { DCHECK_EQ(-1, base_seq_); diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h index 357355d281..1bca184612 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h @@ -52,6 +52,8 @@ class TransportFeedback : public RtcpPacket { // is relative the base time. std::vector GetReceiveDeltasUs() const; + uint32_t GetPacketSenderSsrc() const; + uint32_t GetMediaSourceSsrc() const; static const int kDeltaScaleFactor = 250; // Convert to multiples of 0.25ms. static const uint8_t kFeedbackMessageType = 15; // TODO(sprang): IANA reg? static const uint8_t kPayloadType = 205; // RTPFB, see RFC4585. diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc index eed5cd625f..6040805d16 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc @@ -21,6 +21,7 @@ #include "webrtc/common_types.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/trace_event.h" @@ -1210,4 +1211,29 @@ bool RTCPSender::AllVolatileFlagsConsumed() const { return true; } +bool RTCPSender::SendFeedbackPacket(const rtcp::TransportFeedback& packet) { + CriticalSectionScoped lock(critical_section_transport_.get()); + if (!cbTransport_) + return false; + + class Sender : public rtcp::RtcpPacket::PacketReadyCallback { + public: + Sender(Transport* transport, int32_t id) + : transport_(transport), id_(id), send_failure_(false) {} + + void OnPacketReady(uint8_t* data, size_t length) override { + if (transport_->SendRTCPPacket(id_, data, length) <= 0) + send_failure_ = true; + } + + Transport* const transport_; + int32_t id_; + bool send_failure_; + } sender(cbTransport_, id_); + + uint8_t buffer[IP_PACKET_SIZE]; + return packet.BuildExternalBuffer(buffer, IP_PACKET_SIZE, &sender) && + !sender.send_failure_; +} + } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender.h b/webrtc/modules/rtp_rtcp/source/rtcp_sender.h index 67d58aa8b8..083ce781ae 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_sender.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender.h @@ -33,6 +33,9 @@ namespace webrtc { class ModuleRtpRtcpImpl; class RTCPReceiver; +namespace rtcp { +class TransportFeedback; +} class NACKStringBuilder { public: NACKStringBuilder(); @@ -147,6 +150,7 @@ public: void SetCsrcs(const std::vector& csrcs); void SetTargetBitrate(unsigned int target_bitrate); + bool SendFeedbackPacket(const rtcp::TransportFeedback& packet); private: struct RtcpContext; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index df5fb653a4..523d000e6c 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -768,6 +768,11 @@ RtcpStatisticsCallback* ModuleRtpRtcpImpl::GetRtcpStatisticsCallback() { return rtcp_receiver_.GetRtcpStatisticsCallback(); } +bool ModuleRtpRtcpImpl::SendFeedbackPacket( + const rtcp::TransportFeedback& packet) { + return rtcp_sender_.SendFeedbackPacket(packet); +} + // Send a TelephoneEvent tone using RFC 2833 (4733). int32_t ModuleRtpRtcpImpl::SendTelephoneEventOutband( const uint8_t key, diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h index 5548e540d0..fe694372c9 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -228,6 +228,7 @@ class ModuleRtpRtcpImpl : public RtpRtcp { RtcpStatisticsCallback* callback) override; RtcpStatisticsCallback* GetRtcpStatisticsCallback() override; + bool SendFeedbackPacket(const rtcp::TransportFeedback& packet) override; // (APP) Application specific data. int32_t SetRTCPApplicationSpecificData(uint8_t sub_type, uint32_t name,