webrtc_m130/talk/media/base/capturemanager.cc
nisse e73afbaf17 New rtc::VideoSinkInterface.
The plan is that this interface should be used by all classes which receive a stream of video frames, and replace the two generic classes webrtc::VideoRendererInterface and cricket::VideoRenderer.

And the list goes on, there's a dozen of different classes which act as video frame sinks.

At some point, we will likely add some method to handle sink properties like, e.g, maximum useful width and height. But hopefully this can be done while keeping the interface very simple.

BUG=webrtc:5426
R=perkj@webrtc.org, pthatcher@webrtc.org

Committed: https://crrev.com/a862d4563fbc26e52bed442de784094ae1dfe5ee
Cr-Commit-Position: refs/heads/master@{#11396}

Review URL: https://codereview.webrtc.org/1594973006

Cr-Commit-Position: refs/heads/master@{#11414}
2016-01-28 12:47:13 +00:00

407 lines
14 KiB
C++

/*
* 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 "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;
}
void CaptureManager::AddVideoSink(VideoCapturer* video_capturer,
rtc::VideoSinkInterface<VideoFrame>* sink) {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
// TODO(nisse): Do we really need to tolerate NULL inputs?
if (!video_capturer || !sink) {
return;
}
CaptureRenderAdapter* adapter = GetAdapter(video_capturer);
if (!adapter) {
return;
}
adapter->AddSink(sink);
}
void CaptureManager::RemoveVideoSink(
VideoCapturer* video_capturer,
rtc::VideoSinkInterface<VideoFrame>* sink) {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
if (!video_capturer || !sink) {
return;
}
CaptureRenderAdapter* adapter = GetAdapter(video_capturer);
if (!adapter) {
return;
}
adapter->RemoveSink(sink);
}
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