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
54namespace cricket {
55
56enum {
57  MSG_VIDEOCAPTURESTATE = 1,
58};
59
60using rtc::Bind;
61
62static const int kNotSetOutputVolume = -1;
63
64struct 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
72static DataEngineInterface* ConstructDataEngine() {
73#ifdef HAVE_SCTP
74  return new HybridDataEngine(new RtpDataEngine(), new SctpDataEngine());
75#else
76  return new RtpDataEngine();
77#endif
78}
79
80ChannelManager::ChannelManager(MediaEngineInterface* me,
81                               DataEngineInterface* dme,
82                               CaptureManager* cm,
83                               rtc::Thread* worker_thread) {
84  Construct(me, dme, cm, worker_thread);
85}
86
87ChannelManager::ChannelManager(MediaEngineInterface* me,
88                               rtc::Thread* worker_thread) {
89  Construct(me,
90            ConstructDataEngine(),
91            new CaptureManager(),
92            worker_thread);
93}
94
95void 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
114ChannelManager::~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
130bool 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
145void 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
156void ChannelManager::GetSupportedAudioRtpHeaderExtensions(
157    RtpHeaderExtensions* ext) const {
158  *ext = media_engine_->GetAudioCapabilities().header_extensions;
159}
160
161void 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
175void ChannelManager::GetSupportedVideoRtpHeaderExtensions(
176    RtpHeaderExtensions* ext) const {
177  *ext = media_engine_->GetVideoCapabilities().header_extensions;
178}
179
180void ChannelManager::GetSupportedDataCodecs(
181    std::vector<DataCodec>* codecs) const {
182  *codecs = data_media_engine_->data_codecs();
183}
184
185bool 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
218bool ChannelManager::InitMediaEngine_w() {
219  ASSERT(worker_thread_ == rtc::Thread::Current());
220  return (media_engine_->Init(worker_thread_));
221}
222
223void 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
232void ChannelManager::DestructorDeletes_w() {
233  ASSERT(worker_thread_ == rtc::Thread::Current());
234  media_engine_.reset(NULL);
235  capture_manager_.reset(NULL);
236}
237
238void 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
250VoiceChannel* 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
261VoiceChannel* 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
286void 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
294void 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
308VideoChannel* 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
319VideoChannel* 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
344void 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
352void 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
367DataChannel* 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
377DataChannel* 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
403void 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
411void 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
425bool 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
433bool 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
448std::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
457void 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.
469bool 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
476bool 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
486bool 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
493bool 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
503bool 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
510bool 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
517bool ChannelManager::IsScreencastRunning() const {
518  return initialized_ && worker_thread_->Invoke<bool>(
519      Bind(&ChannelManager::IsScreencastRunning_w, this));
520}
521
522bool 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
532void 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
541void 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
553bool ChannelManager::StartAecDump(rtc::PlatformFile file) {
554  return worker_thread_->Invoke<bool>(
555      Bind(&MediaEngineInterface::StartAecDump, media_engine_.get(), file));
556}
557
558void ChannelManager::StopAecDump() {
559  worker_thread_->Invoke<void>(
560      Bind(&MediaEngineInterface::StopAecDump, media_engine_.get()));
561}
562
563bool ChannelManager::StartRtcEventLog(rtc::PlatformFile file) {
564  return worker_thread_->Invoke<bool>(
565      Bind(&MediaEngineInterface::StartRtcEventLog, media_engine_.get(), file));
566}
567
568void ChannelManager::StopRtcEventLog() {
569  worker_thread_->Invoke<void>(
570      Bind(&MediaEngineInterface::StopRtcEventLog, media_engine_.get()));
571}
572
573}  // namespace cricket
574