| /* |
| * Copyright (c) 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 <utility> |
| |
| #include "absl/memory/memory.h" |
| #include "call/degraded_call.h" |
| #include "rtc_base/location.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| constexpr int64_t kDoNothingProcessIntervalMs = 5000; |
| } // namespace |
| |
| FakeNetworkPipeModule::~FakeNetworkPipeModule() = default; |
| |
| FakeNetworkPipeModule::FakeNetworkPipeModule( |
| Clock* clock, |
| std::unique_ptr<NetworkBehaviorInterface> network_behavior, |
| Transport* transport) |
| : pipe_(clock, std::move(network_behavior), transport) {} |
| |
| void FakeNetworkPipeModule::SendRtp(const uint8_t* packet, |
| size_t length, |
| const PacketOptions& options) { |
| pipe_.SendRtp(packet, length, options); |
| MaybeResumeProcess(); |
| } |
| |
| void FakeNetworkPipeModule::SendRtcp(const uint8_t* packet, size_t length) { |
| pipe_.SendRtcp(packet, length); |
| MaybeResumeProcess(); |
| } |
| |
| void FakeNetworkPipeModule::MaybeResumeProcess() { |
| rtc::CritScope cs(&process_thread_lock_); |
| if (!pending_process_ && pipe_.TimeUntilNextProcess() && process_thread_) { |
| process_thread_->WakeUp(nullptr); |
| } |
| } |
| |
| int64_t FakeNetworkPipeModule::TimeUntilNextProcess() { |
| auto delay = pipe_.TimeUntilNextProcess(); |
| rtc::CritScope cs(&process_thread_lock_); |
| pending_process_ = delay.has_value(); |
| return delay.value_or(kDoNothingProcessIntervalMs); |
| } |
| |
| void FakeNetworkPipeModule::ProcessThreadAttached( |
| ProcessThread* process_thread) { |
| rtc::CritScope cs(&process_thread_lock_); |
| process_thread_ = process_thread; |
| } |
| |
| void FakeNetworkPipeModule::Process() { |
| pipe_.Process(); |
| } |
| |
| DegradedCall::DegradedCall( |
| std::unique_ptr<Call> call, |
| absl::optional<BuiltInNetworkBehaviorConfig> send_config, |
| absl::optional<BuiltInNetworkBehaviorConfig> receive_config) |
| : clock_(Clock::GetRealTimeClock()), |
| call_(std::move(call)), |
| send_config_(send_config), |
| send_process_thread_( |
| send_config_ ? ProcessThread::Create("DegradedSendThread") : nullptr), |
| num_send_streams_(0), |
| receive_config_(receive_config) { |
| if (receive_config_) { |
| auto network = absl::make_unique<SimulatedNetwork>(*receive_config_); |
| receive_simulated_network_ = network.get(); |
| receive_pipe_ = |
| absl::make_unique<webrtc::FakeNetworkPipe>(clock_, std::move(network)); |
| receive_pipe_->SetReceiver(call_->Receiver()); |
| } |
| if (send_process_thread_) { |
| send_process_thread_->Start(); |
| } |
| } |
| |
| DegradedCall::~DegradedCall() { |
| if (send_pipe_) { |
| send_process_thread_->DeRegisterModule(send_pipe_.get()); |
| } |
| if (send_process_thread_) { |
| send_process_thread_->Stop(); |
| } |
| } |
| |
| AudioSendStream* DegradedCall::CreateAudioSendStream( |
| const AudioSendStream::Config& config) { |
| return call_->CreateAudioSendStream(config); |
| } |
| |
| void DegradedCall::DestroyAudioSendStream(AudioSendStream* send_stream) { |
| call_->DestroyAudioSendStream(send_stream); |
| } |
| |
| AudioReceiveStream* DegradedCall::CreateAudioReceiveStream( |
| const AudioReceiveStream::Config& config) { |
| return call_->CreateAudioReceiveStream(config); |
| } |
| |
| void DegradedCall::DestroyAudioReceiveStream( |
| AudioReceiveStream* receive_stream) { |
| call_->DestroyAudioReceiveStream(receive_stream); |
| } |
| |
| VideoSendStream* DegradedCall::CreateVideoSendStream( |
| VideoSendStream::Config config, |
| VideoEncoderConfig encoder_config) { |
| if (send_config_ && !send_pipe_) { |
| auto network = absl::make_unique<SimulatedNetwork>(*send_config_); |
| send_simulated_network_ = network.get(); |
| send_pipe_ = absl::make_unique<FakeNetworkPipeModule>( |
| clock_, std::move(network), config.send_transport); |
| config.send_transport = this; |
| send_process_thread_->RegisterModule(send_pipe_.get(), RTC_FROM_HERE); |
| } |
| ++num_send_streams_; |
| return call_->CreateVideoSendStream(std::move(config), |
| std::move(encoder_config)); |
| } |
| |
| VideoSendStream* DegradedCall::CreateVideoSendStream( |
| VideoSendStream::Config config, |
| VideoEncoderConfig encoder_config, |
| std::unique_ptr<FecController> fec_controller) { |
| if (send_config_ && !send_pipe_) { |
| auto network = absl::make_unique<SimulatedNetwork>(*send_config_); |
| send_simulated_network_ = network.get(); |
| send_pipe_ = absl::make_unique<FakeNetworkPipeModule>( |
| clock_, std::move(network), config.send_transport); |
| config.send_transport = this; |
| send_process_thread_->RegisterModule(send_pipe_.get(), RTC_FROM_HERE); |
| } |
| ++num_send_streams_; |
| return call_->CreateVideoSendStream( |
| std::move(config), std::move(encoder_config), std::move(fec_controller)); |
| } |
| |
| void DegradedCall::DestroyVideoSendStream(VideoSendStream* send_stream) { |
| call_->DestroyVideoSendStream(send_stream); |
| if (send_pipe_ && num_send_streams_ > 0) { |
| --num_send_streams_; |
| if (num_send_streams_ == 0) { |
| send_process_thread_->DeRegisterModule(send_pipe_.get()); |
| send_pipe_.reset(); |
| } |
| } |
| } |
| |
| VideoReceiveStream* DegradedCall::CreateVideoReceiveStream( |
| VideoReceiveStream::Config configuration) { |
| return call_->CreateVideoReceiveStream(std::move(configuration)); |
| } |
| |
| void DegradedCall::DestroyVideoReceiveStream( |
| VideoReceiveStream* receive_stream) { |
| call_->DestroyVideoReceiveStream(receive_stream); |
| } |
| |
| FlexfecReceiveStream* DegradedCall::CreateFlexfecReceiveStream( |
| const FlexfecReceiveStream::Config& config) { |
| return call_->CreateFlexfecReceiveStream(config); |
| } |
| |
| void DegradedCall::DestroyFlexfecReceiveStream( |
| FlexfecReceiveStream* receive_stream) { |
| call_->DestroyFlexfecReceiveStream(receive_stream); |
| } |
| |
| PacketReceiver* DegradedCall::Receiver() { |
| if (receive_config_) { |
| return this; |
| } |
| return call_->Receiver(); |
| } |
| |
| RtpTransportControllerSendInterface* |
| DegradedCall::GetTransportControllerSend() { |
| return call_->GetTransportControllerSend(); |
| } |
| |
| Call::Stats DegradedCall::GetStats() const { |
| return call_->GetStats(); |
| } |
| |
| void DegradedCall::SetBitrateAllocationStrategy( |
| std::unique_ptr<rtc::BitrateAllocationStrategy> |
| bitrate_allocation_strategy) { |
| call_->SetBitrateAllocationStrategy(std::move(bitrate_allocation_strategy)); |
| } |
| |
| void DegradedCall::SignalChannelNetworkState(MediaType media, |
| NetworkState state) { |
| call_->SignalChannelNetworkState(media, state); |
| } |
| |
| void DegradedCall::OnAudioTransportOverheadChanged( |
| int transport_overhead_per_packet) { |
| call_->OnAudioTransportOverheadChanged(transport_overhead_per_packet); |
| } |
| |
| void DegradedCall::OnSentPacket(const rtc::SentPacket& sent_packet) { |
| if (send_config_) { |
| // If we have a degraded send-transport, we have already notified call |
| // about the supposed network send time. Discard the actual network send |
| // time in order to properly fool the BWE. |
| return; |
| } |
| call_->OnSentPacket(sent_packet); |
| } |
| |
| bool DegradedCall::SendRtp(const uint8_t* packet, |
| size_t length, |
| const PacketOptions& options) { |
| // A call here comes from the RTP stack (probably pacer). We intercept it and |
| // put it in the fake network pipe instead, but report to Call that is has |
| // been sent, so that the bandwidth estimator sees the delay we add. |
| send_pipe_->SendRtp(packet, length, options); |
| if (options.packet_id != -1) { |
| rtc::SentPacket sent_packet; |
| sent_packet.packet_id = options.packet_id; |
| sent_packet.send_time_ms = clock_->TimeInMilliseconds(); |
| sent_packet.info.included_in_feedback = options.included_in_feedback; |
| sent_packet.info.included_in_allocation = options.included_in_allocation; |
| sent_packet.info.packet_size_bytes = length; |
| sent_packet.info.packet_type = rtc::PacketType::kData; |
| call_->OnSentPacket(sent_packet); |
| } |
| return true; |
| } |
| |
| bool DegradedCall::SendRtcp(const uint8_t* packet, size_t length) { |
| send_pipe_->SendRtcp(packet, length); |
| return true; |
| } |
| |
| PacketReceiver::DeliveryStatus DegradedCall::DeliverPacket( |
| MediaType media_type, |
| rtc::CopyOnWriteBuffer packet, |
| int64_t packet_time_us) { |
| PacketReceiver::DeliveryStatus status = receive_pipe_->DeliverPacket( |
| media_type, std::move(packet), packet_time_us); |
| // This is not optimal, but there are many places where there are thread |
| // checks that fail if we're not using the worker thread call into this |
| // method. If we want to fix this we probably need a task queue to do handover |
| // of all overriden methods, which feels like overikill for the current use |
| // case. |
| // By just having this thread call out via the Process() method we work around |
| // that, with the tradeoff that a non-zero delay may become a little larger |
| // than anticipated at very low packet rates. |
| receive_pipe_->Process(); |
| return status; |
| } |
| |
| void DegradedCall::MediaTransportChange( |
| MediaTransportInterface* media_transport) { |
| // TODO(bugs.webrtc.org/9719) We should add support for media transport here |
| // at some point. |
| } |
| |
| } // namespace webrtc |