Files
platform-external-webrtc/webrtc/media/engine/webrtcvideocapturer.cc
nisse b29b9c8e49 Replace VideoCaptureDataCallback by VideoSinkInterface.
This also deletes unused features of the video_capturer interface, the classes
VideoCaptureFeedBack, VideoCaptureEncodeInterface and related methods,
and the module id which used to be passed as an argument to the
VideoCaptureDataCallback.

In theory the module id could have been used to let a single
VideoCaptureDataCallback serve several capturers, and demultiplex
on the id, but in practice, it was unused. With this change, it is
required to use a separate VideoSinkInterface for each capturer.

BUG=webrtc:6789

Review-Url: https://codereview.webrtc.org/2534553002
Cr-Commit-Position: refs/heads/master@{#15540}
2016-12-12 08:23:05 +00:00

357 lines
11 KiB
C++

/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/media/engine/webrtcvideocapturer.h"
#include "webrtc/base/arraysize.h"
#include "webrtc/base/bind.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/safe_conversions.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/base/win32.h" // Need this to #include the impl files.
#include "webrtc/modules/video_capture/video_capture_factory.h"
#include "webrtc/system_wrappers/include/field_trial.h"
namespace cricket {
struct kVideoFourCCEntry {
uint32_t fourcc;
webrtc::RawVideoType webrtc_type;
};
// This indicates our format preferences and defines a mapping between
// webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
static kVideoFourCCEntry kSupportedFourCCs[] = {
{ FOURCC_I420, webrtc::kVideoI420 }, // 12 bpp, no conversion.
{ FOURCC_YV12, webrtc::kVideoYV12 }, // 12 bpp, no conversion.
{ FOURCC_YUY2, webrtc::kVideoYUY2 }, // 16 bpp, fast conversion.
{ FOURCC_UYVY, webrtc::kVideoUYVY }, // 16 bpp, fast conversion.
{ FOURCC_NV12, webrtc::kVideoNV12 }, // 12 bpp, fast conversion.
{ FOURCC_NV21, webrtc::kVideoNV21 }, // 12 bpp, fast conversion.
{ FOURCC_MJPG, webrtc::kVideoMJPEG }, // compressed, slow conversion.
{ FOURCC_ARGB, webrtc::kVideoARGB }, // 32 bpp, slow conversion.
{ FOURCC_24BG, webrtc::kVideoRGB24 }, // 24 bpp, slow conversion.
};
class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
public:
virtual rtc::scoped_refptr<webrtc::VideoCaptureModule> Create(
const char* device) {
return webrtc::VideoCaptureFactory::Create(device);
}
virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo() {
return webrtc::VideoCaptureFactory::CreateDeviceInfo();
}
virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
delete info;
}
};
static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
VideoFormat* format) {
uint32_t fourcc = 0;
for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
fourcc = kSupportedFourCCs[i].fourcc;
break;
}
}
if (fourcc == 0) {
return false;
}
format->fourcc = fourcc;
format->width = cap.width;
format->height = cap.height;
format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
return true;
}
static bool FormatToCapability(const VideoFormat& format,
webrtc::VideoCaptureCapability* cap) {
webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
if (kSupportedFourCCs[i].fourcc == format.fourcc) {
webrtc_type = kSupportedFourCCs[i].webrtc_type;
break;
}
}
if (webrtc_type == webrtc::kVideoUnknown) {
return false;
}
cap->width = format.width;
cap->height = format.height;
cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
cap->expectedCaptureDelay = 0;
cap->rawType = webrtc_type;
cap->codecType = webrtc::kVideoCodecUnknown;
cap->interlaced = false;
return true;
}
///////////////////////////////////////////////////////////////////////////
// Implementation of class WebRtcVideoCapturer
///////////////////////////////////////////////////////////////////////////
WebRtcVideoCapturer::WebRtcVideoCapturer()
: factory_(new WebRtcVcmFactory),
module_(nullptr),
captured_frames_(0),
start_thread_(nullptr),
async_invoker_(nullptr) {}
WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
: factory_(factory),
module_(nullptr),
captured_frames_(0),
start_thread_(nullptr),
async_invoker_(nullptr) {}
WebRtcVideoCapturer::~WebRtcVideoCapturer() {}
bool WebRtcVideoCapturer::Init(const Device& device) {
RTC_DCHECK(!start_thread_);
if (module_) {
LOG(LS_ERROR) << "The capturer is already initialized";
return false;
}
webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo();
if (!info) {
return false;
}
// Find the desired camera, by name.
// In the future, comparing IDs will be more robust.
// TODO(juberti): Figure what's needed to allow this.
int num_cams = info->NumberOfDevices();
char vcm_id[256] = "";
bool found = false;
for (int index = 0; index < num_cams; ++index) {
char vcm_name[256];
if (info->GetDeviceName(index, vcm_name, arraysize(vcm_name), vcm_id,
arraysize(vcm_id)) != -1) {
if (device.name == reinterpret_cast<char*>(vcm_name)) {
found = true;
break;
}
}
}
if (!found) {
LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
factory_->DestroyDeviceInfo(info);
return false;
}
// Enumerate the supported formats.
// TODO(juberti): Find out why this starts/stops the camera...
std::vector<VideoFormat> supported;
int32_t num_caps = info->NumberOfCapabilities(vcm_id);
for (int32_t i = 0; i < num_caps; ++i) {
webrtc::VideoCaptureCapability cap;
if (info->GetCapability(vcm_id, i, cap) != -1) {
VideoFormat format;
if (CapabilityToFormat(cap, &format)) {
supported.push_back(format);
} else {
LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
<< cap.rawType;
}
}
}
factory_->DestroyDeviceInfo(info);
if (supported.empty()) {
LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
return false;
}
module_ = factory_->Create(vcm_id);
if (!module_) {
LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
return false;
}
// It is safe to change member attributes now.
SetId(device.id);
SetSupportedFormats(supported);
return true;
}
bool WebRtcVideoCapturer::Init(
const rtc::scoped_refptr<webrtc::VideoCaptureModule>& module) {
RTC_DCHECK(!start_thread_);
if (module_) {
LOG(LS_ERROR) << "The capturer is already initialized";
return false;
}
if (!module) {
LOG(LS_ERROR) << "Invalid VCM supplied";
return false;
}
// TODO(juberti): Set id and formats.
module_ = module;
return true;
}
bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
VideoFormat* best_format) {
if (!best_format) {
return false;
}
if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
// We maybe using a manually injected VCM which doesn't support enum.
// Use the desired format as the best format.
best_format->width = desired.width;
best_format->height = desired.height;
best_format->fourcc = FOURCC_I420;
best_format->interval = desired.interval;
LOG(LS_INFO) << "Failed to find best capture format,"
<< " fall back to the requested format "
<< best_format->ToString();
}
return true;
}
void WebRtcVideoCapturer::OnSinkWantsChanged(const rtc::VideoSinkWants& wants) {
// Can't take lock here as this will cause deadlock with
// OnIncomingCapturedFrame. In fact, the whole method, including methods it
// calls, can't take lock.
RTC_DCHECK(module_);
const std::string group_name =
webrtc::field_trial::FindFullName("WebRTC-CVO");
if (group_name == "Disabled") {
return;
}
VideoCapturer::OnSinkWantsChanged(wants);
bool result = module_->SetApplyRotation(wants.rotation_applied);
RTC_CHECK(result);
return;
}
CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
if (!module_) {
LOG(LS_ERROR) << "The capturer has not been initialized";
return CS_FAILED;
}
if (start_thread_) {
LOG(LS_ERROR) << "The capturer is already running";
RTC_DCHECK(start_thread_->IsCurrent())
<< "Trying to start capturer on different threads";
return CS_FAILED;
}
start_thread_ = rtc::Thread::Current();
RTC_DCHECK(!async_invoker_);
async_invoker_.reset(new rtc::AsyncInvoker());
captured_frames_ = 0;
SetCaptureFormat(&capture_format);
webrtc::VideoCaptureCapability cap;
if (!FormatToCapability(capture_format, &cap)) {
LOG(LS_ERROR) << "Invalid capture format specified";
return CS_FAILED;
}
int64_t start = rtc::TimeMillis();
module_->RegisterCaptureDataCallback(this);
if (module_->StartCapture(cap) != 0) {
LOG(LS_ERROR) << "Camera '" << GetId() << "' failed to start";
module_->DeRegisterCaptureDataCallback();
async_invoker_.reset();
SetCaptureFormat(nullptr);
start_thread_ = nullptr;
return CS_FAILED;
}
LOG(LS_INFO) << "Camera '" << GetId() << "' started with format "
<< capture_format.ToString() << ", elapsed time "
<< rtc::TimeSince(start) << " ms";
SetCaptureState(CS_RUNNING);
return CS_STARTING;
}
void WebRtcVideoCapturer::Stop() {
if (!start_thread_) {
LOG(LS_ERROR) << "The capturer is already stopped";
return;
}
RTC_DCHECK(start_thread_);
RTC_DCHECK(start_thread_->IsCurrent());
RTC_DCHECK(async_invoker_);
if (IsRunning()) {
// The module is responsible for OnIncomingCapturedFrame being called, if
// we stop it we will get no further callbacks.
module_->StopCapture();
}
module_->DeRegisterCaptureDataCallback();
// TODO(juberti): Determine if the VCM exposes any drop stats we can use.
double drop_ratio = 0.0;
LOG(LS_INFO) << "Camera '" << GetId() << "' stopped after capturing "
<< captured_frames_ << " frames and dropping "
<< drop_ratio << "%";
// Clear any pending async invokes (that OnIncomingCapturedFrame may have
// caused).
async_invoker_.reset();
SetCaptureFormat(NULL);
start_thread_ = nullptr;
SetCaptureState(CS_STOPPED);
}
bool WebRtcVideoCapturer::IsRunning() {
return (module_ != NULL && module_->CaptureStarted());
}
bool WebRtcVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
if (!fourccs) {
return false;
}
fourccs->clear();
for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
fourccs->push_back(kSupportedFourCCs[i].fourcc);
}
return true;
}
void WebRtcVideoCapturer::OnFrame(
const webrtc::VideoFrame& sample) {
// This can only happen between Start() and Stop().
RTC_DCHECK(start_thread_);
RTC_DCHECK(async_invoker_);
++captured_frames_;
// Log the size and pixel aspect ratio of the first captured frame.
if (1 == captured_frames_) {
LOG(LS_INFO) << "Captured frame size "
<< sample.width() << "x" << sample.height()
<< ". Expected format " << GetCaptureFormat()->ToString();
}
VideoCapturer::OnFrame(sample, sample.width(), sample.height());
}
} // namespace cricket