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, |