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