diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc index 83e0318771..d412dd5202 100644 --- a/call/rtp_transport_controller_send.cc +++ b/call/rtp_transport_controller_send.cc @@ -199,13 +199,9 @@ void RtpTransportControllerSend::SetPacingFactor(float pacing_factor) { void RtpTransportControllerSend::SetQueueTimeLimit(int limit_ms) { pacer()->SetQueueTimeLimit(TimeDelta::ms(limit_ms)); } -void RtpTransportControllerSend::RegisterPacketFeedbackObserver( - PacketFeedbackObserver* observer) { - transport_feedback_adapter_.RegisterPacketFeedbackObserver(observer); -} -void RtpTransportControllerSend::DeRegisterPacketFeedbackObserver( - PacketFeedbackObserver* observer) { - transport_feedback_adapter_.DeRegisterPacketFeedbackObserver(observer); +StreamFeedbackProvider* +RtpTransportControllerSend::GetStreamFeedbackProvider() { + return &transport_feedback_adapter_; } void RtpTransportControllerSend::RegisterTargetTransferRateObserver( diff --git a/call/rtp_transport_controller_send.h b/call/rtp_transport_controller_send.h index c9944a75cf..259fbd4c74 100644 --- a/call/rtp_transport_controller_send.h +++ b/call/rtp_transport_controller_send.h @@ -83,10 +83,7 @@ class RtpTransportControllerSend final void SetPacingFactor(float pacing_factor) override; void SetQueueTimeLimit(int limit_ms) override; - void RegisterPacketFeedbackObserver( - PacketFeedbackObserver* observer) override; - void DeRegisterPacketFeedbackObserver( - PacketFeedbackObserver* observer) override; + StreamFeedbackProvider* GetStreamFeedbackProvider() override; void RegisterTargetTransferRateObserver( TargetTransferRateObserver* observer) override; void OnNetworkRouteChanged(const std::string& transport_name, diff --git a/call/rtp_transport_controller_send_interface.h b/call/rtp_transport_controller_send_interface.h index 75677039fc..ba23512827 100644 --- a/call/rtp_transport_controller_send_interface.h +++ b/call/rtp_transport_controller_send_interface.h @@ -44,7 +44,6 @@ class TargetTransferRateObserver; class Transport; class Module; class PacedSender; -class PacketFeedbackObserver; class PacketRouter; class RtpVideoSenderInterface; class RateLimiter; @@ -52,7 +51,6 @@ class RtcpBandwidthObserver; class RtpPacketSender; class SendDelayStats; class SendStatisticsProxy; -class TransportFeedbackObserver; struct RtpSenderObservers { RtcpRttStats* rtcp_rtt_stats; @@ -129,10 +127,12 @@ class RtpTransportControllerSendInterface { virtual void SetPacingFactor(float pacing_factor) = 0; virtual void SetQueueTimeLimit(int limit_ms) = 0; + virtual StreamFeedbackProvider* GetStreamFeedbackProvider() = 0; + // DEPRECATED, use GetStreamFeedbackProvider instead. virtual void RegisterPacketFeedbackObserver( - PacketFeedbackObserver* observer) = 0; + PacketFeedbackObserver* observer) {} virtual void DeRegisterPacketFeedbackObserver( - PacketFeedbackObserver* observer) = 0; + PacketFeedbackObserver* observer) {} virtual void RegisterTargetTransferRateObserver( TargetTransferRateObserver* observer) = 0; virtual void OnNetworkRouteChanged( diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc index 8b340487fd..f9ef569670 100644 --- a/call/rtp_video_sender.cc +++ b/call/rtp_video_sender.cc @@ -399,14 +399,16 @@ RtpVideoSender::RtpVideoSender( fec_controller_->SetProtectionCallback(this); // Signal congestion controller this object is ready for OnPacket* callbacks. - transport_->RegisterPacketFeedbackObserver(this); + transport_->GetStreamFeedbackProvider()->RegisterStreamFeedbackObserver( + rtp_config_.ssrcs, this); } RtpVideoSender::~RtpVideoSender() { for (const RtpStreamSender& stream : rtp_streams_) { transport_->packet_router()->RemoveSendRtpModule(stream.rtp_rtcp.get()); } - transport_->DeRegisterPacketFeedbackObserver(this); + transport_->GetStreamFeedbackProvider()->DeRegisterStreamFeedbackObserver( + this); } void RtpVideoSender::RegisterProcessThread( @@ -810,30 +812,19 @@ void RtpVideoSender::SetFecAllowed(bool fec_allowed) { } void RtpVideoSender::OnPacketFeedbackVector( - const std::vector& packet_feedback_vector) { + std::vector packet_feedback_vector) { if (fec_controller_->UseLossVectorMask()) { rtc::CritScope cs(&crit_); - for (const PacketFeedback& packet : packet_feedback_vector) { - if (packet.send_time_ms == PacketFeedback::kNoSendTime || !packet.ssrc || - absl::c_find(rtp_config_.ssrcs, *packet.ssrc) == - rtp_config_.ssrcs.end()) { - // If packet send time is missing, the feedback for this packet has - // probably already been processed, so ignore it. - // If packet does not belong to a registered media ssrc, we are also - // not interested in it. - continue; - } - loss_mask_vector_.push_back(packet.arrival_time_ms == - PacketFeedback::kNotReceived); + for (const StreamPacketInfo& packet : packet_feedback_vector) { + loss_mask_vector_.push_back(!packet.received); } } // Map from SSRC to all acked packets for that RTP module. std::map> acked_packets_per_ssrc; - for (const PacketFeedback& packet : packet_feedback_vector) { - if (packet.ssrc && packet.arrival_time_ms != PacketFeedback::kNotReceived) { - acked_packets_per_ssrc[*packet.ssrc].push_back( - packet.rtp_sequence_number); + for (const StreamPacketInfo& packet : packet_feedback_vector) { + if (packet.received) { + acked_packets_per_ssrc[packet.ssrc].push_back(packet.rtp_sequence_number); } } @@ -842,25 +833,15 @@ void RtpVideoSender::OnPacketFeedbackVector( // lost by feedback, without being trailed by any received packets. std::map> early_loss_detected_per_ssrc; - for (const PacketFeedback& packet : packet_feedback_vector) { - if (packet.send_time_ms == PacketFeedback::kNoSendTime || !packet.ssrc || - absl::c_find(rtp_config_.ssrcs, *packet.ssrc) == - rtp_config_.ssrcs.end()) { - // If packet send time is missing, the feedback for this packet has - // probably already been processed, so ignore it. - // If packet does not belong to a registered media ssrc, we are also - // not interested in it. - continue; - } - - if (packet.arrival_time_ms == PacketFeedback::kNotReceived) { + for (const StreamPacketInfo& packet : packet_feedback_vector) { + if (!packet.received) { // Last known lost packet, might not be detectable as lost by remote // jitter buffer. - early_loss_detected_per_ssrc[*packet.ssrc].push_back( + early_loss_detected_per_ssrc[packet.ssrc].push_back( packet.rtp_sequence_number); } else { // Packet received, so any loss prior to this is already detectable. - early_loss_detected_per_ssrc.erase(*packet.ssrc); + early_loss_detected_per_ssrc.erase(packet.ssrc); } } diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h index 1568bd9478..fb01f1b263 100644 --- a/call/rtp_video_sender.h +++ b/call/rtp_video_sender.h @@ -71,7 +71,7 @@ struct RtpStreamSender { class RtpVideoSender : public RtpVideoSenderInterface, public OverheadObserver, public VCMProtectionCallback, - public PacketFeedbackObserver { + public StreamFeedbackObserver { public: // Rtp modules are assumed to be sorted in simulcast index order. RtpVideoSender( @@ -147,9 +147,9 @@ class RtpVideoSender : public RtpVideoSenderInterface, uint32_t ssrc, rtc::ArrayView sequence_numbers) const override; - // From PacketFeedbackObserver. + // From StreamFeedbackObserver. void OnPacketFeedbackVector( - const std::vector& packet_feedback_vector) override; + std::vector packet_feedback_vector) override; private: void UpdateModuleSendingState() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); diff --git a/call/rtp_video_sender_unittest.cc b/call/rtp_video_sender_unittest.cc index bac60f8f9f..7df3a474b5 100644 --- a/call/rtp_video_sender_unittest.cc +++ b/call/rtp_video_sender_unittest.cc @@ -434,7 +434,6 @@ TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { EXPECT_EQ( EncodedImageCallback::Result::OK, test.router()->OnEncodedImage(encoded_image, nullptr, nullptr).error); - const int64_t send_time_ms = test.clock().TimeInMilliseconds(); test.clock().AdvanceTimeMilliseconds(33); @@ -472,21 +471,18 @@ TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { // Simulate transport feedback indicating fist packet received, next packet // lost. - PacketFeedback received_packet_feedback(test.clock().TimeInMilliseconds(), - transport_sequence_numbers[0]); + StreamFeedbackObserver::StreamPacketInfo received_packet_feedback; received_packet_feedback.rtp_sequence_number = rtp_sequence_numbers[0]; received_packet_feedback.ssrc = kSsrc1; - received_packet_feedback.send_time_ms = send_time_ms; + received_packet_feedback.received = true; - PacketFeedback lost_packet_feedback(PacketFeedback::kNotReceived, - transport_sequence_numbers[1]); + StreamFeedbackObserver::StreamPacketInfo lost_packet_feedback; lost_packet_feedback.rtp_sequence_number = rtp_sequence_numbers[1]; lost_packet_feedback.ssrc = kSsrc1; - lost_packet_feedback.send_time_ms = send_time_ms; - std::vector feedback_vector = {received_packet_feedback, - lost_packet_feedback}; + lost_packet_feedback.received = false; - test.router()->OnPacketFeedbackVector(feedback_vector); + test.router()->OnPacketFeedbackVector( + {received_packet_feedback, lost_packet_feedback}); // Advance time to make sure retransmission would be allowed and try again. // This time the retransmission should not happen for the first packet since @@ -555,7 +551,6 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { ->OnEncodedImage(encoded_image, &codec_specific, nullptr) .error, EncodedImageCallback::Result::OK); - const int64_t send_time_ms = test.clock().TimeInMilliseconds(); test.clock().AdvanceTimeMilliseconds(33); ASSERT_TRUE(event.Wait(kTimeoutMs)); @@ -593,7 +588,7 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet, length)); - EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc2); + EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); // Retransmitted sequence number from the RTX header should match // the lost packet. @@ -604,22 +599,18 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { return true; }); - PacketFeedback first_packet_feedback(PacketFeedback::kNotReceived, - frame1_transport_sequence_number); + StreamFeedbackObserver::StreamPacketInfo first_packet_feedback; first_packet_feedback.rtp_sequence_number = frame1_rtp_sequence_number; first_packet_feedback.ssrc = kSsrc1; - first_packet_feedback.send_time_ms = send_time_ms; + first_packet_feedback.received = false; - PacketFeedback second_packet_feedback(test.clock().TimeInMilliseconds(), - frame2_transport_sequence_number); - first_packet_feedback.rtp_sequence_number = frame2_rtp_sequence_number; - first_packet_feedback.ssrc = kSsrc2; - first_packet_feedback.send_time_ms = send_time_ms + 33; + StreamFeedbackObserver::StreamPacketInfo second_packet_feedback; + second_packet_feedback.rtp_sequence_number = frame2_rtp_sequence_number; + second_packet_feedback.ssrc = kSsrc2; + second_packet_feedback.received = true; - std::vector feedback_vector = {first_packet_feedback, - second_packet_feedback}; - - test.router()->OnPacketFeedbackVector(feedback_vector); + test.router()->OnPacketFeedbackVector( + {first_packet_feedback, second_packet_feedback}); // Wait for pacer to run and send the RTX packet. test.clock().AdvanceTimeMilliseconds(33); diff --git a/call/test/mock_rtp_transport_controller_send.h b/call/test/mock_rtp_transport_controller_send.h index b6948f4210..04dac29f33 100644 --- a/call/test/mock_rtp_transport_controller_send.h +++ b/call/test/mock_rtp_transport_controller_send.h @@ -52,8 +52,7 @@ class MockRtpTransportControllerSend MOCK_METHOD1(SetAllocatedSendBitrateLimits, void(BitrateAllocationLimits)); MOCK_METHOD1(SetPacingFactor, void(float)); MOCK_METHOD1(SetQueueTimeLimit, void(int)); - MOCK_METHOD1(RegisterPacketFeedbackObserver, void(PacketFeedbackObserver*)); - MOCK_METHOD1(DeRegisterPacketFeedbackObserver, void(PacketFeedbackObserver*)); + MOCK_METHOD0(GetStreamFeedbackProvider, StreamFeedbackProvider*()); MOCK_METHOD1(RegisterTargetTransferRateObserver, void(TargetTransferRateObserver*)); MOCK_METHOD2(OnNetworkRouteChanged, diff --git a/modules/congestion_controller/rtp/BUILD.gn b/modules/congestion_controller/rtp/BUILD.gn index 3e21c3a414..5aeefcf8a8 100644 --- a/modules/congestion_controller/rtp/BUILD.gn +++ b/modules/congestion_controller/rtp/BUILD.gn @@ -58,6 +58,7 @@ rtc_library("transport_feedback") { "../../../system_wrappers", "../../../system_wrappers:field_trial", "../../rtp_rtcp:rtp_rtcp_format", + "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/types:optional", ] } diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.cc b/modules/congestion_controller/rtp/transport_feedback_adapter.cc index 20a8566bdb..676d7c2d01 100644 --- a/modules/congestion_controller/rtp/transport_feedback_adapter.cc +++ b/modules/congestion_controller/rtp/transport_feedback_adapter.cc @@ -16,6 +16,7 @@ #include #include +#include "absl/algorithm/container.h" #include "api/units/timestamp.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" @@ -57,20 +58,23 @@ TransportFeedbackAdapter::~TransportFeedbackAdapter() { RTC_DCHECK(observers_.empty()); } -void TransportFeedbackAdapter::RegisterPacketFeedbackObserver( - PacketFeedbackObserver* observer) { +void TransportFeedbackAdapter::RegisterStreamFeedbackObserver( + std::vector ssrcs, + StreamFeedbackObserver* observer) { rtc::CritScope cs(&observers_lock_); RTC_DCHECK(observer); - RTC_DCHECK(std::find(observers_.begin(), observers_.end(), observer) == - observers_.end()); - observers_.push_back(observer); + RTC_DCHECK(absl::c_find_if(observers_, [=](const auto& pair) { + return pair.second == observer; + }) == observers_.end()); + observers_.push_back({ssrcs, observer}); } -void TransportFeedbackAdapter::DeRegisterPacketFeedbackObserver( - PacketFeedbackObserver* observer) { +void TransportFeedbackAdapter::DeRegisterStreamFeedbackObserver( + StreamFeedbackObserver* observer) { rtc::CritScope cs(&observers_lock_); RTC_DCHECK(observer); - const auto it = std::find(observers_.begin(), observers_.end(), observer); + const auto it = absl::c_find_if( + observers_, [=](const auto& pair) { return pair.second == observer; }); RTC_DCHECK(it != observers_.end()); observers_.erase(it); } @@ -157,8 +161,35 @@ TransportFeedbackAdapter::ProcessTransportFeedback( GetPacketFeedbackVector(feedback, feedback_receive_time); { rtc::CritScope cs(&observers_lock_); - for (auto* observer : observers_) { - observer->OnPacketFeedbackVector(last_packet_feedback_vector_); + for (auto& observer : observers_) { + std::vector selected_feedback; + for (const auto& packet : last_packet_feedback_vector_) { + if (packet.ssrc && absl::c_count(observer.first, *packet.ssrc) > 0) { + // If we found the ssrc, it means the the packet was in the + // history and we expect the the send time has been set. A reason why + // this would be false would be if ProcessTransportFeedback covering a + // packet would be called before ProcessSentPacket for the same + // packet. This should not happen if we handle ordering of events + // correctly. + // TODO(srte): Fix the tests that makes this happen and make this a + // DCHECK. + if (packet.send_time_ms == PacketFeedback::kNoSendTime) { + RTC_LOG(LS_ERROR) + << "Received feedback before packet was indicated as sent"; + continue; + } + + StreamFeedbackObserver::StreamPacketInfo feedback_info; + feedback_info.ssrc = *packet.ssrc; + feedback_info.rtp_sequence_number = packet.rtp_sequence_number; + feedback_info.received = + packet.arrival_time_ms != PacketFeedback::kNotReceived; + selected_feedback.push_back(feedback_info); + } + } + if (!selected_feedback.empty()) { + observer.second->OnPacketFeedbackVector(std::move(selected_feedback)); + } } } diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.h b/modules/congestion_controller/rtp/transport_feedback_adapter.h index edd3fb86c3..7a0e9bf6eb 100644 --- a/modules/congestion_controller/rtp/transport_feedback_adapter.h +++ b/modules/congestion_controller/rtp/transport_feedback_adapter.h @@ -26,20 +26,16 @@ namespace webrtc { -class PacketFeedbackObserver; -struct RtpPacketSendInfo; - -namespace rtcp { -class TransportFeedback; -} // namespace rtcp - -class TransportFeedbackAdapter { +class TransportFeedbackAdapter : public StreamFeedbackProvider { public: TransportFeedbackAdapter(); virtual ~TransportFeedbackAdapter(); - void RegisterPacketFeedbackObserver(PacketFeedbackObserver* observer); - void DeRegisterPacketFeedbackObserver(PacketFeedbackObserver* observer); + void RegisterStreamFeedbackObserver( + std::vector ssrcs, + StreamFeedbackObserver* observer) override; + void DeRegisterStreamFeedbackObserver( + StreamFeedbackObserver* observer) override; void AddPacket(const RtpPacketSendInfo& packet_info, size_t overhead_bytes, @@ -99,8 +95,11 @@ class TransportFeedbackAdapter { uint16_t remote_net_id_ RTC_GUARDED_BY(&lock_); rtc::CriticalSection observers_lock_; - std::vector observers_ - RTC_GUARDED_BY(&observers_lock_); + // Maps a set of ssrcs to corresponding observer. Vectors are used rather than + // set/map to ensure that the processing order is consistent independently of + // the randomized ssrcs. + std::vector, StreamFeedbackObserver*>> + observers_ RTC_GUARDED_BY(&observers_lock_); }; } // namespace webrtc diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc b/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc index ff08cea208..91e6fc9e02 100644 --- a/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc +++ b/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc @@ -40,10 +40,10 @@ const PacedPacketInfo kPacingInfo4(4, 22, 10000); namespace test { -class MockPacketFeedbackObserver : public webrtc::PacketFeedbackObserver { +class MockStreamFeedbackObserver : public webrtc::StreamFeedbackObserver { public: MOCK_METHOD1(OnPacketFeedbackVector, - void(const std::vector& packet_feedback_vector)); + void(std::vector packet_feedback_vector)); }; class TransportFeedbackAdapterTest : public ::testing::Test { @@ -85,14 +85,25 @@ class TransportFeedbackAdapterTest : public ::testing::Test { }; TEST_F(TransportFeedbackAdapterTest, ObserverSanity) { - MockPacketFeedbackObserver mock; - adapter_->RegisterPacketFeedbackObserver(&mock); + const uint32_t kSsrc = 8832; + MockStreamFeedbackObserver mock; + adapter_->RegisterStreamFeedbackObserver({kSsrc}, &mock); const std::vector packets = { PacketFeedback(100, 200, 0, 1000, kPacingInfo0), PacketFeedback(110, 210, 1, 2000, kPacingInfo0), PacketFeedback(120, 220, 2, 3000, kPacingInfo0)}; - + for (auto& packet : packets) { + const size_t kOverhead = 40; + RtpPacketSendInfo send_info; + send_info.ssrc = kSsrc; + send_info.pacing_info = packet.pacing_info; + send_info.has_rtp_sequence_number = true; + send_info.length = packet.payload_size; + send_info.rtp_sequence_number = packet.rtp_sequence_number; + send_info.rtp_sequence_number = packet.sequence_number; + adapter_->AddPacket(send_info, kOverhead, clock_.CurrentTime()); + } rtcp::TransportFeedback feedback; feedback.SetBase(packets[0].sequence_number, packets[0].arrival_time_ms * 1000); @@ -107,7 +118,7 @@ TEST_F(TransportFeedbackAdapterTest, ObserverSanity) { adapter_->ProcessTransportFeedback( feedback, Timestamp::ms(clock_.TimeInMilliseconds())); - adapter_->DeRegisterPacketFeedbackObserver(&mock); + adapter_->DeRegisterStreamFeedbackObserver(&mock); const PacketFeedback new_packet(130, 230, 3, 4000, kPacingInfo0); OnSentPacket(new_packet); @@ -124,17 +135,17 @@ TEST_F(TransportFeedbackAdapterTest, ObserverSanity) { #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) TEST_F(TransportFeedbackAdapterTest, ObserverDoubleRegistrationDeathTest) { - MockPacketFeedbackObserver mock; - adapter_->RegisterPacketFeedbackObserver(&mock); - EXPECT_DEATH(adapter_->RegisterPacketFeedbackObserver(&mock), ""); - adapter_->DeRegisterPacketFeedbackObserver(&mock); + MockStreamFeedbackObserver mock; + adapter_->RegisterStreamFeedbackObserver({0}, &mock); + EXPECT_DEATH(adapter_->RegisterStreamFeedbackObserver({0}, &mock), ""); + adapter_->DeRegisterStreamFeedbackObserver(&mock); } TEST_F(TransportFeedbackAdapterTest, ObserverMissingDeRegistrationDeathTest) { - MockPacketFeedbackObserver mock; - adapter_->RegisterPacketFeedbackObserver(&mock); + MockStreamFeedbackObserver mock; + adapter_->RegisterStreamFeedbackObserver({0}, &mock); EXPECT_DEATH(adapter_.reset(), ""); - adapter_->DeRegisterPacketFeedbackObserver(&mock); + adapter_->DeRegisterStreamFeedbackObserver(&mock); } #endif diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/modules/rtp_rtcp/include/rtp_rtcp_defines.h index 8181e9bd33..3d13027994 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp_defines.h +++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.h @@ -320,6 +320,7 @@ class RtcpFeedbackSenderInterface { virtual void UnsetRemb() = 0; }; +// DEPRECATED: To be removed when usages have been removed. class PacketFeedbackObserver { public: virtual ~PacketFeedbackObserver() = default; @@ -331,6 +332,29 @@ class PacketFeedbackObserver { const std::vector& packet_feedback_vector) = 0; }; +class StreamFeedbackObserver { + public: + struct StreamPacketInfo { + uint32_t ssrc; + uint16_t rtp_sequence_number; + bool received; + }; + virtual ~StreamFeedbackObserver() = default; + + virtual void OnPacketFeedbackVector( + std::vector packet_feedback_vector) = 0; +}; + +class StreamFeedbackProvider { + public: + virtual void RegisterStreamFeedbackObserver( + std::vector ssrcs, + StreamFeedbackObserver* observer) = 0; + virtual void DeRegisterStreamFeedbackObserver( + StreamFeedbackObserver* observer) = 0; + virtual ~StreamFeedbackProvider() = default; +}; + class RtcpRttStats { public: virtual void OnRttUpdate(int64_t rtt) = 0;