Chromium Code Reviews| Index: content/browser/renderer_host/media/video_capture_device_client.cc |
| diff --git a/content/browser/renderer_host/media/video_capture_device_client.cc b/content/browser/renderer_host/media/video_capture_device_client.cc |
| index e9900a5810f57cd2567a369ea3e00bf547e0f5ac..01a67814ef4355d2ecf63fecd7386a42e841b427 100644 |
| --- a/content/browser/renderer_host/media/video_capture_device_client.cc |
| +++ b/content/browser/renderer_host/media/video_capture_device_client.cc |
| @@ -6,13 +6,18 @@ |
| #include "base/bind.h" |
| #include "base/strings/stringprintf.h" |
| +#include "base/synchronization/waitable_event.h" |
| #include "base/trace_event/trace_event.h" |
| +#include "content/browser/gpu/browser_gpu_channel_host_factory.h" |
| #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" |
| #include "content/browser/renderer_host/media/video_capture_controller.h" |
| +#include "content/common/gpu/client/gpu_jpeg_decode_accelerator_host.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "media/base/bind_to_current_loop.h" |
| #include "media/base/video_capture_types.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" |
| using media::VideoCaptureFormat; |
| @@ -27,16 +32,19 @@ class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { |
| AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool, |
| int buffer_id, |
| void* data, |
| - size_t size) |
| + size_t size, |
| + base::SharedMemoryHandle handle) |
| : pool_(pool), |
| id_(buffer_id), |
| data_(data), |
| - size_(size) { |
| + size_(size), |
| + handle_(handle) { |
| DCHECK(pool_.get()); |
| } |
| int id() const override { return id_; } |
| void* data() const override { return data_; } |
| size_t size() const override { return size_; } |
| + base::SharedMemoryHandle handle() const override { return handle_; } |
| private: |
| ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); } |
| @@ -45,16 +53,154 @@ class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { |
| const int id_; |
| void* const data_; |
| const size_t size_; |
| + const base::SharedMemoryHandle handle_; |
| }; |
| +JpegDecodeTask::JpegDecodeTask() |
| + : decoding(false) {} |
| + |
| VideoCaptureDeviceClient::VideoCaptureDeviceClient( |
| const base::WeakPtr<VideoCaptureController>& controller, |
| const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) |
| : controller_(controller), |
| + jpeg_failed_(false), |
| buffer_pool_(buffer_pool), |
| - last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) {} |
| + last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN), |
| + next_bitstream_buffer_id_(0) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| +} |
| + |
| +VideoCaptureDeviceClient::~VideoCaptureDeviceClient() { |
| +} |
| + |
| +bool VideoCaptureDeviceClient::InitializeJpegDecoder() { |
| + // called from device thread |
| + //DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + DVLOG(3) << __func__; |
| + GpuChannelHost* const host = |
| + BrowserGpuChannelHostFactory::instance()->GetGpuChannel(); |
| + GpuJpegDecodeAcceleratorHost* const decoder = host->CreateJpegDecoder(); |
| + if (!decoder->Initialize(this)) { |
| + decoder->Destroy(); |
| + return false; |
| + } |
| + jpeg_decoder_.reset(decoder); |
| + |
| + return true; |
| +} |
| -VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} |
| +void VideoCaptureDeviceClient::VideoFrameReady( |
| + int32_t bitstream_buffer_id) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + DVLOG(3) << __func__ << " " << bitstream_buffer_id; |
| + base::AutoLock lock(jpeg_decode_task_.lock_); |
| + DCHECK(jpeg_decode_task_.decoding); |
| + |
| + DCHECK(jpeg_decode_task_.in_buffer.get()); |
| + if (bitstream_buffer_id != jpeg_decode_task_.in_buffer->id()) { |
| + DLOG(ERROR) << "bitstream_buffer_id mismatched, expected " |
| + << jpeg_decode_task_.in_buffer->id() << ", but got " |
| + << bitstream_buffer_id; |
| + return; |
| + } |
| + |
| + auto& captured_data = jpeg_decode_task_.captured_data; |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind( |
| + &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, |
| + controller_, |
| + jpeg_decode_task_.out_buffer, |
| + jpeg_decode_task_.out_frame, |
| + captured_data.timestamp)); |
| + |
| + jpeg_decode_task_.out_frame = nullptr; |
| + jpeg_decode_task_.out_buffer = nullptr; |
| + jpeg_decode_task_.decoding = false; |
| +} |
| + |
| +void VideoCaptureDeviceClient::NotifyError( |
| + int32_t bitstream_buffer_id, |
| + media::JpegDecodeAccelerator::Error error) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + DVLOG(3) << __func__; |
| + base::AutoLock lock(jpeg_decode_task_.lock_); |
| + DCHECK(jpeg_decode_task_.decoding); |
| + |
| + // TODO check bitstream_buffer_id |
| + OnIncomingCapturedData2(jpeg_decode_task_.captured_data); |
|
wuchengli
2015/04/14 09:41:44
Add a LOG(ERROR) and call VideoCaptureDeviceClient
kcwu
2015/04/16 14:38:27
Done.
|
| + |
| + jpeg_decode_task_.out_frame = nullptr; |
| + jpeg_decode_task_.out_buffer = nullptr; |
| + jpeg_decode_task_.decoding = false; |
| +} |
| + |
| +bool VideoCaptureDeviceClient::PrepareJpegDecode( |
| + const CapturedData& captured_data, |
| + const scoped_refptr<Buffer> out_buffer) { |
| + DVLOG(3) << __func__; |
| + DCHECK(!jpeg_decode_task_.decoding); |
| + |
| + jpeg_decode_task_.captured_data = captured_data; |
| + |
| + // enlarge input buffer if necessary |
| + size_t in_buffer_size = captured_data.length; |
| + if (!jpeg_decode_task_.in_shared_memory.get() || |
| + in_buffer_size > jpeg_decode_task_.in_shared_memory->mapped_size()) { |
| + jpeg_decode_task_.in_shared_memory.reset(new base::SharedMemory); |
| + |
| + |
| + if (!jpeg_decode_task_.in_shared_memory->CreateAnonymous(in_buffer_size)) { |
| + DLOG(ERROR) << "CreateAnonymous failed"; |
| + return false; |
| + } |
| + |
| + if (!jpeg_decode_task_.in_shared_memory->Map(in_buffer_size)) { |
| + DLOG(ERROR) << "Map failed"; |
| + return false; |
| + } |
| + |
| + jpeg_decode_task_.captured_data.data = reinterpret_cast<uint8*>( |
| + jpeg_decode_task_.in_shared_memory->memory()); |
| + } |
| + |
| + jpeg_decode_task_.in_buffer.reset(new media::BitstreamBuffer( |
| + next_bitstream_buffer_id_, |
| + jpeg_decode_task_.in_shared_memory->handle(), |
| + in_buffer_size)); |
| + // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. |
| + next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF; |
| + |
| + gfx::Size dimensions = captured_data.frame_format.frame_size; |
| + |
| + scoped_refptr<media::VideoFrame> out_frame = |
| + media::VideoFrame::WrapExternalPackedMemory( |
| + media::VideoFrame::I420, |
| + dimensions, |
| + gfx::Rect(dimensions), |
| + dimensions, |
| + reinterpret_cast<uint8*>(out_buffer->data()), |
| + out_buffer->size(), |
| + out_buffer->handle(), |
| + 0, |
| + base::TimeDelta(), |
| + base::Closure()); |
| + DCHECK(out_frame.get()); |
| + |
| + out_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, |
| + captured_data.frame_format.frame_rate); |
| + |
| + jpeg_decode_task_.out_frame = out_frame; |
| + jpeg_decode_task_.out_buffer = out_buffer; |
| + |
| + memcpy(jpeg_decode_task_.in_shared_memory->memory(), |
| + captured_data.data, |
| + captured_data.length); |
| + |
| + return true; |
| +} |
| void VideoCaptureDeviceClient::OnIncomingCapturedData( |
| const uint8* data, |
| @@ -63,6 +209,14 @@ void VideoCaptureDeviceClient::OnIncomingCapturedData( |
| int rotation, |
| const base::TimeTicks& timestamp) { |
| TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData"); |
| + DVLOG(3) << __func__; |
| + |
| + CapturedData captured_data; |
| + captured_data.data = const_cast<uint8*>(data); |
| + captured_data.length = length; |
| + captured_data.frame_format = frame_format; |
| + captured_data.rotation = rotation; |
| + captured_data.timestamp = timestamp; |
| if (last_captured_pixel_format_ != frame_format.pixel_format) { |
| OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString( |
| @@ -73,8 +227,67 @@ void VideoCaptureDeviceClient::OnIncomingCapturedData( |
| if (!frame_format.IsValid()) |
| return; |
| + const gfx::Size size = frame_format.frame_size; |
| + if (rotation != 0 || frame_format.pixel_format != media::PIXEL_FORMAT_MJPEG || |
| + size.width() % 2 != 0 || size.height() % 2 != 0 || jpeg_failed_) { |
| + OnIncomingCapturedData2(captured_data); |
| + return; |
| + } |
| + |
| + if (!jpeg_decoder_) { |
| + if (!InitializeJpegDecoder()) { |
| + // TODO fallback to software decode |
|
wuchengli
2015/04/14 09:41:44
Add a LOG(ERROR) here. If we have GpuJpegDecoder e
kcwu
2015/04/16 14:38:27
Done.
|
| + jpeg_failed_ = true; |
| + return; |
| + } |
| + } |
| + |
| + gfx::Size dimensions = frame_format.frame_size; |
| + scoped_refptr<Buffer> out_buffer = |
| + ReserveOutputBuffer(media::VideoFrame::I420, dimensions); |
| + if (!out_buffer.get()) { |
| + DVLOG(1) << "Drop captured frame. No available buffers" |
| + << " (all buffers are still queued to display)"; |
| + return; |
| + } |
| + |
| + base::AutoLock lock(jpeg_decode_task_.lock_); |
|
wuchengli
2015/04/14 09:41:44
Try to hold the lock as minimum as possible so IO
kcwu
2015/04/14 20:02:34
Done.
|
| + if (jpeg_decode_task_.decoding) { |
|
wuchengli
2015/04/14 09:41:44
Checking out_frame == nullptr should be enough. Re
kcwu
2015/04/16 14:38:27
Done.
|
| + DVLOG(1) << "Drop captured frame. Previous jpeg frame is still decoding"; |
| + return; |
| + } |
| + |
| + if (!PrepareJpegDecode(captured_data, out_buffer)) { |
| + // TODO(kcwu): fallback to software decode |
|
wuchengli
2015/04/14 09:41:44
Add a LOG(ERROR), call VideoCaptureDeviceClient::O
kcwu
2015/04/16 14:38:27
Done.
|
| + return; |
| + } |
| + |
| + jpeg_decode_task_.decoding = true; |
| + jpeg_decoder_->Decode( |
| + *jpeg_decode_task_.in_buffer, |
| + jpeg_decode_task_.out_frame); |
| +} |
| + |
| +// TODO(kcwu): need better name |
| +void VideoCaptureDeviceClient::OnIncomingCapturedData2( |
|
wuchengli
2015/04/14 09:41:44
The name OnIncomingCapturedData should be used for
kcwu
2015/04/16 14:38:27
Done.
|
| + const CapturedData& captured_data) { |
| + DVLOG(3) << __func__; |
| + |
| + const uint8* data = captured_data.data; |
| + int length = captured_data.length; |
| + const VideoCaptureFormat& frame_format = captured_data.frame_format; |
| + int rotation = captured_data.rotation; |
| + base::TimeTicks timestamp = captured_data.timestamp; |
| + |
| + if (last_captured_pixel_format_ != frame_format.pixel_format) { |
| + OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString( |
| + frame_format.pixel_format)); |
| + last_captured_pixel_format_ = frame_format.pixel_format; |
| + } |
| + |
| // Chopped pixels in width/height in case video capture device has odd |
| // numbers for width/height. |
| + // XXX kcwu: why crop?? |
|
wuchengli
2015/04/14 09:41:44
remove
kcwu
2015/04/14 20:02:34
Done.
|
| int chopped_width = 0; |
| int chopped_height = 0; |
| int new_unrotated_width = frame_format.frame_size.width(); |
| @@ -105,6 +318,13 @@ void VideoCaptureDeviceClient::OnIncomingCapturedData( |
| scoped_refptr<Buffer> buffer = ReserveOutputBuffer(VideoFrame::I420, |
| dimensions); |
| + { |
| + static int drop_count = 0; |
| + static base::Time start_time = base::Time::Now(); |
| + if (!buffer.get()) |
| + drop_count++; |
| + LOG(ERROR) << "drop fps = " << drop_count / (base::Time::Now() - start_time).InSecondsF(); |
|
wuchengli
2015/04/14 09:41:44
remove
kcwu
2015/04/14 20:02:34
Done.
|
| + } |
| if (!buffer.get()) |
| return; |
| @@ -247,10 +467,11 @@ VideoCaptureDeviceClient::ReserveOutputBuffer(VideoFrame::Format format, |
| return NULL; |
| void* data; |
| size_t size; |
| - buffer_pool_->GetBufferInfo(buffer_id, &data, &size); |
| + base::SharedMemoryHandle handle; |
| + buffer_pool_->GetBufferInfo(buffer_id, &data, &size, &handle); |
| scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer( |
| - new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size)); |
| + new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size, handle)); |
| if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) { |
| BrowserThread::PostTask(BrowserThread::IO, |