1 /*
2 * libjingle
3 * Copyright 2004 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "talk/session/media/channelmanager.h"
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <algorithm>
35
36 #include "talk/app/webrtc/mediacontroller.h"
37 #include "talk/media/base/capturemanager.h"
38 #include "talk/media/base/device.h"
39 #include "talk/media/base/hybriddataengine.h"
40 #include "talk/media/base/rtpdataengine.h"
41 #include "talk/media/base/videocapturer.h"
42 #ifdef HAVE_SCTP
43 #include "talk/media/sctp/sctpdataengine.h"
44 #endif
45 #include "talk/session/media/srtpfilter.h"
46 #include "webrtc/base/bind.h"
47 #include "webrtc/base/common.h"
48 #include "webrtc/base/logging.h"
49 #include "webrtc/base/sigslotrepeater.h"
50 #include "webrtc/base/stringencode.h"
51 #include "webrtc/base/stringutils.h"
52 #include "webrtc/base/trace_event.h"
53
54 namespace cricket {
55
56 enum {
57 MSG_VIDEOCAPTURESTATE = 1,
58 };
59
60 using rtc::Bind;
61
62 static const int kNotSetOutputVolume = -1;
63
64 struct CaptureStateParams : public rtc::MessageData {
CaptureStateParamscricket::CaptureStateParams65 CaptureStateParams(cricket::VideoCapturer* c, cricket::CaptureState s)
66 : capturer(c),
67 state(s) {}
68 cricket::VideoCapturer* capturer;
69 cricket::CaptureState state;
70 };
71
ConstructDataEngine()72 static DataEngineInterface* ConstructDataEngine() {
73 #ifdef HAVE_SCTP
74 return new HybridDataEngine(new RtpDataEngine(), new SctpDataEngine());
75 #else
76 return new RtpDataEngine();
77 #endif
78 }
79
ChannelManager(MediaEngineInterface * me,DataEngineInterface * dme,CaptureManager * cm,rtc::Thread * worker_thread)80 ChannelManager::ChannelManager(MediaEngineInterface* me,
81 DataEngineInterface* dme,
82 CaptureManager* cm,
83 rtc::Thread* worker_thread) {
84 Construct(me, dme, cm, worker_thread);
85 }
86
ChannelManager(MediaEngineInterface * me,rtc::Thread * worker_thread)87 ChannelManager::ChannelManager(MediaEngineInterface* me,
88 rtc::Thread* worker_thread) {
89 Construct(me,
90 ConstructDataEngine(),
91 new CaptureManager(),
92 worker_thread);
93 }
94
Construct(MediaEngineInterface * me,DataEngineInterface * dme,CaptureManager * cm,rtc::Thread * worker_thread)95 void ChannelManager::Construct(MediaEngineInterface* me,
96 DataEngineInterface* dme,
97 CaptureManager* cm,
98 rtc::Thread* worker_thread) {
99 media_engine_.reset(me);
100 data_media_engine_.reset(dme);
101 capture_manager_.reset(cm);
102 initialized_ = false;
103 main_thread_ = rtc::Thread::Current();
104 worker_thread_ = worker_thread;
105 audio_output_volume_ = kNotSetOutputVolume;
106 local_renderer_ = NULL;
107 capturing_ = false;
108 enable_rtx_ = false;
109
110 capture_manager_->SignalCapturerStateChange.connect(
111 this, &ChannelManager::OnVideoCaptureStateChange);
112 }
113
~ChannelManager()114 ChannelManager::~ChannelManager() {
115 if (initialized_) {
116 Terminate();
117 // If srtp is initialized (done by the Channel) then we must call
118 // srtp_shutdown to free all crypto kernel lists. But we need to make sure
119 // shutdown always called at the end, after channels are destroyed.
120 // ChannelManager d'tor is always called last, it's safe place to call
121 // shutdown.
122 ShutdownSrtp();
123 }
124 // Some deletes need to be on the worker thread for thread safe destruction,
125 // this includes the media engine and capture manager.
126 worker_thread_->Invoke<void>(Bind(
127 &ChannelManager::DestructorDeletes_w, this));
128 }
129
SetVideoRtxEnabled(bool enable)130 bool ChannelManager::SetVideoRtxEnabled(bool enable) {
131 // To be safe, this call is only allowed before initialization. Apps like
132 // Flute only have a singleton ChannelManager and we don't want this flag to
133 // be toggled between calls or when there's concurrent calls. We expect apps
134 // to enable this at startup and retain that setting for the lifetime of the
135 // app.
136 if (!initialized_) {
137 enable_rtx_ = enable;
138 return true;
139 } else {
140 LOG(LS_WARNING) << "Cannot toggle rtx after initialization!";
141 return false;
142 }
143 }
144
GetSupportedAudioCodecs(std::vector<AudioCodec> * codecs) const145 void ChannelManager::GetSupportedAudioCodecs(
146 std::vector<AudioCodec>* codecs) const {
147 codecs->clear();
148
149 for (std::vector<AudioCodec>::const_iterator it =
150 media_engine_->audio_codecs().begin();
151 it != media_engine_->audio_codecs().end(); ++it) {
152 codecs->push_back(*it);
153 }
154 }
155
GetSupportedAudioRtpHeaderExtensions(RtpHeaderExtensions * ext) const156 void ChannelManager::GetSupportedAudioRtpHeaderExtensions(
157 RtpHeaderExtensions* ext) const {
158 *ext = media_engine_->GetAudioCapabilities().header_extensions;
159 }
160
GetSupportedVideoCodecs(std::vector<VideoCodec> * codecs) const161 void ChannelManager::GetSupportedVideoCodecs(
162 std::vector<VideoCodec>* codecs) const {
163 codecs->clear();
164
165 std::vector<VideoCodec>::const_iterator it;
166 for (it = media_engine_->video_codecs().begin();
167 it != media_engine_->video_codecs().end(); ++it) {
168 if (!enable_rtx_ && _stricmp(kRtxCodecName, it->name.c_str()) == 0) {
169 continue;
170 }
171 codecs->push_back(*it);
172 }
173 }
174
GetSupportedVideoRtpHeaderExtensions(RtpHeaderExtensions * ext) const175 void ChannelManager::GetSupportedVideoRtpHeaderExtensions(
176 RtpHeaderExtensions* ext) const {
177 *ext = media_engine_->GetVideoCapabilities().header_extensions;
178 }
179
GetSupportedDataCodecs(std::vector<DataCodec> * codecs) const180 void ChannelManager::GetSupportedDataCodecs(
181 std::vector<DataCodec>* codecs) const {
182 *codecs = data_media_engine_->data_codecs();
183 }
184
Init()185 bool ChannelManager::Init() {
186 ASSERT(!initialized_);
187 if (initialized_) {
188 return false;
189 }
190 ASSERT(worker_thread_ != NULL);
191 if (!worker_thread_) {
192 return false;
193 }
194 if (worker_thread_ != rtc::Thread::Current()) {
195 // Do not allow invoking calls to other threads on the worker thread.
196 worker_thread_->Invoke<bool>(rtc::Bind(
197 &rtc::Thread::SetAllowBlockingCalls, worker_thread_, false));
198 }
199
200 initialized_ = worker_thread_->Invoke<bool>(Bind(
201 &ChannelManager::InitMediaEngine_w, this));
202 ASSERT(initialized_);
203 if (!initialized_) {
204 return false;
205 }
206
207 // If audio_output_volume_ has been set via SetOutputVolume(), set the
208 // audio output volume of the engine.
209 if (kNotSetOutputVolume != audio_output_volume_ &&
210 !SetOutputVolume(audio_output_volume_)) {
211 LOG(LS_WARNING) << "Failed to SetOutputVolume to "
212 << audio_output_volume_;
213 }
214
215 return initialized_;
216 }
217
InitMediaEngine_w()218 bool ChannelManager::InitMediaEngine_w() {
219 ASSERT(worker_thread_ == rtc::Thread::Current());
220 return (media_engine_->Init(worker_thread_));
221 }
222
Terminate()223 void ChannelManager::Terminate() {
224 ASSERT(initialized_);
225 if (!initialized_) {
226 return;
227 }
228 worker_thread_->Invoke<void>(Bind(&ChannelManager::Terminate_w, this));
229 initialized_ = false;
230 }
231
DestructorDeletes_w()232 void ChannelManager::DestructorDeletes_w() {
233 ASSERT(worker_thread_ == rtc::Thread::Current());
234 media_engine_.reset(NULL);
235 capture_manager_.reset(NULL);
236 }
237
Terminate_w()238 void ChannelManager::Terminate_w() {
239 ASSERT(worker_thread_ == rtc::Thread::Current());
240 // Need to destroy the voice/video channels
241 while (!video_channels_.empty()) {
242 DestroyVideoChannel_w(video_channels_.back());
243 }
244 while (!voice_channels_.empty()) {
245 DestroyVoiceChannel_w(voice_channels_.back());
246 }
247 media_engine_->Terminate();
248 }
249
CreateVoiceChannel(webrtc::MediaControllerInterface * media_controller,TransportController * transport_controller,const std::string & content_name,bool rtcp,const AudioOptions & options)250 VoiceChannel* ChannelManager::CreateVoiceChannel(
251 webrtc::MediaControllerInterface* media_controller,
252 TransportController* transport_controller,
253 const std::string& content_name,
254 bool rtcp,
255 const AudioOptions& options) {
256 return worker_thread_->Invoke<VoiceChannel*>(
257 Bind(&ChannelManager::CreateVoiceChannel_w, this, media_controller,
258 transport_controller, content_name, rtcp, options));
259 }
260
CreateVoiceChannel_w(webrtc::MediaControllerInterface * media_controller,TransportController * transport_controller,const std::string & content_name,bool rtcp,const AudioOptions & options)261 VoiceChannel* ChannelManager::CreateVoiceChannel_w(
262 webrtc::MediaControllerInterface* media_controller,
263 TransportController* transport_controller,
264 const std::string& content_name,
265 bool rtcp,
266 const AudioOptions& options) {
267 ASSERT(initialized_);
268 ASSERT(worker_thread_ == rtc::Thread::Current());
269 ASSERT(nullptr != media_controller);
270 VoiceMediaChannel* media_channel =
271 media_engine_->CreateChannel(media_controller->call_w(), options);
272 if (!media_channel)
273 return nullptr;
274
275 VoiceChannel* voice_channel =
276 new VoiceChannel(worker_thread_, media_engine_.get(), media_channel,
277 transport_controller, content_name, rtcp);
278 if (!voice_channel->Init()) {
279 delete voice_channel;
280 return nullptr;
281 }
282 voice_channels_.push_back(voice_channel);
283 return voice_channel;
284 }
285
DestroyVoiceChannel(VoiceChannel * voice_channel)286 void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) {
287 TRACE_EVENT0("webrtc", "ChannelManager::DestroyVoiceChannel");
288 if (voice_channel) {
289 worker_thread_->Invoke<void>(
290 Bind(&ChannelManager::DestroyVoiceChannel_w, this, voice_channel));
291 }
292 }
293
DestroyVoiceChannel_w(VoiceChannel * voice_channel)294 void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) {
295 TRACE_EVENT0("webrtc", "ChannelManager::DestroyVoiceChannel_w");
296 // Destroy voice channel.
297 ASSERT(initialized_);
298 ASSERT(worker_thread_ == rtc::Thread::Current());
299 VoiceChannels::iterator it = std::find(voice_channels_.begin(),
300 voice_channels_.end(), voice_channel);
301 ASSERT(it != voice_channels_.end());
302 if (it == voice_channels_.end())
303 return;
304 voice_channels_.erase(it);
305 delete voice_channel;
306 }
307
CreateVideoChannel(webrtc::MediaControllerInterface * media_controller,TransportController * transport_controller,const std::string & content_name,bool rtcp,const VideoOptions & options)308 VideoChannel* ChannelManager::CreateVideoChannel(
309 webrtc::MediaControllerInterface* media_controller,
310 TransportController* transport_controller,
311 const std::string& content_name,
312 bool rtcp,
313 const VideoOptions& options) {
314 return worker_thread_->Invoke<VideoChannel*>(
315 Bind(&ChannelManager::CreateVideoChannel_w, this, media_controller,
316 transport_controller, content_name, rtcp, options));
317 }
318
CreateVideoChannel_w(webrtc::MediaControllerInterface * media_controller,TransportController * transport_controller,const std::string & content_name,bool rtcp,const VideoOptions & options)319 VideoChannel* ChannelManager::CreateVideoChannel_w(
320 webrtc::MediaControllerInterface* media_controller,
321 TransportController* transport_controller,
322 const std::string& content_name,
323 bool rtcp,
324 const VideoOptions& options) {
325 ASSERT(initialized_);
326 ASSERT(worker_thread_ == rtc::Thread::Current());
327 ASSERT(nullptr != media_controller);
328 VideoMediaChannel* media_channel =
329 media_engine_->CreateVideoChannel(media_controller->call_w(), options);
330 if (media_channel == NULL) {
331 return NULL;
332 }
333
334 VideoChannel* video_channel = new VideoChannel(
335 worker_thread_, media_channel, transport_controller, content_name, rtcp);
336 if (!video_channel->Init()) {
337 delete video_channel;
338 return NULL;
339 }
340 video_channels_.push_back(video_channel);
341 return video_channel;
342 }
343
DestroyVideoChannel(VideoChannel * video_channel)344 void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) {
345 TRACE_EVENT0("webrtc", "ChannelManager::DestroyVideoChannel");
346 if (video_channel) {
347 worker_thread_->Invoke<void>(
348 Bind(&ChannelManager::DestroyVideoChannel_w, this, video_channel));
349 }
350 }
351
DestroyVideoChannel_w(VideoChannel * video_channel)352 void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) {
353 TRACE_EVENT0("webrtc", "ChannelManager::DestroyVideoChannel_w");
354 // Destroy video channel.
355 ASSERT(initialized_);
356 ASSERT(worker_thread_ == rtc::Thread::Current());
357 VideoChannels::iterator it = std::find(video_channels_.begin(),
358 video_channels_.end(), video_channel);
359 ASSERT(it != video_channels_.end());
360 if (it == video_channels_.end())
361 return;
362
363 video_channels_.erase(it);
364 delete video_channel;
365 }
366
CreateDataChannel(TransportController * transport_controller,const std::string & content_name,bool rtcp,DataChannelType channel_type)367 DataChannel* ChannelManager::CreateDataChannel(
368 TransportController* transport_controller,
369 const std::string& content_name,
370 bool rtcp,
371 DataChannelType channel_type) {
372 return worker_thread_->Invoke<DataChannel*>(
373 Bind(&ChannelManager::CreateDataChannel_w, this, transport_controller,
374 content_name, rtcp, channel_type));
375 }
376
CreateDataChannel_w(TransportController * transport_controller,const std::string & content_name,bool rtcp,DataChannelType data_channel_type)377 DataChannel* ChannelManager::CreateDataChannel_w(
378 TransportController* transport_controller,
379 const std::string& content_name,
380 bool rtcp,
381 DataChannelType data_channel_type) {
382 // This is ok to alloc from a thread other than the worker thread.
383 ASSERT(initialized_);
384 DataMediaChannel* media_channel = data_media_engine_->CreateChannel(
385 data_channel_type);
386 if (!media_channel) {
387 LOG(LS_WARNING) << "Failed to create data channel of type "
388 << data_channel_type;
389 return NULL;
390 }
391
392 DataChannel* data_channel = new DataChannel(
393 worker_thread_, media_channel, transport_controller, content_name, rtcp);
394 if (!data_channel->Init()) {
395 LOG(LS_WARNING) << "Failed to init data channel.";
396 delete data_channel;
397 return NULL;
398 }
399 data_channels_.push_back(data_channel);
400 return data_channel;
401 }
402
DestroyDataChannel(DataChannel * data_channel)403 void ChannelManager::DestroyDataChannel(DataChannel* data_channel) {
404 TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel");
405 if (data_channel) {
406 worker_thread_->Invoke<void>(
407 Bind(&ChannelManager::DestroyDataChannel_w, this, data_channel));
408 }
409 }
410
DestroyDataChannel_w(DataChannel * data_channel)411 void ChannelManager::DestroyDataChannel_w(DataChannel* data_channel) {
412 TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel_w");
413 // Destroy data channel.
414 ASSERT(initialized_);
415 DataChannels::iterator it = std::find(data_channels_.begin(),
416 data_channels_.end(), data_channel);
417 ASSERT(it != data_channels_.end());
418 if (it == data_channels_.end())
419 return;
420
421 data_channels_.erase(it);
422 delete data_channel;
423 }
424
GetOutputVolume(int * level)425 bool ChannelManager::GetOutputVolume(int* level) {
426 if (!initialized_) {
427 return false;
428 }
429 return worker_thread_->Invoke<bool>(
430 Bind(&MediaEngineInterface::GetOutputVolume, media_engine_.get(), level));
431 }
432
SetOutputVolume(int level)433 bool ChannelManager::SetOutputVolume(int level) {
434 bool ret = level >= 0 && level <= 255;
435 if (initialized_) {
436 ret &= worker_thread_->Invoke<bool>(
437 Bind(&MediaEngineInterface::SetOutputVolume,
438 media_engine_.get(), level));
439 }
440
441 if (ret) {
442 audio_output_volume_ = level;
443 }
444
445 return ret;
446 }
447
GetSupportedFormats(VideoCapturer * capturer) const448 std::vector<cricket::VideoFormat> ChannelManager::GetSupportedFormats(
449 VideoCapturer* capturer) const {
450 ASSERT(capturer != NULL);
451 std::vector<VideoFormat> formats;
452 worker_thread_->Invoke<void>(rtc::Bind(&ChannelManager::GetSupportedFormats_w,
453 this, capturer, &formats));
454 return formats;
455 }
456
GetSupportedFormats_w(VideoCapturer * capturer,std::vector<cricket::VideoFormat> * out_formats) const457 void ChannelManager::GetSupportedFormats_w(
458 VideoCapturer* capturer,
459 std::vector<cricket::VideoFormat>* out_formats) const {
460 const std::vector<VideoFormat>* formats = capturer->GetSupportedFormats();
461 if (formats != NULL)
462 *out_formats = *formats;
463 }
464
465 // The following are done in the new "CaptureManager" style that
466 // all local video capturers, processors, and managers should move
467 // to.
468 // TODO(pthatcher): Add more of the CaptureManager interface.
StartVideoCapture(VideoCapturer * capturer,const VideoFormat & video_format)469 bool ChannelManager::StartVideoCapture(
470 VideoCapturer* capturer, const VideoFormat& video_format) {
471 return initialized_ && worker_thread_->Invoke<bool>(
472 Bind(&CaptureManager::StartVideoCapture,
473 capture_manager_.get(), capturer, video_format));
474 }
475
MuteToBlackThenPause(VideoCapturer * video_capturer,bool muted)476 bool ChannelManager::MuteToBlackThenPause(
477 VideoCapturer* video_capturer, bool muted) {
478 if (!initialized_) {
479 return false;
480 }
481 worker_thread_->Invoke<void>(
482 Bind(&VideoCapturer::MuteToBlackThenPause, video_capturer, muted));
483 return true;
484 }
485
StopVideoCapture(VideoCapturer * capturer,const VideoFormat & video_format)486 bool ChannelManager::StopVideoCapture(
487 VideoCapturer* capturer, const VideoFormat& video_format) {
488 return initialized_ && worker_thread_->Invoke<bool>(
489 Bind(&CaptureManager::StopVideoCapture,
490 capture_manager_.get(), capturer, video_format));
491 }
492
RestartVideoCapture(VideoCapturer * video_capturer,const VideoFormat & previous_format,const VideoFormat & desired_format,CaptureManager::RestartOptions options)493 bool ChannelManager::RestartVideoCapture(
494 VideoCapturer* video_capturer,
495 const VideoFormat& previous_format,
496 const VideoFormat& desired_format,
497 CaptureManager::RestartOptions options) {
498 return initialized_ && worker_thread_->Invoke<bool>(
499 Bind(&CaptureManager::RestartVideoCapture, capture_manager_.get(),
500 video_capturer, previous_format, desired_format, options));
501 }
502
AddVideoRenderer(VideoCapturer * capturer,VideoRenderer * renderer)503 bool ChannelManager::AddVideoRenderer(
504 VideoCapturer* capturer, VideoRenderer* renderer) {
505 return initialized_ && worker_thread_->Invoke<bool>(
506 Bind(&CaptureManager::AddVideoRenderer,
507 capture_manager_.get(), capturer, renderer));
508 }
509
RemoveVideoRenderer(VideoCapturer * capturer,VideoRenderer * renderer)510 bool ChannelManager::RemoveVideoRenderer(
511 VideoCapturer* capturer, VideoRenderer* renderer) {
512 return initialized_ && worker_thread_->Invoke<bool>(
513 Bind(&CaptureManager::RemoveVideoRenderer,
514 capture_manager_.get(), capturer, renderer));
515 }
516
IsScreencastRunning() const517 bool ChannelManager::IsScreencastRunning() const {
518 return initialized_ && worker_thread_->Invoke<bool>(
519 Bind(&ChannelManager::IsScreencastRunning_w, this));
520 }
521
IsScreencastRunning_w() const522 bool ChannelManager::IsScreencastRunning_w() const {
523 VideoChannels::const_iterator it = video_channels_.begin();
524 for ( ; it != video_channels_.end(); ++it) {
525 if ((*it) && (*it)->IsScreencasting()) {
526 return true;
527 }
528 }
529 return false;
530 }
531
OnVideoCaptureStateChange(VideoCapturer * capturer,CaptureState result)532 void ChannelManager::OnVideoCaptureStateChange(VideoCapturer* capturer,
533 CaptureState result) {
534 // TODO(whyuan): Check capturer and signal failure only for camera video, not
535 // screencast.
536 capturing_ = result == CS_RUNNING;
537 main_thread_->Post(this, MSG_VIDEOCAPTURESTATE,
538 new CaptureStateParams(capturer, result));
539 }
540
OnMessage(rtc::Message * message)541 void ChannelManager::OnMessage(rtc::Message* message) {
542 switch (message->message_id) {
543 case MSG_VIDEOCAPTURESTATE: {
544 CaptureStateParams* data =
545 static_cast<CaptureStateParams*>(message->pdata);
546 SignalVideoCaptureStateChange(data->capturer, data->state);
547 delete data;
548 break;
549 }
550 }
551 }
552
StartAecDump(rtc::PlatformFile file)553 bool ChannelManager::StartAecDump(rtc::PlatformFile file) {
554 return worker_thread_->Invoke<bool>(
555 Bind(&MediaEngineInterface::StartAecDump, media_engine_.get(), file));
556 }
557
StopAecDump()558 void ChannelManager::StopAecDump() {
559 worker_thread_->Invoke<void>(
560 Bind(&MediaEngineInterface::StopAecDump, media_engine_.get()));
561 }
562
StartRtcEventLog(rtc::PlatformFile file)563 bool ChannelManager::StartRtcEventLog(rtc::PlatformFile file) {
564 return worker_thread_->Invoke<bool>(
565 Bind(&MediaEngineInterface::StartRtcEventLog, media_engine_.get(), file));
566 }
567
StopRtcEventLog()568 void ChannelManager::StopRtcEventLog() {
569 worker_thread_->Invoke<void>(
570 Bind(&MediaEngineInterface::StopRtcEventLog, media_engine_.get()));
571 }
572
573 } // namespace cricket
574