blob: 7dcd92e3739cf489ae30fc508834d41693bf74f5 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/media/media_stream_video_capturer_source.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/render_thread_impl.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h"
namespace {
struct SourceVideoResolution {
int width;
int height;
};
// Resolutions used if the source doesn't support capability enumeration.
const SourceVideoResolution kVideoResolutions[] = {{1920, 1080},
{1280, 720},
{960, 720},
{640, 480},
{640, 360},
{320, 240},
{320, 180}};
// Frame rates for sources with no support for capability enumeration.
const int kVideoFrameRates[] = {30, 60};
// Hard upper-bound frame rate for tab/desktop capture.
const double kMaxScreenCastFrameRate = 120.0;
} // namespace
namespace content {
VideoCapturerDelegate::VideoCapturerDelegate(
const StreamDeviceInfo& device_info)
: session_id_(device_info.session_id),
is_screen_cast_(device_info.device.type == MEDIA_TAB_VIDEO_CAPTURE ||
device_info.device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
DVLOG(3) << "VideoCapturerDelegate::ctor";
// NULL in unit test.
if (RenderThreadImpl::current()) {
VideoCaptureImplManager* manager =
RenderThreadImpl::current()->video_capture_impl_manager();
if (manager)
release_device_cb_ = manager->UseDevice(session_id_);
}
}
VideoCapturerDelegate::~VideoCapturerDelegate() {
DVLOG(3) << "VideoCapturerDelegate::dtor";
if (!release_device_cb_.is_null())
release_device_cb_.Run();
}
void VideoCapturerDelegate::GetCurrentSupportedFormats(
int max_requested_width,
int max_requested_height,
double max_requested_frame_rate,
const VideoCaptureDeviceFormatsCB& callback) {
DVLOG(3)
<< "GetCurrentSupportedFormats("
<< " { max_requested_height = " << max_requested_height << "})"
<< " { max_requested_width = " << max_requested_width << "})"
<< " { max_requested_frame_rate = " << max_requested_frame_rate << "})";
if (is_screen_cast_) {
const int width = max_requested_width ?
max_requested_width : MediaStreamVideoSource::kDefaultWidth;
const int height = max_requested_height ?
max_requested_height : MediaStreamVideoSource::kDefaultHeight;
callback.Run(media::VideoCaptureFormats(1, media::VideoCaptureFormat(
gfx::Size(width, height),
static_cast<float>(std::min(kMaxScreenCastFrameRate,
max_requested_frame_rate)),
media::PIXEL_FORMAT_I420)));
return;
}
// NULL in unit test.
if (!RenderThreadImpl::current())
return;
VideoCaptureImplManager* manager =
RenderThreadImpl::current()->video_capture_impl_manager();
if (!manager)
return;
DCHECK(source_formats_callback_.is_null());
source_formats_callback_ = callback;
manager->GetDeviceFormatsInUse(
session_id_,
media::BindToCurrentLoop(
base::Bind(
&VideoCapturerDelegate::OnDeviceFormatsInUseReceived, this)));
}
void VideoCapturerDelegate::StartCapture(
const media::VideoCaptureParams& params,
const VideoCaptureDeliverFrameCB& new_frame_callback,
const RunningCallback& running_callback) {
DCHECK(params.requested_format.IsValid());
DCHECK(thread_checker_.CalledOnValidThread());
running_callback_ = running_callback;
// NULL in unit test.
if (!RenderThreadImpl::current())
return;
VideoCaptureImplManager* manager =
RenderThreadImpl::current()->video_capture_impl_manager();
if (!manager)
return;
stop_capture_cb_ =
manager->StartCapture(
session_id_,
params,
media::BindToCurrentLoop(base::Bind(
&VideoCapturerDelegate::OnStateUpdateOnRenderThread, this)),
new_frame_callback);
}
void VideoCapturerDelegate::StopCapture() {
// Immediately make sure we don't provide more frames.
DVLOG(3) << "VideoCapturerDelegate::StopCapture()";
DCHECK(thread_checker_.CalledOnValidThread());
if (!stop_capture_cb_.is_null()) {
base::ResetAndReturn(&stop_capture_cb_).Run();
}
running_callback_.Reset();
source_formats_callback_.Reset();
}
void VideoCapturerDelegate::OnStateUpdateOnRenderThread(
VideoCaptureState state) {
DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(3) << "OnStateUpdateOnRenderThread state = " << state;
if (state == VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) {
running_callback_.Run(MEDIA_DEVICE_OK);
return;
}
if (state > VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) {
base::ResetAndReturn(&running_callback_).Run(
MEDIA_DEVICE_TRACK_START_FAILURE);
}
}
void VideoCapturerDelegate::OnDeviceFormatsInUseReceived(
const media::VideoCaptureFormats& formats_in_use) {
DVLOG(3) << "OnDeviceFormatsInUseReceived: " << formats_in_use.size();
DCHECK(thread_checker_.CalledOnValidThread());
// StopCapture() might have destroyed |source_formats_callback_| before
// arriving here.
if (source_formats_callback_.is_null())
return;
// If there are no formats in use, try to retrieve the whole list of
// supported form.
if (!formats_in_use.empty()) {
source_formats_callback_.Run(formats_in_use);
source_formats_callback_.Reset();
return;
}
// NULL in unit test.
if (!RenderThreadImpl::current())
return;
VideoCaptureImplManager* manager =
RenderThreadImpl::current()->video_capture_impl_manager();
if (!manager)
return;
manager->GetDeviceSupportedFormats(
session_id_,
media::BindToCurrentLoop(
base::Bind(
&VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated,
this)));
}
void VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated(
const media::VideoCaptureFormats& formats) {
DVLOG(3) << "OnDeviceSupportedFormatsEnumerated: " << formats.size()
<< " received";
DCHECK(thread_checker_.CalledOnValidThread());
// StopCapture() might have destroyed |source_formats_callback_| before
// arriving here.
if (source_formats_callback_.is_null())
return;
if (formats.size()) {
source_formats_callback_.Run(formats);
} else {
// The capture device doesn't seem to support capability enumeration,
// compose a fallback list of capabilities.
media::VideoCaptureFormats default_formats;
for (size_t i = 0; i < arraysize(kVideoResolutions); ++i) {
for (size_t j = 0; j < arraysize(kVideoFrameRates); ++j) {
default_formats.push_back(media::VideoCaptureFormat(
gfx::Size(kVideoResolutions[i].width, kVideoResolutions[i].height),
kVideoFrameRates[j], media::PIXEL_FORMAT_I420));
}
}
source_formats_callback_.Run(default_formats);
}
source_formats_callback_.Reset();
}
MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
const StreamDeviceInfo& device_info,
const SourceStoppedCallback& stop_callback,
const scoped_refptr<VideoCapturerDelegate>& delegate)
: delegate_(delegate) {
SetDeviceInfo(device_info);
SetStopCallback(stop_callback);
}
MediaStreamVideoCapturerSource::~MediaStreamVideoCapturerSource() {
}
void MediaStreamVideoCapturerSource::GetCurrentSupportedFormats(
int max_requested_width,
int max_requested_height,
double max_requested_frame_rate,
const VideoCaptureDeviceFormatsCB& callback) {
delegate_->GetCurrentSupportedFormats(
max_requested_width,
max_requested_height,
max_requested_frame_rate,
callback);
}
void MediaStreamVideoCapturerSource::StartSourceImpl(
const media::VideoCaptureFormat& format,
const VideoCaptureDeliverFrameCB& frame_callback) {
media::VideoCaptureParams new_params;
new_params.requested_format = format;
if (device_info().device.type == MEDIA_TAB_VIDEO_CAPTURE ||
device_info().device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
new_params.resolution_change_policy =
media::RESOLUTION_POLICY_DYNAMIC_WITHIN_LIMIT;
}
delegate_->StartCapture(
new_params,
frame_callback,
base::Bind(&MediaStreamVideoCapturerSource::OnStartDone,
base::Unretained(this)));
}
void MediaStreamVideoCapturerSource::StopSourceImpl() {
delegate_->StopCapture();
}
} // namespace content