1c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg/* 2c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg * 4c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg * Use of this source code is governed by a BSD-style license 5c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg * that can be found in the LICENSE file in the root of the source 6c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg * tree. An additional intellectual property rights grant can be found 7c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg * in the file PATENTS. All contributing project authors may 8c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg * be found in the AUTHORS file in the root of the source tree. 9c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg */ 10c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 11c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg#include "webrtc/audio/audio_send_stream.h" 12c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 13c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg#include <string> 14c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 15566ef247b9779f6c9d0e7ec9eea6b037f4682c53solenberg#include "webrtc/audio/audio_state.h" 1685a0496b8c4ac01da7c716ea7950093659864c8esolenberg#include "webrtc/audio/conversion.h" 17566ef247b9779f6c9d0e7ec9eea6b037f4682c53solenberg#include "webrtc/audio/scoped_voe_interface.h" 18c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg#include "webrtc/base/checks.h" 19c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg#include "webrtc/base/logging.h" 20b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer#include "webrtc/call/congestion_controller.h" 21b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer#include "webrtc/modules/pacing/paced_sender.h" 22b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" 2313725089ef91f932b37b2447c3f05d9cd9f89984solenberg#include "webrtc/voice_engine/channel_proxy.h" 2485a0496b8c4ac01da7c716ea7950093659864c8esolenberg#include "webrtc/voice_engine/include/voe_audio_processing.h" 2585a0496b8c4ac01da7c716ea7950093659864c8esolenberg#include "webrtc/voice_engine/include/voe_codec.h" 2685a0496b8c4ac01da7c716ea7950093659864c8esolenberg#include "webrtc/voice_engine/include/voe_rtp_rtcp.h" 2785a0496b8c4ac01da7c716ea7950093659864c8esolenberg#include "webrtc/voice_engine/include/voe_volume_control.h" 2813725089ef91f932b37b2447c3f05d9cd9f89984solenberg#include "webrtc/voice_engine/voice_engine_impl.h" 29c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 30c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenbergnamespace webrtc { 31c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenbergstd::string AudioSendStream::Config::Rtp::ToString() const { 32c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg std::stringstream ss; 33c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << "{ssrc: " << ssrc; 34c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << ", extensions: ["; 35c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg for (size_t i = 0; i < extensions.size(); ++i) { 36c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << extensions[i].ToString(); 3785a0496b8c4ac01da7c716ea7950093659864c8esolenberg if (i != extensions.size() - 1) { 38c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << ", "; 3985a0496b8c4ac01da7c716ea7950093659864c8esolenberg } 40c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg } 41c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << ']'; 423a94154035fa16e4efd91125311f076b547c38b9solenberg ss << ", c_name: " << c_name; 43c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << '}'; 44c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg return ss.str(); 45c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg} 46c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 47c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenbergstd::string AudioSendStream::Config::ToString() const { 48c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg std::stringstream ss; 49c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << "{rtp: " << rtp.ToString(); 50c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << ", voe_channel_id: " << voe_channel_id; 51c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg // TODO(solenberg): Encoder config. 52c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << ", cng_payload_type: " << cng_payload_type; 53c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << ", red_payload_type: " << red_payload_type; 54c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg ss << '}'; 55c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg return ss.str(); 56c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg} 57c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 58c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenbergnamespace internal { 59566ef247b9779f6c9d0e7ec9eea6b037f4682c53solenbergAudioSendStream::AudioSendStream( 60566ef247b9779f6c9d0e7ec9eea6b037f4682c53solenberg const webrtc::AudioSendStream::Config& config, 61b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer const rtc::scoped_refptr<webrtc::AudioState>& audio_state, 62b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer CongestionController* congestion_controller) 63566ef247b9779f6c9d0e7ec9eea6b037f4682c53solenberg : config_(config), audio_state_(audio_state) { 64c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg LOG(LS_INFO) << "AudioSendStream: " << config_.ToString(); 65566ef247b9779f6c9d0e7ec9eea6b037f4682c53solenberg RTC_DCHECK_NE(config_.voe_channel_id, -1); 66566ef247b9779f6c9d0e7ec9eea6b037f4682c53solenberg RTC_DCHECK(audio_state_.get()); 67b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer RTC_DCHECK(congestion_controller); 683a94154035fa16e4efd91125311f076b547c38b9solenberg 6913725089ef91f932b37b2447c3f05d9cd9f89984solenberg VoiceEngineImpl* voe_impl = static_cast<VoiceEngineImpl*>(voice_engine()); 7013725089ef91f932b37b2447c3f05d9cd9f89984solenberg channel_proxy_ = voe_impl->GetChannelProxy(config_.voe_channel_id); 71b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer channel_proxy_->SetCongestionControlObjects( 72b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer congestion_controller->pacer(), 73b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer congestion_controller->GetTransportFeedbackObserver(), 74b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer congestion_controller->packet_router()); 7513725089ef91f932b37b2447c3f05d9cd9f89984solenberg channel_proxy_->SetRTCPStatus(true); 7613725089ef91f932b37b2447c3f05d9cd9f89984solenberg channel_proxy_->SetLocalSSRC(config.rtp.ssrc); 7713725089ef91f932b37b2447c3f05d9cd9f89984solenberg channel_proxy_->SetRTCP_CNAME(config.rtp.c_name); 78b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer 793a94154035fa16e4efd91125311f076b547c38b9solenberg for (const auto& extension : config.rtp.extensions) { 803a94154035fa16e4efd91125311f076b547c38b9solenberg if (extension.name == RtpExtension::kAbsSendTime) { 81358057b945725390bcecc330513160aa823f651esolenberg channel_proxy_->SetSendAbsoluteSenderTimeStatus(true, extension.id); 823a94154035fa16e4efd91125311f076b547c38b9solenberg } else if (extension.name == RtpExtension::kAudioLevel) { 83358057b945725390bcecc330513160aa823f651esolenberg channel_proxy_->SetSendAudioLevelIndicationStatus(true, extension.id); 84b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer } else if (extension.name == RtpExtension::kTransportSequenceNumber) { 85b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer channel_proxy_->EnableSendTransportSequenceNumber(extension.id); 863a94154035fa16e4efd91125311f076b547c38b9solenberg } else { 873a94154035fa16e4efd91125311f076b547c38b9solenberg RTC_NOTREACHED() << "Registering unsupported RTP extension."; 883a94154035fa16e4efd91125311f076b547c38b9solenberg } 893a94154035fa16e4efd91125311f076b547c38b9solenberg } 90c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg} 91c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 92c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenbergAudioSendStream::~AudioSendStream() { 9385a0496b8c4ac01da7c716ea7950093659864c8esolenberg RTC_DCHECK(thread_checker_.CalledOnValidThread()); 94c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg LOG(LS_INFO) << "~AudioSendStream: " << config_.ToString(); 95b86d4e4a8dec1eb1b801244a2a97cda66f561d8eStefan Holmer channel_proxy_->SetCongestionControlObjects(nullptr, nullptr, nullptr); 96c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg} 97c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 983a94154035fa16e4efd91125311f076b547c38b9solenbergvoid AudioSendStream::Start() { 993a94154035fa16e4efd91125311f076b547c38b9solenberg RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1003a94154035fa16e4efd91125311f076b547c38b9solenberg} 1013a94154035fa16e4efd91125311f076b547c38b9solenberg 1023a94154035fa16e4efd91125311f076b547c38b9solenbergvoid AudioSendStream::Stop() { 1033a94154035fa16e4efd91125311f076b547c38b9solenberg RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1043a94154035fa16e4efd91125311f076b547c38b9solenberg} 1053a94154035fa16e4efd91125311f076b547c38b9solenberg 1063a94154035fa16e4efd91125311f076b547c38b9solenbergvoid AudioSendStream::SignalNetworkState(NetworkState state) { 1073a94154035fa16e4efd91125311f076b547c38b9solenberg RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1083a94154035fa16e4efd91125311f076b547c38b9solenberg} 1093a94154035fa16e4efd91125311f076b547c38b9solenberg 1103a94154035fa16e4efd91125311f076b547c38b9solenbergbool AudioSendStream::DeliverRtcp(const uint8_t* packet, size_t length) { 1113a94154035fa16e4efd91125311f076b547c38b9solenberg // TODO(solenberg): Tests call this function on a network thread, libjingle 1123a94154035fa16e4efd91125311f076b547c38b9solenberg // calls on the worker thread. We should move towards always using a network 1133a94154035fa16e4efd91125311f076b547c38b9solenberg // thread. Then this check can be enabled. 1143a94154035fa16e4efd91125311f076b547c38b9solenberg // RTC_DCHECK(!thread_checker_.CalledOnValidThread()); 1153a94154035fa16e4efd91125311f076b547c38b9solenberg return false; 1163a94154035fa16e4efd91125311f076b547c38b9solenberg} 1173a94154035fa16e4efd91125311f076b547c38b9solenberg 118b572768efbc1e52b97a5ad98932c667956aba4b8Fredrik Solenbergbool AudioSendStream::SendTelephoneEvent(int payload_type, uint8_t event, 119b572768efbc1e52b97a5ad98932c667956aba4b8Fredrik Solenberg uint32_t duration_ms) { 120b572768efbc1e52b97a5ad98932c667956aba4b8Fredrik Solenberg RTC_DCHECK(thread_checker_.CalledOnValidThread()); 121b572768efbc1e52b97a5ad98932c667956aba4b8Fredrik Solenberg return channel_proxy_->SetSendTelephoneEventPayloadType(payload_type) && 122b572768efbc1e52b97a5ad98932c667956aba4b8Fredrik Solenberg channel_proxy_->SendTelephoneEventOutband(event, duration_ms); 123b572768efbc1e52b97a5ad98932c667956aba4b8Fredrik Solenberg} 124b572768efbc1e52b97a5ad98932c667956aba4b8Fredrik Solenberg 125c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenbergwebrtc::AudioSendStream::Stats AudioSendStream::GetStats() const { 12685a0496b8c4ac01da7c716ea7950093659864c8esolenberg RTC_DCHECK(thread_checker_.CalledOnValidThread()); 12785a0496b8c4ac01da7c716ea7950093659864c8esolenberg webrtc::AudioSendStream::Stats stats; 12885a0496b8c4ac01da7c716ea7950093659864c8esolenberg stats.local_ssrc = config_.rtp.ssrc; 1293a94154035fa16e4efd91125311f076b547c38b9solenberg ScopedVoEInterface<VoEAudioProcessing> processing(voice_engine()); 1303a94154035fa16e4efd91125311f076b547c38b9solenberg ScopedVoEInterface<VoECodec> codec(voice_engine()); 1313a94154035fa16e4efd91125311f076b547c38b9solenberg ScopedVoEInterface<VoEVolumeControl> volume(voice_engine()); 13285a0496b8c4ac01da7c716ea7950093659864c8esolenberg 133358057b945725390bcecc330513160aa823f651esolenberg webrtc::CallStatistics call_stats = channel_proxy_->GetRTCPStatistics(); 13485a0496b8c4ac01da7c716ea7950093659864c8esolenberg stats.bytes_sent = call_stats.bytesSent; 13585a0496b8c4ac01da7c716ea7950093659864c8esolenberg stats.packets_sent = call_stats.packetsSent; 1368b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg // RTT isn't known until a RTCP report is received. Until then, VoiceEngine 1378b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg // returns 0 to indicate an error value. 1388b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg if (call_stats.rttMs > 0) { 1398b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.rtt_ms = call_stats.rttMs; 1408b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg } 1418b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg // TODO(solenberg): [was ajm]: Re-enable this metric once we have a reliable 1428b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg // implementation. 1438b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.aec_quality_min = -1; 14485a0496b8c4ac01da7c716ea7950093659864c8esolenberg 14585a0496b8c4ac01da7c716ea7950093659864c8esolenberg webrtc::CodecInst codec_inst = {0}; 14685a0496b8c4ac01da7c716ea7950093659864c8esolenberg if (codec->GetSendCodec(config_.voe_channel_id, codec_inst) != -1) { 14785a0496b8c4ac01da7c716ea7950093659864c8esolenberg RTC_DCHECK_NE(codec_inst.pltype, -1); 14885a0496b8c4ac01da7c716ea7950093659864c8esolenberg stats.codec_name = codec_inst.plname; 14985a0496b8c4ac01da7c716ea7950093659864c8esolenberg 15085a0496b8c4ac01da7c716ea7950093659864c8esolenberg // Get data from the last remote RTCP report. 151358057b945725390bcecc330513160aa823f651esolenberg for (const auto& block : channel_proxy_->GetRemoteRTCPReportBlocks()) { 1528b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg // Lookup report for send ssrc only. 1538b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg if (block.source_SSRC == stats.local_ssrc) { 1548b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.packets_lost = block.cumulative_num_packets_lost; 1558b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.fraction_lost = Q8ToFloat(block.fraction_lost); 1568b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.ext_seqnum = block.extended_highest_sequence_number; 1578b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg // Convert samples to milliseconds. 1588b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg if (codec_inst.plfreq / 1000 > 0) { 1598b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.jitter_ms = 1608b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg block.interarrival_jitter / (codec_inst.plfreq / 1000); 16185a0496b8c4ac01da7c716ea7950093659864c8esolenberg } 1628b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg break; 16385a0496b8c4ac01da7c716ea7950093659864c8esolenberg } 16485a0496b8c4ac01da7c716ea7950093659864c8esolenberg } 16585a0496b8c4ac01da7c716ea7950093659864c8esolenberg } 16685a0496b8c4ac01da7c716ea7950093659864c8esolenberg 16785a0496b8c4ac01da7c716ea7950093659864c8esolenberg // Local speech level. 16885a0496b8c4ac01da7c716ea7950093659864c8esolenberg { 16985a0496b8c4ac01da7c716ea7950093659864c8esolenberg unsigned int level = 0; 170358057b945725390bcecc330513160aa823f651esolenberg int error = volume->GetSpeechInputLevelFullRange(level); 1718b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg RTC_DCHECK_EQ(0, error); 1728b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.audio_level = static_cast<int32_t>(level); 17385a0496b8c4ac01da7c716ea7950093659864c8esolenberg } 17485a0496b8c4ac01da7c716ea7950093659864c8esolenberg 17585a0496b8c4ac01da7c716ea7950093659864c8esolenberg bool echo_metrics_on = false; 176358057b945725390bcecc330513160aa823f651esolenberg int error = processing->GetEcMetricsStatus(echo_metrics_on); 1778b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg RTC_DCHECK_EQ(0, error); 1788b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg if (echo_metrics_on) { 17985a0496b8c4ac01da7c716ea7950093659864c8esolenberg // These can also be negative, but in practice -1 is only used to signal 18085a0496b8c4ac01da7c716ea7950093659864c8esolenberg // insufficient data, since the resolution is limited to multiples of 4 ms. 18185a0496b8c4ac01da7c716ea7950093659864c8esolenberg int median = -1; 18285a0496b8c4ac01da7c716ea7950093659864c8esolenberg int std = -1; 18385a0496b8c4ac01da7c716ea7950093659864c8esolenberg float dummy = 0.0f; 1848b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg error = processing->GetEcDelayMetrics(median, std, dummy); 1858b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg RTC_DCHECK_EQ(0, error); 1868b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.echo_delay_median_ms = median; 1878b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.echo_delay_std_ms = std; 18885a0496b8c4ac01da7c716ea7950093659864c8esolenberg 18985a0496b8c4ac01da7c716ea7950093659864c8esolenberg // These can take on valid negative values, so use the lowest possible level 19085a0496b8c4ac01da7c716ea7950093659864c8esolenberg // as default rather than -1. 19185a0496b8c4ac01da7c716ea7950093659864c8esolenberg int erl = -100; 19285a0496b8c4ac01da7c716ea7950093659864c8esolenberg int erle = -100; 19385a0496b8c4ac01da7c716ea7950093659864c8esolenberg int dummy1 = 0; 19485a0496b8c4ac01da7c716ea7950093659864c8esolenberg int dummy2 = 0; 1958b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg error = processing->GetEchoMetrics(erl, erle, dummy1, dummy2); 1968b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg RTC_DCHECK_EQ(0, error); 1978b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.echo_return_loss = erl; 1988b85de2ba1a8885b70bf9fe8beadc54c5c405335solenberg stats.echo_return_loss_enhancement = erle; 19985a0496b8c4ac01da7c716ea7950093659864c8esolenberg } 20085a0496b8c4ac01da7c716ea7950093659864c8esolenberg 2013a94154035fa16e4efd91125311f076b547c38b9solenberg internal::AudioState* audio_state = 2023a94154035fa16e4efd91125311f076b547c38b9solenberg static_cast<internal::AudioState*>(audio_state_.get()); 203566ef247b9779f6c9d0e7ec9eea6b037f4682c53solenberg stats.typing_noise_detected = audio_state->typing_noise_detected(); 20485a0496b8c4ac01da7c716ea7950093659864c8esolenberg 20585a0496b8c4ac01da7c716ea7950093659864c8esolenberg return stats; 20685a0496b8c4ac01da7c716ea7950093659864c8esolenberg} 20785a0496b8c4ac01da7c716ea7950093659864c8esolenberg 20885a0496b8c4ac01da7c716ea7950093659864c8esolenbergconst webrtc::AudioSendStream::Config& AudioSendStream::config() const { 20985a0496b8c4ac01da7c716ea7950093659864c8esolenberg RTC_DCHECK(thread_checker_.CalledOnValidThread()); 21085a0496b8c4ac01da7c716ea7950093659864c8esolenberg return config_; 211c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg} 212c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg 2133a94154035fa16e4efd91125311f076b547c38b9solenbergVoiceEngine* AudioSendStream::voice_engine() const { 2143a94154035fa16e4efd91125311f076b547c38b9solenberg internal::AudioState* audio_state = 2153a94154035fa16e4efd91125311f076b547c38b9solenberg static_cast<internal::AudioState*>(audio_state_.get()); 2163a94154035fa16e4efd91125311f076b547c38b9solenberg VoiceEngine* voice_engine = audio_state->voice_engine(); 2173a94154035fa16e4efd91125311f076b547c38b9solenberg RTC_DCHECK(voice_engine); 2183a94154035fa16e4efd91125311f076b547c38b9solenberg return voice_engine; 219c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg} 220c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg} // namespace internal 221c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35solenberg} // namespace webrtc 222