Add ICU message format support
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_device_client.cc
blob1c7f8c5c18d61a45f6897577948e0ef282366ce9
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
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/media/video_capture_device_client.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/trace_event/trace_event.h"
12 #include "content/browser/compositor/image_transport_factory.h"
13 #include "content/browser/gpu/browser_gpu_channel_host_factory.h"
14 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
15 #include "content/browser/gpu/gpu_data_manager_impl.h"
16 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h"
17 #include "content/browser/renderer_host/media/video_capture_controller.h"
18 #include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
19 #include "content/common/gpu/client/context_provider_command_buffer.h"
20 #include "content/common/gpu/client/gl_helper.h"
21 #include "content/common/gpu/client/gpu_channel_host.h"
22 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
23 #include "content/common/gpu/gpu_process_launch_causes.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "gpu/command_buffer/common/mailbox_holder.h"
26 #include "media/base/bind_to_current_loop.h"
27 #include "media/base/video_capture_types.h"
28 #include "media/base/video_frame.h"
29 #include "third_party/khronos/GLES2/gl2ext.h"
30 #include "third_party/libyuv/include/libyuv.h"
32 using media::VideoCaptureFormat;
33 using media::VideoFrame;
34 using media::VideoFrameMetadata;
36 namespace content {
38 namespace {
40 #if !defined(OS_ANDROID)
41 // Modelled after GpuProcessTransportFactory::CreateContextCommon().
42 scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> CreateContextCommon(
43 scoped_refptr<content::GpuChannelHost> gpu_channel_host,
44 int surface_id) {
45 if (!content::GpuDataManagerImpl::GetInstance()->
46 CanUseGpuBrowserCompositor()) {
47 DLOG(ERROR) << "No accelerated graphics found. Check chrome://gpu";
48 return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>();
50 blink::WebGraphicsContext3D::Attributes attrs;
51 attrs.shareResources = true;
52 attrs.depth = false;
53 attrs.stencil = false;
54 attrs.antialias = false;
55 attrs.noAutomaticFlushes = true;
57 if (!gpu_channel_host.get()) {
58 DLOG(ERROR) << "Failed to establish GPU channel.";
59 return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>();
61 GURL url("chrome://gpu/GpuProcessTransportFactory::CreateCaptureContext");
62 return make_scoped_ptr(
63 new WebGraphicsContext3DCommandBufferImpl(
64 surface_id,
65 url,
66 gpu_channel_host.get(),
67 attrs,
68 true /* lose_context_when_out_of_memory */,
69 content::WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(),
70 NULL));
73 // Modelled after
74 // GpuProcessTransportFactory::CreateOffscreenCommandBufferContext().
75 scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>
76 CreateOffscreenCommandBufferContext() {
77 content::CauseForGpuLaunch cause = content::CAUSE_FOR_GPU_LAUNCH_CANVAS_2D;
78 // Android does not support synchronous opening of GPU channels. Should use
79 // EstablishGpuChannel() instead.
80 if (!content::BrowserGpuChannelHostFactory::instance())
81 return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>();
82 scoped_refptr<content::GpuChannelHost> gpu_channel_host(
83 content::BrowserGpuChannelHostFactory::instance()->
84 EstablishGpuChannelSync(cause));
85 DCHECK(gpu_channel_host);
86 return CreateContextCommon(gpu_channel_host, 0);
88 #endif
90 typedef base::Callback<void(scoped_refptr<ContextProviderCommandBuffer>)>
91 ProcessContextCallback;
93 void CreateContextOnUIThread(ProcessContextCallback bottom_half) {
94 DCHECK_CURRENTLY_ON(BrowserThread::UI);
95 #if !defined(OS_ANDROID)
96 bottom_half.Run(ContextProviderCommandBuffer::Create(
97 CreateOffscreenCommandBufferContext(), OFFSCREEN_VIDEO_CAPTURE_CONTEXT));
98 return;
99 #endif
102 void ResetLostContextCallback(
103 const scoped_refptr<ContextProviderCommandBuffer>& capture_thread_context) {
104 capture_thread_context->SetLostContextCallback(
105 cc::ContextProvider::LostContextCallback());
108 } // anonymous namespace
110 // Class combining a Client::Buffer interface implementation and a pool buffer
111 // implementation to guarantee proper cleanup on destruction on our side.
112 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer {
113 public:
114 AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
115 int buffer_id)
116 : id_(buffer_id),
117 pool_(pool),
118 buffer_handle_(pool_->GetBufferHandle(buffer_id).Pass()) {
119 DCHECK(pool_.get());
121 int id() const override { return id_; }
122 size_t size() const override { return buffer_handle_->size(); }
123 void* data() override { return buffer_handle_->data(); }
124 ClientBuffer AsClientBuffer() override {
125 return buffer_handle_->AsClientBuffer();
127 #if defined(OS_POSIX)
128 base::FileDescriptor AsPlatformFile() override {
129 return buffer_handle_->AsPlatformFile();
131 #endif
133 private:
134 ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
136 const int id_;
137 const scoped_refptr<VideoCaptureBufferPool> pool_;
138 const scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle_;
141 // Internal ref-counted class wrapping an incoming GpuMemoryBuffer into a
142 // Texture backed VideoFrame. This VideoFrame creation is balanced by a waiting
143 // on the associated |sync_point|. After VideoFrame consumption the inserted
144 // ReleaseCallback() will be called, where the Texture is destroyed.
146 // This class jumps between threads due to GPU-related thread limitations, i.e.
147 // some objects cannot be accessed from IO Thread whereas others need to be
148 // constructed on UI Thread. For this reason most of the operations are carried
149 // out on Capture Thread (|capture_task_runner_|).
150 class VideoCaptureDeviceClient::TextureWrapHelper final
151 : public base::RefCountedThreadSafe<TextureWrapHelper> {
152 public:
153 TextureWrapHelper(
154 const base::WeakPtr<VideoCaptureController>& controller,
155 const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner);
157 // Wraps the GpuMemoryBuffer-backed |buffer| into a Texture, and sends it to
158 // |controller_| wrapped in a VideoFrame.
159 void OnIncomingCapturedGpuMemoryBuffer(
160 scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
161 const media::VideoCaptureFormat& frame_format,
162 const base::TimeTicks& timestamp);
164 private:
165 friend class base::RefCountedThreadSafe<TextureWrapHelper>;
166 ~TextureWrapHelper();
168 // Creates some necessary members in |capture_task_runner_|.
169 void Init();
170 // Runs the bottom half of the GlHelper creation.
171 void CreateGlHelper(
172 scoped_refptr<ContextProviderCommandBuffer> capture_thread_context);
174 // Recycles |memory_buffer|, deletes Image and Texture on VideoFrame release.
175 void ReleaseCallback(GLuint image_id,
176 GLuint texture_id,
177 uint32 sync_point);
179 // The Command Buffer lost the GL context, f.i. GPU process crashed. Signal
180 // error to our owner so the capture can be torn down.
181 void LostContextCallback();
183 // Prints the error |message| and notifies |controller_| of an error.
184 void OnError(const std::string& message);
186 // |controller_| should only be used on IO thread.
187 const base::WeakPtr<VideoCaptureController> controller_;
188 const scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_;
190 // Command buffer reference, needs to be destroyed when unused. It is created
191 // on UI Thread and bound to Capture Thread. In particular, it cannot be used
192 // from IO Thread.
193 scoped_refptr<ContextProviderCommandBuffer> capture_thread_context_;
194 // Created and used from Capture Thread. Cannot be used from IO Thread.
195 scoped_ptr<GLHelper> gl_helper_;
197 DISALLOW_COPY_AND_ASSIGN(TextureWrapHelper);
200 VideoCaptureDeviceClient::VideoCaptureDeviceClient(
201 const base::WeakPtr<VideoCaptureController>& controller,
202 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool,
203 const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner)
204 : controller_(controller),
205 external_jpeg_decoder_initialized_(false),
206 buffer_pool_(buffer_pool),
207 capture_task_runner_(capture_task_runner),
208 last_captured_pixel_format_(media::VIDEO_CAPTURE_PIXEL_FORMAT_UNKNOWN) {
209 DCHECK_CURRENTLY_ON(BrowserThread::IO);
212 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {
213 // This should be on the platform auxiliary thread since
214 // |external_jpeg_decoder_| need to be destructed on the same thread as
215 // OnIncomingCapturedData.
218 void VideoCaptureDeviceClient::OnIncomingCapturedData(
219 const uint8* data,
220 int length,
221 const VideoCaptureFormat& frame_format,
222 int rotation,
223 const base::TimeTicks& timestamp) {
224 TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedData");
225 DCHECK_EQ(media::PIXEL_STORAGE_CPU, frame_format.pixel_storage);
227 if (last_captured_pixel_format_ != frame_format.pixel_format) {
228 OnLog("Pixel format: " +
229 VideoCaptureFormat::PixelFormatToString(frame_format.pixel_format));
230 last_captured_pixel_format_ = frame_format.pixel_format;
232 if (frame_format.pixel_format == media::VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG &&
233 !external_jpeg_decoder_initialized_) {
234 external_jpeg_decoder_initialized_ = true;
235 external_jpeg_decoder_.reset(new VideoCaptureGpuJpegDecoder(
236 base::Bind(
237 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
238 controller_)));
239 external_jpeg_decoder_->Initialize();
243 if (!frame_format.IsValid())
244 return;
246 // |chopped_{width,height} and |new_unrotated_{width,height}| are the lowest
247 // bit decomposition of {width, height}, grabbing the odd and even parts.
248 const int chopped_width = frame_format.frame_size.width() & 1;
249 const int chopped_height = frame_format.frame_size.height() & 1;
250 const int new_unrotated_width = frame_format.frame_size.width() & ~1;
251 const int new_unrotated_height = frame_format.frame_size.height() & ~1;
253 int destination_width = new_unrotated_width;
254 int destination_height = new_unrotated_height;
255 if (rotation == 90 || rotation == 270)
256 std::swap(destination_width, destination_height);
258 DCHECK_EQ(0, rotation % 90)
259 << " Rotation must be a multiple of 90, now: " << rotation;
260 libyuv::RotationMode rotation_mode = libyuv::kRotate0;
261 if (rotation == 90)
262 rotation_mode = libyuv::kRotate90;
263 else if (rotation == 180)
264 rotation_mode = libyuv::kRotate180;
265 else if (rotation == 270)
266 rotation_mode = libyuv::kRotate270;
268 const gfx::Size dimensions(destination_width, destination_height);
269 if (!VideoFrame::IsValidConfig(media::PIXEL_FORMAT_I420,
270 VideoFrame::STORAGE_UNKNOWN, dimensions,
271 gfx::Rect(dimensions), dimensions)) {
272 return;
275 scoped_ptr<Buffer> buffer(
276 ReserveOutputBuffer(dimensions, media::VIDEO_CAPTURE_PIXEL_FORMAT_I420,
277 media::PIXEL_STORAGE_CPU));
278 if (!buffer.get())
279 return;
281 const size_t y_plane_size =
282 VideoFrame::PlaneSize(media::PIXEL_FORMAT_I420, VideoFrame::kYPlane,
283 dimensions).GetArea();
284 const size_t u_plane_size =
285 VideoFrame::PlaneSize(media::PIXEL_FORMAT_I420, VideoFrame::kUPlane,
286 dimensions).GetArea();
287 uint8* const yplane = reinterpret_cast<uint8*>(buffer->data());
288 uint8* const uplane = yplane + y_plane_size;
289 uint8* const vplane = uplane + u_plane_size;
291 const int yplane_stride = dimensions.width();
292 const int uv_plane_stride = yplane_stride / 2;
293 int crop_x = 0;
294 int crop_y = 0;
295 libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY;
297 bool flip = false;
298 switch (frame_format.pixel_format) {
299 case media::VIDEO_CAPTURE_PIXEL_FORMAT_UNKNOWN: // Color format not set.
300 break;
301 case media::VIDEO_CAPTURE_PIXEL_FORMAT_I420:
302 DCHECK(!chopped_width && !chopped_height);
303 origin_colorspace = libyuv::FOURCC_I420;
304 break;
305 case media::VIDEO_CAPTURE_PIXEL_FORMAT_YV12:
306 DCHECK(!chopped_width && !chopped_height);
307 origin_colorspace = libyuv::FOURCC_YV12;
308 break;
309 case media::VIDEO_CAPTURE_PIXEL_FORMAT_NV12:
310 DCHECK(!chopped_width && !chopped_height);
311 origin_colorspace = libyuv::FOURCC_NV12;
312 break;
313 case media::VIDEO_CAPTURE_PIXEL_FORMAT_NV21:
314 DCHECK(!chopped_width && !chopped_height);
315 origin_colorspace = libyuv::FOURCC_NV21;
316 break;
317 case media::VIDEO_CAPTURE_PIXEL_FORMAT_YUY2:
318 DCHECK(!chopped_width && !chopped_height);
319 origin_colorspace = libyuv::FOURCC_YUY2;
320 break;
321 case media::VIDEO_CAPTURE_PIXEL_FORMAT_UYVY:
322 DCHECK(!chopped_width && !chopped_height);
323 origin_colorspace = libyuv::FOURCC_UYVY;
324 break;
325 case media::VIDEO_CAPTURE_PIXEL_FORMAT_RGB24:
326 // Linux RGB24 defines red at lowest byte address,
327 // see http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html.
328 // Windows RGB24 defines blue at lowest byte,
329 // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd407253
330 #if defined(OS_LINUX)
331 origin_colorspace = libyuv::FOURCC_RAW;
332 #elif defined(OS_WIN)
333 origin_colorspace = libyuv::FOURCC_24BG;
334 #else
335 NOTREACHED() << "RGB24 is only available in Linux and Windows platforms";
336 #endif
337 #if defined(OS_WIN)
338 // TODO(wjia): Currently, for RGB24 on WIN, capture device always
339 // passes in positive src_width and src_height. Remove this hardcoded
340 // value when nagative src_height is supported. The negative src_height
341 // indicates that vertical flipping is needed.
342 flip = true;
343 #endif
344 break;
345 case media::VIDEO_CAPTURE_PIXEL_FORMAT_RGB32:
346 // Fallback to VIDEO_CAPTURE_PIXEL_FORMAT_ARGB setting |flip| in Windows
347 // platforms.
348 #if defined(OS_WIN)
349 flip = true;
350 #endif
351 case media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB:
352 origin_colorspace = libyuv::FOURCC_ARGB;
353 break;
354 case media::VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG:
355 origin_colorspace = libyuv::FOURCC_MJPG;
356 break;
357 default:
358 NOTREACHED();
361 // The input |length| can be greater than the required buffer size because of
362 // paddings and/or alignments, but it cannot be smaller.
363 DCHECK_GE(static_cast<size_t>(length), frame_format.ImageAllocationSize());
365 if (external_jpeg_decoder_) {
366 const VideoCaptureGpuJpegDecoder::STATUS status =
367 external_jpeg_decoder_->GetStatus();
368 if (status == VideoCaptureGpuJpegDecoder::FAILED) {
369 external_jpeg_decoder_.reset();
370 } else if (status == VideoCaptureGpuJpegDecoder::INIT_PASSED &&
371 frame_format.pixel_format == media::VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG &&
372 rotation == 0 && !flip) {
373 external_jpeg_decoder_->DecodeCapturedData(data, length, frame_format,
374 timestamp, buffer.Pass());
375 return;
379 if (libyuv::ConvertToI420(data,
380 length,
381 yplane,
382 yplane_stride,
383 uplane,
384 uv_plane_stride,
385 vplane,
386 uv_plane_stride,
387 crop_x,
388 crop_y,
389 frame_format.frame_size.width(),
390 (flip ? -1 : 1) * frame_format.frame_size.height(),
391 new_unrotated_width,
392 new_unrotated_height,
393 rotation_mode,
394 origin_colorspace) != 0) {
395 DLOG(WARNING) << "Failed to convert buffer's pixel format to I420 from "
396 << VideoCaptureFormat::PixelFormatToString(
397 frame_format.pixel_format);
398 return;
401 const VideoCaptureFormat output_format = VideoCaptureFormat(
402 dimensions, frame_format.frame_rate,
403 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420, media::PIXEL_STORAGE_CPU);
404 OnIncomingCapturedBuffer(buffer.Pass(), output_format, timestamp);
407 void
408 VideoCaptureDeviceClient::OnIncomingCapturedYuvData(
409 const uint8* y_data,
410 const uint8* u_data,
411 const uint8* v_data,
412 size_t y_stride,
413 size_t u_stride,
414 size_t v_stride,
415 const VideoCaptureFormat& frame_format,
416 int clockwise_rotation,
417 const base::TimeTicks& timestamp) {
418 TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedYuvData");
419 DCHECK_EQ(media::VIDEO_CAPTURE_PIXEL_FORMAT_I420, frame_format.pixel_format);
420 DCHECK_EQ(media::PIXEL_STORAGE_CPU, frame_format.pixel_storage);
421 DCHECK_EQ(0, clockwise_rotation) << "Rotation not supported";
423 scoped_ptr<Buffer> buffer(ReserveOutputBuffer(frame_format.frame_size,
424 frame_format.pixel_format,
425 frame_format.pixel_storage));
426 if (!buffer.get())
427 return;
429 // Blit (copy) here from y,u,v into buffer.data()). Needed so we can return
430 // the parameter buffer synchronously to the driver.
431 const size_t y_plane_size =
432 VideoFrame::PlaneSize(media::PIXEL_FORMAT_I420, VideoFrame::kYPlane,
433 frame_format.frame_size).GetArea();
434 const size_t u_plane_size =
435 VideoFrame::PlaneSize(media::PIXEL_FORMAT_I420, VideoFrame::kUPlane,
436 frame_format.frame_size).GetArea();
437 uint8* const dst_y = reinterpret_cast<uint8*>(buffer->data());
438 uint8* const dst_u = dst_y + y_plane_size;
439 uint8* const dst_v = dst_u + u_plane_size;
441 const size_t dst_y_stride =
442 VideoFrame::RowBytes(VideoFrame::kYPlane, media::PIXEL_FORMAT_I420,
443 frame_format.frame_size.width());
444 const size_t dst_u_stride =
445 VideoFrame::RowBytes(VideoFrame::kUPlane, media::PIXEL_FORMAT_I420,
446 frame_format.frame_size.width());
447 const size_t dst_v_stride =
448 VideoFrame::RowBytes(VideoFrame::kVPlane, media::PIXEL_FORMAT_I420,
449 frame_format.frame_size.width());
450 DCHECK_GE(y_stride, dst_y_stride);
451 DCHECK_GE(u_stride, dst_u_stride);
452 DCHECK_GE(v_stride, dst_v_stride);
454 if (libyuv::I420Copy(y_data, y_stride,
455 u_data, u_stride,
456 v_data, v_stride,
457 dst_y, dst_y_stride,
458 dst_u, dst_u_stride,
459 dst_v, dst_v_stride,
460 frame_format.frame_size.width(),
461 frame_format.frame_size.height())) {
462 DLOG(WARNING) << "Failed to copy buffer";
463 return;
466 OnIncomingCapturedBuffer(buffer.Pass(), frame_format, timestamp);
469 scoped_ptr<media::VideoCaptureDevice::Client::Buffer>
470 VideoCaptureDeviceClient::ReserveOutputBuffer(
471 const gfx::Size& frame_size,
472 media::VideoCapturePixelFormat pixel_format,
473 media::VideoPixelStorage pixel_storage) {
474 DCHECK(pixel_format == media::VIDEO_CAPTURE_PIXEL_FORMAT_I420 ||
475 pixel_format == media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB);
476 DCHECK_GT(frame_size.width(), 0);
477 DCHECK_GT(frame_size.height(), 0);
479 if (pixel_storage == media::PIXEL_STORAGE_GPUMEMORYBUFFER &&
480 !texture_wrap_helper_) {
481 texture_wrap_helper_ =
482 new TextureWrapHelper(controller_, capture_task_runner_);
485 // TODO(mcasas): For PIXEL_STORAGE_GPUMEMORYBUFFER, find a way to indicate if
486 // it's a ShMem GMB or a DmaBuf GMB.
487 int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId;
488 const int buffer_id = buffer_pool_->ReserveForProducer(
489 pixel_format, pixel_storage, frame_size, &buffer_id_to_drop);
490 if (buffer_id == VideoCaptureBufferPool::kInvalidId)
491 return NULL;
493 scoped_ptr<media::VideoCaptureDevice::Client::Buffer> output_buffer(
494 new AutoReleaseBuffer(buffer_pool_, buffer_id));
496 if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) {
497 BrowserThread::PostTask(BrowserThread::IO,
498 FROM_HERE,
499 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread,
500 controller_, buffer_id_to_drop));
503 return output_buffer.Pass();
506 void VideoCaptureDeviceClient::OnIncomingCapturedBuffer(
507 scoped_ptr<Buffer> buffer,
508 const VideoCaptureFormat& frame_format,
509 const base::TimeTicks& timestamp) {
510 if (frame_format.pixel_storage == media::PIXEL_STORAGE_GPUMEMORYBUFFER) {
511 capture_task_runner_->PostTask(
512 FROM_HERE,
513 base::Bind(&TextureWrapHelper::OnIncomingCapturedGpuMemoryBuffer,
514 texture_wrap_helper_,
515 base::Passed(&buffer),
516 frame_format,
517 timestamp));
518 } else {
519 DCHECK(frame_format.pixel_format ==
520 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420 ||
521 frame_format.pixel_format == media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB);
522 scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalData(
523 media::PIXEL_FORMAT_I420, frame_format.frame_size,
524 gfx::Rect(frame_format.frame_size), frame_format.frame_size,
525 reinterpret_cast<uint8*>(buffer->data()),
526 VideoFrame::AllocationSize(media::PIXEL_FORMAT_I420,
527 frame_format.frame_size),
528 base::TimeDelta());
529 DCHECK(video_frame.get());
530 video_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
531 frame_format.frame_rate);
532 OnIncomingCapturedVideoFrame(buffer.Pass(), video_frame, timestamp);
536 void VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
537 scoped_ptr<Buffer> buffer,
538 const scoped_refptr<VideoFrame>& frame,
539 const base::TimeTicks& timestamp) {
540 BrowserThread::PostTask(
541 BrowserThread::IO,
542 FROM_HERE,
543 base::Bind(
544 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
545 controller_,
546 base::Passed(&buffer),
547 frame,
548 timestamp));
551 void VideoCaptureDeviceClient::OnError(
552 const std::string& reason) {
553 const std::string log_message = base::StringPrintf(
554 "Error on video capture: %s, OS message: %s",
555 reason.c_str(),
556 logging::SystemErrorCodeToString(
557 logging::GetLastSystemErrorCode()).c_str());
558 DLOG(ERROR) << log_message;
559 OnLog(log_message);
560 BrowserThread::PostTask(BrowserThread::IO,
561 FROM_HERE,
562 base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_));
565 void VideoCaptureDeviceClient::OnLog(
566 const std::string& message) {
567 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
568 base::Bind(&VideoCaptureController::DoLogOnIOThread,
569 controller_, message));
572 double VideoCaptureDeviceClient::GetBufferPoolUtilization() const {
573 // VideoCaptureBufferPool::GetBufferPoolUtilization() is thread-safe.
574 return buffer_pool_->GetBufferPoolUtilization();
577 VideoCaptureDeviceClient::TextureWrapHelper::TextureWrapHelper(
578 const base::WeakPtr<VideoCaptureController>& controller,
579 const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner)
580 : controller_(controller),
581 capture_task_runner_(capture_task_runner) {
582 capture_task_runner_->PostTask(FROM_HERE,
583 base::Bind(&TextureWrapHelper::Init, this));
586 void
587 VideoCaptureDeviceClient::TextureWrapHelper::OnIncomingCapturedGpuMemoryBuffer(
588 scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer,
589 const media::VideoCaptureFormat& frame_format,
590 const base::TimeTicks& timestamp) {
591 DCHECK(capture_task_runner_->BelongsToCurrentThread());
592 DCHECK_EQ(media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB, frame_format.pixel_format);
593 DCHECK_EQ(media::PIXEL_STORAGE_GPUMEMORYBUFFER, frame_format.pixel_storage);
594 if (!gl_helper_) {
595 // |gl_helper_| might not exist due to asynchronous initialization not
596 // finished or due to termination in process after a context loss.
597 DVLOG(1) << " Skipping ingress frame, no GL context.";
598 return;
601 gpu::gles2::GLES2Interface* gl = capture_thread_context_->ContextGL();
602 GLuint image_id = gl->CreateImageCHROMIUM(buffer->AsClientBuffer(),
603 frame_format.frame_size.width(),
604 frame_format.frame_size.height(),
605 GL_BGRA_EXT);
606 DCHECK(image_id);
608 const GLuint texture_id = gl_helper_->CreateTexture();
609 DCHECK(texture_id);
611 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_id);
612 gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id);
615 const gpu::MailboxHolder& mailbox_holder(
616 gl_helper_->ProduceMailboxHolderFromTexture(texture_id));
617 DCHECK(!mailbox_holder.mailbox.IsZero());
618 DCHECK(mailbox_holder.mailbox.Verify());
619 DCHECK(mailbox_holder.texture_target);
620 DCHECK(mailbox_holder.sync_point);
622 scoped_refptr<media::VideoFrame> video_frame =
623 media::VideoFrame::WrapNativeTexture(
624 media::PIXEL_FORMAT_ARGB, mailbox_holder,
625 media::BindToCurrentLoop(base::Bind(
626 &VideoCaptureDeviceClient::TextureWrapHelper::ReleaseCallback,
627 this, image_id, texture_id)),
628 frame_format.frame_size, gfx::Rect(frame_format.frame_size),
629 frame_format.frame_size, base::TimeDelta());
630 video_frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
631 video_frame->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE,
632 frame_format.frame_rate);
633 #if defined(OS_LINUX)
634 // TODO(mcasas): After http://crev.com/1179323002, use |frame_format| to query
635 // the storage type of the buffer and use the appropriate |video_frame| method.
636 #if defined(USE_OZONE)
637 DCHECK_EQ(1u, media::VideoFrame::NumPlanes(video_frame->format()));
638 video_frame->DuplicateFileDescriptors(
639 std::vector<int>(1, buffer->AsPlatformFile().fd));
640 #else
641 video_frame->AddSharedMemoryHandle(buffer->AsPlatformFile());
642 #endif
644 #endif
645 //TODO(mcasas): use AddSharedMemoryHandle() for gfx::SHARED_MEMORY_BUFFER.
647 BrowserThread::PostTask(
648 BrowserThread::IO, FROM_HERE,
649 base::Bind(
650 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
651 controller_, base::Passed(&buffer), video_frame, timestamp));
654 VideoCaptureDeviceClient::TextureWrapHelper::~TextureWrapHelper() {
655 // Might not be running on capture_task_runner_'s thread. Ensure owned objects
656 // are destroyed on the correct threads.
657 if (gl_helper_)
658 capture_task_runner_->DeleteSoon(FROM_HERE, gl_helper_.release());
660 if (capture_thread_context_) {
661 capture_task_runner_->PostTask(
662 FROM_HERE,
663 base::Bind(&ResetLostContextCallback, capture_thread_context_));
664 capture_thread_context_->AddRef();
665 ContextProviderCommandBuffer* raw_capture_thread_context =
666 capture_thread_context_.get();
667 capture_thread_context_ = nullptr;
668 capture_task_runner_->ReleaseSoon(FROM_HERE, raw_capture_thread_context);
672 void VideoCaptureDeviceClient::TextureWrapHelper::Init() {
673 DCHECK(capture_task_runner_->BelongsToCurrentThread());
675 // In threaded compositing mode, we have to create our own context for Capture
676 // to avoid using the GPU command queue from multiple threads. Context
677 // creation must happen on UI thread; then the context needs to be bound to
678 // the appropriate thread, which is done in CreateGlHelper().
679 BrowserThread::PostTask(
680 BrowserThread::UI, FROM_HERE,
681 base::Bind(
682 &CreateContextOnUIThread,
683 media::BindToCurrentLoop(base::Bind(
684 &VideoCaptureDeviceClient::TextureWrapHelper::CreateGlHelper,
685 this))));
688 void VideoCaptureDeviceClient::TextureWrapHelper::CreateGlHelper(
689 scoped_refptr<ContextProviderCommandBuffer> capture_thread_context) {
690 DCHECK(capture_task_runner_->BelongsToCurrentThread());
692 if (!capture_thread_context.get()) {
693 DLOG(ERROR) << "No offscreen GL Context!";
694 return;
696 // This may not happen in IO Thread. The destructor resets the context lost
697 // callback, so base::Unretained is safe; otherwise it'd be a circular ref
698 // counted dependency.
699 capture_thread_context->SetLostContextCallback(media::BindToCurrentLoop(
700 base::Bind(
701 &VideoCaptureDeviceClient::TextureWrapHelper::LostContextCallback,
702 base::Unretained(this))));
703 if (!capture_thread_context->BindToCurrentThread()) {
704 capture_thread_context = NULL;
705 DLOG(ERROR) << "Couldn't bind the Capture Context to the Capture Thread.";
706 return;
708 DCHECK(capture_thread_context);
709 capture_thread_context_ = capture_thread_context;
711 // At this point, |capture_thread_context| is a cc::ContextProvider. Creation
712 // of our GLHelper should happen on Capture Thread.
713 gl_helper_.reset(new GLHelper(capture_thread_context->ContextGL(),
714 capture_thread_context->ContextSupport()));
715 DCHECK(gl_helper_);
718 void VideoCaptureDeviceClient::TextureWrapHelper::ReleaseCallback(
719 GLuint image_id,
720 GLuint texture_id,
721 uint32 sync_point) {
722 DCHECK(capture_task_runner_->BelongsToCurrentThread());
724 if (gl_helper_) {
725 gl_helper_->DeleteTexture(texture_id);
726 capture_thread_context_->ContextGL()->DestroyImageCHROMIUM(image_id);
730 void VideoCaptureDeviceClient::TextureWrapHelper::LostContextCallback() {
731 DCHECK(capture_task_runner_->BelongsToCurrentThread());
732 // Prevent incoming frames from being processed while OnError gets groked.
733 gl_helper_.reset();
734 OnError("GLContext lost");
737 void VideoCaptureDeviceClient::TextureWrapHelper::OnError(
738 const std::string& message) {
739 DCHECK(capture_task_runner_->BelongsToCurrentThread());
740 DLOG(ERROR) << message;
741 BrowserThread::PostTask(
742 BrowserThread::IO, FROM_HERE,
743 base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_));
746 } // namespace content