| // 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 <map> |
| #include <set> |
| |
| #include "base/bind.h" |
| #include "base/debug/trace_event.h" |
| #include "base/metrics/histogram.h" |
| #include "base/metrics/sparse_histogram.h" |
| #include "base/stl_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "content/browser/renderer_host/media/media_stream_manager.h" |
| #include "content/browser/renderer_host/media/video_capture_manager.h" |
| #include "content/common/gpu/client/gl_helper.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "gpu/command_buffer/common/mailbox_holder.h" |
| #include "media/base/video_frame.h" |
| #include "media/base/video_util.h" |
| #include "media/base/yuv_convert.h" |
| #include "third_party/libyuv/include/libyuv.h" |
| |
| #if defined(OS_ANDROID) |
| #include "content/browser/renderer_host/image_transport_factory_android.h" |
| #else |
| #include "content/browser/compositor/image_transport_factory.h" |
| #endif |
| |
| using media::VideoCaptureFormat; |
| |
| namespace content { |
| |
| namespace { |
| |
| static const int kInfiniteRatio = 99999; |
| |
| #define UMA_HISTOGRAM_ASPECT_RATIO(name, width, height) \ |
| UMA_HISTOGRAM_SPARSE_SLOWLY( \ |
| name, \ |
| (height) ? ((width) * 100) / (height) : kInfiniteRatio); |
| |
| class PoolBuffer : public media::VideoCaptureDevice::Client::Buffer { |
| public: |
| PoolBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool, |
| int buffer_id, |
| void* data, |
| size_t size) |
| : Buffer(buffer_id, data, size), pool_(pool) { |
| DCHECK(pool_.get()); |
| } |
| |
| private: |
| virtual ~PoolBuffer() { pool_->RelinquishProducerReservation(id()); } |
| |
| const scoped_refptr<VideoCaptureBufferPool> pool_; |
| }; |
| |
| class SyncPointClientImpl : public media::VideoFrame::SyncPointClient { |
| public: |
| explicit SyncPointClientImpl(GLHelper* gl_helper) : gl_helper_(gl_helper) {} |
| virtual ~SyncPointClientImpl() {} |
| virtual uint32 InsertSyncPoint() override { |
| return gl_helper_->InsertSyncPoint(); |
| } |
| virtual void WaitSyncPoint(uint32 sync_point) override { |
| gl_helper_->WaitSyncPoint(sync_point); |
| } |
| |
| private: |
| GLHelper* gl_helper_; |
| }; |
| |
| void ReturnVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame, |
| uint32 sync_point) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| #if defined(OS_ANDROID) |
| GLHelper* gl_helper = |
| ImageTransportFactoryAndroid::GetInstance()->GetGLHelper(); |
| #else |
| GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); |
| #endif |
| DCHECK(gl_helper); |
| // UpdateReleaseSyncPoint() creates a new sync_point using |gl_helper|, so |
| // wait the given |sync_point| using |gl_helper|. |
| gl_helper->WaitSyncPoint(sync_point); |
| SyncPointClientImpl client(gl_helper); |
| video_frame->UpdateReleaseSyncPoint(&client); |
| } |
| |
| } // anonymous namespace |
| |
| struct VideoCaptureController::ControllerClient { |
| ControllerClient(const VideoCaptureControllerID& id, |
| VideoCaptureControllerEventHandler* handler, |
| base::ProcessHandle render_process, |
| media::VideoCaptureSessionId session_id, |
| const media::VideoCaptureParams& params) |
| : controller_id(id), |
| event_handler(handler), |
| render_process_handle(render_process), |
| session_id(session_id), |
| parameters(params), |
| session_closed(false), |
| paused(false) {} |
| |
| ~ControllerClient() {} |
| |
| // ID used for identifying this object. |
| const VideoCaptureControllerID controller_id; |
| VideoCaptureControllerEventHandler* const event_handler; |
| |
| // Handle to the render process that will receive the capture buffers. |
| const base::ProcessHandle render_process_handle; |
| const media::VideoCaptureSessionId session_id; |
| const media::VideoCaptureParams parameters; |
| |
| // Buffers that are currently known to this client. |
| std::set<int> known_buffers; |
| |
| // Buffers currently held by this client, and syncpoint callback to call when |
| // they are returned from the client. |
| typedef std::map<int, scoped_refptr<media::VideoFrame> > ActiveBufferMap; |
| ActiveBufferMap active_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; |
| |
| // Indicates whether the client is paused, if true, VideoCaptureController |
| // stops updating its buffer. |
| bool paused; |
| }; |
| |
| // 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, |
| const scoped_refptr<VideoCaptureBufferPool>& buffer_pool); |
| virtual ~VideoCaptureDeviceClient(); |
| |
| // VideoCaptureDevice::Client implementation. |
| virtual scoped_refptr<Buffer> ReserveOutputBuffer( |
| media::VideoFrame::Format format, |
| const gfx::Size& size) override; |
| virtual void OnIncomingCapturedData(const uint8* data, |
| int length, |
| const VideoCaptureFormat& frame_format, |
| int rotation, |
| base::TimeTicks timestamp) override; |
| virtual void OnIncomingCapturedVideoFrame( |
| const scoped_refptr<Buffer>& buffer, |
| const VideoCaptureFormat& buffer_format, |
| const scoped_refptr<media::VideoFrame>& frame, |
| base::TimeTicks timestamp) override; |
| virtual void OnError(const std::string& reason) override; |
| virtual void OnLog(const std::string& message) override; |
| |
| private: |
| scoped_refptr<Buffer> DoReserveOutputBuffer(media::VideoFrame::Format format, |
| const gfx::Size& dimensions); |
| |
| // The controller to which we post events. |
| const base::WeakPtr<VideoCaptureController> controller_; |
| |
| // The pool of shared-memory buffers used for capturing. |
| const scoped_refptr<VideoCaptureBufferPool> buffer_pool_; |
| }; |
| |
| VideoCaptureController::VideoCaptureController(int max_buffers) |
| : buffer_pool_(new VideoCaptureBufferPool(max_buffers)), |
| state_(VIDEO_CAPTURE_STATE_STARTED), |
| frame_received_(false), |
| weak_ptr_factory_(this) { |
| } |
| |
| VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient( |
| const base::WeakPtr<VideoCaptureController>& controller, |
| const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) |
| : controller_(controller), buffer_pool_(buffer_pool) {} |
| |
| 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(), buffer_pool_)); |
| return result.Pass(); |
| } |
| |
| void VideoCaptureController::AddClient( |
| const VideoCaptureControllerID& id, |
| VideoCaptureControllerEventHandler* event_handler, |
| base::ProcessHandle render_process, |
| media::VideoCaptureSessionId session_id, |
| const media::VideoCaptureParams& params) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DVLOG(1) << "VideoCaptureController::AddClient, id " << id.device_id |
| << ", " << params.requested_format.frame_size.ToString() |
| << ", " << params.requested_format.frame_rate |
| << ", " << session_id |
| << ")"; |
| |
| // If this is the first client added to the controller, cache the parameters. |
| if (!controller_clients_.size()) |
| video_capture_format_ = params.requested_format; |
| |
| // 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, session_id, params); |
| // If we already have gotten frame_info from the device, repeat it to the new |
| // client. |
| if (state_ == VIDEO_CAPTURE_STATE_STARTED) { |
| controller_clients_.push_back(client); |
| return; |
| } |
| } |
| |
| int VideoCaptureController::RemoveClient( |
| const VideoCaptureControllerID& id, |
| VideoCaptureControllerEventHandler* event_handler) { |
| DCHECK_CURRENTLY_ON(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|. |
| for (ControllerClient::ActiveBufferMap::iterator buffer_it = |
| client->active_buffers.begin(); |
| buffer_it != client->active_buffers.end(); |
| ++buffer_it) { |
| buffer_pool_->RelinquishConsumerHold(buffer_it->first, 1); |
| } |
| client->active_buffers.clear(); |
| |
| int session_id = client->session_id; |
| controller_clients_.remove(client); |
| delete client; |
| |
| return session_id; |
| } |
| |
| void VideoCaptureController::PauseOrResumeClient( |
| const VideoCaptureControllerID& id, |
| VideoCaptureControllerEventHandler* event_handler, |
| bool pause) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DVLOG(1) << "VideoCaptureController::PauseOrResumeClient, id " |
| << id.device_id << ", " << pause; |
| |
| ControllerClient* client = FindClient(id, event_handler, controller_clients_); |
| if (!client) |
| return; |
| |
| DCHECK(client->paused != pause); |
| client->paused = pause; |
| } |
| |
| void VideoCaptureController::StopSession(int session_id) { |
| DCHECK_CURRENTLY_ON(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, |
| uint32 sync_point) { |
| DCHECK_CURRENTLY_ON(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. |
| ControllerClient::ActiveBufferMap::iterator iter; |
| if (!client || (iter = client->active_buffers.find(buffer_id)) == |
| client->active_buffers.end()) { |
| NOTREACHED(); |
| return; |
| } |
| scoped_refptr<media::VideoFrame> frame = iter->second; |
| client->active_buffers.erase(iter); |
| buffer_pool_->RelinquishConsumerHold(buffer_id, 1); |
| |
| if (sync_point) |
| BrowserThread::PostTask(BrowserThread::UI, |
| FROM_HERE, |
| base::Bind(&ReturnVideoFrame, frame, sync_point)); |
| } |
| |
| const media::VideoCaptureFormat& |
| VideoCaptureController::GetVideoCaptureFormat() const { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| return video_capture_format_; |
| } |
| |
| scoped_refptr<media::VideoCaptureDevice::Client::Buffer> |
| VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer( |
| media::VideoFrame::Format format, |
| const gfx::Size& size) { |
| return DoReserveOutputBuffer(format, size); |
| } |
| |
| void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedData( |
| const uint8* data, |
| int length, |
| const VideoCaptureFormat& frame_format, |
| int rotation, |
| base::TimeTicks timestamp) { |
| TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData"); |
| |
| if (!frame_format.IsValid()) |
| return; |
| |
| // Chopped pixels in width/height in case video capture device has odd |
| // numbers for width/height. |
| int chopped_width = 0; |
| int chopped_height = 0; |
| int new_unrotated_width = frame_format.frame_size.width(); |
| int new_unrotated_height = frame_format.frame_size.height(); |
| |
| if (new_unrotated_width & 1) { |
| --new_unrotated_width; |
| chopped_width = 1; |
| } |
| if (new_unrotated_height & 1) { |
| --new_unrotated_height; |
| chopped_height = 1; |
| } |
| |
| int destination_width = new_unrotated_width; |
| int destination_height = new_unrotated_height; |
| if (rotation == 90 || rotation == 270) { |
| destination_width = new_unrotated_height; |
| destination_height = new_unrotated_width; |
| } |
| const gfx::Size dimensions(destination_width, destination_height); |
| if (!media::VideoFrame::IsValidConfig(media::VideoFrame::I420, |
| dimensions, |
| gfx::Rect(dimensions), |
| dimensions)) { |
| return; |
| } |
| |
| scoped_refptr<Buffer> buffer = |
| DoReserveOutputBuffer(media::VideoFrame::I420, dimensions); |
| |
| if (!buffer.get()) |
| return; |
| uint8* yplane = NULL; |
| bool flip = false; |
| yplane = reinterpret_cast<uint8*>(buffer->data()); |
| uint8* uplane = |
| yplane + |
| media::VideoFrame::PlaneAllocationSize( |
| media::VideoFrame::I420, media::VideoFrame::kYPlane, dimensions); |
| uint8* vplane = |
| uplane + |
| media::VideoFrame::PlaneAllocationSize( |
| media::VideoFrame::I420, media::VideoFrame::kUPlane, dimensions); |
| int yplane_stride = dimensions.width(); |
| int uv_plane_stride = yplane_stride / 2; |
| int crop_x = 0; |
| int crop_y = 0; |
| libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY; |
| |
| libyuv::RotationMode rotation_mode = libyuv::kRotate0; |
| if (rotation == 90) |
| rotation_mode = libyuv::kRotate90; |
| else if (rotation == 180) |
| rotation_mode = libyuv::kRotate180; |
| else if (rotation == 270) |
| rotation_mode = libyuv::kRotate270; |
| |
| switch (frame_format.pixel_format) { |
| 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_NV21; |
| 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_24BG; |
| #if defined(OS_WIN) |
| // TODO(wjia): Currently, for RGB24 on WIN, capture device always |
| // passes in positive src_width and src_height. Remove this hardcoded |
| // value when nagative src_height is supported. The negative src_height |
| // indicates that vertical flipping is needed. |
| flip = true; |
| #endif |
| 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(); |
| } |
| |
| libyuv::ConvertToI420(data, |
| length, |
| yplane, |
| yplane_stride, |
| uplane, |
| uv_plane_stride, |
| vplane, |
| uv_plane_stride, |
| crop_x, |
| crop_y, |
| frame_format.frame_size.width(), |
| (flip ? -frame_format.frame_size.height() : |
| frame_format.frame_size.height()), |
| new_unrotated_width, |
| new_unrotated_height, |
| rotation_mode, |
| origin_colorspace); |
| scoped_refptr<media::VideoFrame> frame = |
| media::VideoFrame::WrapExternalPackedMemory( |
| media::VideoFrame::I420, |
| dimensions, |
| gfx::Rect(dimensions), |
| dimensions, |
| yplane, |
| media::VideoFrame::AllocationSize(media::VideoFrame::I420, |
| dimensions), |
| base::SharedMemory::NULLHandle(), |
| base::TimeDelta(), |
| base::Closure()); |
| DCHECK(frame.get()); |
| |
| VideoCaptureFormat format( |
| dimensions, frame_format.frame_rate, media::PIXEL_FORMAT_I420); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind( |
| &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, |
| controller_, |
| buffer, |
| format, |
| frame, |
| timestamp)); |
| } |
| |
| void |
| VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( |
| const scoped_refptr<Buffer>& buffer, |
| const VideoCaptureFormat& buffer_format, |
| const scoped_refptr<media::VideoFrame>& frame, |
| base::TimeTicks timestamp) { |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind( |
| &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, |
| controller_, |
| buffer, |
| buffer_format, |
| frame, |
| timestamp)); |
| } |
| |
| void VideoCaptureController::VideoCaptureDeviceClient::OnError( |
| const std::string& reason) { |
| const std::string log_message = base::StringPrintf( |
| "Error on video capture: %s, OS message: %s", |
| reason.c_str(), |
| logging::SystemErrorCodeToString( |
| logging::GetLastSystemErrorCode()).c_str()); |
| DLOG(ERROR) << log_message; |
| MediaStreamManager::SendMessageToNativeLog(log_message); |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); |
| } |
| |
| void VideoCaptureController::VideoCaptureDeviceClient::OnLog( |
| const std::string& message) { |
| MediaStreamManager::SendMessageToNativeLog("Video capture: " + message); |
| } |
| |
| scoped_refptr<media::VideoCaptureDevice::Client::Buffer> |
| VideoCaptureController::VideoCaptureDeviceClient::DoReserveOutputBuffer( |
| media::VideoFrame::Format format, |
| const gfx::Size& dimensions) { |
| size_t frame_bytes = 0; |
| if (format == media::VideoFrame::NATIVE_TEXTURE) { |
| DCHECK_EQ(dimensions.width(), 0); |
| DCHECK_EQ(dimensions.height(), 0); |
| } else { |
| // The capture pipeline expects I420 for now. |
| DCHECK_EQ(format, media::VideoFrame::I420) |
| << "Non-I420 output buffer format " << format << " requested"; |
| frame_bytes = media::VideoFrame::AllocationSize(format, dimensions); |
| } |
| |
| int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId; |
| int buffer_id = |
| buffer_pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop); |
| if (buffer_id == VideoCaptureBufferPool::kInvalidId) |
| return NULL; |
| void* data; |
| size_t size; |
| buffer_pool_->GetBufferInfo(buffer_id, &data, &size); |
| |
| scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer( |
| new PoolBuffer(buffer_pool_, buffer_id, data, size)); |
| |
| if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) { |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread, |
| controller_, buffer_id_to_drop)); |
| } |
| |
| return output_buffer; |
| } |
| |
| VideoCaptureController::~VideoCaptureController() { |
| STLDeleteContainerPointers(controller_clients_.begin(), |
| controller_clients_.end()); |
| UMA_HISTOGRAM_BOOLEAN("Media.VideoCapture.FramesReceived", frame_received_); |
| } |
| |
| void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread( |
| const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer, |
| const media::VideoCaptureFormat& buffer_format, |
| const scoped_refptr<media::VideoFrame>& frame, |
| base::TimeTicks timestamp) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK_NE(buffer->id(), VideoCaptureBufferPool::kInvalidId); |
| |
| int count = 0; |
| if (state_ == VIDEO_CAPTURE_STATE_STARTED) { |
| for (ControllerClients::iterator client_it = controller_clients_.begin(); |
| client_it != controller_clients_.end(); ++client_it) { |
| ControllerClient* client = *client_it; |
| if (client->session_closed || client->paused) |
| continue; |
| |
| if (frame->format() == media::VideoFrame::NATIVE_TEXTURE) { |
| client->event_handler->OnMailboxBufferReady(client->controller_id, |
| buffer->id(), |
| *frame->mailbox_holder(), |
| buffer_format, |
| timestamp); |
| } else { |
| bool is_new_buffer = client->known_buffers.insert(buffer->id()).second; |
| if (is_new_buffer) { |
| // On the first use of a buffer on a client, share the memory handle. |
| size_t memory_size = 0; |
| base::SharedMemoryHandle remote_handle = buffer_pool_->ShareToProcess( |
| buffer->id(), client->render_process_handle, &memory_size); |
| client->event_handler->OnBufferCreated( |
| client->controller_id, remote_handle, memory_size, buffer->id()); |
| } |
| |
| client->event_handler->OnBufferReady( |
| client->controller_id, buffer->id(), buffer_format, |
| frame->visible_rect(), timestamp); |
| } |
| |
| bool inserted = |
| client->active_buffers.insert(std::make_pair(buffer->id(), frame)) |
| .second; |
| DCHECK(inserted) << "Unexpected duplicate buffer: " << buffer->id(); |
| count++; |
| } |
| } |
| |
| if (!frame_received_) { |
| UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Width", |
| buffer_format.frame_size.width()); |
| UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Height", |
| buffer_format.frame_size.height()); |
| UMA_HISTOGRAM_ASPECT_RATIO("Media.VideoCapture.AspectRatio", |
| buffer_format.frame_size.width(), |
| buffer_format.frame_size.height()); |
| UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate", |
| buffer_format.frame_rate); |
| UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.PixelFormat", |
| buffer_format.pixel_format, |
| media::PIXEL_FORMAT_MAX); |
| frame_received_ = true; |
| } |
| |
| buffer_pool_->HoldForConsumers(buffer->id(), count); |
| } |
| |
| void VideoCaptureController::DoErrorOnIOThread() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| state_ = VIDEO_CAPTURE_STATE_ERROR; |
| |
| for (ControllerClients::iterator client_it = controller_clients_.begin(); |
| client_it != controller_clients_.end(); ++client_it) { |
| ControllerClient* client = *client_it; |
| if (client->session_closed) |
| continue; |
| |
| client->event_handler->OnError(client->controller_id); |
| } |
| } |
| |
| void VideoCaptureController::DoBufferDestroyedOnIOThread( |
| int buffer_id_to_drop) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| for (ControllerClients::iterator client_it = controller_clients_.begin(); |
| client_it != controller_clients_.end(); ++client_it) { |
| ControllerClient* client = *client_it; |
| if (client->session_closed) |
| continue; |
| |
| if (client->known_buffers.erase(buffer_id_to_drop)) { |
| client->event_handler->OnBufferDestroyed(client->controller_id, |
| buffer_id_to_drop); |
| } |
| } |
| } |
| |
| 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)->session_id == session_id) { |
| return *client_it; |
| } |
| } |
| return NULL; |
| } |
| |
| int VideoCaptureController::GetClientCount() const { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| return controller_clients_.size(); |
| } |
| |
| int VideoCaptureController::GetActiveClientCount() const { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| int active_client_count = 0; |
| for (ControllerClient* client : controller_clients_) { |
| if (!client->paused) |
| ++active_client_count; |
| } |
| return active_client_count; |
| } |
| |
| } // namespace content |