2013-07-10 02:45:36 +02:00
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
|
2014-07-29 19:36:52 +02:00
|
|
|
#include "webrtc/base/logging.h"
|
2013-07-10 02:45:36 +02:00
|
|
|
#include "talk/media/base/videocapturer.h"
|
|
|
|
#include "talk/media/base/videoprocessor.h"
|
|
|
|
#include "talk/media/base/videorenderer.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() { return adapter_.get(); }
|
|
|
|
VideoCapturer* GetVideoCapturer() { return adapter()->video_capturer(); }
|
|
|
|
|
|
|
|
int start_count() const { return start_count_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct CaptureResolutionInfo {
|
|
|
|
VideoFormat video_format;
|
|
|
|
int format_ref_count;
|
|
|
|
};
|
|
|
|
typedef std::vector<CaptureResolutionInfo> CaptureFormats;
|
|
|
|
|
|
|
|
explicit VideoCapturerState(CaptureRenderAdapter* adapter);
|
|
|
|
|
2014-07-29 19:36:52 +02:00
|
|
|
rtc::scoped_ptr<CaptureRenderAdapter> adapter_;
|
2013-07-10 02:45:36 +02:00
|
|
|
|
|
|
|
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) {}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
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) {
|
|
|
|
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 {
|
|
|
|
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() { return ++start_count_; }
|
|
|
|
|
|
|
|
int VideoCapturerState::DecCaptureStartRef() {
|
|
|
|
if (start_count_ > 0) {
|
|
|
|
// Start count may be 0 if a capturer was added but never started.
|
|
|
|
--start_count_;
|
|
|
|
}
|
|
|
|
return start_count_;
|
|
|
|
}
|
|
|
|
|
|
|
|
CaptureManager::~CaptureManager() {
|
2013-08-01 02:00:07 +02:00
|
|
|
// 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.
|
|
|
|
if (!capture_states_.empty()) {
|
|
|
|
ASSERT(false &&
|
|
|
|
"CaptureManager destructing while still tracking capturers!");
|
|
|
|
// Delete remaining VideoCapturerStates, but don't touch the capturers.
|
|
|
|
do {
|
|
|
|
CaptureStates::iterator it = capture_states_.begin();
|
|
|
|
delete it->second;
|
|
|
|
capture_states_.erase(it);
|
|
|
|
} while (!capture_states_.empty());
|
2013-07-10 02:45:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CaptureManager::StartVideoCapture(VideoCapturer* video_capturer,
|
|
|
|
const VideoFormat& desired_format) {
|
|
|
|
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) {
|
|
|
|
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) {
|
|
|
|
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) {
|
|
|
|
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) {
|
|
|
|
if (!video_capturer || !video_renderer) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
CaptureRenderAdapter* adapter = GetAdapter(video_capturer);
|
|
|
|
if (!adapter) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return adapter->RemoveRenderer(video_renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CaptureManager::AddVideoProcessor(VideoCapturer* video_capturer,
|
|
|
|
VideoProcessor* video_processor) {
|
|
|
|
if (!video_capturer || !video_processor) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!IsCapturerRegistered(video_capturer)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
video_capturer->AddVideoProcessor(video_processor);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CaptureManager::RemoveVideoProcessor(VideoCapturer* video_capturer,
|
|
|
|
VideoProcessor* video_processor) {
|
|
|
|
if (!video_capturer || !video_processor) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!IsCapturerRegistered(video_capturer)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return video_capturer->RemoveVideoProcessor(video_processor);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CaptureManager::IsCapturerRegistered(VideoCapturer* video_capturer) const {
|
|
|
|
return GetCaptureState(video_capturer) != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CaptureManager::RegisterVideoCapturer(VideoCapturer* video_capturer) {
|
|
|
|
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) {
|
|
|
|
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);
|
|
|
|
video_capturer->Stop();
|
|
|
|
SignalCapturerStateChange(video_capturer, CS_STOPPED);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CaptureManager::StartWithBestCaptureFormat(
|
|
|
|
VideoCapturerState* capture_state, VideoCapturer* video_capturer) {
|
|
|
|
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 {
|
|
|
|
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 {
|
|
|
|
VideoCapturerState* capture_state = GetCaptureState(video_capturer);
|
|
|
|
if (!capture_state) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return capture_state->adapter();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace cricket
|