blob: 07986fa0f788417986bd28b4b45be4eeb2702338 [file] [log] [blame]
// Copyright (c) 2012 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/browser/renderer_host/media/video_capture_controller.h"
#include <set>
#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/browser/renderer_host/media/video_capture_manager.h"
#include "content/public/browser/browser_thread.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/base/yuv_convert.h"
#if !defined(OS_IOS) && !defined(OS_ANDROID)
#include "third_party/libyuv/include/libyuv.h"
#endif
namespace {
#if defined(OS_IOS) || defined(OS_ANDROID)
// TODO(wjia): Support stride.
void RotatePackedYV12Frame(
const uint8* src,
uint8* dest_yplane,
uint8* dest_uplane,
uint8* dest_vplane,
int width,
int height,
int rotation,
bool flip_vert,
bool flip_horiz) {
media::RotatePlaneByPixels(
src, dest_yplane, width, height, rotation, flip_vert, flip_horiz);
int y_size = width * height;
src += y_size;
media::RotatePlaneByPixels(
src, dest_uplane, width/2, height/2, rotation, flip_vert, flip_horiz);
src += y_size/4;
media::RotatePlaneByPixels(
src, dest_vplane, width/2, height/2, rotation, flip_vert, flip_horiz);
}
#endif // #if defined(OS_IOS) || defined(OS_ANDROID)
} // namespace
namespace content {
// The number of buffers that VideoCaptureBufferPool should allocate.
static const int kNoOfBuffers = 3;
struct VideoCaptureController::ControllerClient {
ControllerClient(
const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* handler,
base::ProcessHandle render_process,
const media::VideoCaptureParams& params)
: controller_id(id),
event_handler(handler),
render_process_handle(render_process),
parameters(params),
session_closed(false) {
}
~ControllerClient() {}
// ID used for identifying this object.
VideoCaptureControllerID controller_id;
VideoCaptureControllerEventHandler* event_handler;
// Handle to the render process that will receive the capture buffers.
base::ProcessHandle render_process_handle;
media::VideoCaptureParams parameters;
// Buffers used by this client.
std::set<int> buffers;
// State of capture session, controlled by VideoCaptureManager directly. This
// transitions to true as soon as StopSession() occurs, at which point the
// client is sent an OnEnded() event. However, because the client retains a
// VideoCaptureController* pointer, its ControllerClient entry lives on until
// it unregisters itself via RemoveClient(), which may happen asynchronously.
//
// TODO(nick): If we changed the semantics of VideoCaptureHost so that
// OnEnded() events were processed synchronously (with the RemoveClient() done
// implicitly), we could avoid tracking this state here in the Controller, and
// simplify the code in both places.
bool session_closed;
};
// Receives events from the VideoCaptureDevice and posts them to a
// VideoCaptureController on the IO thread. An instance of this class may safely
// outlive its target VideoCaptureController.
//
// Methods of this class may be called from any thread, and in practice will
// often be called on some auxiliary thread depending on the platform and the
// device type; including, for example, the DirectShow thread on Windows, the
// v4l2_thread on Linux, and the UI thread for tab capture.
class VideoCaptureController::VideoCaptureDeviceClient
: public media::VideoCaptureDevice::Client {
public:
explicit VideoCaptureDeviceClient(
const base::WeakPtr<VideoCaptureController>& controller);
virtual ~VideoCaptureDeviceClient();
// VideoCaptureDevice::Client implementation.
virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE;
virtual void OnIncomingCapturedFrame(const uint8* data,
int length,
base::Time timestamp,
int rotation,
bool flip_vert,
bool flip_horiz) OVERRIDE;
virtual void OnIncomingCapturedVideoFrame(
const scoped_refptr<media::VideoFrame>& frame,
base::Time timestamp) OVERRIDE;
virtual void OnError() OVERRIDE;
virtual void OnFrameInfo(
const media::VideoCaptureCapability& info) OVERRIDE;
virtual void OnFrameInfoChanged(
const media::VideoCaptureCapability& info) OVERRIDE;
private:
// The controller to which we post events.
const base::WeakPtr<VideoCaptureController> controller_;
// The pool of shared-memory buffers used for capturing.
scoped_refptr<VideoCaptureBufferPool> buffer_pool_;
// Chopped pixels in width/height in case video capture device has odd
// numbers for width/height.
int chopped_width_;
int chopped_height_;
// Tracks the current frame format.
media::VideoCaptureCapability frame_info_;
// For NV21 we have to do color conversion into the intermediate buffer and
// from there the rotations. This variable won't be needed after
// http://crbug.com/292400
#if defined(OS_IOS) || defined(OS_ANDROID)
scoped_ptr<uint8[]> i420_intermediate_buffer_;
#endif // #if defined(OS_IOS) || defined(OS_ANDROID)
};
VideoCaptureController::VideoCaptureController()
: state_(VIDEO_CAPTURE_STATE_STARTED),
weak_ptr_factory_(this) {
memset(&current_params_, 0, sizeof(current_params_));
}
VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient(
const base::WeakPtr<VideoCaptureController>& controller)
: controller_(controller),
chopped_width_(0),
chopped_height_(0) {}
VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
base::WeakPtr<VideoCaptureController> VideoCaptureController::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
scoped_ptr<media::VideoCaptureDevice::Client>
VideoCaptureController::NewDeviceClient() {
scoped_ptr<media::VideoCaptureDevice::Client> result(
new VideoCaptureDeviceClient(this->GetWeakPtr()));
return result.Pass();
}
void VideoCaptureController::AddClient(
const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler,
base::ProcessHandle render_process,
const media::VideoCaptureParams& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << "VideoCaptureController::AddClient, id " << id.device_id
<< ", (" << params.width
<< ", " << params.height
<< ", " << params.frame_rate
<< ", " << params.session_id
<< ")";
// Signal error in case device is already in error state.
if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
event_handler->OnError(id);
return;
}
// Do nothing if this client has called AddClient before.
if (FindClient(id, event_handler, controller_clients_))
return;
ControllerClient* client = new ControllerClient(id, event_handler,
render_process, params);
// If we already have gotten frame_info from the device, repeat it to the new
// client.
if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
if (frame_info_.IsValid()) {
SendFrameInfoAndBuffers(client);
}
controller_clients_.push_back(client);
return;
}
}
int VideoCaptureController::RemoveClient(
const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id.device_id;
ControllerClient* client = FindClient(id, event_handler, controller_clients_);
if (!client)
return kInvalidMediaCaptureSessionId;
// Take back all buffers held by the |client|.
if (buffer_pool_.get()) {
for (std::set<int>::iterator buffer_it = client->buffers.begin();
buffer_it != client->buffers.end();
++buffer_it) {
int buffer_id = *buffer_it;
buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
}
}
client->buffers.clear();
int session_id = client->parameters.session_id;
controller_clients_.remove(client);
delete client;
return session_id;
}
void VideoCaptureController::StopSession(int session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id;
ControllerClient* client = FindClient(session_id, controller_clients_);
if (client) {
client->session_closed = true;
client->event_handler->OnEnded(client->controller_id);
}
}
void VideoCaptureController::ReturnBuffer(
const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler,
int buffer_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ControllerClient* client = FindClient(id, event_handler, controller_clients_);
// If this buffer is not held by this client, or this client doesn't exist
// in controller, do nothing.
if (!client ||
client->buffers.find(buffer_id) == client->buffers.end()) {
NOTREACHED();
return;
}
client->buffers.erase(buffer_id);
buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
}
scoped_refptr<media::VideoFrame>
VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer() {
return buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
frame_info_.height),
0);
}
#if !defined(OS_IOS) && !defined(OS_ANDROID)
void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame(
const uint8* data,
int length,
base::Time timestamp,
int rotation,
bool flip_vert,
bool flip_horiz) {
TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame");
if (!buffer_pool_.get())
return;
scoped_refptr<media::VideoFrame> dst = buffer_pool_->ReserveI420VideoFrame(
gfx::Size(frame_info_.width, frame_info_.height), rotation);
if (!dst.get())
return;
uint8* yplane = dst->data(media::VideoFrame::kYPlane);
uint8* uplane = dst->data(media::VideoFrame::kUPlane);
uint8* vplane = dst->data(media::VideoFrame::kVPlane);
int yplane_stride = frame_info_.width;
int uv_plane_stride = (frame_info_.width + 1) / 2;
int crop_x = 0;
int crop_y = 0;
libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY;
// Assuming rotation happens first and flips next, we can consolidate both
// vertical and horizontal flips together with rotation into two variables:
// new_rotation = (rotation + 180 * vertical_flip) modulo 360
// new_vertical_flip = horizontal_flip XOR vertical_flip
int new_rotation_angle = (rotation + 180 * flip_vert) % 360;
libyuv::RotationMode rotation_mode = libyuv::kRotate0;
if (new_rotation_angle == 90)
rotation_mode = libyuv::kRotate90;
else if (new_rotation_angle == 180)
rotation_mode = libyuv::kRotate180;
else if (new_rotation_angle == 270)
rotation_mode = libyuv::kRotate270;
switch (frame_info_.color) {
case media::PIXEL_FORMAT_UNKNOWN: // Color format not set.
break;
case media::PIXEL_FORMAT_I420:
DCHECK(!chopped_width_ && !chopped_height_);
origin_colorspace = libyuv::FOURCC_I420;
break;
case media::PIXEL_FORMAT_YV12:
DCHECK(!chopped_width_ && !chopped_height_);
origin_colorspace = libyuv::FOURCC_YV12;
break;
case media::PIXEL_FORMAT_NV21:
DCHECK(!chopped_width_ && !chopped_height_);
origin_colorspace = libyuv::FOURCC_NV12;
break;
case media::PIXEL_FORMAT_YUY2:
DCHECK(!chopped_width_ && !chopped_height_);
origin_colorspace = libyuv::FOURCC_YUY2;
break;
case media::PIXEL_FORMAT_UYVY:
DCHECK(!chopped_width_ && !chopped_height_);
origin_colorspace = libyuv::FOURCC_UYVY;
break;
case media::PIXEL_FORMAT_RGB24:
origin_colorspace = libyuv::FOURCC_RAW;
break;
case media::PIXEL_FORMAT_ARGB:
origin_colorspace = libyuv::FOURCC_ARGB;
break;
case media::PIXEL_FORMAT_MJPEG:
origin_colorspace = libyuv::FOURCC_MJPG;
break;
default:
NOTREACHED();
}
int need_convert_rgb24_on_win = false;
#if defined(OS_WIN)
// kRGB24 on Windows start at the bottom line and has a negative stride. This
// is not supported by libyuv, so the media API is used instead.
if (frame_info_.color == media::PIXEL_FORMAT_RGB24) {
// Rotation and flipping is not supported in kRGB24 and OS_WIN case.
DCHECK(!rotation && !flip_vert && !flip_horiz);
need_convert_rgb24_on_win = true;
}
#endif
if (need_convert_rgb24_on_win) {
int rgb_stride = -3 * (frame_info_.width + chopped_width_);
const uint8* rgb_src =
data + 3 * (frame_info_.width + chopped_width_) *
(frame_info_.height - 1 + chopped_height_);
media::ConvertRGB24ToYUV(rgb_src,
yplane,
uplane,
vplane,
frame_info_.width,
frame_info_.height,
rgb_stride,
yplane_stride,
uv_plane_stride);
} else {
libyuv::ConvertToI420(
data,
length,
yplane,
yplane_stride,
uplane,
uv_plane_stride,
vplane,
uv_plane_stride,
crop_x,
crop_y,
frame_info_.width + chopped_width_,
frame_info_.height * (flip_vert ^ flip_horiz ? -1 : 1),
frame_info_.width,
frame_info_.height,
rotation_mode,
origin_colorspace);
}
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
controller_,
dst,
timestamp));
}
#else
void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame(
const uint8* data,
int length,
base::Time timestamp,
int rotation,
bool flip_vert,
bool flip_horiz) {
DCHECK(frame_info_.color == media::PIXEL_FORMAT_I420 ||
frame_info_.color == media::PIXEL_FORMAT_YV12 ||
frame_info_.color == media::PIXEL_FORMAT_NV21 ||
(rotation == 0 && !flip_vert && !flip_horiz));
TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame");
if (!buffer_pool_)
return;
scoped_refptr<media::VideoFrame> dst =
buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
frame_info_.height),
rotation);
if (!dst.get())
return;
uint8* yplane = dst->data(media::VideoFrame::kYPlane);
uint8* uplane = dst->data(media::VideoFrame::kUPlane);
uint8* vplane = dst->data(media::VideoFrame::kVPlane);
// Do color conversion from the camera format to I420.
switch (frame_info_.color) {
case media::PIXEL_FORMAT_UNKNOWN: // Color format not set.
break;
case media::PIXEL_FORMAT_I420:
DCHECK(!chopped_width_ && !chopped_height_);
RotatePackedYV12Frame(
data, yplane, uplane, vplane, frame_info_.width, frame_info_.height,
rotation, flip_vert, flip_horiz);
break;
case media::PIXEL_FORMAT_YV12:
DCHECK(!chopped_width_ && !chopped_height_);
RotatePackedYV12Frame(
data, yplane, vplane, uplane, frame_info_.width, frame_info_.height,
rotation, flip_vert, flip_horiz);
break;
case media::PIXEL_FORMAT_NV21: {
DCHECK(!chopped_width_ && !chopped_height_);
int num_pixels = frame_info_.width * frame_info_.height;
media::ConvertNV21ToYUV(data,
&i420_intermediate_buffer_[0],
&i420_intermediate_buffer_[num_pixels],
&i420_intermediate_buffer_[num_pixels * 5 / 4],
frame_info_.width,
frame_info_.height);
RotatePackedYV12Frame(
i420_intermediate_buffer_.get(), yplane, uplane, vplane,
frame_info_.width, frame_info_.height,
rotation, flip_vert, flip_horiz);
break;
}
case media::PIXEL_FORMAT_YUY2:
DCHECK(!chopped_width_ && !chopped_height_);
if (frame_info_.width * frame_info_.height * 2 != length) {
// If |length| of |data| does not match the expected width and height
// we can't convert the frame to I420. YUY2 is 2 bytes per pixel.
break;
}
media::ConvertYUY2ToYUV(data, yplane, uplane, vplane, frame_info_.width,
frame_info_.height);
break;
case media::PIXEL_FORMAT_RGB24: {
int ystride = frame_info_.width;
int uvstride = frame_info_.width / 2;
int rgb_stride = 3 * (frame_info_.width + chopped_width_);
const uint8* rgb_src = data;
media::ConvertRGB24ToYUV(rgb_src, yplane, uplane, vplane,
frame_info_.width, frame_info_.height,
rgb_stride, ystride, uvstride);
break;
}
case media::PIXEL_FORMAT_ARGB:
media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width,
frame_info_.height,
(frame_info_.width + chopped_width_) * 4,
frame_info_.width, frame_info_.width / 2);
break;
default:
NOTREACHED();
}
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
controller_, dst, timestamp));
}
#endif // #if !defined(OS_IOS) && !defined(OS_ANDROID)
void
VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
const scoped_refptr<media::VideoFrame>& frame,
base::Time timestamp) {
if (!buffer_pool_)
return;
// If this is a frame that belongs to the buffer pool, we can forward it
// directly to the IO thread and be done.
if (buffer_pool_->RecognizeReservedBuffer(
frame->shared_memory_handle()) >= 0) {
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
controller_, frame, timestamp));
return;
}
// Otherwise, this is a frame that belongs to the caller, and we must copy
// it to a frame from the buffer pool.
scoped_refptr<media::VideoFrame> target =
buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
frame_info_.height),
0);
if (!target.get())
return;
// Validate the inputs.
if (frame->coded_size() != target->coded_size())
return; // Only exact copies are supported.
if (!(frame->format() == media::VideoFrame::I420 ||
frame->format() == media::VideoFrame::YV12 ||
frame->format() == media::VideoFrame::RGB32)) {
NOTREACHED() << "Unsupported format passed to OnIncomingCapturedVideoFrame";
return;
}
const int kYPlane = media::VideoFrame::kYPlane;
const int kUPlane = media::VideoFrame::kUPlane;
const int kVPlane = media::VideoFrame::kVPlane;
const int kAPlane = media::VideoFrame::kAPlane;
const int kRGBPlane = media::VideoFrame::kRGBPlane;
// Do color conversion from the camera format to I420.
switch (frame->format()) {
#if defined(GOOGLE_TV)
case media::VideoFrame::HOLE:
// Fall-through to NOTREACHED() block.
#endif
case media::VideoFrame::INVALID:
case media::VideoFrame::YV16:
case media::VideoFrame::EMPTY:
case media::VideoFrame::NATIVE_TEXTURE: {
NOTREACHED();
break;
}
case media::VideoFrame::I420:
case media::VideoFrame::YV12: {
DCHECK(!chopped_width_ && !chopped_height_);
media::CopyYPlane(frame->data(kYPlane),
frame->stride(kYPlane),
frame->rows(kYPlane),
target.get());
media::CopyUPlane(frame->data(kUPlane),
frame->stride(kUPlane),
frame->rows(kUPlane),
target.get());
media::CopyVPlane(frame->data(kVPlane),
frame->stride(kVPlane),
frame->rows(kVPlane),
target.get());
break;
}
case media::VideoFrame::YV12A: {
DCHECK(!chopped_width_ && !chopped_height_);
media::CopyYPlane(frame->data(kYPlane),
frame->stride(kYPlane),
frame->rows(kYPlane),
target.get());
media::CopyUPlane(frame->data(kUPlane),
frame->stride(kUPlane),
frame->rows(kUPlane),
target.get());
media::CopyVPlane(frame->data(kVPlane),
frame->stride(kVPlane),
frame->rows(kVPlane),
target.get());
media::CopyAPlane(frame->data(kAPlane),
frame->stride(kAPlane),
frame->rows(kAPlane),
target.get());
break;
}
case media::VideoFrame::RGB32: {
media::ConvertRGB32ToYUV(frame->data(kRGBPlane),
target->data(kYPlane),
target->data(kUPlane),
target->data(kVPlane),
target->coded_size().width(),
target->coded_size().height(),
frame->stride(kRGBPlane),
target->stride(kYPlane),
target->stride(kUPlane));
break;
}
}
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
controller_, target, timestamp));
}
void VideoCaptureController::VideoCaptureDeviceClient::OnError() {
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_));
}
void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfo(
const media::VideoCaptureCapability& info) {
frame_info_ = info;
// Handle cases when |info| has odd numbers for width/height.
if (info.width & 1) {
--frame_info_.width;
chopped_width_ = 1;
} else {
chopped_width_ = 0;
}
if (info.height & 1) {
--frame_info_.height;
chopped_height_ = 1;
} else {
chopped_height_ = 0;
}
#if defined(OS_IOS) || defined(OS_ANDROID)
if (frame_info_.color == media::PIXEL_FORMAT_NV21 &&
!i420_intermediate_buffer_) {
i420_intermediate_buffer_.reset(
new uint8[frame_info_.width * frame_info_.height * 12 / 8]);
}
#endif // #if defined(OS_IOS) || defined(OS_ANDROID)
DCHECK(!buffer_pool_.get());
// TODO(nick): Give BufferPool the same lifetime as the controller, have it
// support frame size changes, and stop checking it for NULL everywhere.
// http://crbug.com/266082
buffer_pool_ = new VideoCaptureBufferPool(
media::VideoFrame::AllocationSize(
media::VideoFrame::I420,
gfx::Size(frame_info_.width, frame_info_.height)),
kNoOfBuffers);
// Check whether all buffers were created successfully.
if (!buffer_pool_->Allocate()) {
// Transition to the error state.
buffer_pool_ = NULL;
OnError();
return;
}
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, controller_,
frame_info_, buffer_pool_));
}
void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfoChanged(
const media::VideoCaptureCapability& info) {
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&VideoCaptureController::DoFrameInfoChangedOnIOThread,
controller_, info));
}
VideoCaptureController::~VideoCaptureController() {
buffer_pool_ = NULL; // Release all buffers.
STLDeleteContainerPointers(controller_clients_.begin(),
controller_clients_.end());
}
void VideoCaptureController::DoIncomingCapturedFrameOnIOThread(
const scoped_refptr<media::VideoFrame>& reserved_frame,
base::Time timestamp) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!buffer_pool_.get())
return;
int buffer_id = buffer_pool_->RecognizeReservedBuffer(
reserved_frame->shared_memory_handle());
if (buffer_id < 0) {
NOTREACHED();
return;
}
int count = 0;
if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
for (ControllerClients::iterator client_it = controller_clients_.begin();
client_it != controller_clients_.end(); ++client_it) {
if ((*client_it)->session_closed)
continue;
(*client_it)->event_handler->OnBufferReady((*client_it)->controller_id,
buffer_id, timestamp);
(*client_it)->buffers.insert(buffer_id);
count++;
}
}
buffer_pool_->HoldForConsumers(buffer_id, count);
}
void VideoCaptureController::DoFrameInfoOnIOThread(
const media::VideoCaptureCapability& frame_info,
const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(!buffer_pool_.get()) << "Frame info should happen only once.";
// Allocate memory only when device has been started.
if (state_ != VIDEO_CAPTURE_STATE_STARTED)
return;
frame_info_ = frame_info;
buffer_pool_ = buffer_pool;
for (ControllerClients::iterator client_it = controller_clients_.begin();
client_it != controller_clients_.end(); ++client_it) {
if ((*client_it)->session_closed)
continue;
SendFrameInfoAndBuffers(*client_it);
}
}
void VideoCaptureController::DoFrameInfoChangedOnIOThread(
const media::VideoCaptureCapability& info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// TODO(mcasas): Here we should reallocate the VideoCaptureBufferPool, if
// needed, to support the new video capture format. See crbug.com/266082.
for (ControllerClients::iterator client_it = controller_clients_.begin();
client_it != controller_clients_.end(); ++client_it) {
if ((*client_it)->session_closed)
continue;
(*client_it)->event_handler->OnFrameInfoChanged(
(*client_it)->controller_id,
info.width,
info.height,
info.frame_rate);
}
}
void VideoCaptureController::DoErrorOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
state_ = VIDEO_CAPTURE_STATE_ERROR;
ControllerClients::iterator client_it;
for (client_it = controller_clients_.begin();
client_it != controller_clients_.end(); ++client_it) {
if ((*client_it)->session_closed)
continue;
(*client_it)->event_handler->OnError((*client_it)->controller_id);
}
}
void VideoCaptureController::SendFrameInfoAndBuffers(ControllerClient* client) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(frame_info_.IsValid());
client->event_handler->OnFrameInfo(client->controller_id,
frame_info_);
for (int buffer_id = 0; buffer_id < buffer_pool_->count(); ++buffer_id) {
base::SharedMemoryHandle remote_handle =
buffer_pool_->ShareToProcess(buffer_id, client->render_process_handle);
client->event_handler->OnBufferCreated(client->controller_id,
remote_handle,
buffer_pool_->GetMemorySize(),
buffer_id);
}
}
VideoCaptureController::ControllerClient*
VideoCaptureController::FindClient(
const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* handler,
const ControllerClients& clients) {
for (ControllerClients::const_iterator client_it = clients.begin();
client_it != clients.end(); ++client_it) {
if ((*client_it)->controller_id == id &&
(*client_it)->event_handler == handler) {
return *client_it;
}
}
return NULL;
}
VideoCaptureController::ControllerClient*
VideoCaptureController::FindClient(
int session_id,
const ControllerClients& clients) {
for (ControllerClients::const_iterator client_it = clients.begin();
client_it != clients.end(); ++client_it) {
if ((*client_it)->parameters.session_id == session_id) {
return *client_it;
}
}
return NULL;
}
int VideoCaptureController::GetClientCount() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
return controller_clients_.size();
}
} // namespace content