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"
8 #include "base/strings/stringprintf.h"
9 #include "base/trace_event/trace_event.h"
10 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h"
11 #include "content/browser/renderer_host/media/video_capture_controller.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "media/base/bind_to_current_loop.h"
14 #include "media/base/video_capture_types.h"
15 #include "media/base/video_frame.h"
16 #include "third_party/libyuv/include/libyuv.h"
18 using media::VideoCaptureFormat
;
19 using media::VideoFrame
;
23 // Class combining a Client::Buffer interface implementation and a pool buffer
24 // implementation to guarantee proper cleanup on destruction on our side.
25 class AutoReleaseBuffer
: public media::VideoCaptureDevice::Client::Buffer
{
27 AutoReleaseBuffer(const scoped_refptr
<VideoCaptureBufferPool
>& pool
,
37 int id() const override
{ return id_
; }
38 void* data() const override
{ return data_
; }
39 size_t size() const override
{ return size_
; }
42 ~AutoReleaseBuffer() override
{ pool_
->RelinquishProducerReservation(id_
); }
44 const scoped_refptr
<VideoCaptureBufferPool
> pool_
;
50 VideoCaptureDeviceClient::VideoCaptureDeviceClient(
51 const base::WeakPtr
<VideoCaptureController
>& controller
,
52 const scoped_refptr
<VideoCaptureBufferPool
>& buffer_pool
)
53 : controller_(controller
),
54 buffer_pool_(buffer_pool
),
55 last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN
) {}
57 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
59 void VideoCaptureDeviceClient::OnIncomingCapturedData(
62 const VideoCaptureFormat
& frame_format
,
64 const base::TimeTicks
& timestamp
) {
65 TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedData");
67 if (last_captured_pixel_format_
!= frame_format
.pixel_format
) {
68 OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString(
69 frame_format
.pixel_format
));
70 last_captured_pixel_format_
= frame_format
.pixel_format
;
73 if (!frame_format
.IsValid())
76 // |chopped_{width,height} and |new_unrotated_{width,height}| are the lowest
77 // bit decomposition of {width, height}, grabbing the odd and even parts.
78 const int chopped_width
= frame_format
.frame_size
.width() & 1;
79 const int chopped_height
= frame_format
.frame_size
.height() & 1;
80 const int new_unrotated_width
= frame_format
.frame_size
.width() & ~1;
81 const int new_unrotated_height
= frame_format
.frame_size
.height() & ~1;
83 int destination_width
= new_unrotated_width
;
84 int destination_height
= new_unrotated_height
;
85 if (rotation
== 90 || rotation
== 270) {
86 destination_width
= new_unrotated_height
;
87 destination_height
= new_unrotated_width
;
90 DCHECK_EQ(rotation
% 90, 0)
91 << " Rotation must be a multiple of 90, now: " << rotation
;
92 libyuv::RotationMode rotation_mode
= libyuv::kRotate0
;
94 rotation_mode
= libyuv::kRotate90
;
95 else if (rotation
== 180)
96 rotation_mode
= libyuv::kRotate180
;
97 else if (rotation
== 270)
98 rotation_mode
= libyuv::kRotate270
;
100 const gfx::Size
dimensions(destination_width
, destination_height
);
101 if (!VideoFrame::IsValidConfig(VideoFrame::I420
,
103 gfx::Rect(dimensions
),
108 scoped_refptr
<Buffer
> buffer
= ReserveOutputBuffer(VideoFrame::I420
,
113 uint8
* const yplane
= reinterpret_cast<uint8
*>(buffer
->data());
114 uint8
* const uplane
=
115 yplane
+ VideoFrame::PlaneAllocationSize(VideoFrame::I420
,
116 VideoFrame::kYPlane
, dimensions
);
117 uint8
* const vplane
=
118 uplane
+ VideoFrame::PlaneAllocationSize(VideoFrame::I420
,
119 VideoFrame::kUPlane
, dimensions
);
120 int yplane_stride
= dimensions
.width();
121 int uv_plane_stride
= yplane_stride
/ 2;
124 libyuv::FourCC origin_colorspace
= libyuv::FOURCC_ANY
;
127 switch (frame_format
.pixel_format
) {
128 case media::PIXEL_FORMAT_UNKNOWN
: // Color format not set.
130 case media::PIXEL_FORMAT_I420
:
131 DCHECK(!chopped_width
&& !chopped_height
);
132 origin_colorspace
= libyuv::FOURCC_I420
;
134 case media::PIXEL_FORMAT_YV12
:
135 DCHECK(!chopped_width
&& !chopped_height
);
136 origin_colorspace
= libyuv::FOURCC_YV12
;
138 case media::PIXEL_FORMAT_NV12
:
139 DCHECK(!chopped_width
&& !chopped_height
);
140 origin_colorspace
= libyuv::FOURCC_NV12
;
142 case media::PIXEL_FORMAT_NV21
:
143 DCHECK(!chopped_width
&& !chopped_height
);
144 origin_colorspace
= libyuv::FOURCC_NV21
;
146 case media::PIXEL_FORMAT_YUY2
:
147 DCHECK(!chopped_width
&& !chopped_height
);
148 origin_colorspace
= libyuv::FOURCC_YUY2
;
150 case media::PIXEL_FORMAT_UYVY
:
151 DCHECK(!chopped_width
&& !chopped_height
);
152 origin_colorspace
= libyuv::FOURCC_UYVY
;
154 case media::PIXEL_FORMAT_RGB24
:
155 origin_colorspace
= libyuv::FOURCC_24BG
;
157 // TODO(wjia): Currently, for RGB24 on WIN, capture device always
158 // passes in positive src_width and src_height. Remove this hardcoded
159 // value when nagative src_height is supported. The negative src_height
160 // indicates that vertical flipping is needed.
164 case media::PIXEL_FORMAT_RGB32
:
165 // Fallback to PIXEL_FORMAT_ARGB setting |flip| in Windows platforms.
169 case media::PIXEL_FORMAT_ARGB
:
170 origin_colorspace
= libyuv::FOURCC_ARGB
;
172 case media::PIXEL_FORMAT_MJPEG
:
173 origin_colorspace
= libyuv::FOURCC_MJPG
;
179 // The input |length| can be greater than the required buffer size because of
180 // paddings and/or alignments, but it cannot be smaller.
181 DCHECK_GE(static_cast<size_t>(length
), frame_format
.ImageAllocationSize());
183 if (libyuv::ConvertToI420(data
,
193 frame_format
.frame_size
.width(),
194 (flip
? -1 : 1) * frame_format
.frame_size
.height(),
196 new_unrotated_height
,
198 origin_colorspace
) != 0) {
199 DLOG(WARNING
) << "Failed to convert buffer's pixel format to I420 from "
200 << media::VideoCaptureFormat::PixelFormatToString(
201 frame_format
.pixel_format
);
204 scoped_refptr
<VideoFrame
> frame
=
205 VideoFrame::WrapExternalPackedMemory(
208 gfx::Rect(dimensions
),
211 VideoFrame::AllocationSize(VideoFrame::I420
, dimensions
),
212 base::SharedMemory::NULLHandle(),
217 frame
->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE
,
218 frame_format
.frame_rate
);
220 BrowserThread::PostTask(
224 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread
,
232 VideoCaptureDeviceClient::OnIncomingCapturedYuvData(
239 const VideoCaptureFormat
& frame_format
,
240 int clockwise_rotation
,
241 const base::TimeTicks
& timestamp
) {
242 TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedYuvData");
243 DCHECK_EQ(frame_format
.pixel_format
, media::PIXEL_FORMAT_I420
);
244 DCHECK_EQ(clockwise_rotation
, 0) << "Rotation not supported";
246 scoped_refptr
<Buffer
> buffer
= ReserveOutputBuffer(VideoFrame::I420
,
247 frame_format
.frame_size
);
251 // Blit (copy) here from y,u,v into buffer.data()). Needed so we can return
252 // the parameter buffer synchronously to the driver.
253 const size_t y_plane_size
= VideoFrame::PlaneAllocationSize(VideoFrame::I420
,
254 VideoFrame::kYPlane
, frame_format
.frame_size
);
255 const size_t u_plane_size
= VideoFrame::PlaneAllocationSize(
256 VideoFrame::I420
, VideoFrame::kUPlane
, frame_format
.frame_size
);
257 uint8
* const dst_y
= reinterpret_cast<uint8
*>(buffer
->data());
258 uint8
* const dst_u
= dst_y
+ y_plane_size
;
259 uint8
* const dst_v
= dst_u
+ u_plane_size
;
261 const size_t dst_y_stride
= VideoFrame::RowBytes(
262 VideoFrame::kYPlane
, VideoFrame::I420
, frame_format
.frame_size
.width());
263 const size_t dst_u_stride
= VideoFrame::RowBytes(
264 VideoFrame::kUPlane
, VideoFrame::I420
, frame_format
.frame_size
.width());
265 const size_t dst_v_stride
= VideoFrame::RowBytes(
266 VideoFrame::kVPlane
, VideoFrame::I420
, frame_format
.frame_size
.width());
267 DCHECK_GE(y_stride
, dst_y_stride
);
268 DCHECK_GE(u_stride
, dst_u_stride
);
269 DCHECK_GE(v_stride
, dst_v_stride
);
271 if (libyuv::I420Copy(y_data
, y_stride
,
277 frame_format
.frame_size
.width(),
278 frame_format
.frame_size
.height())) {
279 DLOG(WARNING
) << "Failed to copy buffer";
283 scoped_refptr
<VideoFrame
> video_frame
= VideoFrame::WrapExternalYuvData(
284 VideoFrame::I420
, frame_format
.frame_size
,
285 gfx::Rect(frame_format
.frame_size
), frame_format
.frame_size
, y_stride
,
286 u_stride
, v_stride
, dst_y
, dst_u
, dst_v
, base::TimeDelta(),
288 DCHECK(video_frame
.get());
289 video_frame
->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE
,
290 frame_format
.frame_rate
);
292 BrowserThread::PostTask(
296 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread
,
303 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>
304 VideoCaptureDeviceClient::ReserveOutputBuffer(VideoFrame::Format format
,
305 const gfx::Size
& dimensions
) {
306 const size_t frame_bytes
= VideoFrame::AllocationSize(format
, dimensions
);
307 if (format
== VideoFrame::NATIVE_TEXTURE
) {
308 DCHECK_EQ(dimensions
.width(), 0);
309 DCHECK_EQ(dimensions
.height(), 0);
311 DLOG_IF(ERROR
, frame_bytes
== 0) << "Error calculating allocation size";
314 int buffer_id_to_drop
= VideoCaptureBufferPool::kInvalidId
;
315 const int buffer_id
=
316 buffer_pool_
->ReserveForProducer(frame_bytes
, &buffer_id_to_drop
);
317 if (buffer_id
== VideoCaptureBufferPool::kInvalidId
)
321 buffer_pool_
->GetBufferInfo(buffer_id
, &data
, &size
);
323 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> output_buffer(
324 new AutoReleaseBuffer(buffer_pool_
, buffer_id
, data
, size
));
326 if (buffer_id_to_drop
!= VideoCaptureBufferPool::kInvalidId
) {
327 BrowserThread::PostTask(BrowserThread::IO
,
329 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread
,
330 controller_
, buffer_id_to_drop
));
333 return output_buffer
;
337 VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
338 const scoped_refptr
<Buffer
>& buffer
,
339 const scoped_refptr
<VideoFrame
>& frame
,
340 const base::TimeTicks
& timestamp
) {
341 BrowserThread::PostTask(
345 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread
,
352 void VideoCaptureDeviceClient::OnError(
353 const std::string
& reason
) {
354 const std::string log_message
= base::StringPrintf(
355 "Error on video capture: %s, OS message: %s",
357 logging::SystemErrorCodeToString(
358 logging::GetLastSystemErrorCode()).c_str());
359 DLOG(ERROR
) << log_message
;
361 BrowserThread::PostTask(BrowserThread::IO
,
363 base::Bind(&VideoCaptureController::DoErrorOnIOThread
, controller_
));
366 void VideoCaptureDeviceClient::OnLog(
367 const std::string
& message
) {
368 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
369 base::Bind(&VideoCaptureController::DoLogOnIOThread
,
370 controller_
, message
));
373 } // namespace content