OLD | NEW |
| (Empty) |
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/session/media/srtpfilter.h" | |
37 #include "webrtc/api/mediacontroller.h" | |
38 #include "webrtc/base/bind.h" | |
39 #include "webrtc/base/common.h" | |
40 #include "webrtc/base/logging.h" | |
41 #include "webrtc/base/sigslotrepeater.h" | |
42 #include "webrtc/base/stringencode.h" | |
43 #include "webrtc/base/stringutils.h" | |
44 #include "webrtc/base/trace_event.h" | |
45 #include "webrtc/media/base/capturemanager.h" | |
46 #include "webrtc/media/base/device.h" | |
47 #include "webrtc/media/base/hybriddataengine.h" | |
48 #include "webrtc/media/base/rtpdataengine.h" | |
49 #include "webrtc/media/base/videocapturer.h" | |
50 #ifdef HAVE_SCTP | |
51 #include "webrtc/media/sctp/sctpdataengine.h" | |
52 #endif | |
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 { | |
65 CaptureStateParams(cricket::VideoCapturer* c, cricket::CaptureState s) | |
66 : capturer(c), | |
67 state(s) {} | |
68 cricket::VideoCapturer* capturer; | |
69 cricket::CaptureState state; | |
70 }; | |
71 | |
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 | |
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 | |
87 ChannelManager::ChannelManager(MediaEngineInterface* me, | |
88 rtc::Thread* worker_thread) { | |
89 Construct(me, | |
90 ConstructDataEngine(), | |
91 new CaptureManager(), | |
92 worker_thread); | |
93 } | |
94 | |
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 | |
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 | |
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 | |
145 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 | |
156 void ChannelManager::GetSupportedAudioRtpHeaderExtensions( | |
157 RtpHeaderExtensions* ext) const { | |
158 *ext = media_engine_->GetAudioCapabilities().header_extensions; | |
159 } | |
160 | |
161 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 | |
175 void ChannelManager::GetSupportedVideoRtpHeaderExtensions( | |
176 RtpHeaderExtensions* ext) const { | |
177 *ext = media_engine_->GetVideoCapabilities().header_extensions; | |
178 } | |
179 | |
180 void ChannelManager::GetSupportedDataCodecs( | |
181 std::vector<DataCodec>* codecs) const { | |
182 *codecs = data_media_engine_->data_codecs(); | |
183 } | |
184 | |
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 | |
218 bool ChannelManager::InitMediaEngine_w() { | |
219 ASSERT(worker_thread_ == rtc::Thread::Current()); | |
220 return (media_engine_->Init(worker_thread_)); | |
221 } | |
222 | |
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 | |
232 void ChannelManager::DestructorDeletes_w() { | |
233 ASSERT(worker_thread_ == rtc::Thread::Current()); | |
234 media_engine_.reset(NULL); | |
235 capture_manager_.reset(NULL); | |
236 } | |
237 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
448 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 | |
457 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. | |
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 | |
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 | |
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 | |
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 | |
503 void ChannelManager::AddVideoSink( | |
504 VideoCapturer* capturer, rtc::VideoSinkInterface<VideoFrame>* sink) { | |
505 if (initialized_) | |
506 worker_thread_->Invoke<void>( | |
507 Bind(&CaptureManager::AddVideoSink, | |
508 capture_manager_.get(), capturer, sink)); | |
509 } | |
510 | |
511 void ChannelManager::RemoveVideoSink( | |
512 VideoCapturer* capturer, rtc::VideoSinkInterface<VideoFrame>* sink) { | |
513 if (initialized_) | |
514 worker_thread_->Invoke<void>( | |
515 Bind(&CaptureManager::RemoveVideoSink, | |
516 capture_manager_.get(), capturer, sink)); | |
517 } | |
518 | |
519 bool ChannelManager::IsScreencastRunning() const { | |
520 return initialized_ && worker_thread_->Invoke<bool>( | |
521 Bind(&ChannelManager::IsScreencastRunning_w, this)); | |
522 } | |
523 | |
524 bool ChannelManager::IsScreencastRunning_w() const { | |
525 VideoChannels::const_iterator it = video_channels_.begin(); | |
526 for ( ; it != video_channels_.end(); ++it) { | |
527 if ((*it) && (*it)->IsScreencasting()) { | |
528 return true; | |
529 } | |
530 } | |
531 return false; | |
532 } | |
533 | |
534 void ChannelManager::OnVideoCaptureStateChange(VideoCapturer* capturer, | |
535 CaptureState result) { | |
536 // TODO(whyuan): Check capturer and signal failure only for camera video, not | |
537 // screencast. | |
538 capturing_ = result == CS_RUNNING; | |
539 main_thread_->Post(this, MSG_VIDEOCAPTURESTATE, | |
540 new CaptureStateParams(capturer, result)); | |
541 } | |
542 | |
543 void ChannelManager::OnMessage(rtc::Message* message) { | |
544 switch (message->message_id) { | |
545 case MSG_VIDEOCAPTURESTATE: { | |
546 CaptureStateParams* data = | |
547 static_cast<CaptureStateParams*>(message->pdata); | |
548 SignalVideoCaptureStateChange(data->capturer, data->state); | |
549 delete data; | |
550 break; | |
551 } | |
552 } | |
553 } | |
554 | |
555 bool ChannelManager::StartAecDump(rtc::PlatformFile file, | |
556 int64_t max_size_bytes) { | |
557 return worker_thread_->Invoke<bool>(Bind(&MediaEngineInterface::StartAecDump, | |
558 media_engine_.get(), file, | |
559 max_size_bytes)); | |
560 } | |
561 | |
562 void ChannelManager::StopAecDump() { | |
563 worker_thread_->Invoke<void>( | |
564 Bind(&MediaEngineInterface::StopAecDump, media_engine_.get())); | |
565 } | |
566 | |
567 bool ChannelManager::StartRtcEventLog(rtc::PlatformFile file) { | |
568 return worker_thread_->Invoke<bool>( | |
569 Bind(&MediaEngineInterface::StartRtcEventLog, media_engine_.get(), file)); | |
570 } | |
571 | |
572 void ChannelManager::StopRtcEventLog() { | |
573 worker_thread_->Invoke<void>( | |
574 Bind(&MediaEngineInterface::StopRtcEventLog, media_engine_.get())); | |
575 } | |
576 | |
577 } // namespace cricket | |
OLD | NEW |