| /* |
| * Copyright 2018 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 "api/test/loopback_media_transport.h" |
| |
| #include "absl/algorithm/container.h" |
| #include "absl/memory/memory.h" |
| #include "rtc_base/time_utils.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| |
| // Wrapper used to hand out unique_ptrs to loopback media transports without |
| // ownership changes. |
| class WrapperMediaTransport : public MediaTransportInterface { |
| public: |
| explicit WrapperMediaTransport(MediaTransportInterface* wrapped) |
| : wrapped_(wrapped) {} |
| |
| RTCError SendAudioFrame(uint64_t channel_id, |
| MediaTransportEncodedAudioFrame frame) override { |
| return wrapped_->SendAudioFrame(channel_id, std::move(frame)); |
| } |
| |
| RTCError SendVideoFrame( |
| uint64_t channel_id, |
| const MediaTransportEncodedVideoFrame& frame) override { |
| return wrapped_->SendVideoFrame(channel_id, frame); |
| } |
| |
| void SetKeyFrameRequestCallback( |
| MediaTransportKeyFrameRequestCallback* callback) override { |
| wrapped_->SetKeyFrameRequestCallback(callback); |
| } |
| |
| RTCError RequestKeyFrame(uint64_t channel_id) override { |
| return wrapped_->RequestKeyFrame(channel_id); |
| } |
| |
| void SetReceiveAudioSink(MediaTransportAudioSinkInterface* sink) override { |
| wrapped_->SetReceiveAudioSink(sink); |
| } |
| |
| void SetReceiveVideoSink(MediaTransportVideoSinkInterface* sink) override { |
| wrapped_->SetReceiveVideoSink(sink); |
| } |
| |
| void AddTargetTransferRateObserver( |
| TargetTransferRateObserver* observer) override { |
| wrapped_->AddTargetTransferRateObserver(observer); |
| } |
| |
| void RemoveTargetTransferRateObserver( |
| TargetTransferRateObserver* observer) override { |
| wrapped_->RemoveTargetTransferRateObserver(observer); |
| } |
| |
| void SetMediaTransportStateCallback( |
| MediaTransportStateCallback* callback) override { |
| wrapped_->SetMediaTransportStateCallback(callback); |
| } |
| |
| RTCError OpenChannel(int channel_id) override { |
| return wrapped_->OpenChannel(channel_id); |
| } |
| |
| RTCError SendData(int channel_id, |
| const SendDataParams& params, |
| const rtc::CopyOnWriteBuffer& buffer) override { |
| return wrapped_->SendData(channel_id, params, buffer); |
| } |
| |
| RTCError CloseChannel(int channel_id) override { |
| return wrapped_->CloseChannel(channel_id); |
| } |
| |
| void SetDataSink(DataChannelSink* sink) override { |
| wrapped_->SetDataSink(sink); |
| } |
| |
| void SetAllocatedBitrateLimits( |
| const MediaTransportAllocatedBitrateLimits& limits) override {} |
| |
| absl::optional<std::string> GetTransportParametersOffer() const override { |
| return wrapped_->GetTransportParametersOffer(); |
| } |
| |
| private: |
| MediaTransportInterface* wrapped_; |
| }; |
| |
| } // namespace |
| |
| WrapperMediaTransportFactory::WrapperMediaTransportFactory( |
| MediaTransportInterface* wrapped) |
| : wrapped_(wrapped) {} |
| |
| WrapperMediaTransportFactory::WrapperMediaTransportFactory( |
| MediaTransportFactory* wrapped) |
| : wrapped_factory_(wrapped) {} |
| |
| RTCErrorOr<std::unique_ptr<MediaTransportInterface>> |
| WrapperMediaTransportFactory::CreateMediaTransport( |
| rtc::PacketTransportInternal* packet_transport, |
| rtc::Thread* network_thread, |
| const MediaTransportSettings& settings) { |
| created_transport_count_++; |
| if (wrapped_factory_) { |
| return wrapped_factory_->CreateMediaTransport(packet_transport, |
| network_thread, settings); |
| } |
| return {absl::make_unique<WrapperMediaTransport>(wrapped_)}; |
| } |
| |
| std::string WrapperMediaTransportFactory::GetTransportName() const { |
| if (wrapped_factory_) { |
| return wrapped_factory_->GetTransportName(); |
| } |
| return "wrapped-transport"; |
| } |
| |
| int WrapperMediaTransportFactory::created_transport_count() const { |
| return created_transport_count_; |
| } |
| |
| RTCErrorOr<std::unique_ptr<MediaTransportInterface>> |
| WrapperMediaTransportFactory::CreateMediaTransport( |
| rtc::Thread* network_thread, |
| const MediaTransportSettings& settings) { |
| created_transport_count_++; |
| if (wrapped_factory_) { |
| return wrapped_factory_->CreateMediaTransport(network_thread, settings); |
| } |
| return {absl::make_unique<WrapperMediaTransport>(wrapped_)}; |
| } |
| |
| MediaTransportPair::MediaTransportPair(rtc::Thread* thread) |
| : first_(thread, &second_), |
| second_(thread, &first_), |
| first_factory_(&first_), |
| second_factory_(&second_) {} |
| |
| MediaTransportPair::~MediaTransportPair() = default; |
| |
| MediaTransportPair::LoopbackMediaTransport::LoopbackMediaTransport( |
| rtc::Thread* thread, |
| LoopbackMediaTransport* other) |
| : thread_(thread), other_(other) { |
| RTC_LOG(LS_INFO) << "LoopbackMediaTransport"; |
| } |
| |
| MediaTransportPair::LoopbackMediaTransport::~LoopbackMediaTransport() { |
| RTC_LOG(LS_INFO) << "~LoopbackMediaTransport"; |
| rtc::CritScope lock(&sink_lock_); |
| RTC_CHECK(audio_sink_ == nullptr); |
| RTC_CHECK(video_sink_ == nullptr); |
| RTC_CHECK(data_sink_ == nullptr); |
| RTC_CHECK(target_transfer_rate_observers_.empty()); |
| RTC_CHECK(rtt_observers_.empty()); |
| } |
| |
| absl::optional<std::string> |
| MediaTransportPair::LoopbackMediaTransport::GetTransportParametersOffer() |
| const { |
| return "loopback-media-transport-parameters"; |
| } |
| |
| RTCError MediaTransportPair::LoopbackMediaTransport::SendAudioFrame( |
| uint64_t channel_id, |
| MediaTransportEncodedAudioFrame frame) { |
| { |
| rtc::CritScope lock(&stats_lock_); |
| ++stats_.sent_audio_frames; |
| } |
| invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this, channel_id, frame] { |
| other_->OnData(channel_id, frame); |
| }); |
| return RTCError::OK(); |
| } |
| |
| RTCError MediaTransportPair::LoopbackMediaTransport::SendVideoFrame( |
| uint64_t channel_id, |
| const MediaTransportEncodedVideoFrame& frame) { |
| { |
| rtc::CritScope lock(&stats_lock_); |
| ++stats_.sent_video_frames; |
| } |
| // Ensure that we own the referenced data. |
| MediaTransportEncodedVideoFrame frame_copy = frame; |
| frame_copy.Retain(); |
| invoker_.AsyncInvoke<void>( |
| RTC_FROM_HERE, thread_, [this, channel_id, frame_copy]() mutable { |
| other_->OnData(channel_id, std::move(frame_copy)); |
| }); |
| return RTCError::OK(); |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::SetKeyFrameRequestCallback( |
| MediaTransportKeyFrameRequestCallback* callback) { |
| rtc::CritScope lock(&sink_lock_); |
| if (callback) { |
| RTC_CHECK(key_frame_callback_ == nullptr); |
| } |
| key_frame_callback_ = callback; |
| } |
| |
| RTCError MediaTransportPair::LoopbackMediaTransport::RequestKeyFrame( |
| uint64_t channel_id) { |
| invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this, channel_id] { |
| other_->OnKeyFrameRequested(channel_id); |
| }); |
| return RTCError::OK(); |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::SetReceiveAudioSink( |
| MediaTransportAudioSinkInterface* sink) { |
| rtc::CritScope lock(&sink_lock_); |
| if (sink) { |
| RTC_CHECK(audio_sink_ == nullptr); |
| } |
| audio_sink_ = sink; |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::SetReceiveVideoSink( |
| MediaTransportVideoSinkInterface* sink) { |
| rtc::CritScope lock(&sink_lock_); |
| if (sink) { |
| RTC_CHECK(video_sink_ == nullptr); |
| } |
| video_sink_ = sink; |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::AddTargetTransferRateObserver( |
| TargetTransferRateObserver* observer) { |
| RTC_CHECK(observer); |
| { |
| rtc::CritScope cs(&sink_lock_); |
| RTC_CHECK( |
| !absl::c_linear_search(target_transfer_rate_observers_, observer)); |
| target_transfer_rate_observers_.push_back(observer); |
| } |
| invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this] { |
| RTC_DCHECK_RUN_ON(thread_); |
| const DataRate kBitrate = DataRate::kbps(300); |
| const Timestamp now = Timestamp::us(rtc::TimeMicros()); |
| |
| TargetTransferRate transfer_rate; |
| transfer_rate.at_time = now; |
| transfer_rate.target_rate = kBitrate; |
| transfer_rate.network_estimate.at_time = now; |
| transfer_rate.network_estimate.round_trip_time = TimeDelta::ms(20); |
| transfer_rate.network_estimate.bwe_period = TimeDelta::seconds(3); |
| transfer_rate.network_estimate.bandwidth = kBitrate; |
| |
| rtc::CritScope cs(&sink_lock_); |
| |
| for (auto* o : target_transfer_rate_observers_) { |
| o->OnTargetTransferRate(transfer_rate); |
| } |
| }); |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport:: |
| RemoveTargetTransferRateObserver(TargetTransferRateObserver* observer) { |
| rtc::CritScope cs(&sink_lock_); |
| auto it = absl::c_find(target_transfer_rate_observers_, observer); |
| if (it == target_transfer_rate_observers_.end()) { |
| RTC_LOG(LS_WARNING) |
| << "Attempt to remove an unknown TargetTransferRate observer"; |
| return; |
| } |
| target_transfer_rate_observers_.erase(it); |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::AddRttObserver( |
| MediaTransportRttObserver* observer) { |
| RTC_CHECK(observer); |
| { |
| rtc::CritScope cs(&sink_lock_); |
| RTC_CHECK(!absl::c_linear_search(rtt_observers_, observer)); |
| rtt_observers_.push_back(observer); |
| } |
| invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this] { |
| RTC_DCHECK_RUN_ON(thread_); |
| |
| rtc::CritScope cs(&sink_lock_); |
| for (auto* o : rtt_observers_) { |
| o->OnRttUpdated(20); |
| } |
| }); |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::RemoveRttObserver( |
| MediaTransportRttObserver* observer) { |
| rtc::CritScope cs(&sink_lock_); |
| auto it = absl::c_find(rtt_observers_, observer); |
| if (it == rtt_observers_.end()) { |
| RTC_LOG(LS_WARNING) << "Attempt to remove an unknown RTT observer"; |
| return; |
| } |
| rtt_observers_.erase(it); |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::SetMediaTransportStateCallback( |
| MediaTransportStateCallback* callback) { |
| rtc::CritScope lock(&sink_lock_); |
| state_callback_ = callback; |
| invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this] { |
| RTC_DCHECK_RUN_ON(thread_); |
| OnStateChanged(); |
| }); |
| } |
| |
| RTCError MediaTransportPair::LoopbackMediaTransport::OpenChannel( |
| int channel_id) { |
| // No-op. No need to open channels for the loopback. |
| return RTCError::OK(); |
| } |
| |
| RTCError MediaTransportPair::LoopbackMediaTransport::SendData( |
| int channel_id, |
| const SendDataParams& params, |
| const rtc::CopyOnWriteBuffer& buffer) { |
| invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, |
| [this, channel_id, params, buffer] { |
| other_->OnData(channel_id, params.type, buffer); |
| }); |
| return RTCError::OK(); |
| } |
| |
| RTCError MediaTransportPair::LoopbackMediaTransport::CloseChannel( |
| int channel_id) { |
| invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this, channel_id] { |
| other_->OnRemoteCloseChannel(channel_id); |
| rtc::CritScope lock(&sink_lock_); |
| if (data_sink_) { |
| data_sink_->OnChannelClosed(channel_id); |
| } |
| }); |
| return RTCError::OK(); |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::SetDataSink( |
| DataChannelSink* sink) { |
| rtc::CritScope lock(&sink_lock_); |
| data_sink_ = sink; |
| } |
| void MediaTransportPair::LoopbackMediaTransport::SetState( |
| MediaTransportState state) { |
| invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this, state] { |
| RTC_DCHECK_RUN_ON(thread_); |
| state_ = state; |
| OnStateChanged(); |
| }); |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::FlushAsyncInvokes() { |
| invoker_.Flush(thread_); |
| } |
| |
| MediaTransportPair::Stats |
| MediaTransportPair::LoopbackMediaTransport::GetStats() { |
| rtc::CritScope lock(&stats_lock_); |
| return stats_; |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::OnData( |
| uint64_t channel_id, |
| MediaTransportEncodedAudioFrame frame) { |
| { |
| rtc::CritScope lock(&sink_lock_); |
| if (audio_sink_) { |
| audio_sink_->OnData(channel_id, frame); |
| } |
| } |
| { |
| rtc::CritScope lock(&stats_lock_); |
| ++stats_.received_audio_frames; |
| } |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::OnData( |
| uint64_t channel_id, |
| MediaTransportEncodedVideoFrame frame) { |
| { |
| rtc::CritScope lock(&sink_lock_); |
| if (video_sink_) { |
| video_sink_->OnData(channel_id, frame); |
| } |
| } |
| { |
| rtc::CritScope lock(&stats_lock_); |
| ++stats_.received_video_frames; |
| } |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::OnData( |
| int channel_id, |
| DataMessageType type, |
| const rtc::CopyOnWriteBuffer& buffer) { |
| rtc::CritScope lock(&sink_lock_); |
| if (data_sink_) { |
| data_sink_->OnDataReceived(channel_id, type, buffer); |
| } |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::OnKeyFrameRequested( |
| int channel_id) { |
| rtc::CritScope lock(&sink_lock_); |
| if (key_frame_callback_) { |
| key_frame_callback_->OnKeyFrameRequested(channel_id); |
| } |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::OnRemoteCloseChannel( |
| int channel_id) { |
| rtc::CritScope lock(&sink_lock_); |
| if (data_sink_) { |
| data_sink_->OnChannelClosing(channel_id); |
| data_sink_->OnChannelClosed(channel_id); |
| } |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::OnStateChanged() { |
| rtc::CritScope lock(&sink_lock_); |
| if (state_callback_) { |
| state_callback_->OnStateChanged(state_); |
| } |
| } |
| |
| void MediaTransportPair::LoopbackMediaTransport::SetAllocatedBitrateLimits( |
| const MediaTransportAllocatedBitrateLimits& limits) {} |
| |
| } // namespace webrtc |