diff --git a/third_party_mods/libjingle/libjingle.gyp b/third_party_mods/libjingle/libjingle.gyp index 2a11c8c6d..8ef849d0e 100644 --- a/third_party_mods/libjingle/libjingle.gyp +++ b/third_party_mods/libjingle/libjingle.gyp @@ -458,7 +458,7 @@ '<(libjingle_orig)/source/talk/session/phone/call.h', '<(libjingle_orig)/source/talk/session/phone/channel.cc', '<(libjingle_orig)/source/talk/session/phone/channel.h', - '<(libjingle_orig)/source/talk/session/phone/channelmanager.cc', + '<(libjingle_mods)/source/talk/session/phone/channelmanager.cc', '<(libjingle_orig)/source/talk/session/phone/channelmanager.h', '<(libjingle_orig)/source/talk/session/phone/codec.cc', '<(libjingle_orig)/source/talk/session/phone/codec.h', diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.cc b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.cc index 84701e8e9..896c53224 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.cc +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.cc @@ -33,9 +33,10 @@ PeerConnection* PeerConnection::Create(const std::string& config, cricket::PortAllocator* port_allocator, cricket::MediaEngine* media_engine, talk_base::Thread* worker_thread, + talk_base::Thread* signaling_thread, cricket::DeviceManager* device_manager) { return new PeerConnectionImpl(config, port_allocator, media_engine, - worker_thread, device_manager); + worker_thread, signaling_thread, device_manager); } PeerConnection* PeerConnection::Create(const std::string& config, diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.h b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.h index f3404d016..29a1d6a4d 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.h +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.h @@ -79,6 +79,7 @@ class PeerConnection { cricket::PortAllocator* port_allocator, cricket::MediaEngine* media_engine, talk_base::Thread* worker_thread, + talk_base::Thread* signaling_thread, cricket::DeviceManager* device_manager); static PeerConnection* Create(const std::string& config, cricket::PortAllocator* port_allocator, diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.cc b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.cc index 82c27b3b4..0ded0173b 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.cc +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.cc @@ -134,18 +134,19 @@ PeerConnectionImpl::PeerConnectionImpl(const std::string& config, cricket::PortAllocator* port_allocator, cricket::MediaEngine* media_engine, talk_base::Thread* worker_thread, + talk_base::Thread* signaling_thread, cricket::DeviceManager* device_manager) : config_(config), port_allocator_(port_allocator), media_engine_(media_engine), worker_thread_(worker_thread), device_manager_(device_manager), - signaling_thread_(NULL), initialized_(false), service_type_(SERVICE_COUNT), event_callback_(NULL), session_(NULL), incoming_(false) { + signaling_thread_.reset(signaling_thread); } PeerConnectionImpl::PeerConnectionImpl(const std::string& config, @@ -181,12 +182,13 @@ bool PeerConnectionImpl::Init() { return false; stun_hosts.push_back(stun_addr); - signaling_thread_.reset(new talk_base::Thread()); - ASSERT(signaling_thread_.get()); - if (!signaling_thread_->SetName("signaling thread", this) || - !signaling_thread_->Start()) { - LOG(WARNING) << "Failed to start libjingle signaling thread"; - return false; + if (!signaling_thread_.get()) { + signaling_thread_.reset(new talk_base::Thread()); + if (!signaling_thread_->SetName("signaling thread", this) || + !signaling_thread_->Start()) { + LOG(WARNING) << "Failed to start libjingle signaling thread"; + return false; + } } signaling_thread_->Post(this, MSG_WEBRTC_INIT_CHANNELMANAGER); diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.h b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.h index 8b7f310d7..39cf309c8 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.h +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.h @@ -28,9 +28,10 @@ #ifndef TALK_APP_WEBRTC_PEERCONNECTION_IMPL_H_ #define TALK_APP_WEBRTC_PEERCONNECTION_IMPL_H_ -#include "talk/app/webrtc/peerconnection.h" - #include +#include + +#include "talk/app/webrtc/peerconnection.h" #include "talk/base/sigslot.h" #include "talk/base/scoped_ptr.h" #include "talk/base/packetsocketfactory.h" @@ -62,6 +63,7 @@ class PeerConnectionImpl : public PeerConnection, cricket::PortAllocator* port_allocator, cricket::MediaEngine* media_engine, talk_base::Thread* worker_thread, + talk_base::Thread* signaling_thread, cricket::DeviceManager* device_manager); PeerConnectionImpl(const std::string& config, cricket::PortAllocator* port_allocator, diff --git a/third_party_mods/libjingle/source/talk/session/phone/channelmanager.cc b/third_party_mods/libjingle/source/talk/session/phone/channelmanager.cc new file mode 100644 index 000000000..0fda744b2 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/channelmanager.cc @@ -0,0 +1,799 @@ +/* + * libjingle + * Copyright 2004--2008, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/session/phone/channelmanager.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/sigslotrepeater.h" +#include "talk/base/stringencode.h" +#include "talk/session/phone/mediaengine.h" +#include "talk/session/phone/soundclip.h" + +namespace cricket { + +enum { + MSG_CREATEVOICECHANNEL = 1, + MSG_DESTROYVOICECHANNEL = 2, + MSG_SETAUDIOOPTIONS = 3, + MSG_GETOUTPUTVOLUME = 4, + MSG_SETOUTPUTVOLUME = 5, + MSG_SETLOCALMONITOR = 6, + MSG_SETVOICELOGGING = 7, + MSG_CREATEVIDEOCHANNEL = 11, + MSG_DESTROYVIDEOCHANNEL = 12, + MSG_SETVIDEOOPTIONS = 13, + MSG_SETLOCALRENDERER = 14, + MSG_SETDEFAULTVIDEOENCODERCONFIG = 15, + MSG_SETVIDEOLOGGING = 16, + MSG_CREATESOUNDCLIP = 17, + MSG_DESTROYSOUNDCLIP = 18, + MSG_CAMERASTARTED = 19, + MSG_SETVIDEOCAPTURE = 20, +}; + +struct CreationParams : public talk_base::MessageData { + CreationParams(BaseSession* session, const std::string& content_name, + bool rtcp, VoiceChannel* voice_channel) + : session(session), + content_name(content_name), + rtcp(rtcp), + voice_channel(voice_channel), + video_channel(NULL) {} + BaseSession* session; + std::string content_name; + bool rtcp; + VoiceChannel* voice_channel; + VideoChannel* video_channel; +}; + +struct AudioOptions : public talk_base::MessageData { + AudioOptions(int o, const Device* in, const Device* out) + : options(o), in_device(in), out_device(out) {} + int options; + const Device* in_device; + const Device* out_device; + bool result; +}; + +struct VolumeLevel : public talk_base::MessageData { + VolumeLevel() : level(-1), result(false) {} + explicit VolumeLevel(int l) : level(l), result(false) {} + int level; + bool result; +}; + +struct VideoOptions : public talk_base::MessageData { + explicit VideoOptions(const Device* d) : cam_device(d), result(false) {} + const Device* cam_device; + bool result; +}; + +struct DefaultVideoEncoderConfig : public talk_base::MessageData { + explicit DefaultVideoEncoderConfig(const VideoEncoderConfig& c) + : config(c), result(false) {} + VideoEncoderConfig config; + bool result; +}; + +struct LocalMonitor : public talk_base::MessageData { + explicit LocalMonitor(bool e) : enable(e), result(false) {} + bool enable; + bool result; +}; + +struct LocalRenderer : public talk_base::MessageData { + explicit LocalRenderer(VideoRenderer* r) : renderer(r), result(false) {} + VideoRenderer* renderer; + bool result; +}; + +struct LoggingOptions : public talk_base::MessageData { + explicit LoggingOptions(int lev, const char* f) : level(lev), filter(f) {} + int level; + std::string filter; +}; + +struct CaptureParams : public talk_base::MessageData { + explicit CaptureParams(bool c) : capture(c), result(CR_FAILURE) {} + + bool capture; + CaptureResult result; +}; + +ChannelManager::ChannelManager(talk_base::Thread* worker_thread) + : media_engine_(MediaEngine::Create()), + device_manager_(new DeviceManager()), + initialized_(false), + main_thread_(talk_base::Thread::Current()), + worker_thread_(worker_thread), + audio_in_device_(DeviceManager::kDefaultDeviceName), + audio_out_device_(DeviceManager::kDefaultDeviceName), + audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS), + local_renderer_(NULL), + capturing_(false), + monitoring_(false) { + Construct(); +} + +ChannelManager::ChannelManager(MediaEngine* me, DeviceManager* dm, + talk_base::Thread* worker_thread) + : media_engine_(me), + device_manager_(dm), + initialized_(false), + main_thread_(talk_base::Thread::Current()), + worker_thread_(worker_thread), + audio_in_device_(DeviceManager::kDefaultDeviceName), + audio_out_device_(DeviceManager::kDefaultDeviceName), + audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS), + local_renderer_(NULL), + capturing_(false), + monitoring_(false) { + Construct(); +} + +void ChannelManager::Construct() { + // Init the device manager immediately, and set up our default video device. + SignalDevicesChange.repeat(device_manager_->SignalDevicesChange); + device_manager_->Init(); + // Set camera_device_ to the name of the default video capturer. + SetVideoOptions(DeviceManager::kDefaultDeviceName); + + // Camera is started asynchronously, request callbacks when startup + // completes to be able to forward them to the rendering manager. + media_engine_->SignalVideoCaptureResult.connect( + this, &ChannelManager::OnVideoCaptureResult); +} + +ChannelManager::~ChannelManager() { + if (initialized_) + Terminate(); +} + +int ChannelManager::GetCapabilities() { + return media_engine_->GetCapabilities() & device_manager_->GetCapabilities(); +} + +void ChannelManager::GetSupportedAudioCodecs( + std::vector* codecs) const { + codecs->clear(); + + for (std::vector::const_iterator it = + media_engine_->audio_codecs().begin(); + it != media_engine_->audio_codecs().end(); ++it) { + codecs->push_back(*it); + } +} + +void ChannelManager::GetSupportedVideoCodecs( + std::vector* codecs) const { + codecs->clear(); + + std::vector::const_iterator it; + for (it = media_engine_->video_codecs().begin(); + it != media_engine_->video_codecs().end(); ++it) { + codecs->push_back(*it); + } +} + +bool ChannelManager::Init() { + ASSERT(!initialized_); + if (initialized_) { + return false; + } + + ASSERT(worker_thread_ != NULL); + if (worker_thread_) { + if (media_engine_->Init()) { + initialized_ = true; + + // Now that we're initialized, apply any stored preferences. A preferred + // device might have been unplugged. In this case, we fallback to the + // default device but keep the user preferences. The preferences are + // changed only when the Javascript FE changes them. + const std::string preferred_audio_in_device = audio_in_device_; + const std::string preferred_audio_out_device = audio_out_device_; + const std::string preferred_camera_device = camera_device_; + Device device; + if (!device_manager_->GetAudioInputDevice(audio_in_device_, &device)) { + LOG(LS_WARNING) << "The preferred microphone '" << audio_in_device_ + << "' is unavailable. Fall back to the default."; + audio_in_device_ = DeviceManager::kDefaultDeviceName; + } + if (!device_manager_->GetAudioOutputDevice(audio_out_device_, &device)) { + LOG(LS_WARNING) << "The preferred speaker '" << audio_out_device_ + << "' is unavailable. Fall back to the default."; + audio_out_device_ = DeviceManager::kDefaultDeviceName; + } + if (!device_manager_->GetVideoCaptureDevice(camera_device_, &device)) { + if (!camera_device_.empty()) { + LOG(LS_WARNING) << "The preferred camera '" << camera_device_ + << "' is unavailable. Fall back to the default."; + } + camera_device_ = DeviceManager::kDefaultDeviceName; + } + + if (!SetAudioOptions(audio_in_device_, audio_out_device_, + audio_options_)) { + LOG(LS_WARNING) << "Failed to SetAudioOptions with" + << " microphone: " << audio_in_device_ + << " speaker: " << audio_out_device_ + << " options: " << audio_options_; + } + if (!SetVideoOptions(camera_device_) && !camera_device_.empty()) { + LOG(LS_WARNING) << "Failed to SetVideoOptions with camera: " + << camera_device_; + } + + // Restore the user preferences. + audio_in_device_ = preferred_audio_in_device; + audio_out_device_ = preferred_audio_out_device; + camera_device_ = preferred_camera_device; + + // Now apply the default video codec that has been set earlier. + if (default_video_encoder_config_.max_codec.id != 0) { + SetDefaultVideoEncoderConfig(default_video_encoder_config_); + } + // And the local renderer. + if (local_renderer_) { + SetLocalRenderer(local_renderer_); + } + } + } + return initialized_; +} + +void ChannelManager::Terminate() { + ASSERT(initialized_); + if (!initialized_) { + return; + } + + // Need to destroy the voice/video channels + while (!video_channels_.empty()) { + DestroyVideoChannel_w(video_channels_.back()); + } + while (!voice_channels_.empty()) { + DestroyVoiceChannel_w(voice_channels_.back()); + } + while (!soundclips_.empty()) { + DestroySoundclip_w(soundclips_.back()); + } + + media_engine_->Terminate(); + initialized_ = false; +} + +VoiceChannel* ChannelManager::CreateVoiceChannel( + BaseSession* session, const std::string& content_name, bool rtcp) { + CreationParams params(session, content_name, rtcp, NULL); + return (Send(MSG_CREATEVOICECHANNEL, ¶ms)) ? params.voice_channel : NULL; +} + +VoiceChannel* ChannelManager::CreateVoiceChannel_w( + BaseSession* session, const std::string& content_name, bool rtcp) { + talk_base::CritScope cs(&crit_); + + // This is ok to alloc from a thread other than the worker thread + ASSERT(initialized_); + VoiceMediaChannel* media_channel = media_engine_->CreateChannel(); + if (media_channel == NULL) + return NULL; + + VoiceChannel* voice_channel = new VoiceChannel( + worker_thread_, media_engine_.get(), media_channel, + session, content_name, rtcp); + voice_channels_.push_back(voice_channel); + return voice_channel; +} + +void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) { + if (voice_channel) { + talk_base::TypedMessageData data(voice_channel); + Send(MSG_DESTROYVOICECHANNEL, &data); + } +} + +void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) { + talk_base::CritScope cs(&crit_); + // Destroy voice channel. + ASSERT(initialized_); + VoiceChannels::iterator it = std::find(voice_channels_.begin(), + voice_channels_.end(), voice_channel); + ASSERT(it != voice_channels_.end()); + if (it == voice_channels_.end()) + return; + + voice_channels_.erase(it); + delete voice_channel; +} + +VideoChannel* ChannelManager::CreateVideoChannel( + BaseSession* session, const std::string& content_name, bool rtcp, + VoiceChannel* voice_channel) { + CreationParams params(session, content_name, rtcp, voice_channel); + return (Send(MSG_CREATEVIDEOCHANNEL, ¶ms)) ? params.video_channel : NULL; +} + +VideoChannel* ChannelManager::CreateVideoChannel_w( + BaseSession* session, const std::string& content_name, bool rtcp, + VoiceChannel* voice_channel) { + talk_base::CritScope cs(&crit_); + + // This is ok to alloc from a thread other than the worker thread + ASSERT(initialized_); + VideoMediaChannel* media_channel = + // voice_channel can be NULL in case of NullVoiceEngine. + media_engine_->CreateVideoChannel(voice_channel ? + voice_channel->media_channel() : NULL); + if (media_channel == NULL) + return NULL; + + VideoChannel* video_channel = new VideoChannel( + worker_thread_, media_engine_.get(), media_channel, + session, content_name, rtcp, voice_channel); + video_channels_.push_back(video_channel); + return video_channel; +} + +void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) { + if (video_channel) { + talk_base::TypedMessageData data(video_channel); + Send(MSG_DESTROYVIDEOCHANNEL, &data); + } +} + +void ChannelManager::DestroyVideoChannel_w(VideoChannel *video_channel) { + talk_base::CritScope cs(&crit_); + // Destroy voice channel. + ASSERT(initialized_); + VideoChannels::iterator it = std::find(video_channels_.begin(), + video_channels_.end(), video_channel); + ASSERT(it != video_channels_.end()); + if (it == video_channels_.end()) + return; + + video_channels_.erase(it); + delete video_channel; +} + +Soundclip* ChannelManager::CreateSoundclip() { + talk_base::TypedMessageData data(NULL); + Send(MSG_CREATESOUNDCLIP, &data); + return data.data(); +} + +Soundclip* ChannelManager::CreateSoundclip_w() { + talk_base::CritScope cs(&crit_); + + ASSERT(initialized_); + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + SoundclipMedia* soundclip_media = media_engine_->CreateSoundclip(); + if (!soundclip_media) { + return NULL; + } + + Soundclip* soundclip = new Soundclip(worker_thread_, soundclip_media); + soundclips_.push_back(soundclip); + return soundclip; +} + +void ChannelManager::DestroySoundclip(Soundclip* soundclip) { + if (soundclip) { + talk_base::TypedMessageData data(soundclip); + Send(MSG_DESTROYSOUNDCLIP, &data); + } +} + +void ChannelManager::DestroySoundclip_w(Soundclip* soundclip) { + talk_base::CritScope cs(&crit_); + // Destroy soundclip. + ASSERT(initialized_); + Soundclips::iterator it = std::find(soundclips_.begin(), + soundclips_.end(), soundclip); + ASSERT(it != soundclips_.end()); + if (it == soundclips_.end()) + return; + + soundclips_.erase(it); + delete soundclip; +} + +bool ChannelManager::GetAudioOptions(std::string* in_name, + std::string* out_name, int* opts) { + *in_name = audio_in_device_; + *out_name = audio_out_device_; + *opts = audio_options_; + return true; +} + +bool ChannelManager::SetAudioOptions(const std::string& in_name, + const std::string& out_name, int opts) { + // Get device ids from DeviceManager. + Device in_dev, out_dev; + if (!device_manager_->GetAudioInputDevice(in_name, &in_dev)) { + LOG(LS_WARNING) << "Failed to GetAudioInputDevice: " << in_name; + return false; + } + if (!device_manager_->GetAudioOutputDevice(out_name, &out_dev)) { + LOG(LS_WARNING) << "Failed to GetAudioOutputDevice: " << out_name; + return false; + } + + // If we're initialized, pass the settings to the media engine. + bool ret = true; + if (initialized_) { + AudioOptions options(opts, &in_dev, &out_dev); + ret = (Send(MSG_SETAUDIOOPTIONS, &options) && options.result); + } + + // If all worked well, save the values for use in GetAudioOptions. + if (ret) { + audio_options_ = opts; + audio_in_device_ = in_name; + audio_out_device_ = out_name; + } + return ret; +} + +bool ChannelManager::SetAudioOptions_w(int opts, const Device* in_dev, + const Device* out_dev) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + + // Set audio options + bool ret = media_engine_->SetAudioOptions(opts); + + // Set the audio devices + if (ret) { + talk_base::CritScope cs(&crit_); + ret = media_engine_->SetSoundDevices(in_dev, out_dev); + } + + return ret; +} + +bool ChannelManager::GetOutputVolume(int* level) { + VolumeLevel volume; + if (!Send(MSG_GETOUTPUTVOLUME, &volume) || !volume.result) { + return false; + } + + *level = volume.level; + return true; +} + +bool ChannelManager::GetOutputVolume_w(int* level) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + return media_engine_->GetOutputVolume(level); +} + +bool ChannelManager::SetOutputVolume(int level) { + VolumeLevel volume(level); + return (Send(MSG_SETOUTPUTVOLUME, &volume) && volume.result); +} + +bool ChannelManager::SetOutputVolume_w(int level) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + return media_engine_->SetOutputVolume(level); +} + +bool ChannelManager::GetVideoOptions(std::string* cam_name) { + *cam_name = camera_device_; + return true; +} + +bool ChannelManager::SetVideoOptions(const std::string& cam_name) { + Device device; + if (!device_manager_->GetVideoCaptureDevice(cam_name, &device)) { + if (!cam_name.empty()) { + LOG(LS_WARNING) << "Device manager can't find camera: " << cam_name; + } + return false; + } + + // If we're running, tell the media engine about it. + bool ret = true; + if (initialized_) { + VideoOptions options(&device); + ret = (Send(MSG_SETVIDEOOPTIONS, &options) && options.result); + } + + // If everything worked, retain the name of the selected camera. + if (ret) { + camera_device_ = device.name; + } + return ret; +} + +bool ChannelManager::SetVideoOptions_w(const Device* cam_device) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + + // Set the video input device + return media_engine_->SetVideoCaptureDevice(cam_device); +} + +bool ChannelManager::SetDefaultVideoEncoderConfig(const VideoEncoderConfig& c) { + bool ret = true; + if (initialized_) { + DefaultVideoEncoderConfig config(c); + ret = Send(MSG_SETDEFAULTVIDEOENCODERCONFIG, &config) && config.result; + } + if (ret) { + default_video_encoder_config_ = c; + } + return ret; +} + +bool ChannelManager::SetDefaultVideoEncoderConfig_w( + const VideoEncoderConfig& c) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + return media_engine_->SetDefaultVideoEncoderConfig(c); +} + +bool ChannelManager::SetLocalMonitor(bool enable) { + LocalMonitor monitor(enable); + bool ret = Send(MSG_SETLOCALMONITOR, &monitor) && monitor.result; + if (ret) { + monitoring_ = enable; + } + return ret; +} + +bool ChannelManager::SetLocalMonitor_w(bool enable) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + return media_engine_->SetLocalMonitor(enable); +} + +bool ChannelManager::SetLocalRenderer(VideoRenderer* renderer) { + bool ret = true; + if (initialized_) { + LocalRenderer local(renderer); + ret = (Send(MSG_SETLOCALRENDERER, &local) && local.result); + } + if (ret) { + local_renderer_ = renderer; + } + return ret; +} + +bool ChannelManager::SetLocalRenderer_w(VideoRenderer* renderer) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + return media_engine_->SetLocalRenderer(renderer); +} + +CaptureResult ChannelManager::SetVideoCapture(bool capture) { + bool ret; + CaptureParams capture_params(capture); + ret = (Send(MSG_SETVIDEOCAPTURE, &capture_params) && + (capture_params.result != CR_FAILURE)); + if (ret) { + capturing_ = capture; + } + return capture_params.result; +} + +CaptureResult ChannelManager::SetVideoCapture_w(bool capture) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + return media_engine_->SetVideoCapture(capture); +} + +void ChannelManager::SetVoiceLogging(int level, const char* filter) { + SetMediaLogging(false, level, filter); +} + +void ChannelManager::SetVideoLogging(int level, const char* filter) { + SetMediaLogging(true, level, filter); +} + +void ChannelManager::SetMediaLogging(bool video, int level, + const char* filter) { + // Can be called before initialization; in this case, the worker function + // is simply called on the main thread. + if (initialized_) { + LoggingOptions options(level, filter); + Send((video) ? MSG_SETVIDEOLOGGING : MSG_SETVOICELOGGING, &options); + } else { + SetMediaLogging_w(video, level, filter); + } +} + +void ChannelManager::SetMediaLogging_w(bool video, int level, + const char* filter) { + // Can be called before initialization + ASSERT(worker_thread_ == talk_base::Thread::Current() || !initialized_); + if (video) { + media_engine_->SetVideoLogging(level, filter); + } else { + media_engine_->SetVoiceLogging(level, filter); + } +} + +bool ChannelManager::Send(uint32 id, talk_base::MessageData* data) { + if (!worker_thread_ || !initialized_) return false; + worker_thread_->Send(this, id, data); + return true; +} + +void ChannelManager::OnVideoCaptureResult(CaptureResult result) { + capturing_ = result == CR_SUCCESS; + main_thread_->Post(this, MSG_CAMERASTARTED, + new talk_base::TypedMessageData(result)); +} + +void ChannelManager::OnMessage(talk_base::Message* message) { + talk_base::MessageData* data = message->pdata; + switch (message->message_id) { + case MSG_CREATEVOICECHANNEL: { + CreationParams* p = static_cast(data); + p->voice_channel = + CreateVoiceChannel_w(p->session, p->content_name, p->rtcp); + break; + } + case MSG_DESTROYVOICECHANNEL: { + VoiceChannel* p = static_cast*> + (data)->data(); + DestroyVoiceChannel_w(p); + break; + } + case MSG_CREATEVIDEOCHANNEL: { + CreationParams* p = static_cast(data); + p->video_channel = CreateVideoChannel_w(p->session, p->content_name, + p->rtcp, p->voice_channel); + break; + } + case MSG_DESTROYVIDEOCHANNEL: { + VideoChannel* p = static_cast*> + (data)->data(); + DestroyVideoChannel_w(p); + break; + } + case MSG_CREATESOUNDCLIP: { + talk_base::TypedMessageData *p = + static_cast*>(data); + p->data() = CreateSoundclip_w(); + break; + } + case MSG_DESTROYSOUNDCLIP: { + talk_base::TypedMessageData *p = + static_cast*>(data); + DestroySoundclip_w(p->data()); + break; + } + case MSG_SETAUDIOOPTIONS: { + AudioOptions* p = static_cast(data); + p->result = SetAudioOptions_w(p->options, + p->in_device, p->out_device); + break; + } + case MSG_GETOUTPUTVOLUME: { + VolumeLevel* p = static_cast(data); + p->result = GetOutputVolume_w(&p->level); + break; + } + case MSG_SETOUTPUTVOLUME: { + VolumeLevel* p = static_cast(data); + p->result = SetOutputVolume_w(p->level); + break; + } + case MSG_SETLOCALMONITOR: { + LocalMonitor* p = static_cast(data); + p->result = SetLocalMonitor_w(p->enable); + break; + } + case MSG_SETVIDEOOPTIONS: { + VideoOptions* p = static_cast(data); + p->result = SetVideoOptions_w(p->cam_device); + break; + } + case MSG_SETDEFAULTVIDEOENCODERCONFIG: { + DefaultVideoEncoderConfig* p = + static_cast(data); + p->result = SetDefaultVideoEncoderConfig_w(p->config); + break; + } + case MSG_SETLOCALRENDERER: { + LocalRenderer* p = static_cast(data); + p->result = SetLocalRenderer_w(p->renderer); + break; + } + case MSG_SETVIDEOCAPTURE: { + CaptureParams* p = static_cast(data); + p->result = SetVideoCapture_w(p->capture); + break; + } + case MSG_SETVOICELOGGING: + case MSG_SETVIDEOLOGGING: { + LoggingOptions* p = static_cast(data); + bool video = (message->message_id == MSG_SETVIDEOLOGGING); + SetMediaLogging_w(video, p->level, p->filter.c_str()); + break; + } + case MSG_CAMERASTARTED: { + talk_base::TypedMessageData* data = + static_cast*>( + message->pdata); + SignalVideoCaptureResult(data->data()); + delete data; + break; + } + } +} + +static void GetDeviceNames(const std::vector& devs, + std::vector* names) { + names->clear(); + for (size_t i = 0; i < devs.size(); ++i) { + names->push_back(devs[i].name); + } +} + +bool ChannelManager::GetAudioInputDevices(std::vector* names) { + names->clear(); + std::vector devs; + bool ret = device_manager_->GetAudioInputDevices(&devs); + if (ret) + GetDeviceNames(devs, names); + + return ret; +} + +bool ChannelManager::GetAudioOutputDevices(std::vector* names) { + names->clear(); + std::vector devs; + bool ret = device_manager_->GetAudioOutputDevices(&devs); + if (ret) + GetDeviceNames(devs, names); + + return ret; +} + +bool ChannelManager::GetVideoCaptureDevices(std::vector* names) { + names->clear(); + std::vector devs; + bool ret = device_manager_->GetVideoCaptureDevices(&devs); + if (ret) + GetDeviceNames(devs, names); + + return ret; +} + +} // namespace cricket