1b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org/* 2b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org * 4b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org * Use of this source code is governed by a BSD-style license 5b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org * that can be found in the LICENSE file in the root of the source 6b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org * tree. An additional intellectual property rights grant can be found 7b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org * in the file PATENTS. All contributing project authors may 8b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org * be found in the AUTHORS file in the root of the source tree. 9b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org */ 10b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 11471ae72f18e7b23a96b245dbd508386fe139449cpbos@webrtc.org#include "webrtc/voice_engine/voe_external_media_impl.h" 12b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 13471ae72f18e7b23a96b245dbd508386fe139449cpbos@webrtc.org#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" 14471ae72f18e7b23a96b245dbd508386fe139449cpbos@webrtc.org#include "webrtc/system_wrappers/interface/trace.h" 15471ae72f18e7b23a96b245dbd508386fe139449cpbos@webrtc.org#include "webrtc/voice_engine/channel.h" 16471ae72f18e7b23a96b245dbd508386fe139449cpbos@webrtc.org#include "webrtc/voice_engine/include/voe_errors.h" 17471ae72f18e7b23a96b245dbd508386fe139449cpbos@webrtc.org#include "webrtc/voice_engine/output_mixer.h" 18471ae72f18e7b23a96b245dbd508386fe139449cpbos@webrtc.org#include "webrtc/voice_engine/transmit_mixer.h" 19471ae72f18e7b23a96b245dbd508386fe139449cpbos@webrtc.org#include "webrtc/voice_engine/voice_engine_impl.h" 20b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 21b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgnamespace webrtc { 22b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 23b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgVoEExternalMedia* VoEExternalMedia::GetInterface(VoiceEngine* voiceEngine) 24b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org{ 25b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#ifndef WEBRTC_VOICE_ENGINE_EXTERNAL_MEDIA_API 26b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return NULL; 27b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#else 28b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org if (NULL == voiceEngine) 29b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 30b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return NULL; 31b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 32b9e5a3d589349ee55e41cb54eca4ec822018f5c5tommi@webrtc.org VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine); 33b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org s->AddRef(); 34b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return s; 35b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#endif 36b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org} 37b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 38b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#ifdef WEBRTC_VOICE_ENGINE_EXTERNAL_MEDIA_API 39b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 40b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgVoEExternalMediaImpl::VoEExternalMediaImpl(voe::SharedData* shared) 417b1e78d5a3ffef0815e2d3a8cb082a6ec0766708wjia@webrtc.org : 427b1e78d5a3ffef0815e2d3a8cb082a6ec0766708wjia@webrtc.org#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT 437b1e78d5a3ffef0815e2d3a8cb082a6ec0766708wjia@webrtc.org playout_delay_ms_(0), 447b1e78d5a3ffef0815e2d3a8cb082a6ec0766708wjia@webrtc.org#endif 457b1e78d5a3ffef0815e2d3a8cb082a6ec0766708wjia@webrtc.org shared_(shared) 46b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org{ 47b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(shared_->instance_id(), -1), 48b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "VoEExternalMediaImpl() - ctor"); 49b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org} 50b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 51b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgVoEExternalMediaImpl::~VoEExternalMediaImpl() 52b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org{ 53b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(shared_->instance_id(), -1), 54b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "~VoEExternalMediaImpl() - dtor"); 55b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org} 56b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 57b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgint VoEExternalMediaImpl::RegisterExternalMediaProcessing( 58b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org int channel, 59b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org ProcessingTypes type, 60b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org VoEMediaProcess& processObject) 61b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org{ 62b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(shared_->instance_id(), -1), 63b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "RegisterExternalMediaProcessing(channel=%d, type=%d, " 64b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "processObject=0x%x)", channel, type, &processObject); 65b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org if (!shared_->statistics().Initialized()) 66b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 67b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org shared_->SetLastError(VE_NOT_INITED, kTraceError); 68b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return -1; 69b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 70b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org switch (type) 71b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 72b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kPlaybackPerChannel: 73b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kRecordingPerChannel: 74b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 75b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org voe::ChannelOwner ch = 76b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org shared_->channel_manager().GetChannel(channel); 77b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org voe::Channel* channelPtr = ch.channel(); 78b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org if (channelPtr == NULL) 79b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 80b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 81b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "RegisterExternalMediaProcessing() failed to locate " 82b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "channel"); 83b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return -1; 84b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 85b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return channelPtr->RegisterExternalMediaProcessing(type, 86b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org processObject); 87b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 88b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kPlaybackAllChannelsMixed: 89b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 90b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return shared_->output_mixer()->RegisterExternalMediaProcessing( 91b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org processObject); 92b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 93b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kRecordingAllChannelsMixed: 94b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kRecordingPreprocessing: 95b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 96b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return shared_->transmit_mixer()->RegisterExternalMediaProcessing( 97b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org &processObject, type); 98b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 99b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 100b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return -1; 101b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org} 102b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 103b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgint VoEExternalMediaImpl::DeRegisterExternalMediaProcessing( 104b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org int channel, 105b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org ProcessingTypes type) 106b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org{ 107b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(shared_->instance_id(), -1), 108b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "DeRegisterExternalMediaProcessing(channel=%d)", channel); 109b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org if (!shared_->statistics().Initialized()) 110b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 111b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org shared_->SetLastError(VE_NOT_INITED, kTraceError); 112b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return -1; 113b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 114b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org switch (type) 115b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 116b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kPlaybackPerChannel: 117b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kRecordingPerChannel: 118b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 119b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org voe::ChannelOwner ch = 120b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org shared_->channel_manager().GetChannel(channel); 121b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org voe::Channel* channelPtr = ch.channel(); 122b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org if (channelPtr == NULL) 123b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 124b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 125b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "RegisterExternalMediaProcessing() " 126b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org "failed to locate channel"); 127b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return -1; 128b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 129b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return channelPtr->DeRegisterExternalMediaProcessing(type); 130b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 131b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kPlaybackAllChannelsMixed: 132b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 133b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return shared_->output_mixer()-> 134b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org DeRegisterExternalMediaProcessing(); 135b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 136b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kRecordingAllChannelsMixed: 137b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org case kRecordingPreprocessing: 138b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org { 139b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return shared_->transmit_mixer()-> 140b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org DeRegisterExternalMediaProcessing(type); 141b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 142b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org } 143b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org return -1; 144b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org} 145b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 146b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.comint VoEExternalMediaImpl::GetAudioFrame(int channel, int desired_sample_rate_hz, 147b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com AudioFrame* frame) { 148b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com WEBRTC_TRACE(kTraceApiCall, kTraceVoice, 149b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com VoEId(shared_->instance_id(), channel), 150b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com "GetAudioFrame(channel=%d, desired_sample_rate_hz=%d)", 151b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com channel, desired_sample_rate_hz); 152b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com if (!shared_->statistics().Initialized()) 153b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com { 154b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com shared_->SetLastError(VE_NOT_INITED, kTraceError); 155b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return -1; 156b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com } 157b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel); 158b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org voe::Channel* channelPtr = ch.channel(); 159b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com if (channelPtr == NULL) 160b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com { 161b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 162b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com "GetAudioFrame() failed to locate channel"); 163b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return -1; 164b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com } 165b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com if (!channelPtr->ExternalMixing()) { 166b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com shared_->SetLastError(VE_INVALID_OPERATION, kTraceError, 167b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com "GetAudioFrame() was called on channel that is not" 168b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com " externally mixed."); 169b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return -1; 170b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com } 171b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com if (!channelPtr->Playing()) { 172b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com shared_->SetLastError(VE_INVALID_OPERATION, kTraceError, 173b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com "GetAudioFrame() was called on channel that is not playing."); 174b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return -1; 175b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com } 176b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com if (desired_sample_rate_hz == -1) { 177b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com shared_->SetLastError(VE_BAD_ARGUMENT, kTraceError, 178b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com "GetAudioFrame() was called with bad sample rate."); 179b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return -1; 180b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com } 181b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com frame->sample_rate_hz_ = desired_sample_rate_hz == 0 ? -1 : 182b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com desired_sample_rate_hz; 183b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return channelPtr->GetAudioFrame(channel, *frame); 184b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com} 185b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com 186b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.comint VoEExternalMediaImpl::SetExternalMixing(int channel, bool enable) { 187b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com WEBRTC_TRACE(kTraceApiCall, kTraceVoice, 188b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com VoEId(shared_->instance_id(), channel), 189b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com "SetExternalMixing(channel=%d, enable=%d)", channel, enable); 190b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com if (!shared_->statistics().Initialized()) 191b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com { 192b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com shared_->SetLastError(VE_NOT_INITED, kTraceError); 193b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return -1; 194b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com } 195b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel); 196b3ada1540827c60a63058570a94a57dfd260ad11pbos@webrtc.org voe::Channel* channelPtr = ch.channel(); 197b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com if (channelPtr == NULL) 198b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com { 199b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, 200b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com "SetExternalMixing() failed to locate channel"); 201b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return -1; 202b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com } 203b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com return channelPtr->SetExternalMixing(enable); 204b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com} 205b9e3afc6503d3454a6fb68b12be46eb7cfb8effdroosa@google.com 206b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#endif // WEBRTC_VOICE_ENGINE_EXTERNAL_MEDIA_API 207b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org 208b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org} // namespace webrtc 209