Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(326)

Side by Side Diff: content/browser/renderer_host/media/video_capture_device_client.cc

Issue 1016773002: MJPEG acceleration for video capture using VAAPI (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix many thread issues Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/renderer_host/media/video_capture_device_client.h" 5 #include "content/browser/renderer_host/media/video_capture_device_client.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/strings/stringprintf.h" 8 #include "base/strings/stringprintf.h"
9 #include "base/synchronization/waitable_event.h"
9 #include "base/trace_event/trace_event.h" 10 #include "base/trace_event/trace_event.h"
11 #include "content/browser/gpu/browser_gpu_channel_host_factory.h"
10 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" 12 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h"
11 #include "content/browser/renderer_host/media/video_capture_controller.h" 13 #include "content/browser/renderer_host/media/video_capture_controller.h"
14 #include "content/common/gpu/client/gpu_jpeg_decode_accelerator_host.h"
12 #include "content/public/browser/browser_thread.h" 15 #include "content/public/browser/browser_thread.h"
13 #include "media/base/bind_to_current_loop.h" 16 #include "media/base/bind_to_current_loop.h"
14 #include "media/base/video_capture_types.h" 17 #include "media/base/video_capture_types.h"
15 #include "media/base/video_frame.h" 18 #include "media/base/video_frame.h"
19 #include "media/base/video_util.h"
20 #include "media/base/yuv_convert.h"
16 #include "third_party/libyuv/include/libyuv.h" 21 #include "third_party/libyuv/include/libyuv.h"
17 22
18 using media::VideoCaptureFormat; 23 using media::VideoCaptureFormat;
19 using media::VideoFrame; 24 using media::VideoFrame;
20 25
21 namespace content { 26 namespace content {
22 27
23 // Class combining a Client::Buffer interface implementation and a pool buffer 28 // Class combining a Client::Buffer interface implementation and a pool buffer
24 // implementation to guarantee proper cleanup on destruction on our side. 29 // implementation to guarantee proper cleanup on destruction on our side.
25 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { 30 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer {
26 public: 31 public:
27 AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool, 32 AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
28 int buffer_id, 33 int buffer_id,
29 void* data, 34 void* data,
30 size_t size) 35 size_t size,
36 base::SharedMemoryHandle handle)
31 : pool_(pool), 37 : pool_(pool),
32 id_(buffer_id), 38 id_(buffer_id),
33 data_(data), 39 data_(data),
34 size_(size) { 40 size_(size),
41 handle_(handle) {
35 DCHECK(pool_.get()); 42 DCHECK(pool_.get());
36 } 43 }
37 int id() const override { return id_; } 44 int id() const override { return id_; }
38 void* data() const override { return data_; } 45 void* data() const override { return data_; }
39 size_t size() const override { return size_; } 46 size_t size() const override { return size_; }
47 base::SharedMemoryHandle handle() const override { return handle_; }
40 48
41 private: 49 private:
42 ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); } 50 ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
43 51
44 const scoped_refptr<VideoCaptureBufferPool> pool_; 52 const scoped_refptr<VideoCaptureBufferPool> pool_;
45 const int id_; 53 const int id_;
46 void* const data_; 54 void* const data_;
47 const size_t size_; 55 const size_t size_;
56 const base::SharedMemoryHandle handle_;
48 }; 57 };
49 58
59 JpegDecodeTask::JpegDecodeTask()
60 : decoding(false) {}
61
50 VideoCaptureDeviceClient::VideoCaptureDeviceClient( 62 VideoCaptureDeviceClient::VideoCaptureDeviceClient(
51 const base::WeakPtr<VideoCaptureController>& controller, 63 const base::WeakPtr<VideoCaptureController>& controller,
52 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) 64 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool)
53 : controller_(controller), 65 : controller_(controller),
66 jpeg_failed_(false),
54 buffer_pool_(buffer_pool), 67 buffer_pool_(buffer_pool),
55 last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) {} 68 last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN),
69 next_bitstream_buffer_id_(0) {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
71 }
56 72
57 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} 73 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {
74 }
75
76 bool VideoCaptureDeviceClient::InitializeJpegDecoder() {
77 // called from device thread
78 //DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
79 DVLOG(3) << __func__;
80 GpuChannelHost* const host =
81 BrowserGpuChannelHostFactory::instance()->GetGpuChannel();
82 GpuJpegDecodeAcceleratorHost* const decoder = host->CreateJpegDecoder();
83 if (!decoder->Initialize(this)) {
84 decoder->Destroy();
85 return false;
86 }
87 jpeg_decoder_.reset(decoder);
88
89 return true;
90 }
91
92 void VideoCaptureDeviceClient::VideoFrameReady(
93 int32_t bitstream_buffer_id) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
95 DVLOG(3) << __func__ << " " << bitstream_buffer_id;
96 base::AutoLock lock(jpeg_decode_task_.lock_);
97 DCHECK(jpeg_decode_task_.decoding);
98
99 DCHECK(jpeg_decode_task_.in_buffer.get());
100 if (bitstream_buffer_id != jpeg_decode_task_.in_buffer->id()) {
101 DLOG(ERROR) << "bitstream_buffer_id mismatched, expected "
102 << jpeg_decode_task_.in_buffer->id() << ", but got "
103 << bitstream_buffer_id;
104 return;
105 }
106
107 auto& captured_data = jpeg_decode_task_.captured_data;
108
109 BrowserThread::PostTask(
110 BrowserThread::IO,
111 FROM_HERE,
112 base::Bind(
113 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
114 controller_,
115 jpeg_decode_task_.out_buffer,
116 jpeg_decode_task_.out_frame,
117 captured_data.timestamp));
118
119 jpeg_decode_task_.out_frame = nullptr;
120 jpeg_decode_task_.out_buffer = nullptr;
121 jpeg_decode_task_.decoding = false;
122 }
123
124 void VideoCaptureDeviceClient::NotifyError(
125 int32_t bitstream_buffer_id,
126 media::JpegDecodeAccelerator::Error error) {
127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
128 DVLOG(3) << __func__;
129 base::AutoLock lock(jpeg_decode_task_.lock_);
130 DCHECK(jpeg_decode_task_.decoding);
131
132 // TODO check bitstream_buffer_id
133 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.
134
135 jpeg_decode_task_.out_frame = nullptr;
136 jpeg_decode_task_.out_buffer = nullptr;
137 jpeg_decode_task_.decoding = false;
138 }
139
140 bool VideoCaptureDeviceClient::PrepareJpegDecode(
141 const CapturedData& captured_data,
142 const scoped_refptr<Buffer> out_buffer) {
143 DVLOG(3) << __func__;
144 DCHECK(!jpeg_decode_task_.decoding);
145
146 jpeg_decode_task_.captured_data = captured_data;
147
148 // enlarge input buffer if necessary
149 size_t in_buffer_size = captured_data.length;
150 if (!jpeg_decode_task_.in_shared_memory.get() ||
151 in_buffer_size > jpeg_decode_task_.in_shared_memory->mapped_size()) {
152 jpeg_decode_task_.in_shared_memory.reset(new base::SharedMemory);
153
154
155 if (!jpeg_decode_task_.in_shared_memory->CreateAnonymous(in_buffer_size)) {
156 DLOG(ERROR) << "CreateAnonymous failed";
157 return false;
158 }
159
160 if (!jpeg_decode_task_.in_shared_memory->Map(in_buffer_size)) {
161 DLOG(ERROR) << "Map failed";
162 return false;
163 }
164
165 jpeg_decode_task_.captured_data.data = reinterpret_cast<uint8*>(
166 jpeg_decode_task_.in_shared_memory->memory());
167 }
168
169 jpeg_decode_task_.in_buffer.reset(new media::BitstreamBuffer(
170 next_bitstream_buffer_id_,
171 jpeg_decode_task_.in_shared_memory->handle(),
172 in_buffer_size));
173 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
174 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
175
176 gfx::Size dimensions = captured_data.frame_format.frame_size;
177
178 scoped_refptr<media::VideoFrame> out_frame =
179 media::VideoFrame::WrapExternalPackedMemory(
180 media::VideoFrame::I420,
181 dimensions,
182 gfx::Rect(dimensions),
183 dimensions,
184 reinterpret_cast<uint8*>(out_buffer->data()),
185 out_buffer->size(),
186 out_buffer->handle(),
187 0,
188 base::TimeDelta(),
189 base::Closure());
190 DCHECK(out_frame.get());
191
192 out_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
193 captured_data.frame_format.frame_rate);
194
195 jpeg_decode_task_.out_frame = out_frame;
196 jpeg_decode_task_.out_buffer = out_buffer;
197
198 memcpy(jpeg_decode_task_.in_shared_memory->memory(),
199 captured_data.data,
200 captured_data.length);
201
202 return true;
203 }
58 204
59 void VideoCaptureDeviceClient::OnIncomingCapturedData( 205 void VideoCaptureDeviceClient::OnIncomingCapturedData(
60 const uint8* data, 206 const uint8* data,
61 int length, 207 int length,
62 const VideoCaptureFormat& frame_format, 208 const VideoCaptureFormat& frame_format,
63 int rotation, 209 int rotation,
64 const base::TimeTicks& timestamp) { 210 const base::TimeTicks& timestamp) {
65 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData"); 211 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData");
212 DVLOG(3) << __func__;
213
214 CapturedData captured_data;
215 captured_data.data = const_cast<uint8*>(data);
216 captured_data.length = length;
217 captured_data.frame_format = frame_format;
218 captured_data.rotation = rotation;
219 captured_data.timestamp = timestamp;
66 220
67 if (last_captured_pixel_format_ != frame_format.pixel_format) { 221 if (last_captured_pixel_format_ != frame_format.pixel_format) {
68 OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString( 222 OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString(
69 frame_format.pixel_format)); 223 frame_format.pixel_format));
70 last_captured_pixel_format_ = frame_format.pixel_format; 224 last_captured_pixel_format_ = frame_format.pixel_format;
71 } 225 }
72 226
73 if (!frame_format.IsValid()) 227 if (!frame_format.IsValid())
74 return; 228 return;
75 229
230 const gfx::Size size = frame_format.frame_size;
231 if (rotation != 0 || frame_format.pixel_format != media::PIXEL_FORMAT_MJPEG ||
232 size.width() % 2 != 0 || size.height() % 2 != 0 || jpeg_failed_) {
233 OnIncomingCapturedData2(captured_data);
234 return;
235 }
236
237 if (!jpeg_decoder_) {
238 if (!InitializeJpegDecoder()) {
239 // 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.
240 jpeg_failed_ = true;
241 return;
242 }
243 }
244
245 gfx::Size dimensions = frame_format.frame_size;
246 scoped_refptr<Buffer> out_buffer =
247 ReserveOutputBuffer(media::VideoFrame::I420, dimensions);
248 if (!out_buffer.get()) {
249 DVLOG(1) << "Drop captured frame. No available buffers"
250 << " (all buffers are still queued to display)";
251 return;
252 }
253
254 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.
255 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.
256 DVLOG(1) << "Drop captured frame. Previous jpeg frame is still decoding";
257 return;
258 }
259
260 if (!PrepareJpegDecode(captured_data, out_buffer)) {
261 // 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.
262 return;
263 }
264
265 jpeg_decode_task_.decoding = true;
266 jpeg_decoder_->Decode(
267 *jpeg_decode_task_.in_buffer,
268 jpeg_decode_task_.out_frame);
269 }
270
271 // TODO(kcwu): need better name
272 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.
273 const CapturedData& captured_data) {
274 DVLOG(3) << __func__;
275
276 const uint8* data = captured_data.data;
277 int length = captured_data.length;
278 const VideoCaptureFormat& frame_format = captured_data.frame_format;
279 int rotation = captured_data.rotation;
280 base::TimeTicks timestamp = captured_data.timestamp;
281
282 if (last_captured_pixel_format_ != frame_format.pixel_format) {
283 OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString(
284 frame_format.pixel_format));
285 last_captured_pixel_format_ = frame_format.pixel_format;
286 }
287
76 // Chopped pixels in width/height in case video capture device has odd 288 // Chopped pixels in width/height in case video capture device has odd
77 // numbers for width/height. 289 // numbers for width/height.
290 // XXX kcwu: why crop??
wuchengli 2015/04/14 09:41:44 remove
kcwu 2015/04/14 20:02:34 Done.
78 int chopped_width = 0; 291 int chopped_width = 0;
79 int chopped_height = 0; 292 int chopped_height = 0;
80 int new_unrotated_width = frame_format.frame_size.width(); 293 int new_unrotated_width = frame_format.frame_size.width();
81 int new_unrotated_height = frame_format.frame_size.height(); 294 int new_unrotated_height = frame_format.frame_size.height();
82 295
83 if (new_unrotated_width & 1) { 296 if (new_unrotated_width & 1) {
84 --new_unrotated_width; 297 --new_unrotated_width;
85 chopped_width = 1; 298 chopped_width = 1;
86 } 299 }
87 if (new_unrotated_height & 1) { 300 if (new_unrotated_height & 1) {
(...skipping 10 matching lines...) Expand all
98 const gfx::Size dimensions(destination_width, destination_height); 311 const gfx::Size dimensions(destination_width, destination_height);
99 if (!VideoFrame::IsValidConfig(VideoFrame::I420, 312 if (!VideoFrame::IsValidConfig(VideoFrame::I420,
100 dimensions, 313 dimensions,
101 gfx::Rect(dimensions), 314 gfx::Rect(dimensions),
102 dimensions)) { 315 dimensions)) {
103 return; 316 return;
104 } 317 }
105 318
106 scoped_refptr<Buffer> buffer = ReserveOutputBuffer(VideoFrame::I420, 319 scoped_refptr<Buffer> buffer = ReserveOutputBuffer(VideoFrame::I420,
107 dimensions); 320 dimensions);
321 {
322 static int drop_count = 0;
323 static base::Time start_time = base::Time::Now();
324 if (!buffer.get())
325 drop_count++;
326 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.
327 }
108 if (!buffer.get()) 328 if (!buffer.get())
109 return; 329 return;
110 330
111 uint8* const yplane = reinterpret_cast<uint8*>(buffer->data()); 331 uint8* const yplane = reinterpret_cast<uint8*>(buffer->data());
112 uint8* const uplane = 332 uint8* const uplane =
113 yplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420, 333 yplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420,
114 VideoFrame::kYPlane, dimensions); 334 VideoFrame::kYPlane, dimensions);
115 uint8* const vplane = 335 uint8* const vplane =
116 uplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420, 336 uplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420,
117 VideoFrame::kUPlane, dimensions); 337 VideoFrame::kUPlane, dimensions);
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 DLOG_IF(ERROR, frame_bytes == 0) << "Error calculating allocation size"; 460 DLOG_IF(ERROR, frame_bytes == 0) << "Error calculating allocation size";
241 } 461 }
242 462
243 int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId; 463 int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId;
244 const int buffer_id = 464 const int buffer_id =
245 buffer_pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop); 465 buffer_pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop);
246 if (buffer_id == VideoCaptureBufferPool::kInvalidId) 466 if (buffer_id == VideoCaptureBufferPool::kInvalidId)
247 return NULL; 467 return NULL;
248 void* data; 468 void* data;
249 size_t size; 469 size_t size;
250 buffer_pool_->GetBufferInfo(buffer_id, &data, &size); 470 base::SharedMemoryHandle handle;
471 buffer_pool_->GetBufferInfo(buffer_id, &data, &size, &handle);
251 472
252 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer( 473 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer(
253 new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size)); 474 new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size, handle));
254 475
255 if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) { 476 if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) {
256 BrowserThread::PostTask(BrowserThread::IO, 477 BrowserThread::PostTask(BrowserThread::IO,
257 FROM_HERE, 478 FROM_HERE,
258 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread, 479 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread,
259 controller_, buffer_id_to_drop)); 480 controller_, buffer_id_to_drop));
260 } 481 }
261 482
262 return output_buffer; 483 return output_buffer;
263 } 484 }
(...skipping 29 matching lines...) Expand all
293 } 514 }
294 515
295 void VideoCaptureDeviceClient::OnLog( 516 void VideoCaptureDeviceClient::OnLog(
296 const std::string& message) { 517 const std::string& message) {
297 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 518 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
298 base::Bind(&VideoCaptureController::DoLogOnIOThread, 519 base::Bind(&VideoCaptureController::DoLogOnIOThread,
299 controller_, message)); 520 controller_, message));
300 } 521 }
301 522
302 } // namespace content 523 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698