1 // Copyright (c) 2012 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 "media/base/video_frame.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/memory/aligned_memory.h"
13 #include "base/strings/string_piece.h"
14 #include "media/base/limits.h"
15 #include "media/base/video_util.h"
16 #include "ui/gfx/geometry/point.h"
20 static bool IsPowerOfTwo(size_t x
) {
21 return x
!= 0 && (x
& (x
- 1)) == 0;
24 static inline size_t RoundUp(size_t value
, size_t alignment
) {
25 DCHECK(IsPowerOfTwo(alignment
));
26 return ((value
+ (alignment
- 1)) & ~(alignment
- 1));
29 static inline size_t RoundDown(size_t value
, size_t alignment
) {
30 DCHECK(IsPowerOfTwo(alignment
));
31 return value
& ~(alignment
- 1);
34 // Returns true if |plane| is a valid plane index for the given |format|.
35 static bool IsValidPlane(size_t plane
, VideoPixelFormat format
) {
36 DCHECK_LE(VideoFrame::NumPlanes(format
),
37 static_cast<size_t>(VideoFrame::kMaxPlanes
));
38 return (plane
< VideoFrame::NumPlanes(format
));
41 // Returns true if |frame| is accesible mapped in the VideoFrame memory space.
43 static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type
) {
46 // This is not strictly needed but makes explicit that, at VideoFrame
47 // level, DmaBufs are not mappable from userspace.
48 storage_type
!= VideoFrame::STORAGE_DMABUFS
&&
50 (storage_type
== VideoFrame::STORAGE_UNOWNED_MEMORY
||
51 storage_type
== VideoFrame::STORAGE_OWNED_MEMORY
||
52 storage_type
== VideoFrame::STORAGE_SHMEM
);
55 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
56 // for the U-plane in PIXEL_FORMAT_I420.
57 static gfx::Size
SampleSize(VideoPixelFormat format
, size_t plane
) {
58 DCHECK(IsValidPlane(plane
, format
));
61 case VideoFrame::kYPlane
:
62 case VideoFrame::kAPlane
:
63 return gfx::Size(1, 1);
65 case VideoFrame::kUPlane
: // and VideoFrame::kUVPlane:
66 case VideoFrame::kVPlane
:
68 case PIXEL_FORMAT_YV24
:
69 return gfx::Size(1, 1);
71 case PIXEL_FORMAT_YV16
:
72 return gfx::Size(2, 1);
74 case PIXEL_FORMAT_YV12
:
75 case PIXEL_FORMAT_I420
:
76 case PIXEL_FORMAT_YV12A
:
77 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
78 case PIXEL_FORMAT_NV12
:
80 return gfx::Size(2, 2);
82 case PIXEL_FORMAT_UNKNOWN
:
83 case PIXEL_FORMAT_ARGB
:
84 case PIXEL_FORMAT_XRGB
:
92 // Return the alignment for the whole frame, calculated as the max of the
93 // alignment for each individual plane.
94 static gfx::Size
CommonAlignment(VideoPixelFormat format
) {
95 int max_sample_width
= 0;
96 int max_sample_height
= 0;
97 for (size_t plane
= 0; plane
< VideoFrame::NumPlanes(format
); ++plane
) {
98 const gfx::Size sample_size
= SampleSize(format
, plane
);
99 max_sample_width
= std::max(max_sample_width
, sample_size
.width());
100 max_sample_height
= std::max(max_sample_height
, sample_size
.height());
102 return gfx::Size(max_sample_width
, max_sample_height
);
105 // Returns the number of bytes per element for given |plane| and |format|.
106 static int BytesPerElement(VideoPixelFormat format
, size_t plane
) {
107 DCHECK(IsValidPlane(plane
, format
));
108 if (format
== PIXEL_FORMAT_ARGB
|| format
== PIXEL_FORMAT_XRGB
)
111 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
112 if (format
== PIXEL_FORMAT_NV12
&& plane
== VideoFrame::kUVPlane
)
119 // Rounds up |coded_size| if necessary for |format|.
120 static gfx::Size
AdjustCodedSize(VideoPixelFormat format
,
121 const gfx::Size
& coded_size
) {
122 const gfx::Size alignment
= CommonAlignment(format
);
123 return gfx::Size(RoundUp(coded_size
.width(), alignment
.width()),
124 RoundUp(coded_size
.height(), alignment
.height()));
128 bool VideoFrame::IsValidConfig(VideoPixelFormat format
,
129 StorageType storage_type
,
130 const gfx::Size
& coded_size
,
131 const gfx::Rect
& visible_rect
,
132 const gfx::Size
& natural_size
) {
133 // Check maximum limits for all formats.
134 if (coded_size
.GetArea() > limits::kMaxCanvas
||
135 coded_size
.width() > limits::kMaxDimension
||
136 coded_size
.height() > limits::kMaxDimension
||
137 visible_rect
.x() < 0 || visible_rect
.y() < 0 ||
138 visible_rect
.right() > coded_size
.width() ||
139 visible_rect
.bottom() > coded_size
.height() ||
140 natural_size
.GetArea() > limits::kMaxCanvas
||
141 natural_size
.width() > limits::kMaxDimension
||
142 natural_size
.height() > limits::kMaxDimension
)
145 // TODO(mcasas): Remove parameter |storage_type| when the opaque storage types
146 // comply with the checks below. Right now we skip them.
147 if (!IsStorageTypeMappable(storage_type
))
150 // Check format-specific width/height requirements.
152 case PIXEL_FORMAT_UNKNOWN
:
153 return (coded_size
.IsEmpty() && visible_rect
.IsEmpty() &&
154 natural_size
.IsEmpty());
155 case PIXEL_FORMAT_YV24
:
156 case PIXEL_FORMAT_YV12
:
157 case PIXEL_FORMAT_I420
:
158 case PIXEL_FORMAT_YV12A
:
159 case PIXEL_FORMAT_YV16
:
160 case PIXEL_FORMAT_ARGB
:
161 case PIXEL_FORMAT_XRGB
:
162 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
163 case PIXEL_FORMAT_NV12
:
165 // Check that software-allocated buffer formats are aligned correctly and
167 const gfx::Size alignment
= CommonAlignment(format
);
168 return RoundUp(visible_rect
.right(), alignment
.width()) <=
169 static_cast<size_t>(coded_size
.width()) &&
170 RoundUp(visible_rect
.bottom(), alignment
.height()) <=
171 static_cast<size_t>(coded_size
.height()) &&
172 !coded_size
.IsEmpty() && !visible_rect
.IsEmpty() &&
173 !natural_size
.IsEmpty();
176 // TODO(mcasas): Check that storage type and underlying mailboxes/dataptr are
183 scoped_refptr
<VideoFrame
> VideoFrame::CreateFrame(VideoPixelFormat format
,
184 const gfx::Size
& coded_size
,
185 const gfx::Rect
& visible_rect
,
186 const gfx::Size
& natural_size
,
187 base::TimeDelta timestamp
) {
188 if (!IsYuvPlanar(format
)) {
193 // Since we're creating a new YUV frame (and allocating memory for it
194 // ourselves), we can pad the requested |coded_size| if necessary if the
195 // request does not line up on sample boundaries.
196 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
197 DCHECK(IsValidConfig(format
, STORAGE_OWNED_MEMORY
, new_coded_size
,
198 visible_rect
, natural_size
));
200 scoped_refptr
<VideoFrame
> frame(new VideoFrame(format
, STORAGE_OWNED_MEMORY
,
201 new_coded_size
, visible_rect
,
202 natural_size
, timestamp
));
203 frame
->AllocateYUV();
208 scoped_refptr
<VideoFrame
> VideoFrame::WrapNativeTexture(
209 VideoPixelFormat format
,
210 const gpu::MailboxHolder
& mailbox_holder
,
211 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
212 const gfx::Size
& coded_size
,
213 const gfx::Rect
& visible_rect
,
214 const gfx::Size
& natural_size
,
215 base::TimeDelta timestamp
) {
216 if (format
!= PIXEL_FORMAT_ARGB
) {
217 DLOG(ERROR
) << "Only ARGB pixel format supported, got "
218 << VideoPixelFormatToString(format
);
221 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
222 mailbox_holders
[kARGBPlane
] = mailbox_holder
;
223 return new VideoFrame(format
, STORAGE_OPAQUE
, coded_size
,
224 visible_rect
, natural_size
, mailbox_holders
,
225 mailbox_holder_release_cb
, timestamp
);
229 scoped_refptr
<VideoFrame
> VideoFrame::WrapYUV420NativeTextures(
230 const gpu::MailboxHolder
& y_mailbox_holder
,
231 const gpu::MailboxHolder
& u_mailbox_holder
,
232 const gpu::MailboxHolder
& v_mailbox_holder
,
233 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
234 const gfx::Size
& coded_size
,
235 const gfx::Rect
& visible_rect
,
236 const gfx::Size
& natural_size
,
237 base::TimeDelta timestamp
) {
238 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
239 mailbox_holders
[kYPlane
] = y_mailbox_holder
;
240 mailbox_holders
[kUPlane
] = u_mailbox_holder
;
241 mailbox_holders
[kVPlane
] = v_mailbox_holder
;
242 return new VideoFrame(PIXEL_FORMAT_I420
, STORAGE_OPAQUE
, coded_size
,
243 visible_rect
, natural_size
, mailbox_holders
,
244 mailbox_holder_release_cb
, timestamp
);
248 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalData(
249 VideoPixelFormat format
,
250 const gfx::Size
& coded_size
,
251 const gfx::Rect
& visible_rect
,
252 const gfx::Size
& natural_size
,
255 base::TimeDelta timestamp
) {
256 return WrapExternalStorage(format
, STORAGE_UNOWNED_MEMORY
, coded_size
,
257 visible_rect
, natural_size
, data
, data_size
,
258 timestamp
, base::SharedMemory::NULLHandle(), 0);
262 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalSharedMemory(
263 VideoPixelFormat format
,
264 const gfx::Size
& coded_size
,
265 const gfx::Rect
& visible_rect
,
266 const gfx::Size
& natural_size
,
269 base::SharedMemoryHandle handle
,
271 base::TimeDelta timestamp
) {
272 return WrapExternalStorage(format
, STORAGE_SHMEM
, coded_size
, visible_rect
,
273 natural_size
, data
, data_size
, timestamp
, handle
,
278 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalYuvData(
279 VideoPixelFormat format
,
280 const gfx::Size
& coded_size
,
281 const gfx::Rect
& visible_rect
,
282 const gfx::Size
& natural_size
,
289 base::TimeDelta timestamp
) {
290 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
291 CHECK(IsValidConfig(format
, STORAGE_UNOWNED_MEMORY
, new_coded_size
,
292 visible_rect
, natural_size
));
294 scoped_refptr
<VideoFrame
> frame(new VideoFrame(format
, STORAGE_UNOWNED_MEMORY
,
295 new_coded_size
, visible_rect
,
296 natural_size
, timestamp
));
297 frame
->strides_
[kYPlane
] = y_stride
;
298 frame
->strides_
[kUPlane
] = u_stride
;
299 frame
->strides_
[kVPlane
] = v_stride
;
300 frame
->data_
[kYPlane
] = y_data
;
301 frame
->data_
[kUPlane
] = u_data
;
302 frame
->data_
[kVPlane
] = v_data
;
306 #if defined(OS_LINUX)
308 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalDmabufs(
309 VideoPixelFormat format
,
310 const gfx::Size
& coded_size
,
311 const gfx::Rect
& visible_rect
,
312 const gfx::Size
& natural_size
,
313 const std::vector
<int>& dmabuf_fds
,
314 base::TimeDelta timestamp
) {
315 #if defined(OS_CHROMEOS)
316 DCHECK_EQ(format
, PIXEL_FORMAT_NV12
);
319 if (!IsValidConfig(format
, STORAGE_DMABUFS
, coded_size
, visible_rect
,
323 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
324 scoped_refptr
<VideoFrame
> frame
=
325 new VideoFrame(format
, STORAGE_DMABUFS
, coded_size
, visible_rect
,
326 natural_size
, mailbox_holders
, ReleaseMailboxCB(),
328 if (!frame
|| !frame
->DuplicateFileDescriptors(dmabuf_fds
))
334 #if defined(OS_MACOSX)
336 scoped_refptr
<VideoFrame
> VideoFrame::WrapCVPixelBuffer(
337 CVPixelBufferRef cv_pixel_buffer
,
338 base::TimeDelta timestamp
) {
339 DCHECK(cv_pixel_buffer
);
340 DCHECK(CFGetTypeID(cv_pixel_buffer
) == CVPixelBufferGetTypeID());
342 const OSType cv_format
= CVPixelBufferGetPixelFormatType(cv_pixel_buffer
);
343 VideoPixelFormat format
;
344 // There are very few compatible CV pixel formats, so just check each.
345 if (cv_format
== kCVPixelFormatType_420YpCbCr8Planar
) {
346 format
= PIXEL_FORMAT_I420
;
347 } else if (cv_format
== kCVPixelFormatType_444YpCbCr8
) {
348 format
= PIXEL_FORMAT_YV24
;
349 } else if (cv_format
== '420v') {
350 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
351 // minimum OS X and iOS SDKs permits it.
352 format
= PIXEL_FORMAT_NV12
;
354 DLOG(ERROR
) << "CVPixelBuffer format not supported: " << cv_format
;
358 const gfx::Size
coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer
));
359 const gfx::Rect
visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer
));
360 const gfx::Size
natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer
));
362 if (!IsValidConfig(format
, STORAGE_UNOWNED_MEMORY
,
363 coded_size
, visible_rect
, natural_size
)) {
367 scoped_refptr
<VideoFrame
> frame(new VideoFrame(format
, STORAGE_UNOWNED_MEMORY
,
368 coded_size
, visible_rect
,
369 natural_size
, timestamp
));
371 frame
->cv_pixel_buffer_
.reset(cv_pixel_buffer
, base::scoped_policy::RETAIN
);
377 scoped_refptr
<VideoFrame
> VideoFrame::WrapVideoFrame(
378 const scoped_refptr
<VideoFrame
>& frame
,
379 const gfx::Rect
& visible_rect
,
380 const gfx::Size
& natural_size
) {
381 // Frames with textures need mailbox info propagated, and there's no support
382 // for that here yet, see http://crbug/362521.
383 CHECK(!frame
->HasTextures());
385 DCHECK(frame
->visible_rect().Contains(visible_rect
));
386 scoped_refptr
<VideoFrame
> wrapping_frame(new VideoFrame(
387 frame
->format(), frame
->storage_type(), frame
->coded_size(), visible_rect
,
388 natural_size
, frame
->timestamp()));
389 if (frame
->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM
)) {
390 wrapping_frame
->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM
,
394 for (size_t i
= 0; i
< NumPlanes(frame
->format()); ++i
) {
395 wrapping_frame
->strides_
[i
] = frame
->stride(i
);
396 wrapping_frame
->data_
[i
] = frame
->data(i
);
399 #if defined(OS_LINUX)
400 // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
401 if (frame
->storage_type() == STORAGE_DMABUFS
) {
402 std::vector
<int> original_fds
;
403 for (size_t i
= 0; i
< kMaxPlanes
; ++i
)
404 original_fds
.push_back(frame
->dmabuf_fd(i
));
405 if (!wrapping_frame
->DuplicateFileDescriptors(original_fds
))
410 return wrapping_frame
;
414 scoped_refptr
<VideoFrame
> VideoFrame::CreateEOSFrame() {
415 scoped_refptr
<VideoFrame
> frame
=
416 new VideoFrame(PIXEL_FORMAT_UNKNOWN
, STORAGE_UNKNOWN
, gfx::Size(),
417 gfx::Rect(), gfx::Size(), kNoTimestamp());
418 frame
->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM
, true);
423 scoped_refptr
<VideoFrame
> VideoFrame::CreateColorFrame(
424 const gfx::Size
& size
,
425 uint8 y
, uint8 u
, uint8 v
,
426 base::TimeDelta timestamp
) {
427 scoped_refptr
<VideoFrame
> frame
=
428 CreateFrame(PIXEL_FORMAT_YV12
, size
, gfx::Rect(size
), size
, timestamp
);
429 FillYUV(frame
.get(), y
, u
, v
);
434 scoped_refptr
<VideoFrame
> VideoFrame::CreateBlackFrame(const gfx::Size
& size
) {
435 const uint8 kBlackY
= 0x00;
436 const uint8 kBlackUV
= 0x80;
437 const base::TimeDelta kZero
;
438 return CreateColorFrame(size
, kBlackY
, kBlackUV
, kBlackUV
, kZero
);
442 scoped_refptr
<VideoFrame
> VideoFrame::CreateTransparentFrame(
443 const gfx::Size
& size
) {
444 const uint8 kBlackY
= 0x00;
445 const uint8 kBlackUV
= 0x00;
446 const uint8 kTransparentA
= 0x00;
447 const base::TimeDelta kZero
;
448 scoped_refptr
<VideoFrame
> frame
=
449 CreateFrame(PIXEL_FORMAT_YV12A
, size
, gfx::Rect(size
), size
, kZero
);
450 FillYUVA(frame
.get(), kBlackY
, kBlackUV
, kBlackUV
, kTransparentA
);
454 #if defined(VIDEO_HOLE)
455 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
456 // maintained by the general compositor team. Please contact
457 // wonsik@chromium.org .
460 scoped_refptr
<VideoFrame
> VideoFrame::CreateHoleFrame(
461 const gfx::Size
& size
) {
462 DCHECK(IsValidConfig(PIXEL_FORMAT_UNKNOWN
, STORAGE_HOLE
, size
,
463 gfx::Rect(size
), size
));
464 scoped_refptr
<VideoFrame
> frame(
465 new VideoFrame(PIXEL_FORMAT_UNKNOWN
, STORAGE_HOLE
, size
, gfx::Rect(size
),
466 size
, base::TimeDelta()));
469 #endif // defined(VIDEO_HOLE)
472 size_t VideoFrame::NumPlanes(VideoPixelFormat format
) {
474 case PIXEL_FORMAT_ARGB
:
475 case PIXEL_FORMAT_XRGB
:
477 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
478 case PIXEL_FORMAT_NV12
:
481 case PIXEL_FORMAT_YV12
:
482 case PIXEL_FORMAT_YV16
:
483 case PIXEL_FORMAT_I420
:
484 case PIXEL_FORMAT_YV24
:
486 case PIXEL_FORMAT_YV12A
:
488 case PIXEL_FORMAT_UNKNOWN
:
491 NOTREACHED() << "Unsupported video frame format: " << format
;
496 size_t VideoFrame::AllocationSize(VideoPixelFormat format
,
497 const gfx::Size
& coded_size
) {
499 for (size_t i
= 0; i
< NumPlanes(format
); ++i
)
500 total
+= PlaneSize(format
, i
, coded_size
).GetArea();
505 gfx::Size
VideoFrame::PlaneSize(VideoPixelFormat format
,
507 const gfx::Size
& coded_size
) {
508 DCHECK(IsValidPlane(plane
, format
));
510 int width
= coded_size
.width();
511 int height
= coded_size
.height();
512 if (format
!= PIXEL_FORMAT_ARGB
) {
513 // Align to multiple-of-two size overall. This ensures that non-subsampled
514 // planes can be addressed by pixel with the same scaling as the subsampled
516 width
= RoundUp(width
, 2);
517 height
= RoundUp(height
, 2);
520 const gfx::Size subsample
= SampleSize(format
, plane
);
521 DCHECK(width
% subsample
.width() == 0);
522 DCHECK(height
% subsample
.height() == 0);
523 return gfx::Size(BytesPerElement(format
, plane
) * width
/ subsample
.width(),
524 height
/ subsample
.height());
528 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format
,
530 DCHECK(IsValidPlane(plane
, format
));
531 const int bits_per_element
= 8 * BytesPerElement(format
, plane
);
532 const int horiz_pixels_per_element
= SampleSize(format
, plane
).width();
533 DCHECK_EQ(bits_per_element
% horiz_pixels_per_element
, 0);
534 return bits_per_element
/ horiz_pixels_per_element
;
538 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format
, size_t plane
) {
539 DCHECK(IsValidPlane(plane
, format
));
540 return PlaneHorizontalBitsPerPixel(format
, plane
) /
541 SampleSize(format
, plane
).height();
545 size_t VideoFrame::RowBytes(size_t plane
, VideoPixelFormat format
, int width
) {
546 DCHECK(IsValidPlane(plane
, format
));
547 return BytesPerElement(format
, plane
) * Columns(plane
, format
, width
);
551 size_t VideoFrame::Rows(size_t plane
, VideoPixelFormat format
, int height
) {
552 DCHECK(IsValidPlane(plane
, format
));
553 const int sample_height
= SampleSize(format
, plane
).height();
554 return RoundUp(height
, sample_height
) / sample_height
;
558 size_t VideoFrame::Columns(size_t plane
, VideoPixelFormat format
, int width
) {
559 DCHECK(IsValidPlane(plane
, format
));
560 const int sample_width
= SampleSize(format
, plane
).width();
561 return RoundUp(width
, sample_width
) / sample_width
;
565 void VideoFrame::HashFrameForTesting(base::MD5Context
* context
,
566 const scoped_refptr
<VideoFrame
>& frame
) {
568 for (size_t plane
= 0; plane
< NumPlanes(frame
->format()); ++plane
) {
569 for (int row
= 0; row
< frame
->rows(plane
); ++row
) {
572 base::StringPiece(reinterpret_cast<char*>(frame
->data(plane
) +
573 frame
->stride(plane
) * row
),
574 frame
->row_bytes(plane
)));
579 bool VideoFrame::IsMappable() const {
580 return IsStorageTypeMappable(storage_type_
);
583 bool VideoFrame::HasTextures() const {
584 return !mailbox_holders_
[0].mailbox
.IsZero();
587 int VideoFrame::stride(size_t plane
) const {
588 DCHECK(IsValidPlane(plane
, format_
));
589 return strides_
[plane
];
592 int VideoFrame::row_bytes(size_t plane
) const {
593 return RowBytes(plane
, format_
, coded_size_
.width());
596 int VideoFrame::rows(size_t plane
) const {
597 return Rows(plane
, format_
, coded_size_
.height());
600 const uint8
* VideoFrame::data(size_t plane
) const {
601 DCHECK(IsValidPlane(plane
, format_
));
602 DCHECK(IsMappable());
606 uint8
* VideoFrame::data(size_t plane
) {
607 DCHECK(IsValidPlane(plane
, format_
));
608 DCHECK(IsMappable());
612 const uint8
* VideoFrame::visible_data(size_t plane
) const {
613 DCHECK(IsValidPlane(plane
, format_
));
614 DCHECK(IsMappable());
616 // Calculate an offset that is properly aligned for all planes.
617 const gfx::Size alignment
= CommonAlignment(format_
);
618 const gfx::Point
offset(RoundDown(visible_rect_
.x(), alignment
.width()),
619 RoundDown(visible_rect_
.y(), alignment
.height()));
621 const gfx::Size subsample
= SampleSize(format_
, plane
);
622 DCHECK(offset
.x() % subsample
.width() == 0);
623 DCHECK(offset
.y() % subsample
.height() == 0);
625 stride(plane
) * (offset
.y() / subsample
.height()) + // Row offset.
626 BytesPerElement(format_
, plane
) * // Column offset.
627 (offset
.x() / subsample
.width());
630 uint8
* VideoFrame::visible_data(size_t plane
) {
631 return const_cast<uint8
*>(
632 static_cast<const VideoFrame
*>(this)->visible_data(plane
));
635 const gpu::MailboxHolder
&
636 VideoFrame::mailbox_holder(size_t texture_index
) const {
637 DCHECK(HasTextures());
638 DCHECK(IsValidPlane(texture_index
, format_
));
639 return mailbox_holders_
[texture_index
];
642 base::SharedMemoryHandle
VideoFrame::shared_memory_handle() const {
643 DCHECK_EQ(storage_type_
, STORAGE_SHMEM
);
644 DCHECK(shared_memory_handle_
!= base::SharedMemory::NULLHandle());
645 return shared_memory_handle_
;
648 size_t VideoFrame::shared_memory_offset() const {
649 DCHECK_EQ(storage_type_
, STORAGE_SHMEM
);
650 DCHECK(shared_memory_handle_
!= base::SharedMemory::NULLHandle());
651 return shared_memory_offset_
;
654 #if defined(OS_LINUX)
655 int VideoFrame::dmabuf_fd(size_t plane
) const {
656 DCHECK_EQ(storage_type_
, STORAGE_DMABUFS
);
657 DCHECK(IsValidPlane(plane
, format_
));
658 return dmabuf_fds_
[plane
].get();
661 bool VideoFrame::DuplicateFileDescriptors(const std::vector
<int>& in_fds
) {
662 // TODO(mcasas): Support offsets for e.g. multiplanar inside a single |in_fd|.
664 storage_type_
= STORAGE_DMABUFS
;
665 // TODO(posciak): This is not exactly correct, it's possible for one
666 // buffer to contain more than one plane.
667 if (in_fds
.size() != NumPlanes(format_
)) {
668 LOG(FATAL
) << "Not enough dmabuf fds provided, got: " << in_fds
.size()
669 << ", expected: " << NumPlanes(format_
);
673 // Make sure that all fds are closed if any dup() fails,
674 base::ScopedFD temp_dmabuf_fds
[kMaxPlanes
];
675 for (size_t i
= 0; i
< in_fds
.size(); ++i
) {
676 temp_dmabuf_fds
[i
] = base::ScopedFD(HANDLE_EINTR(dup(in_fds
[i
])));
677 if (!temp_dmabuf_fds
[i
].is_valid()) {
678 DPLOG(ERROR
) << "Failed duplicating a dmabuf fd";
682 for (size_t i
= 0; i
< kMaxPlanes
; ++i
)
683 dmabuf_fds_
[i
].reset(temp_dmabuf_fds
[i
].release());
689 void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle
) {
690 storage_type_
= STORAGE_SHMEM
;
691 shared_memory_handle_
= handle
;
694 #if defined(OS_MACOSX)
695 CVPixelBufferRef
VideoFrame::cv_pixel_buffer() const {
696 return cv_pixel_buffer_
.get();
700 void VideoFrame::AddDestructionObserver(const base::Closure
& callback
) {
701 DCHECK(!callback
.is_null());
702 done_callbacks_
.push_back(callback
);
705 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient
* client
) {
706 DCHECK(HasTextures());
707 base::AutoLock
locker(release_sync_point_lock_
);
708 // Must wait on the previous sync point before inserting a new sync point so
709 // that |mailbox_holders_release_cb_| guarantees the previous sync point
710 // occurred when it waits on |release_sync_point_|.
711 if (release_sync_point_
)
712 client
->WaitSyncPoint(release_sync_point_
);
713 release_sync_point_
= client
->InsertSyncPoint();
717 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalStorage(
718 VideoPixelFormat format
,
719 StorageType storage_type
,
720 const gfx::Size
& coded_size
,
721 const gfx::Rect
& visible_rect
,
722 const gfx::Size
& natural_size
,
725 base::TimeDelta timestamp
,
726 base::SharedMemoryHandle handle
,
727 size_t data_offset
) {
728 DCHECK(IsStorageTypeMappable(storage_type
));
730 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
731 if (!IsValidConfig(format
, storage_type
, new_coded_size
, visible_rect
,
733 data_size
< AllocationSize(format
, new_coded_size
)) {
736 DLOG_IF(ERROR
, format
!= PIXEL_FORMAT_I420
)
737 << "Only PIXEL_FORMAT_I420 format supported: "
738 << VideoPixelFormatToString(format
);
739 if (format
!= PIXEL_FORMAT_I420
)
742 scoped_refptr
<VideoFrame
> frame
;
743 if (storage_type
== STORAGE_SHMEM
) {
744 frame
= new VideoFrame(format
, storage_type
, new_coded_size
,
745 visible_rect
, natural_size
, timestamp
, handle
,
748 frame
= new VideoFrame(format
, storage_type
, new_coded_size
,
749 visible_rect
, natural_size
, timestamp
);
751 frame
->strides_
[kYPlane
] = new_coded_size
.width();
752 frame
->strides_
[kUPlane
] = new_coded_size
.width() / 2;
753 frame
->strides_
[kVPlane
] = new_coded_size
.width() / 2;
754 frame
->data_
[kYPlane
] = data
;
755 frame
->data_
[kUPlane
] = data
+ new_coded_size
.GetArea();
756 frame
->data_
[kVPlane
] = data
+ (new_coded_size
.GetArea() * 5 / 4);
760 VideoFrame::VideoFrame(VideoPixelFormat format
,
761 StorageType storage_type
,
762 const gfx::Size
& coded_size
,
763 const gfx::Rect
& visible_rect
,
764 const gfx::Size
& natural_size
,
765 base::TimeDelta timestamp
)
767 storage_type_(storage_type
),
768 coded_size_(coded_size
),
769 visible_rect_(visible_rect
),
770 natural_size_(natural_size
),
771 shared_memory_handle_(base::SharedMemory::NULLHandle()),
772 shared_memory_offset_(0),
773 timestamp_(timestamp
),
774 release_sync_point_(0) {
775 DCHECK(IsValidConfig(format_
, storage_type
, coded_size_
, visible_rect_
,
777 memset(&mailbox_holders_
, 0, sizeof(mailbox_holders_
));
778 memset(&strides_
, 0, sizeof(strides_
));
779 memset(&data_
, 0, sizeof(data_
));
782 VideoFrame::VideoFrame(VideoPixelFormat format
,
783 StorageType storage_type
,
784 const gfx::Size
& coded_size
,
785 const gfx::Rect
& visible_rect
,
786 const gfx::Size
& natural_size
,
787 base::TimeDelta timestamp
,
788 base::SharedMemoryHandle handle
,
789 size_t shared_memory_offset
)
796 DCHECK_EQ(storage_type
, STORAGE_SHMEM
);
797 AddSharedMemoryHandle(handle
);
798 shared_memory_offset_
= shared_memory_offset
;
801 VideoFrame::VideoFrame(VideoPixelFormat format
,
802 StorageType storage_type
,
803 const gfx::Size
& coded_size
,
804 const gfx::Rect
& visible_rect
,
805 const gfx::Size
& natural_size
,
806 const gpu::MailboxHolder(&mailbox_holders
)[kMaxPlanes
],
807 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
808 base::TimeDelta timestamp
)
815 memcpy(&mailbox_holders_
, mailbox_holders
, sizeof(mailbox_holders_
));
816 mailbox_holders_release_cb_
= mailbox_holder_release_cb
;
819 VideoFrame::~VideoFrame() {
820 if (!mailbox_holders_release_cb_
.is_null()) {
821 uint32 release_sync_point
;
823 // To ensure that changes to |release_sync_point_| are visible on this
824 // thread (imply a memory barrier).
825 base::AutoLock
locker(release_sync_point_lock_
);
826 release_sync_point
= release_sync_point_
;
828 base::ResetAndReturn(&mailbox_holders_release_cb_
).Run(release_sync_point
);
831 for (auto& callback
: done_callbacks_
)
832 base::ResetAndReturn(&callback
).Run();
835 void VideoFrame::AllocateYUV() {
836 DCHECK_EQ(storage_type_
, STORAGE_OWNED_MEMORY
);
837 static_assert(0 == kYPlane
, "y plane data must be index 0");
839 size_t data_size
= 0;
840 size_t offset
[kMaxPlanes
];
841 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
) {
842 // The *2 in alignment for height is because some formats (e.g. h264) allow
843 // interlaced coding, and then the size needs to be a multiple of two
844 // macroblocks (vertically). See
845 // libavcodec/utils.c:avcodec_align_dimensions2().
846 const size_t height
= RoundUp(rows(plane
), kFrameSizeAlignment
* 2);
847 strides_
[plane
] = RoundUp(row_bytes(plane
), kFrameSizeAlignment
);
848 offset
[plane
] = data_size
;
849 data_size
+= height
* strides_
[plane
];
852 // The extra line of UV being allocated is because h264 chroma MC
853 // overreads by one line in some cases, see libavcodec/utils.c:
854 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
855 // put_h264_chroma_mc4_ssse3().
856 DCHECK(IsValidPlane(kUPlane
, format_
));
857 data_size
+= strides_
[kUPlane
] + kFrameSizePadding
;
859 uint8
* data
= reinterpret_cast<uint8
*>(
860 base::AlignedAlloc(data_size
, kFrameAddressAlignment
));
862 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
)
863 data_
[plane
] = data
+ offset
[plane
];
865 AddDestructionObserver(base::Bind(&base::AlignedFree
, data
));