| /* |
| * libjingle |
| * Copyright 2012 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/media/base/capturemanager.h" |
| |
| #include <algorithm> |
| |
| #include "talk/media/base/videocapturer.h" |
| #include "talk/media/base/videorenderer.h" |
| #include "webrtc/base/checks.h" |
| #include "webrtc/base/logging.h" |
| |
| namespace cricket { |
| |
| // CaptureManager helper class. |
| class VideoCapturerState { |
| public: |
| static const VideoFormatPod kDefaultCaptureFormat; |
| |
| static VideoCapturerState* Create(VideoCapturer* video_capturer); |
| ~VideoCapturerState() {} |
| |
| void AddCaptureResolution(const VideoFormat& desired_format); |
| bool RemoveCaptureResolution(const VideoFormat& format); |
| VideoFormat GetHighestFormat(VideoCapturer* video_capturer) const; |
| |
| int IncCaptureStartRef(); |
| int DecCaptureStartRef(); |
| CaptureRenderAdapter* adapter() { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| return adapter_.get(); |
| } |
| VideoCapturer* GetVideoCapturer() { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| return adapter()->video_capturer(); |
| } |
| |
| int start_count() const { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| return start_count_; |
| } |
| |
| private: |
| struct CaptureResolutionInfo { |
| VideoFormat video_format; |
| int format_ref_count; |
| }; |
| typedef std::vector<CaptureResolutionInfo> CaptureFormats; |
| |
| explicit VideoCapturerState(CaptureRenderAdapter* adapter); |
| |
| rtc::ThreadChecker thread_checker_; |
| rtc::scoped_ptr<CaptureRenderAdapter> adapter_; |
| |
| int start_count_; |
| CaptureFormats capture_formats_; |
| }; |
| |
| const VideoFormatPod VideoCapturerState::kDefaultCaptureFormat = { |
| 640, 360, FPS_TO_INTERVAL(30), FOURCC_ANY |
| }; |
| |
| VideoCapturerState::VideoCapturerState(CaptureRenderAdapter* adapter) |
| : adapter_(adapter), start_count_(1) {} |
| |
| // static |
| VideoCapturerState* VideoCapturerState::Create(VideoCapturer* video_capturer) { |
| CaptureRenderAdapter* adapter = CaptureRenderAdapter::Create(video_capturer); |
| if (!adapter) { |
| return NULL; |
| } |
| return new VideoCapturerState(adapter); |
| } |
| |
| void VideoCapturerState::AddCaptureResolution( |
| const VideoFormat& desired_format) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| for (CaptureFormats::iterator iter = capture_formats_.begin(); |
| iter != capture_formats_.end(); ++iter) { |
| if (desired_format == iter->video_format) { |
| ++(iter->format_ref_count); |
| return; |
| } |
| } |
| CaptureResolutionInfo capture_resolution = { desired_format, 1 }; |
| capture_formats_.push_back(capture_resolution); |
| } |
| |
| bool VideoCapturerState::RemoveCaptureResolution(const VideoFormat& format) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| for (CaptureFormats::iterator iter = capture_formats_.begin(); |
| iter != capture_formats_.end(); ++iter) { |
| if (format == iter->video_format) { |
| --(iter->format_ref_count); |
| if (iter->format_ref_count == 0) { |
| capture_formats_.erase(iter); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| VideoFormat VideoCapturerState::GetHighestFormat( |
| VideoCapturer* video_capturer) const { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| VideoFormat highest_format(0, 0, VideoFormat::FpsToInterval(1), FOURCC_ANY); |
| if (capture_formats_.empty()) { |
| VideoFormat default_format(kDefaultCaptureFormat); |
| return default_format; |
| } |
| for (CaptureFormats::const_iterator iter = capture_formats_.begin(); |
| iter != capture_formats_.end(); ++iter) { |
| if (iter->video_format.width > highest_format.width) { |
| highest_format.width = iter->video_format.width; |
| } |
| if (iter->video_format.height > highest_format.height) { |
| highest_format.height = iter->video_format.height; |
| } |
| if (iter->video_format.interval < highest_format.interval) { |
| highest_format.interval = iter->video_format.interval; |
| } |
| } |
| return highest_format; |
| } |
| |
| int VideoCapturerState::IncCaptureStartRef() { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| return ++start_count_; |
| } |
| |
| int VideoCapturerState::DecCaptureStartRef() { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| if (start_count_ > 0) { |
| // Start count may be 0 if a capturer was added but never started. |
| --start_count_; |
| } |
| return start_count_; |
| } |
| |
| CaptureManager::CaptureManager() { |
| // Allowing construction of manager in any thread as long as subsequent calls |
| // are all from the same thread. |
| thread_checker_.DetachFromThread(); |
| } |
| |
| CaptureManager::~CaptureManager() { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // Since we don't own any of the capturers, all capturers should have been |
| // cleaned up before we get here. In fact, in the normal shutdown sequence, |
| // all capturers *will* be shut down by now, so trying to stop them here |
| // will crash. If we're still tracking any, it's a dangling pointer. |
| // TODO(hbos): RTC_DCHECK instead of RTC_CHECK until we figure out why |
| // capture_states_ is not always empty here. |
| RTC_DCHECK(capture_states_.empty()); |
| } |
| |
| bool CaptureManager::StartVideoCapture(VideoCapturer* video_capturer, |
| const VideoFormat& desired_format) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| if (desired_format.width == 0 || desired_format.height == 0) { |
| return false; |
| } |
| if (!video_capturer) { |
| return false; |
| } |
| VideoCapturerState* capture_state = GetCaptureState(video_capturer); |
| if (capture_state) { |
| const int ref_count = capture_state->IncCaptureStartRef(); |
| if (ref_count < 1) { |
| ASSERT(false); |
| } |
| // VideoCapturer has already been started. Don't start listening to |
| // callbacks since that has already been done. |
| capture_state->AddCaptureResolution(desired_format); |
| return true; |
| } |
| if (!RegisterVideoCapturer(video_capturer)) { |
| return false; |
| } |
| capture_state = GetCaptureState(video_capturer); |
| ASSERT(capture_state != NULL); |
| capture_state->AddCaptureResolution(desired_format); |
| if (!StartWithBestCaptureFormat(capture_state, video_capturer)) { |
| UnregisterVideoCapturer(capture_state); |
| return false; |
| } |
| return true; |
| } |
| |
| bool CaptureManager::StopVideoCapture(VideoCapturer* video_capturer, |
| const VideoFormat& format) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| VideoCapturerState* capture_state = GetCaptureState(video_capturer); |
| if (!capture_state) { |
| return false; |
| } |
| if (!capture_state->RemoveCaptureResolution(format)) { |
| return false; |
| } |
| |
| if (capture_state->DecCaptureStartRef() == 0) { |
| // Unregistering cannot fail as capture_state is not NULL. |
| UnregisterVideoCapturer(capture_state); |
| } |
| return true; |
| } |
| |
| bool CaptureManager::RestartVideoCapture( |
| VideoCapturer* video_capturer, |
| const VideoFormat& previous_format, |
| const VideoFormat& desired_format, |
| CaptureManager::RestartOptions options) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!IsCapturerRegistered(video_capturer)) { |
| LOG(LS_ERROR) << "RestartVideoCapture: video_capturer is not registered."; |
| return false; |
| } |
| // Start the new format first. This keeps the capturer running. |
| if (!StartVideoCapture(video_capturer, desired_format)) { |
| LOG(LS_ERROR) << "RestartVideoCapture: unable to start video capture with " |
| "desired_format=" << desired_format.ToString(); |
| return false; |
| } |
| // Stop the old format. |
| if (!StopVideoCapture(video_capturer, previous_format)) { |
| LOG(LS_ERROR) << "RestartVideoCapture: unable to stop video capture with " |
| "previous_format=" << previous_format.ToString(); |
| // Undo the start request we just performed. |
| StopVideoCapture(video_capturer, desired_format); |
| return false; |
| } |
| |
| switch (options) { |
| case kForceRestart: { |
| VideoCapturerState* capture_state = GetCaptureState(video_capturer); |
| ASSERT(capture_state && capture_state->start_count() > 0); |
| // Try a restart using the new best resolution. |
| VideoFormat highest_asked_format = |
| capture_state->GetHighestFormat(video_capturer); |
| VideoFormat capture_format; |
| if (video_capturer->GetBestCaptureFormat(highest_asked_format, |
| &capture_format)) { |
| if (!video_capturer->Restart(capture_format)) { |
| LOG(LS_ERROR) << "RestartVideoCapture: Restart failed."; |
| } |
| } else { |
| LOG(LS_WARNING) |
| << "RestartVideoCapture: Couldn't find a best capture format for " |
| << highest_asked_format.ToString(); |
| } |
| break; |
| } |
| case kRequestRestart: |
| // TODO(ryanpetrie): Support restart requests. Should this |
| // to-be-implemented logic be used for {Start,Stop}VideoCapture as well? |
| break; |
| default: |
| LOG(LS_ERROR) << "Unknown/unimplemented RestartOption"; |
| break; |
| } |
| return true; |
| } |
| |
| bool CaptureManager::AddVideoRenderer(VideoCapturer* video_capturer, |
| VideoRenderer* video_renderer) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!video_capturer || !video_renderer) { |
| return false; |
| } |
| CaptureRenderAdapter* adapter = GetAdapter(video_capturer); |
| if (!adapter) { |
| return false; |
| } |
| return adapter->AddRenderer(video_renderer); |
| } |
| |
| bool CaptureManager::RemoveVideoRenderer(VideoCapturer* video_capturer, |
| VideoRenderer* video_renderer) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!video_capturer || !video_renderer) { |
| return false; |
| } |
| CaptureRenderAdapter* adapter = GetAdapter(video_capturer); |
| if (!adapter) { |
| return false; |
| } |
| return adapter->RemoveRenderer(video_renderer); |
| } |
| |
| bool CaptureManager::IsCapturerRegistered(VideoCapturer* video_capturer) const { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| return GetCaptureState(video_capturer) != NULL; |
| } |
| |
| bool CaptureManager::RegisterVideoCapturer(VideoCapturer* video_capturer) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| VideoCapturerState* capture_state = |
| VideoCapturerState::Create(video_capturer); |
| if (!capture_state) { |
| return false; |
| } |
| capture_states_[video_capturer] = capture_state; |
| SignalCapturerStateChange.repeat(video_capturer->SignalStateChange); |
| return true; |
| } |
| |
| void CaptureManager::UnregisterVideoCapturer( |
| VideoCapturerState* capture_state) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| VideoCapturer* video_capturer = capture_state->GetVideoCapturer(); |
| capture_states_.erase(video_capturer); |
| delete capture_state; |
| |
| // When unregistering a VideoCapturer, the CaptureManager needs to unregister |
| // from all state change callbacks from the VideoCapturer. E.g. to avoid |
| // problems with multiple callbacks if registering the same VideoCapturer |
| // multiple times. The VideoCapturer will update the capturer state. However, |
| // this is done through Post-calls which means it may happen at any time. If |
| // the CaptureManager no longer is listening to the VideoCapturer it will not |
| // receive those callbacks. Here it is made sure that the the callback is |
| // indeed sent by letting the ChannelManager do the signaling. The downside is |
| // that the callback may happen before the VideoCapturer is stopped. However, |
| // for the CaptureManager it doesn't matter as it will no longer receive any |
| // frames from the VideoCapturer. |
| SignalCapturerStateChange.stop(video_capturer->SignalStateChange); |
| if (video_capturer->IsRunning()) { |
| video_capturer->Stop(); |
| SignalCapturerStateChange(video_capturer, CS_STOPPED); |
| } |
| } |
| |
| bool CaptureManager::StartWithBestCaptureFormat( |
| VideoCapturerState* capture_state, VideoCapturer* video_capturer) { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| VideoFormat highest_asked_format = |
| capture_state->GetHighestFormat(video_capturer); |
| VideoFormat capture_format; |
| if (!video_capturer->GetBestCaptureFormat(highest_asked_format, |
| &capture_format)) { |
| LOG(LS_WARNING) << "Unsupported format:" |
| << " width=" << highest_asked_format.width |
| << " height=" << highest_asked_format.height |
| << ". Supported formats are:"; |
| const std::vector<VideoFormat>* formats = |
| video_capturer->GetSupportedFormats(); |
| ASSERT(formats != NULL); |
| for (std::vector<VideoFormat>::const_iterator i = formats->begin(); |
| i != formats->end(); ++i) { |
| const VideoFormat& format = *i; |
| LOG(LS_WARNING) << " " << GetFourccName(format.fourcc) |
| << ":" << format.width << "x" << format.height << "x" |
| << format.framerate(); |
| } |
| return false; |
| } |
| return video_capturer->StartCapturing(capture_format); |
| } |
| |
| VideoCapturerState* CaptureManager::GetCaptureState( |
| VideoCapturer* video_capturer) const { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| CaptureStates::const_iterator iter = capture_states_.find(video_capturer); |
| if (iter == capture_states_.end()) { |
| return NULL; |
| } |
| return iter->second; |
| } |
| |
| CaptureRenderAdapter* CaptureManager::GetAdapter( |
| VideoCapturer* video_capturer) const { |
| RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| VideoCapturerState* capture_state = GetCaptureState(video_capturer); |
| if (!capture_state) { |
| return NULL; |
| } |
| return capture_state->adapter(); |
| } |
| |
| } // namespace cricket |