| /* |
| * Copyright (c) 2020 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 "audio/voip/audio_channel.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "api/audio_codecs/audio_format.h" |
| #include "api/task_queue/task_queue_factory.h" |
| #include "modules/rtp_rtcp/include/receive_statistics.h" |
| #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" |
| #include "rtc_base/logging.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| |
| constexpr int kRtcpReportIntervalMs = 5000; |
| |
| } // namespace |
| |
| AudioChannel::AudioChannel( |
| Transport* transport, |
| uint32_t local_ssrc, |
| TaskQueueFactory* task_queue_factory, |
| AudioMixer* audio_mixer, |
| rtc::scoped_refptr<AudioDecoderFactory> decoder_factory) |
| : audio_mixer_(audio_mixer) { |
| RTC_DCHECK(task_queue_factory); |
| RTC_DCHECK(audio_mixer); |
| |
| Clock* clock = Clock::GetRealTimeClock(); |
| receive_statistics_ = ReceiveStatistics::Create(clock); |
| |
| RtpRtcpInterface::Configuration rtp_config; |
| rtp_config.clock = clock; |
| rtp_config.audio = true; |
| rtp_config.receive_statistics = receive_statistics_.get(); |
| rtp_config.rtcp_report_interval_ms = kRtcpReportIntervalMs; |
| rtp_config.outgoing_transport = transport; |
| rtp_config.local_media_ssrc = local_ssrc; |
| |
| rtp_rtcp_ = ModuleRtpRtcpImpl2::Create(rtp_config); |
| |
| rtp_rtcp_->SetSendingMediaStatus(false); |
| rtp_rtcp_->SetRTCPStatus(RtcpMode::kCompound); |
| |
| ingress_ = std::make_unique<AudioIngress>(rtp_rtcp_.get(), clock, |
| receive_statistics_.get(), |
| std::move(decoder_factory)); |
| egress_ = |
| std::make_unique<AudioEgress>(rtp_rtcp_.get(), clock, task_queue_factory); |
| |
| // Set the instance of audio ingress to be part of audio mixer for ADM to |
| // fetch audio samples to play. |
| audio_mixer_->AddSource(ingress_.get()); |
| } |
| |
| AudioChannel::~AudioChannel() { |
| if (egress_->IsSending()) { |
| StopSend(); |
| } |
| if (ingress_->IsPlaying()) { |
| StopPlay(); |
| } |
| |
| audio_mixer_->RemoveSource(ingress_.get()); |
| |
| // TODO(bugs.webrtc.org/11581): unclear if we still need to clear `egress_` |
| // here. |
| egress_.reset(); |
| ingress_.reset(); |
| } |
| |
| bool AudioChannel::StartSend() { |
| // If encoder has not been set, return false. |
| if (!egress_->StartSend()) { |
| return false; |
| } |
| |
| // Start sending with RTP stack if it has not been sending yet. |
| if (!rtp_rtcp_->Sending()) { |
| rtp_rtcp_->SetSendingStatus(true); |
| } |
| return true; |
| } |
| |
| void AudioChannel::StopSend() { |
| egress_->StopSend(); |
| |
| // Deactivate RTP stack when both sending and receiving are stopped. |
| // SetSendingStatus(false) triggers the transmission of RTCP BYE |
| // message to remote endpoint. |
| if (!ingress_->IsPlaying() && rtp_rtcp_->Sending()) { |
| rtp_rtcp_->SetSendingStatus(false); |
| } |
| } |
| |
| bool AudioChannel::StartPlay() { |
| // If decoders have not been set, return false. |
| if (!ingress_->StartPlay()) { |
| return false; |
| } |
| |
| // If RTP stack is not sending then start sending as in recv-only mode, RTCP |
| // receiver report is expected. |
| if (!rtp_rtcp_->Sending()) { |
| rtp_rtcp_->SetSendingStatus(true); |
| } |
| return true; |
| } |
| |
| void AudioChannel::StopPlay() { |
| ingress_->StopPlay(); |
| |
| // Deactivate RTP stack only when both sending and receiving are stopped. |
| if (!rtp_rtcp_->SendingMedia() && rtp_rtcp_->Sending()) { |
| rtp_rtcp_->SetSendingStatus(false); |
| } |
| } |
| |
| IngressStatistics AudioChannel::GetIngressStatistics() { |
| IngressStatistics ingress_stats; |
| NetworkStatistics stats = ingress_->GetNetworkStatistics(); |
| ingress_stats.neteq_stats.total_samples_received = stats.totalSamplesReceived; |
| ingress_stats.neteq_stats.concealed_samples = stats.concealedSamples; |
| ingress_stats.neteq_stats.concealment_events = stats.concealmentEvents; |
| ingress_stats.neteq_stats.jitter_buffer_delay_ms = stats.jitterBufferDelayMs; |
| ingress_stats.neteq_stats.jitter_buffer_emitted_count = |
| stats.jitterBufferEmittedCount; |
| ingress_stats.neteq_stats.jitter_buffer_target_delay_ms = |
| stats.jitterBufferTargetDelayMs; |
| ingress_stats.neteq_stats.inserted_samples_for_deceleration = |
| stats.insertedSamplesForDeceleration; |
| ingress_stats.neteq_stats.removed_samples_for_acceleration = |
| stats.removedSamplesForAcceleration; |
| ingress_stats.neteq_stats.silent_concealed_samples = |
| stats.silentConcealedSamples; |
| ingress_stats.neteq_stats.fec_packets_received = stats.fecPacketsReceived; |
| ingress_stats.neteq_stats.fec_packets_discarded = stats.fecPacketsDiscarded; |
| ingress_stats.neteq_stats.delayed_packet_outage_samples = |
| stats.delayedPacketOutageSamples; |
| ingress_stats.neteq_stats.relative_packet_arrival_delay_ms = |
| stats.relativePacketArrivalDelayMs; |
| ingress_stats.neteq_stats.interruption_count = stats.interruptionCount; |
| ingress_stats.neteq_stats.total_interruption_duration_ms = |
| stats.totalInterruptionDurationMs; |
| ingress_stats.total_duration = ingress_->GetOutputTotalDuration(); |
| return ingress_stats; |
| } |
| |
| ChannelStatistics AudioChannel::GetChannelStatistics() { |
| ChannelStatistics channel_stat = ingress_->GetChannelStatistics(); |
| |
| StreamDataCounters rtp_stats, rtx_stats; |
| rtp_rtcp_->GetSendStreamDataCounters(&rtp_stats, &rtx_stats); |
| channel_stat.bytes_sent = |
| rtp_stats.transmitted.payload_bytes + rtx_stats.transmitted.payload_bytes; |
| channel_stat.packets_sent = |
| rtp_stats.transmitted.packets + rtx_stats.transmitted.packets; |
| |
| return channel_stat; |
| } |
| |
| } // namespace webrtc |