blob: 0a1c8b48764a28bd6a467ecec987e3e62994186b [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellander65c7f672016-02-12 00:05:01 -08002 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellander65c7f672016-02-12 00:05:01 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Steve Anton10542f22019-01-11 09:11:00 -080011#include "pc/channel_manager.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
Steve Anton36b29d12017-10-30 09:57:42 -070013#include <utility>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014
Steve Anton64b626b2019-01-28 17:25:26 -080015#include "absl/algorithm/container.h"
Karl Wiberg918f50c2018-07-05 11:40:33 +020016#include "absl/memory/memory.h"
Niels Möller3c7d5992018-10-19 15:29:54 +020017#include "absl/strings/match.h"
Steve Anton10542f22019-01-11 09:11:00 -080018#include "media/base/media_constants.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "rtc_base/checks.h"
Yves Gerey3e707812018-11-28 16:47:49 +010020#include "rtc_base/location.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "rtc_base/logging.h"
Yves Gerey3e707812018-11-28 16:47:49 +010022#include "rtc_base/thread_checker.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/trace_event.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000024
25namespace cricket {
26
Steve Antonc9e15602017-11-06 15:40:09 -080027ChannelManager::ChannelManager(
28 std::unique_ptr<MediaEngineInterface> media_engine,
29 std::unique_ptr<DataEngineInterface> data_engine,
30 rtc::Thread* worker_thread,
31 rtc::Thread* network_thread)
32 : media_engine_(std::move(media_engine)),
33 data_engine_(std::move(data_engine)),
34 main_thread_(rtc::Thread::Current()),
35 worker_thread_(worker_thread),
36 network_thread_(network_thread) {
37 RTC_DCHECK(data_engine_);
38 RTC_DCHECK(worker_thread_);
39 RTC_DCHECK(network_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000040}
41
42ChannelManager::~ChannelManager() {
wu@webrtc.org9dba5252013-08-05 20:36:57 +000043 if (initialized_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000044 Terminate();
wu@webrtc.org9dba5252013-08-05 20:36:57 +000045 }
perkjc11b1842016-03-07 17:34:13 -080046 // The media engine needs to be deleted on the worker thread for thread safe
47 // destruction,
Steve Antonc9e15602017-11-06 15:40:09 -080048 worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] { media_engine_.reset(); });
henrike@webrtc.org28e20752013-07-10 00:45:36 +000049}
50
51bool ChannelManager::SetVideoRtxEnabled(bool enable) {
52 // To be safe, this call is only allowed before initialization. Apps like
53 // Flute only have a singleton ChannelManager and we don't want this flag to
54 // be toggled between calls or when there's concurrent calls. We expect apps
55 // to enable this at startup and retain that setting for the lifetime of the
56 // app.
57 if (!initialized_) {
58 enable_rtx_ = enable;
59 return true;
60 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +010061 RTC_LOG(LS_WARNING) << "Cannot toggle rtx after initialization!";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062 return false;
63 }
64}
65
ossudedfd282016-06-14 07:12:39 -070066void ChannelManager::GetSupportedAudioSendCodecs(
henrike@webrtc.org28e20752013-07-10 00:45:36 +000067 std::vector<AudioCodec>* codecs) const {
zhihuang38ede132017-06-15 12:52:32 -070068 if (!media_engine_) {
69 return;
70 }
Sebastian Jansson6eb8a162018-11-16 11:29:55 +010071 *codecs = media_engine_->voice().send_codecs();
ossudedfd282016-06-14 07:12:39 -070072}
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073
ossudedfd282016-06-14 07:12:39 -070074void ChannelManager::GetSupportedAudioReceiveCodecs(
75 std::vector<AudioCodec>* codecs) const {
zhihuang38ede132017-06-15 12:52:32 -070076 if (!media_engine_) {
77 return;
78 }
Sebastian Jansson6eb8a162018-11-16 11:29:55 +010079 *codecs = media_engine_->voice().recv_codecs();
henrike@webrtc.org28e20752013-07-10 00:45:36 +000080}
81
82void ChannelManager::GetSupportedAudioRtpHeaderExtensions(
83 RtpHeaderExtensions* ext) const {
zhihuang38ede132017-06-15 12:52:32 -070084 if (!media_engine_) {
85 return;
86 }
Sebastian Jansson6eb8a162018-11-16 11:29:55 +010087 *ext = media_engine_->voice().GetCapabilities().header_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000088}
89
magjed3cf8ece2016-11-10 03:36:53 -080090void ChannelManager::GetSupportedVideoCodecs(
91 std::vector<VideoCodec>* codecs) const {
zhihuang38ede132017-06-15 12:52:32 -070092 if (!media_engine_) {
93 return;
94 }
magjed3cf8ece2016-11-10 03:36:53 -080095 codecs->clear();
96
Sebastian Jansson6eb8a162018-11-16 11:29:55 +010097 std::vector<VideoCodec> video_codecs = media_engine_->video().codecs();
brandtrffc61182016-11-28 06:02:22 -080098 for (const auto& video_codec : video_codecs) {
99 if (!enable_rtx_ &&
Niels Möller3c7d5992018-10-19 15:29:54 +0200100 absl::EqualsIgnoreCase(kRtxCodecName, video_codec.name)) {
magjed3cf8ece2016-11-10 03:36:53 -0800101 continue;
102 }
brandtrffc61182016-11-28 06:02:22 -0800103 codecs->push_back(video_codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000104 }
105}
106
107void ChannelManager::GetSupportedVideoRtpHeaderExtensions(
108 RtpHeaderExtensions* ext) const {
zhihuang38ede132017-06-15 12:52:32 -0700109 if (!media_engine_) {
110 return;
111 }
Sebastian Jansson6eb8a162018-11-16 11:29:55 +0100112 *ext = media_engine_->video().GetCapabilities().header_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113}
114
115void ChannelManager::GetSupportedDataCodecs(
116 std::vector<DataCodec>* codecs) const {
Steve Antonc9e15602017-11-06 15:40:09 -0800117 *codecs = data_engine_->data_codecs();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000118}
119
120bool ChannelManager::Init() {
nisseede5da42017-01-12 05:15:36 -0800121 RTC_DCHECK(!initialized_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122 if (initialized_) {
123 return false;
124 }
Danil Chapovalov33b01f22016-05-11 19:55:27 +0200125 RTC_DCHECK(network_thread_);
126 RTC_DCHECK(worker_thread_);
127 if (!network_thread_->IsCurrent()) {
128 // Do not allow invoking calls to other threads on the network thread.
Steve Antonc9e15602017-11-06 15:40:09 -0800129 network_thread_->Invoke<void>(
Karl Wiberg32562252019-02-21 13:38:30 +0100130 RTC_FROM_HERE, [&] { network_thread_->DisallowBlockingCalls(); });
henrika@webrtc.org62f6e752015-02-11 08:38:35 +0000131 }
132
zhihuang38ede132017-06-15 12:52:32 -0700133 if (media_engine_) {
Steve Antonc9e15602017-11-06 15:40:09 -0800134 initialized_ = worker_thread_->Invoke<bool>(
135 RTC_FROM_HERE, [&] { return media_engine_->Init(); });
136 RTC_DCHECK(initialized_);
137 } else {
138 initialized_ = true;
zhihuang38ede132017-06-15 12:52:32 -0700139 }
Steve Antonc9e15602017-11-06 15:40:09 -0800140 return initialized_;
henrika@webrtc.org62f6e752015-02-11 08:38:35 +0000141}
142
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000143void ChannelManager::Terminate() {
nisseede5da42017-01-12 05:15:36 -0800144 RTC_DCHECK(initialized_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000145 if (!initialized_) {
146 return;
147 }
Steve Antonc9e15602017-11-06 15:40:09 -0800148 // Need to destroy the channels on the worker thread.
149 worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
150 video_channels_.clear();
151 voice_channels_.clear();
152 data_channels_.clear();
153 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154 initialized_ = false;
155}
156
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000157VoiceChannel* ChannelManager::CreateVoiceChannel(
nisseeaabdf62017-05-05 02:23:02 -0700158 webrtc::Call* call,
159 const cricket::MediaConfig& media_config,
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800160 webrtc::RtpTransportInternal* rtp_transport,
Anton Sukhanov98a462c2018-10-17 13:15:42 -0700161 webrtc::MediaTransportInterface* media_transport,
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800162 rtc::Thread* signaling_thread,
163 const std::string& content_name,
164 bool srtp_required,
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700165 const webrtc::CryptoOptions& crypto_options,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800166 rtc::UniqueRandomIdGenerator* ssrc_generator,
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800167 const AudioOptions& options) {
168 if (!worker_thread_->IsCurrent()) {
169 return worker_thread_->Invoke<VoiceChannel*>(RTC_FROM_HERE, [&] {
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800170 return CreateVoiceChannel(
171 call, media_config, rtp_transport, media_transport, signaling_thread,
172 content_name, srtp_required, crypto_options, ssrc_generator, options);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800173 });
174 }
175
176 RTC_DCHECK_RUN_ON(worker_thread_);
177 RTC_DCHECK(initialized_);
178 RTC_DCHECK(call);
179 if (!media_engine_) {
180 return nullptr;
181 }
182
Sebastian Jansson6eb8a162018-11-16 11:29:55 +0100183 VoiceMediaChannel* media_channel = media_engine_->voice().CreateMediaChannel(
184 call, media_config, options, crypto_options);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800185 if (!media_channel) {
186 return nullptr;
187 }
188
Karl Wiberg918f50c2018-07-05 11:40:33 +0200189 auto voice_channel = absl::make_unique<VoiceChannel>(
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800190 worker_thread_, network_thread_, signaling_thread,
Karl Wiberg918f50c2018-07-05 11:40:33 +0200191 absl::WrapUnique(media_channel), content_name, srtp_required,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800192 crypto_options, ssrc_generator);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800193
Anton Sukhanov98a462c2018-10-17 13:15:42 -0700194 voice_channel->Init_w(rtp_transport, media_transport);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800195
196 VoiceChannel* voice_channel_ptr = voice_channel.get();
197 voice_channels_.push_back(std::move(voice_channel));
198 return voice_channel_ptr;
199}
200
Fredrik Solenberg709ed672015-09-15 12:26:33 +0200201void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) {
Peter Boström1a9d6152015-12-08 22:15:17 +0100202 TRACE_EVENT0("webrtc", "ChannelManager::DestroyVoiceChannel");
Steve Antonc9e15602017-11-06 15:40:09 -0800203 if (!voice_channel) {
204 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000205 }
Steve Antonc9e15602017-11-06 15:40:09 -0800206 if (!worker_thread_->IsCurrent()) {
207 worker_thread_->Invoke<void>(RTC_FROM_HERE,
208 [&] { DestroyVoiceChannel(voice_channel); });
209 return;
210 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000211
nisseede5da42017-01-12 05:15:36 -0800212 RTC_DCHECK(initialized_);
Steve Anton774115c2017-08-30 10:48:46 -0700213
Steve Anton64b626b2019-01-28 17:25:26 -0800214 auto it = absl::c_find_if(voice_channels_,
215 [&](const std::unique_ptr<VoiceChannel>& p) {
216 return p.get() == voice_channel;
217 });
nisseede5da42017-01-12 05:15:36 -0800218 RTC_DCHECK(it != voice_channels_.end());
Steve Antonc9e15602017-11-06 15:40:09 -0800219 if (it == voice_channels_.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220 return;
Steve Antonc9e15602017-11-06 15:40:09 -0800221 }
222
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000223 voice_channels_.erase(it);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000224}
225
226VideoChannel* ChannelManager::CreateVideoChannel(
nisseeaabdf62017-05-05 02:23:02 -0700227 webrtc::Call* call,
228 const cricket::MediaConfig& media_config,
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800229 webrtc::RtpTransportInternal* rtp_transport,
Niels Möller46879152019-01-07 15:54:47 +0100230 webrtc::MediaTransportInterface* media_transport,
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800231 rtc::Thread* signaling_thread,
232 const std::string& content_name,
233 bool srtp_required,
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700234 const webrtc::CryptoOptions& crypto_options,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800235 rtc::UniqueRandomIdGenerator* ssrc_generator,
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800236 const VideoOptions& options) {
237 if (!worker_thread_->IsCurrent()) {
238 return worker_thread_->Invoke<VideoChannel*>(RTC_FROM_HERE, [&] {
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800239 return CreateVideoChannel(
240 call, media_config, rtp_transport, media_transport, signaling_thread,
241 content_name, srtp_required, crypto_options, ssrc_generator, options);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800242 });
243 }
244
245 RTC_DCHECK_RUN_ON(worker_thread_);
246 RTC_DCHECK(initialized_);
247 RTC_DCHECK(call);
248 if (!media_engine_) {
249 return nullptr;
250 }
251
Sebastian Jansson6eb8a162018-11-16 11:29:55 +0100252 VideoMediaChannel* media_channel = media_engine_->video().CreateMediaChannel(
Benjamin Wrightbfb444c2018-10-15 10:20:24 -0700253 call, media_config, options, crypto_options);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800254 if (!media_channel) {
255 return nullptr;
256 }
257
Karl Wiberg918f50c2018-07-05 11:40:33 +0200258 auto video_channel = absl::make_unique<VideoChannel>(
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800259 worker_thread_, network_thread_, signaling_thread,
Karl Wiberg918f50c2018-07-05 11:40:33 +0200260 absl::WrapUnique(media_channel), content_name, srtp_required,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800261 crypto_options, ssrc_generator);
Anton Sukhanov98a462c2018-10-17 13:15:42 -0700262
Niels Möller46879152019-01-07 15:54:47 +0100263 video_channel->Init_w(rtp_transport, media_transport);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800264
265 VideoChannel* video_channel_ptr = video_channel.get();
266 video_channels_.push_back(std::move(video_channel));
267 return video_channel_ptr;
268}
269
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000270void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) {
Peter Boström1a9d6152015-12-08 22:15:17 +0100271 TRACE_EVENT0("webrtc", "ChannelManager::DestroyVideoChannel");
Steve Antonc9e15602017-11-06 15:40:09 -0800272 if (!video_channel) {
273 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274 }
Steve Antonc9e15602017-11-06 15:40:09 -0800275 if (!worker_thread_->IsCurrent()) {
276 worker_thread_->Invoke<void>(RTC_FROM_HERE,
277 [&] { DestroyVideoChannel(video_channel); });
278 return;
279 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280
nisseede5da42017-01-12 05:15:36 -0800281 RTC_DCHECK(initialized_);
Steve Anton774115c2017-08-30 10:48:46 -0700282
Steve Anton64b626b2019-01-28 17:25:26 -0800283 auto it = absl::c_find_if(video_channels_,
284 [&](const std::unique_ptr<VideoChannel>& p) {
285 return p.get() == video_channel;
286 });
nisseede5da42017-01-12 05:15:36 -0800287 RTC_DCHECK(it != video_channels_.end());
Steve Antonc9e15602017-11-06 15:40:09 -0800288 if (it == video_channels_.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 return;
Steve Antonc9e15602017-11-06 15:40:09 -0800290 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000291
292 video_channels_.erase(it);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000293}
294
deadbeef953c2ce2017-01-09 14:53:41 -0800295RtpDataChannel* ChannelManager::CreateRtpDataChannel(
nisseeaabdf62017-05-05 02:23:02 -0700296 const cricket::MediaConfig& media_config,
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800297 webrtc::RtpTransportInternal* rtp_transport,
298 rtc::Thread* signaling_thread,
299 const std::string& content_name,
Zhi Huange830e682018-03-30 10:48:35 -0700300 bool srtp_required,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800301 const webrtc::CryptoOptions& crypto_options,
302 rtc::UniqueRandomIdGenerator* ssrc_generator) {
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800303 if (!worker_thread_->IsCurrent()) {
304 return worker_thread_->Invoke<RtpDataChannel*>(RTC_FROM_HERE, [&] {
305 return CreateRtpDataChannel(media_config, rtp_transport, signaling_thread,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800306 content_name, srtp_required, crypto_options,
307 ssrc_generator);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800308 });
309 }
310
311 // This is ok to alloc from a thread other than the worker thread.
312 RTC_DCHECK(initialized_);
313 DataMediaChannel* media_channel = data_engine_->CreateChannel(media_config);
314 if (!media_channel) {
315 RTC_LOG(LS_WARNING) << "Failed to create RTP data channel.";
316 return nullptr;
317 }
318
Karl Wiberg918f50c2018-07-05 11:40:33 +0200319 auto data_channel = absl::make_unique<RtpDataChannel>(
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800320 worker_thread_, network_thread_, signaling_thread,
Karl Wiberg918f50c2018-07-05 11:40:33 +0200321 absl::WrapUnique(media_channel), content_name, srtp_required,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800322 crypto_options, ssrc_generator);
Zhi Huang2dfc42d2017-12-04 13:38:48 -0800323 data_channel->Init_w(rtp_transport);
324
325 RtpDataChannel* data_channel_ptr = data_channel.get();
326 data_channels_.push_back(std::move(data_channel));
327 return data_channel_ptr;
328}
329
deadbeef953c2ce2017-01-09 14:53:41 -0800330void ChannelManager::DestroyRtpDataChannel(RtpDataChannel* data_channel) {
331 TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel");
Steve Antonc9e15602017-11-06 15:40:09 -0800332 if (!data_channel) {
333 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000334 }
Steve Antonc9e15602017-11-06 15:40:09 -0800335 if (!worker_thread_->IsCurrent()) {
336 worker_thread_->Invoke<void>(
337 RTC_FROM_HERE, [&] { return DestroyRtpDataChannel(data_channel); });
338 return;
339 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000340
nisseede5da42017-01-12 05:15:36 -0800341 RTC_DCHECK(initialized_);
Steve Anton774115c2017-08-30 10:48:46 -0700342
Steve Anton64b626b2019-01-28 17:25:26 -0800343 auto it = absl::c_find_if(data_channels_,
344 [&](const std::unique_ptr<RtpDataChannel>& p) {
345 return p.get() == data_channel;
346 });
nisseede5da42017-01-12 05:15:36 -0800347 RTC_DCHECK(it != data_channels_.end());
Steve Antonc9e15602017-11-06 15:40:09 -0800348 if (it == data_channels_.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000349 return;
Steve Antonc9e15602017-11-06 15:40:09 -0800350 }
Zhi Huang95e7dbb2018-03-29 00:08:03 +0000351
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000352 data_channels_.erase(it);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000353}
354
ivocd66b44d2016-01-15 03:06:36 -0800355bool ChannelManager::StartAecDump(rtc::PlatformFile file,
356 int64_t max_size_bytes) {
Steve Antonc9e15602017-11-06 15:40:09 -0800357 return worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
Sebastian Jansson6eb8a162018-11-16 11:29:55 +0100358 return media_engine_->voice().StartAecDump(file, max_size_bytes);
Steve Antonc9e15602017-11-06 15:40:09 -0800359 });
wu@webrtc.orga9890802013-12-13 00:21:03 +0000360}
361
ivoc797ef122015-10-22 03:25:41 -0700362void ChannelManager::StopAecDump() {
Steve Antonc9e15602017-11-06 15:40:09 -0800363 worker_thread_->Invoke<void>(RTC_FROM_HERE,
Sebastian Jansson6eb8a162018-11-16 11:29:55 +0100364 [&] { media_engine_->voice().StopAecDump(); });
ivoc797ef122015-10-22 03:25:41 -0700365}
366
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000367} // namespace cricket