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 "base/strings/stringprintf.h"
15 #include "media/base/limits.h"
16 #include "media/base/timestamp_constants.h"
17 #include "media/base/video_util.h"
18 #include "ui/gfx/geometry/point.h"
22 static bool IsPowerOfTwo(size_t x
) {
23 return x
!= 0 && (x
& (x
- 1)) == 0;
26 static inline size_t RoundUp(size_t value
, size_t alignment
) {
27 DCHECK(IsPowerOfTwo(alignment
));
28 return ((value
+ (alignment
- 1)) & ~(alignment
- 1));
31 static inline size_t RoundDown(size_t value
, size_t alignment
) {
32 DCHECK(IsPowerOfTwo(alignment
));
33 return value
& ~(alignment
- 1);
36 static std::string
ConfigToString(const VideoPixelFormat format
,
37 const VideoFrame::StorageType storage_type
,
38 const gfx::Size
& coded_size
,
39 const gfx::Rect
& visible_rect
,
40 const gfx::Size
& natural_size
) {
41 return base::StringPrintf(
42 "format:%s coded_size:%s visible_rect:%s natural_size:%s",
43 VideoPixelFormatToString(format
).c_str(), coded_size
.ToString().c_str(),
44 visible_rect
.ToString().c_str(), natural_size
.ToString().c_str());
47 // Returns true if |plane| is a valid plane index for the given |format|.
48 static bool IsValidPlane(size_t plane
, VideoPixelFormat format
) {
49 DCHECK_LE(VideoFrame::NumPlanes(format
),
50 static_cast<size_t>(VideoFrame::kMaxPlanes
));
51 return (plane
< VideoFrame::NumPlanes(format
));
54 // Returns true if |frame| is accesible mapped in the VideoFrame memory space.
56 static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type
) {
59 // This is not strictly needed but makes explicit that, at VideoFrame
60 // level, DmaBufs are not mappable from userspace.
61 storage_type
!= VideoFrame::STORAGE_DMABUFS
&&
63 (storage_type
== VideoFrame::STORAGE_UNOWNED_MEMORY
||
64 storage_type
== VideoFrame::STORAGE_OWNED_MEMORY
||
65 storage_type
== VideoFrame::STORAGE_SHMEM
);
68 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
69 // for the U-plane in PIXEL_FORMAT_I420.
70 static gfx::Size
SampleSize(VideoPixelFormat format
, size_t plane
) {
71 DCHECK(IsValidPlane(plane
, format
));
74 case VideoFrame::kYPlane
:
75 case VideoFrame::kAPlane
:
76 return gfx::Size(1, 1);
78 case VideoFrame::kUPlane
: // and VideoFrame::kUVPlane:
79 case VideoFrame::kVPlane
:
81 case PIXEL_FORMAT_YV24
:
82 return gfx::Size(1, 1);
84 case PIXEL_FORMAT_YV16
:
85 return gfx::Size(2, 1);
87 case PIXEL_FORMAT_YV12
:
88 case PIXEL_FORMAT_I420
:
89 case PIXEL_FORMAT_YV12A
:
90 case PIXEL_FORMAT_NV12
:
91 case PIXEL_FORMAT_NV21
:
92 case PIXEL_FORMAT_MT21
:
93 return gfx::Size(2, 2);
95 case PIXEL_FORMAT_UNKNOWN
:
96 case PIXEL_FORMAT_UYVY
:
97 case PIXEL_FORMAT_YUY2
:
98 case PIXEL_FORMAT_ARGB
:
99 case PIXEL_FORMAT_XRGB
:
100 case PIXEL_FORMAT_RGB24
:
101 case PIXEL_FORMAT_RGB32
:
102 case PIXEL_FORMAT_MJPEG
:
110 // Return the alignment for the whole frame, calculated as the max of the
111 // alignment for each individual plane.
112 static gfx::Size
CommonAlignment(VideoPixelFormat format
) {
113 int max_sample_width
= 0;
114 int max_sample_height
= 0;
115 for (size_t plane
= 0; plane
< VideoFrame::NumPlanes(format
); ++plane
) {
116 const gfx::Size sample_size
= SampleSize(format
, plane
);
117 max_sample_width
= std::max(max_sample_width
, sample_size
.width());
118 max_sample_height
= std::max(max_sample_height
, sample_size
.height());
120 return gfx::Size(max_sample_width
, max_sample_height
);
123 // Returns the number of bytes per element for given |plane| and |format|.
124 static int BytesPerElement(VideoPixelFormat format
, size_t plane
) {
125 DCHECK(IsValidPlane(plane
, format
));
127 case PIXEL_FORMAT_ARGB
:
128 case PIXEL_FORMAT_XRGB
:
129 case PIXEL_FORMAT_RGB32
:
131 case PIXEL_FORMAT_RGB24
:
133 case PIXEL_FORMAT_UYVY
:
134 case PIXEL_FORMAT_YUY2
:
136 case PIXEL_FORMAT_NV12
:
137 case PIXEL_FORMAT_NV21
:
138 case PIXEL_FORMAT_MT21
: {
139 static const int bytes_per_element
[] = {1, 2};
140 DCHECK_LT(plane
, arraysize(bytes_per_element
));
141 return bytes_per_element
[plane
];
143 case PIXEL_FORMAT_YV12
:
144 case PIXEL_FORMAT_I420
:
145 case PIXEL_FORMAT_YV16
:
146 case PIXEL_FORMAT_YV12A
:
147 case PIXEL_FORMAT_YV24
:
149 case PIXEL_FORMAT_MJPEG
:
151 case PIXEL_FORMAT_UNKNOWN
:
159 bool VideoFrame::IsValidConfig(VideoPixelFormat format
,
160 StorageType storage_type
,
161 const gfx::Size
& coded_size
,
162 const gfx::Rect
& visible_rect
,
163 const gfx::Size
& natural_size
) {
164 // Check maximum limits for all formats.
165 if (coded_size
.GetArea() > limits::kMaxCanvas
||
166 coded_size
.width() > limits::kMaxDimension
||
167 coded_size
.height() > limits::kMaxDimension
||
168 visible_rect
.x() < 0 || visible_rect
.y() < 0 ||
169 visible_rect
.right() > coded_size
.width() ||
170 visible_rect
.bottom() > coded_size
.height() ||
171 natural_size
.GetArea() > limits::kMaxCanvas
||
172 natural_size
.width() > limits::kMaxDimension
||
173 natural_size
.height() > limits::kMaxDimension
)
176 // TODO(mcasas): Remove parameter |storage_type| when the opaque storage types
177 // comply with the checks below. Right now we skip them.
178 if (!IsStorageTypeMappable(storage_type
))
181 // Make sure new formats are properly accounted for in the method.
182 static_assert(PIXEL_FORMAT_MAX
== 15,
183 "Added pixel format, please review IsValidConfig()");
185 if (format
== PIXEL_FORMAT_UNKNOWN
) {
186 return coded_size
.IsEmpty() && visible_rect
.IsEmpty() &&
187 natural_size
.IsEmpty();
190 // Check that software-allocated buffer formats are not empty.
191 return !coded_size
.IsEmpty() && !visible_rect
.IsEmpty() &&
192 !natural_size
.IsEmpty();
196 scoped_refptr
<VideoFrame
> VideoFrame::CreateFrame(VideoPixelFormat format
,
197 const gfx::Size
& coded_size
,
198 const gfx::Rect
& visible_rect
,
199 const gfx::Size
& natural_size
,
200 base::TimeDelta timestamp
) {
201 return CreateFrameInternal(format
, coded_size
, visible_rect
, natural_size
,
206 scoped_refptr
<VideoFrame
> VideoFrame::CreateZeroInitializedFrame(
207 VideoPixelFormat format
,
208 const gfx::Size
& coded_size
,
209 const gfx::Rect
& visible_rect
,
210 const gfx::Size
& natural_size
,
211 base::TimeDelta timestamp
) {
212 return CreateFrameInternal(format
, coded_size
, visible_rect
, natural_size
,
217 scoped_refptr
<VideoFrame
> VideoFrame::WrapNativeTexture(
218 VideoPixelFormat format
,
219 const gpu::MailboxHolder
& mailbox_holder
,
220 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
221 const gfx::Size
& coded_size
,
222 const gfx::Rect
& visible_rect
,
223 const gfx::Size
& natural_size
,
224 base::TimeDelta timestamp
) {
225 if (format
!= PIXEL_FORMAT_ARGB
&&
226 format
!= PIXEL_FORMAT_UYVY
&&
227 format
!= PIXEL_FORMAT_NV12
) {
228 DLOG(ERROR
) << "Unsupported pixel format supported, got "
229 << VideoPixelFormatToString(format
);
232 const StorageType storage
= STORAGE_OPAQUE
;
233 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
234 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
235 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
240 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
241 mailbox_holders
[kARGBPlane
] = mailbox_holder
;
242 return new VideoFrame(format
, storage
, coded_size
, visible_rect
, natural_size
,
243 mailbox_holders
, mailbox_holder_release_cb
, timestamp
);
247 scoped_refptr
<VideoFrame
> VideoFrame::WrapYUV420NativeTextures(
248 const gpu::MailboxHolder
& y_mailbox_holder
,
249 const gpu::MailboxHolder
& u_mailbox_holder
,
250 const gpu::MailboxHolder
& v_mailbox_holder
,
251 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
252 const gfx::Size
& coded_size
,
253 const gfx::Rect
& visible_rect
,
254 const gfx::Size
& natural_size
,
255 base::TimeDelta timestamp
) {
256 const StorageType storage
= STORAGE_OPAQUE
;
257 VideoPixelFormat format
= PIXEL_FORMAT_I420
;
258 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
259 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
260 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
265 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
266 mailbox_holders
[kYPlane
] = y_mailbox_holder
;
267 mailbox_holders
[kUPlane
] = u_mailbox_holder
;
268 mailbox_holders
[kVPlane
] = v_mailbox_holder
;
269 return new VideoFrame(format
, storage
, coded_size
, visible_rect
, natural_size
,
270 mailbox_holders
, mailbox_holder_release_cb
, timestamp
);
274 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalData(
275 VideoPixelFormat format
,
276 const gfx::Size
& coded_size
,
277 const gfx::Rect
& visible_rect
,
278 const gfx::Size
& natural_size
,
281 base::TimeDelta timestamp
) {
282 return WrapExternalStorage(format
, STORAGE_UNOWNED_MEMORY
, coded_size
,
283 visible_rect
, natural_size
, data
, data_size
,
284 timestamp
, base::SharedMemory::NULLHandle(), 0);
288 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalSharedMemory(
289 VideoPixelFormat format
,
290 const gfx::Size
& coded_size
,
291 const gfx::Rect
& visible_rect
,
292 const gfx::Size
& natural_size
,
295 base::SharedMemoryHandle handle
,
297 base::TimeDelta timestamp
) {
298 return WrapExternalStorage(format
, STORAGE_SHMEM
, coded_size
, visible_rect
,
299 natural_size
, data
, data_size
, timestamp
, handle
,
304 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalYuvData(
305 VideoPixelFormat format
,
306 const gfx::Size
& coded_size
,
307 const gfx::Rect
& visible_rect
,
308 const gfx::Size
& natural_size
,
315 base::TimeDelta timestamp
) {
316 const StorageType storage
= STORAGE_UNOWNED_MEMORY
;
317 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
318 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
319 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
324 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
325 format
, storage
, coded_size
, visible_rect
, natural_size
, timestamp
));
326 frame
->strides_
[kYPlane
] = y_stride
;
327 frame
->strides_
[kUPlane
] = u_stride
;
328 frame
->strides_
[kVPlane
] = v_stride
;
329 frame
->data_
[kYPlane
] = y_data
;
330 frame
->data_
[kUPlane
] = u_data
;
331 frame
->data_
[kVPlane
] = v_data
;
335 #if defined(OS_LINUX)
337 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalDmabufs(
338 VideoPixelFormat format
,
339 const gfx::Size
& coded_size
,
340 const gfx::Rect
& visible_rect
,
341 const gfx::Size
& natural_size
,
342 const std::vector
<int>& dmabuf_fds
,
343 base::TimeDelta timestamp
) {
344 #if defined(OS_CHROMEOS)
345 DCHECK_EQ(format
, PIXEL_FORMAT_NV12
);
348 const StorageType storage
= STORAGE_DMABUFS
;
349 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
350 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
351 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
356 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
357 scoped_refptr
<VideoFrame
> frame
=
358 new VideoFrame(format
, storage
, coded_size
, visible_rect
, natural_size
,
359 mailbox_holders
, ReleaseMailboxCB(), timestamp
);
360 if (!frame
|| !frame
->DuplicateFileDescriptors(dmabuf_fds
))
366 #if defined(OS_MACOSX)
368 scoped_refptr
<VideoFrame
> VideoFrame::WrapCVPixelBuffer(
369 CVPixelBufferRef cv_pixel_buffer
,
370 base::TimeDelta timestamp
) {
371 DCHECK(cv_pixel_buffer
);
372 DCHECK(CFGetTypeID(cv_pixel_buffer
) == CVPixelBufferGetTypeID());
374 const OSType cv_format
= CVPixelBufferGetPixelFormatType(cv_pixel_buffer
);
375 VideoPixelFormat format
;
376 // There are very few compatible CV pixel formats, so just check each.
377 if (cv_format
== kCVPixelFormatType_420YpCbCr8Planar
) {
378 format
= PIXEL_FORMAT_I420
;
379 } else if (cv_format
== kCVPixelFormatType_444YpCbCr8
) {
380 format
= PIXEL_FORMAT_YV24
;
381 } else if (cv_format
== '420v') {
382 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
383 // minimum OS X and iOS SDKs permits it.
384 format
= PIXEL_FORMAT_NV12
;
386 DLOG(ERROR
) << "CVPixelBuffer format not supported: " << cv_format
;
390 const gfx::Size
coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer
));
391 const gfx::Rect
visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer
));
392 const gfx::Size
natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer
));
393 const StorageType storage
= STORAGE_UNOWNED_MEMORY
;
395 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
396 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
397 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
402 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
403 format
, storage
, coded_size
, visible_rect
, natural_size
, timestamp
));
405 frame
->cv_pixel_buffer_
.reset(cv_pixel_buffer
, base::scoped_policy::RETAIN
);
411 scoped_refptr
<VideoFrame
> VideoFrame::WrapVideoFrame(
412 const scoped_refptr
<VideoFrame
>& frame
,
413 const gfx::Rect
& visible_rect
,
414 const gfx::Size
& natural_size
) {
415 // Frames with textures need mailbox info propagated, and there's no support
416 // for that here yet, see http://crbug/362521.
417 CHECK(!frame
->HasTextures());
419 DCHECK(frame
->visible_rect().Contains(visible_rect
));
421 if (!IsValidConfig(frame
->format(), frame
->storage_type(),
422 frame
->coded_size(), visible_rect
, natural_size
)) {
423 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
424 << ConfigToString(frame
->format(), frame
->storage_type(),
425 frame
->coded_size(), visible_rect
,
430 scoped_refptr
<VideoFrame
> wrapping_frame(new VideoFrame(
431 frame
->format(), frame
->storage_type(), frame
->coded_size(), visible_rect
,
432 natural_size
, frame
->timestamp()));
433 if (frame
->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM
)) {
434 wrapping_frame
->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM
,
438 for (size_t i
= 0; i
< NumPlanes(frame
->format()); ++i
) {
439 wrapping_frame
->strides_
[i
] = frame
->stride(i
);
440 wrapping_frame
->data_
[i
] = frame
->data(i
);
443 #if defined(OS_LINUX)
444 // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
445 if (frame
->storage_type() == STORAGE_DMABUFS
) {
446 std::vector
<int> original_fds
;
447 for (size_t i
= 0; i
< kMaxPlanes
; ++i
)
448 original_fds
.push_back(frame
->dmabuf_fd(i
));
449 if (!wrapping_frame
->DuplicateFileDescriptors(original_fds
))
454 return wrapping_frame
;
458 scoped_refptr
<VideoFrame
> VideoFrame::CreateEOSFrame() {
459 scoped_refptr
<VideoFrame
> frame
=
460 new VideoFrame(PIXEL_FORMAT_UNKNOWN
, STORAGE_UNKNOWN
, gfx::Size(),
461 gfx::Rect(), gfx::Size(), kNoTimestamp());
462 frame
->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM
, true);
467 scoped_refptr
<VideoFrame
> VideoFrame::CreateColorFrame(
468 const gfx::Size
& size
,
469 uint8 y
, uint8 u
, uint8 v
,
470 base::TimeDelta timestamp
) {
471 scoped_refptr
<VideoFrame
> frame
=
472 CreateFrame(PIXEL_FORMAT_YV12
, size
, gfx::Rect(size
), size
, timestamp
);
473 FillYUV(frame
.get(), y
, u
, v
);
478 scoped_refptr
<VideoFrame
> VideoFrame::CreateBlackFrame(const gfx::Size
& size
) {
479 const uint8 kBlackY
= 0x00;
480 const uint8 kBlackUV
= 0x80;
481 const base::TimeDelta kZero
;
482 return CreateColorFrame(size
, kBlackY
, kBlackUV
, kBlackUV
, kZero
);
486 scoped_refptr
<VideoFrame
> VideoFrame::CreateTransparentFrame(
487 const gfx::Size
& size
) {
488 const uint8 kBlackY
= 0x00;
489 const uint8 kBlackUV
= 0x00;
490 const uint8 kTransparentA
= 0x00;
491 const base::TimeDelta kZero
;
492 scoped_refptr
<VideoFrame
> frame
=
493 CreateFrame(PIXEL_FORMAT_YV12A
, size
, gfx::Rect(size
), size
, kZero
);
494 FillYUVA(frame
.get(), kBlackY
, kBlackUV
, kBlackUV
, kTransparentA
);
498 #if defined(VIDEO_HOLE)
499 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
500 // maintained by the general compositor team. Please contact
501 // wonsik@chromium.org .
504 scoped_refptr
<VideoFrame
> VideoFrame::CreateHoleFrame(
505 const gfx::Size
& size
) {
506 const VideoPixelFormat format
= PIXEL_FORMAT_UNKNOWN
;
507 const StorageType storage
= STORAGE_HOLE
;
508 const gfx::Rect visible_rect
= gfx::Rect(size
);
509 if (!IsValidConfig(format
, storage
, size
, visible_rect
, size
)) {
510 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
511 << ConfigToString(format
, storage
, size
, visible_rect
, size
);
514 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
515 format
, storage
, size
, gfx::Rect(size
), size
, base::TimeDelta()));
518 #endif // defined(VIDEO_HOLE)
521 size_t VideoFrame::NumPlanes(VideoPixelFormat format
) {
523 case PIXEL_FORMAT_UYVY
:
524 case PIXEL_FORMAT_YUY2
:
525 case PIXEL_FORMAT_ARGB
:
526 case PIXEL_FORMAT_XRGB
:
527 case PIXEL_FORMAT_RGB24
:
528 case PIXEL_FORMAT_RGB32
:
529 case PIXEL_FORMAT_MJPEG
:
531 case PIXEL_FORMAT_NV12
:
532 case PIXEL_FORMAT_NV21
:
533 case PIXEL_FORMAT_MT21
:
535 case PIXEL_FORMAT_I420
:
536 case PIXEL_FORMAT_YV12
:
537 case PIXEL_FORMAT_YV16
:
538 case PIXEL_FORMAT_YV24
:
540 case PIXEL_FORMAT_YV12A
:
542 case PIXEL_FORMAT_UNKNOWN
:
545 NOTREACHED() << "Unsupported video frame format: " << format
;
550 size_t VideoFrame::AllocationSize(VideoPixelFormat format
,
551 const gfx::Size
& coded_size
) {
553 for (size_t i
= 0; i
< NumPlanes(format
); ++i
)
554 total
+= PlaneSize(format
, i
, coded_size
).GetArea();
559 gfx::Size
VideoFrame::PlaneSize(VideoPixelFormat format
,
561 const gfx::Size
& coded_size
) {
562 DCHECK(IsValidPlane(plane
, format
));
564 int width
= coded_size
.width();
565 int height
= coded_size
.height();
566 if (format
!= PIXEL_FORMAT_ARGB
) {
567 // Align to multiple-of-two size overall. This ensures that non-subsampled
568 // planes can be addressed by pixel with the same scaling as the subsampled
570 width
= RoundUp(width
, 2);
571 height
= RoundUp(height
, 2);
574 const gfx::Size subsample
= SampleSize(format
, plane
);
575 DCHECK(width
% subsample
.width() == 0);
576 DCHECK(height
% subsample
.height() == 0);
577 return gfx::Size(BytesPerElement(format
, plane
) * width
/ subsample
.width(),
578 height
/ subsample
.height());
582 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format
,
584 DCHECK(IsValidPlane(plane
, format
));
585 const int bits_per_element
= 8 * BytesPerElement(format
, plane
);
586 const int horiz_pixels_per_element
= SampleSize(format
, plane
).width();
587 DCHECK_EQ(bits_per_element
% horiz_pixels_per_element
, 0);
588 return bits_per_element
/ horiz_pixels_per_element
;
592 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format
, size_t plane
) {
593 DCHECK(IsValidPlane(plane
, format
));
594 return PlaneHorizontalBitsPerPixel(format
, plane
) /
595 SampleSize(format
, plane
).height();
599 size_t VideoFrame::RowBytes(size_t plane
, VideoPixelFormat format
, int width
) {
600 DCHECK(IsValidPlane(plane
, format
));
601 return BytesPerElement(format
, plane
) * Columns(plane
, format
, width
);
605 size_t VideoFrame::Rows(size_t plane
, VideoPixelFormat format
, int height
) {
606 DCHECK(IsValidPlane(plane
, format
));
607 const int sample_height
= SampleSize(format
, plane
).height();
608 return RoundUp(height
, sample_height
) / sample_height
;
612 size_t VideoFrame::Columns(size_t plane
, VideoPixelFormat format
, int width
) {
613 DCHECK(IsValidPlane(plane
, format
));
614 const int sample_width
= SampleSize(format
, plane
).width();
615 return RoundUp(width
, sample_width
) / sample_width
;
619 void VideoFrame::HashFrameForTesting(base::MD5Context
* context
,
620 const scoped_refptr
<VideoFrame
>& frame
) {
622 for (size_t plane
= 0; plane
< NumPlanes(frame
->format()); ++plane
) {
623 for (int row
= 0; row
< frame
->rows(plane
); ++row
) {
626 base::StringPiece(reinterpret_cast<char*>(frame
->data(plane
) +
627 frame
->stride(plane
) * row
),
628 frame
->row_bytes(plane
)));
633 bool VideoFrame::IsMappable() const {
634 return IsStorageTypeMappable(storage_type_
);
637 bool VideoFrame::HasTextures() const {
638 return !mailbox_holders_
[0].mailbox
.IsZero();
641 int VideoFrame::stride(size_t plane
) const {
642 DCHECK(IsValidPlane(plane
, format_
));
643 return strides_
[plane
];
646 int VideoFrame::row_bytes(size_t plane
) const {
647 return RowBytes(plane
, format_
, coded_size_
.width());
650 int VideoFrame::rows(size_t plane
) const {
651 return Rows(plane
, format_
, coded_size_
.height());
654 const uint8
* VideoFrame::data(size_t plane
) const {
655 DCHECK(IsValidPlane(plane
, format_
));
656 DCHECK(IsMappable());
660 uint8
* VideoFrame::data(size_t plane
) {
661 DCHECK(IsValidPlane(plane
, format_
));
662 DCHECK(IsMappable());
666 const uint8
* VideoFrame::visible_data(size_t plane
) const {
667 DCHECK(IsValidPlane(plane
, format_
));
668 DCHECK(IsMappable());
670 // Calculate an offset that is properly aligned for all planes.
671 const gfx::Size alignment
= CommonAlignment(format_
);
672 const gfx::Point
offset(RoundDown(visible_rect_
.x(), alignment
.width()),
673 RoundDown(visible_rect_
.y(), alignment
.height()));
675 const gfx::Size subsample
= SampleSize(format_
, plane
);
676 DCHECK(offset
.x() % subsample
.width() == 0);
677 DCHECK(offset
.y() % subsample
.height() == 0);
679 stride(plane
) * (offset
.y() / subsample
.height()) + // Row offset.
680 BytesPerElement(format_
, plane
) * // Column offset.
681 (offset
.x() / subsample
.width());
684 uint8
* VideoFrame::visible_data(size_t plane
) {
685 return const_cast<uint8
*>(
686 static_cast<const VideoFrame
*>(this)->visible_data(plane
));
689 const gpu::MailboxHolder
&
690 VideoFrame::mailbox_holder(size_t texture_index
) const {
691 DCHECK(HasTextures());
692 DCHECK(IsValidPlane(texture_index
, format_
));
693 return mailbox_holders_
[texture_index
];
696 base::SharedMemoryHandle
VideoFrame::shared_memory_handle() const {
697 DCHECK_EQ(storage_type_
, STORAGE_SHMEM
);
698 DCHECK(shared_memory_handle_
!= base::SharedMemory::NULLHandle());
699 return shared_memory_handle_
;
702 size_t VideoFrame::shared_memory_offset() const {
703 DCHECK_EQ(storage_type_
, STORAGE_SHMEM
);
704 DCHECK(shared_memory_handle_
!= base::SharedMemory::NULLHandle());
705 return shared_memory_offset_
;
708 #if defined(OS_LINUX)
709 int VideoFrame::dmabuf_fd(size_t plane
) const {
710 DCHECK_EQ(storage_type_
, STORAGE_DMABUFS
);
711 DCHECK(IsValidPlane(plane
, format_
));
712 return dmabuf_fds_
[plane
].get();
715 bool VideoFrame::DuplicateFileDescriptors(const std::vector
<int>& in_fds
) {
716 // TODO(mcasas): Support offsets for e.g. multiplanar inside a single |in_fd|.
718 storage_type_
= STORAGE_DMABUFS
;
719 // TODO(posciak): This is not exactly correct, it's possible for one
720 // buffer to contain more than one plane.
721 if (in_fds
.size() != NumPlanes(format_
)) {
722 LOG(FATAL
) << "Not enough dmabuf fds provided, got: " << in_fds
.size()
723 << ", expected: " << NumPlanes(format_
);
727 // Make sure that all fds are closed if any dup() fails,
728 base::ScopedFD temp_dmabuf_fds
[kMaxPlanes
];
729 for (size_t i
= 0; i
< in_fds
.size(); ++i
) {
730 temp_dmabuf_fds
[i
] = base::ScopedFD(HANDLE_EINTR(dup(in_fds
[i
])));
731 if (!temp_dmabuf_fds
[i
].is_valid()) {
732 DPLOG(ERROR
) << "Failed duplicating a dmabuf fd";
736 for (size_t i
= 0; i
< kMaxPlanes
; ++i
)
737 dmabuf_fds_
[i
].reset(temp_dmabuf_fds
[i
].release());
743 void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle
) {
744 storage_type_
= STORAGE_SHMEM
;
745 shared_memory_handle_
= handle
;
748 #if defined(OS_MACOSX)
749 CVPixelBufferRef
VideoFrame::cv_pixel_buffer() const {
750 return cv_pixel_buffer_
.get();
754 void VideoFrame::AddDestructionObserver(const base::Closure
& callback
) {
755 DCHECK(!callback
.is_null());
756 done_callbacks_
.push_back(callback
);
759 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient
* client
) {
760 DCHECK(HasTextures());
761 base::AutoLock
locker(release_sync_point_lock_
);
762 // Must wait on the previous sync point before inserting a new sync point so
763 // that |mailbox_holders_release_cb_| guarantees the previous sync point
764 // occurred when it waits on |release_sync_point_|.
765 if (release_sync_point_
)
766 client
->WaitSyncPoint(release_sync_point_
);
767 release_sync_point_
= client
->InsertSyncPoint();
771 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalStorage(
772 VideoPixelFormat format
,
773 StorageType storage_type
,
774 const gfx::Size
& coded_size
,
775 const gfx::Rect
& visible_rect
,
776 const gfx::Size
& natural_size
,
779 base::TimeDelta timestamp
,
780 base::SharedMemoryHandle handle
,
781 size_t data_offset
) {
782 DCHECK(IsStorageTypeMappable(storage_type
));
784 if (format
!= PIXEL_FORMAT_I420
) {
785 DLOG(ERROR
) << "Only PIXEL_FORMAT_I420 format supported: "
786 << VideoPixelFormatToString(format
);
790 if (!IsValidConfig(format
, storage_type
, coded_size
, visible_rect
,
792 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
793 << ConfigToString(format
, storage_type
, coded_size
,
794 visible_rect
, natural_size
);
798 scoped_refptr
<VideoFrame
> frame
;
799 if (storage_type
== STORAGE_SHMEM
) {
800 frame
= new VideoFrame(format
, storage_type
, coded_size
, visible_rect
,
801 natural_size
, timestamp
, handle
, data_offset
);
803 frame
= new VideoFrame(format
, storage_type
, coded_size
, visible_rect
,
804 natural_size
, timestamp
);
806 frame
->strides_
[kYPlane
] = coded_size
.width();
807 frame
->strides_
[kUPlane
] = coded_size
.width() / 2;
808 frame
->strides_
[kVPlane
] = coded_size
.width() / 2;
809 frame
->data_
[kYPlane
] = data
;
810 frame
->data_
[kUPlane
] = data
+ coded_size
.GetArea();
811 frame
->data_
[kVPlane
] = data
+ (coded_size
.GetArea() * 5 / 4);
815 VideoFrame::VideoFrame(VideoPixelFormat format
,
816 StorageType storage_type
,
817 const gfx::Size
& coded_size
,
818 const gfx::Rect
& visible_rect
,
819 const gfx::Size
& natural_size
,
820 base::TimeDelta timestamp
)
822 storage_type_(storage_type
),
823 coded_size_(coded_size
),
824 visible_rect_(visible_rect
),
825 natural_size_(natural_size
),
826 shared_memory_handle_(base::SharedMemory::NULLHandle()),
827 shared_memory_offset_(0),
828 timestamp_(timestamp
),
829 release_sync_point_(0) {
830 DCHECK(IsValidConfig(format_
, storage_type
, coded_size_
, visible_rect_
,
832 memset(&mailbox_holders_
, 0, sizeof(mailbox_holders_
));
833 memset(&strides_
, 0, sizeof(strides_
));
834 memset(&data_
, 0, sizeof(data_
));
837 VideoFrame::VideoFrame(VideoPixelFormat format
,
838 StorageType storage_type
,
839 const gfx::Size
& coded_size
,
840 const gfx::Rect
& visible_rect
,
841 const gfx::Size
& natural_size
,
842 base::TimeDelta timestamp
,
843 base::SharedMemoryHandle handle
,
844 size_t shared_memory_offset
)
851 DCHECK_EQ(storage_type
, STORAGE_SHMEM
);
852 AddSharedMemoryHandle(handle
);
853 shared_memory_offset_
= shared_memory_offset
;
856 VideoFrame::VideoFrame(VideoPixelFormat format
,
857 StorageType storage_type
,
858 const gfx::Size
& coded_size
,
859 const gfx::Rect
& visible_rect
,
860 const gfx::Size
& natural_size
,
861 const gpu::MailboxHolder(&mailbox_holders
)[kMaxPlanes
],
862 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
863 base::TimeDelta timestamp
)
870 memcpy(&mailbox_holders_
, mailbox_holders
, sizeof(mailbox_holders_
));
871 mailbox_holders_release_cb_
= mailbox_holder_release_cb
;
874 VideoFrame::~VideoFrame() {
875 if (!mailbox_holders_release_cb_
.is_null()) {
876 uint32 release_sync_point
;
878 // To ensure that changes to |release_sync_point_| are visible on this
879 // thread (imply a memory barrier).
880 base::AutoLock
locker(release_sync_point_lock_
);
881 release_sync_point
= release_sync_point_
;
883 base::ResetAndReturn(&mailbox_holders_release_cb_
).Run(release_sync_point
);
886 for (auto& callback
: done_callbacks_
)
887 base::ResetAndReturn(&callback
).Run();
891 scoped_refptr
<VideoFrame
> VideoFrame::CreateFrameInternal(
892 VideoPixelFormat format
,
893 const gfx::Size
& coded_size
,
894 const gfx::Rect
& visible_rect
,
895 const gfx::Size
& natural_size
,
896 base::TimeDelta timestamp
,
897 bool zero_initialize_memory
) {
898 if (!IsYuvPlanar(format
)) {
903 // Since we're creating a new YUV frame (and allocating memory for it
904 // ourselves), we can pad the requested |coded_size| if necessary if the
905 // request does not line up on sample boundaries. See discussion at
906 // http://crrev.com/1240833003
907 const gfx::Size alignment
= CommonAlignment(format
);
908 const gfx::Size new_coded_size
=
909 gfx::Size(RoundUp(coded_size
.width(), alignment
.width()),
910 RoundUp(coded_size
.height(), alignment
.height()));
911 DCHECK((new_coded_size
.width() % alignment
.width() == 0) &&
912 (new_coded_size
.height() % alignment
.height() == 0));
914 const StorageType storage
= STORAGE_OWNED_MEMORY
;
915 if (!IsValidConfig(format
, storage
, new_coded_size
, visible_rect
,
917 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
918 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
923 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
924 format
, storage
, new_coded_size
, visible_rect
, natural_size
, timestamp
));
925 frame
->AllocateYUV(zero_initialize_memory
);
929 void VideoFrame::AllocateYUV(bool zero_initialize_memory
) {
930 DCHECK_EQ(storage_type_
, STORAGE_OWNED_MEMORY
);
931 static_assert(0 == kYPlane
, "y plane data must be index 0");
933 size_t data_size
= 0;
934 size_t offset
[kMaxPlanes
];
935 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
) {
936 // The *2 in alignment for height is because some formats (e.g. h264) allow
937 // interlaced coding, and then the size needs to be a multiple of two
938 // macroblocks (vertically). See
939 // libavcodec/utils.c:avcodec_align_dimensions2().
940 const size_t height
= RoundUp(rows(plane
), kFrameSizeAlignment
* 2);
941 strides_
[plane
] = RoundUp(row_bytes(plane
), kFrameSizeAlignment
);
942 offset
[plane
] = data_size
;
943 data_size
+= height
* strides_
[plane
];
946 // The extra line of UV being allocated is because h264 chroma MC
947 // overreads by one line in some cases, see libavcodec/utils.c:
948 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
949 // put_h264_chroma_mc4_ssse3().
950 DCHECK(IsValidPlane(kUPlane
, format_
));
951 data_size
+= strides_
[kUPlane
] + kFrameSizePadding
;
953 uint8
* data
= reinterpret_cast<uint8
*>(
954 base::AlignedAlloc(data_size
, kFrameAddressAlignment
));
955 if (zero_initialize_memory
)
956 memset(data
, 0, data_size
);
958 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
)
959 data_
[plane
] = data
+ offset
[plane
];
961 AddDestructionObserver(base::Bind(&base::AlignedFree
, data
));