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 the pixel size per element for given |plane| and |format|. E.g. 2x2
35 // for the U-plane in I420.
36 static gfx::Size
SampleSize(VideoFrame::Format format
, size_t plane
) {
37 DCHECK(VideoFrame::IsValidPlane(plane
, format
));
40 case VideoFrame::kYPlane
:
41 case VideoFrame::kAPlane
:
42 return gfx::Size(1, 1);
44 case VideoFrame::kUPlane
: // and VideoFrame::kUVPlane:
45 case VideoFrame::kVPlane
:
47 case VideoFrame::YV24
:
48 return gfx::Size(1, 1);
50 case VideoFrame::YV16
:
51 return gfx::Size(2, 1);
53 case VideoFrame::YV12
:
54 case VideoFrame::I420
:
55 case VideoFrame::YV12A
:
56 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
57 case VideoFrame::NV12
:
59 return gfx::Size(2, 2);
61 case VideoFrame::UNKNOWN
:
62 case VideoFrame::ARGB
:
63 case VideoFrame::XRGB
:
71 // Return the alignment for the whole frame, calculated as the max of the
72 // alignment for each individual plane.
73 static gfx::Size
CommonAlignment(VideoFrame::Format format
) {
74 int max_sample_width
= 0;
75 int max_sample_height
= 0;
76 for (size_t plane
= 0; plane
< VideoFrame::NumPlanes(format
); ++plane
) {
77 const gfx::Size sample_size
= SampleSize(format
, plane
);
78 max_sample_width
= std::max(max_sample_width
, sample_size
.width());
79 max_sample_height
= std::max(max_sample_height
, sample_size
.height());
81 return gfx::Size(max_sample_width
, max_sample_height
);
84 // Returns the number of bytes per element for given |plane| and |format|.
85 static int BytesPerElement(VideoFrame::Format format
, size_t plane
) {
86 DCHECK(VideoFrame::IsValidPlane(plane
, format
));
87 if (format
== VideoFrame::ARGB
|| format
== VideoFrame::XRGB
)
90 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
91 if (format
== VideoFrame::NV12
&& plane
== VideoFrame::kUVPlane
)
98 // Rounds up |coded_size| if necessary for |format|.
99 static gfx::Size
AdjustCodedSize(VideoFrame::Format format
,
100 const gfx::Size
& coded_size
) {
101 const gfx::Size alignment
= CommonAlignment(format
);
102 return gfx::Size(RoundUp(coded_size
.width(), alignment
.width()),
103 RoundUp(coded_size
.height(), alignment
.height()));
106 // Release data allocated by AllocateYUV().
107 static void ReleaseData(uint8
* data
) {
109 base::AlignedFree(data
);
113 bool VideoFrame::IsYuvPlanar(Format format
) {
120 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
134 bool VideoFrame::IsMappable(StorageType storage_type
) {
135 return storage_type
== STORAGE_SHMEM
||
136 storage_type
== STORAGE_OWNED_MEMORY
||
137 storage_type
== STORAGE_UNOWNED_MEMORY
;
141 std::string
VideoFrame::FormatToString(Format format
) {
159 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
164 NOTREACHED() << "Invalid VideoFrame format provided: " << format
;
169 bool VideoFrame::IsValidConfig(Format format
,
170 StorageType storage_type
,
171 const gfx::Size
& coded_size
,
172 const gfx::Rect
& visible_rect
,
173 const gfx::Size
& natural_size
) {
174 // Check maximum limits for all formats.
175 if (coded_size
.GetArea() > limits::kMaxCanvas
||
176 coded_size
.width() > limits::kMaxDimension
||
177 coded_size
.height() > limits::kMaxDimension
||
178 visible_rect
.x() < 0 || visible_rect
.y() < 0 ||
179 visible_rect
.right() > coded_size
.width() ||
180 visible_rect
.bottom() > coded_size
.height() ||
181 natural_size
.GetArea() > limits::kMaxCanvas
||
182 natural_size
.width() > limits::kMaxDimension
||
183 natural_size
.height() > limits::kMaxDimension
)
186 // TODO(mcasas): Remove parameter |storage_type| when STORAGE_HOLE and
187 // STORAGE_TEXTURE comply with the checks below. Right now we skip them.
188 #if defined(VIDEO_HOLE)
189 if (storage_type
== STORAGE_HOLE
)
192 if(storage_type
== STORAGE_TEXTURE
)
195 // Check format-specific width/height requirements.
198 return (coded_size
.IsEmpty() && visible_rect
.IsEmpty() &&
199 natural_size
.IsEmpty());
207 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
210 // Check that software-allocated buffer formats are aligned correctly and
212 const gfx::Size alignment
= CommonAlignment(format
);
213 return RoundUp(visible_rect
.right(), alignment
.width()) <=
214 static_cast<size_t>(coded_size
.width()) &&
215 RoundUp(visible_rect
.bottom(), alignment
.height()) <=
216 static_cast<size_t>(coded_size
.height()) &&
217 !coded_size
.IsEmpty() && !visible_rect
.IsEmpty() &&
218 !natural_size
.IsEmpty();
221 // TODO(mcasas): Check that storage type and underlying mailboxes/dataptr are
228 scoped_refptr
<VideoFrame
> VideoFrame::CreateFrame(
230 const gfx::Size
& coded_size
,
231 const gfx::Rect
& visible_rect
,
232 const gfx::Size
& natural_size
,
233 base::TimeDelta timestamp
) {
234 if (!IsYuvPlanar(format
)) {
239 // Since we're creating a new YUV frame (and allocating memory for it
240 // ourselves), we can pad the requested |coded_size| if necessary if the
241 // request does not line up on sample boundaries.
242 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
243 DCHECK(IsValidConfig(format
, STORAGE_OWNED_MEMORY
, new_coded_size
,
244 visible_rect
, natural_size
));
246 scoped_refptr
<VideoFrame
> frame(new VideoFrame(format
, STORAGE_OWNED_MEMORY
,
247 new_coded_size
, visible_rect
,
248 natural_size
, timestamp
));
249 frame
->AllocateYUV();
254 scoped_refptr
<VideoFrame
> VideoFrame::WrapNativeTexture(
255 const gpu::MailboxHolder
& mailbox_holder
,
256 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
257 const gfx::Size
& coded_size
,
258 const gfx::Rect
& visible_rect
,
259 const gfx::Size
& natural_size
,
260 base::TimeDelta timestamp
,
263 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
264 mailbox_holders
[kARGBPlane
] = mailbox_holder
;
265 Format texture_format
= has_alpha
? ARGB
: XRGB
;
266 scoped_refptr
<VideoFrame
> frame(
267 new VideoFrame(texture_format
, STORAGE_TEXTURE
, coded_size
, visible_rect
,
268 natural_size
, mailbox_holders
, timestamp
));
269 frame
->mailbox_holders_release_cb_
= mailbox_holder_release_cb
;
270 frame
->allow_overlay_
= allow_overlay
;
275 scoped_refptr
<VideoFrame
> VideoFrame::WrapYUV420NativeTextures(
276 const gpu::MailboxHolder
& y_mailbox_holder
,
277 const gpu::MailboxHolder
& u_mailbox_holder
,
278 const gpu::MailboxHolder
& v_mailbox_holder
,
279 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
280 const gfx::Size
& coded_size
,
281 const gfx::Rect
& visible_rect
,
282 const gfx::Size
& natural_size
,
283 base::TimeDelta timestamp
,
284 bool allow_overlay
) {
285 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
286 mailbox_holders
[kYPlane
] = y_mailbox_holder
;
287 mailbox_holders
[kUPlane
] = u_mailbox_holder
;
288 mailbox_holders
[kVPlane
] = v_mailbox_holder
;
289 scoped_refptr
<VideoFrame
> frame(
290 new VideoFrame(I420
, STORAGE_TEXTURE
, coded_size
, visible_rect
,
291 natural_size
, mailbox_holders
, timestamp
));
292 frame
->mailbox_holders_release_cb_
= mailbox_holder_release_cb
;
293 frame
->allow_overlay_
= allow_overlay
;
298 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalData(
300 const gfx::Size
& coded_size
,
301 const gfx::Rect
& visible_rect
,
302 const gfx::Size
& natural_size
,
305 base::TimeDelta timestamp
) {
306 return WrapExternalStorage(format
, STORAGE_UNOWNED_MEMORY
, coded_size
,
307 visible_rect
, natural_size
, data
, data_size
,
308 timestamp
, base::SharedMemory::NULLHandle(), 0);
312 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalSharedMemory(
314 const gfx::Size
& coded_size
,
315 const gfx::Rect
& visible_rect
,
316 const gfx::Size
& natural_size
,
319 base::SharedMemoryHandle handle
,
321 base::TimeDelta timestamp
) {
322 return WrapExternalStorage(format
, STORAGE_SHMEM
, coded_size
, visible_rect
,
323 natural_size
, data
, data_size
, timestamp
, handle
,
328 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalYuvData(
330 const gfx::Size
& coded_size
,
331 const gfx::Rect
& visible_rect
,
332 const gfx::Size
& natural_size
,
339 base::TimeDelta timestamp
) {
340 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
341 CHECK(IsValidConfig(format
, STORAGE_UNOWNED_MEMORY
, new_coded_size
,
342 visible_rect
, natural_size
));
344 scoped_refptr
<VideoFrame
> frame(new VideoFrame(format
, STORAGE_UNOWNED_MEMORY
,
345 new_coded_size
, visible_rect
,
346 natural_size
, timestamp
));
347 frame
->strides_
[kYPlane
] = y_stride
;
348 frame
->strides_
[kUPlane
] = u_stride
;
349 frame
->strides_
[kVPlane
] = v_stride
;
350 frame
->data_
[kYPlane
] = y_data
;
351 frame
->data_
[kUPlane
] = u_data
;
352 frame
->data_
[kVPlane
] = v_data
;
356 #if defined(OS_LINUX)
358 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalDmabufs(
360 const gfx::Size
& coded_size
,
361 const gfx::Rect
& visible_rect
,
362 const gfx::Size
& natural_size
,
363 const std::vector
<int> dmabuf_fds
,
364 base::TimeDelta timestamp
) {
365 if (!IsValidConfig(format
, STORAGE_DMABUFS
, coded_size
, visible_rect
,
370 // TODO(posciak): This is not exactly correct, it's possible for one
371 // buffer to contain more than one plane.
372 if (dmabuf_fds
.size() != NumPlanes(format
)) {
373 LOG(FATAL
) << "Not enough dmabuf fds provided!";
377 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
378 DCHECK_EQ(format
, NV12
);
380 scoped_refptr
<VideoFrame
> frame(new VideoFrame(format
, STORAGE_DMABUFS
,
381 coded_size
, visible_rect
,
382 natural_size
, timestamp
));
384 for (size_t i
= 0; i
< dmabuf_fds
.size(); ++i
) {
385 int duped_fd
= HANDLE_EINTR(dup(dmabuf_fds
[i
]));
386 if (duped_fd
== -1) {
387 // The already-duped in previous iterations fds will be closed when
388 // the partially-created frame drops out of scope here.
389 DLOG(ERROR
) << "Failed duplicating a dmabuf fd";
393 frame
->dmabuf_fds_
[i
].reset(duped_fd
);
394 // Data is accessible only via fds.
395 frame
->data_
[i
] = NULL
;
396 frame
->strides_
[i
] = 0;
403 #if defined(OS_MACOSX)
405 scoped_refptr
<VideoFrame
> VideoFrame::WrapCVPixelBuffer(
406 CVPixelBufferRef cv_pixel_buffer
,
407 base::TimeDelta timestamp
) {
408 DCHECK(cv_pixel_buffer
);
409 DCHECK(CFGetTypeID(cv_pixel_buffer
) == CVPixelBufferGetTypeID());
411 const OSType cv_format
= CVPixelBufferGetPixelFormatType(cv_pixel_buffer
);
413 // There are very few compatible CV pixel formats, so just check each.
414 if (cv_format
== kCVPixelFormatType_420YpCbCr8Planar
) {
416 } else if (cv_format
== kCVPixelFormatType_444YpCbCr8
) {
418 } else if (cv_format
== '420v') {
419 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
420 // minimum OS X and iOS SDKs permits it.
423 DLOG(ERROR
) << "CVPixelBuffer format not supported: " << cv_format
;
427 const gfx::Size
coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer
));
428 const gfx::Rect
visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer
));
429 const gfx::Size
natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer
));
431 if (!IsValidConfig(format
, STORAGE_UNOWNED_MEMORY
, coded_size
, visible_rect
,
436 scoped_refptr
<VideoFrame
> frame(new VideoFrame(format
, STORAGE_UNOWNED_MEMORY
,
437 coded_size
, visible_rect
,
438 natural_size
, timestamp
));
440 frame
->cv_pixel_buffer_
.reset(cv_pixel_buffer
, base::scoped_policy::RETAIN
);
446 scoped_refptr
<VideoFrame
> VideoFrame::WrapVideoFrame(
447 const scoped_refptr
<VideoFrame
>& frame
,
448 const gfx::Rect
& visible_rect
,
449 const gfx::Size
& natural_size
) {
450 // STORAGE_TEXTURE frames need mailbox info propagated, and there's no support
451 // for that here yet, see http://crbug/362521.
452 CHECK_NE(frame
->storage_type(), STORAGE_TEXTURE
);
454 DCHECK(frame
->visible_rect().Contains(visible_rect
));
455 scoped_refptr
<VideoFrame
> wrapped_frame(new VideoFrame(
456 frame
->format(), frame
->storage_type(), frame
->coded_size(), visible_rect
,
457 natural_size
, frame
->timestamp()));
458 if (frame
->IsEndOfStream())
459 frame
->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM
, true);
461 for (size_t i
= 0; i
< NumPlanes(frame
->format()); ++i
) {
462 wrapped_frame
->strides_
[i
] = frame
->stride(i
);
463 wrapped_frame
->data_
[i
] = frame
->data(i
);
466 return wrapped_frame
;
470 scoped_refptr
<VideoFrame
> VideoFrame::CreateEOSFrame() {
471 scoped_refptr
<VideoFrame
> frame
=
472 new VideoFrame(UNKNOWN
, STORAGE_UNKNOWN
, gfx::Size(), gfx::Rect(),
473 gfx::Size(), kNoTimestamp());
474 frame
->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM
, true);
479 scoped_refptr
<VideoFrame
> VideoFrame::CreateColorFrame(
480 const gfx::Size
& size
,
481 uint8 y
, uint8 u
, uint8 v
,
482 base::TimeDelta timestamp
) {
483 scoped_refptr
<VideoFrame
> frame
=
484 CreateFrame(YV12
, size
, gfx::Rect(size
), size
, timestamp
);
485 FillYUV(frame
.get(), y
, u
, v
);
490 scoped_refptr
<VideoFrame
> VideoFrame::CreateBlackFrame(const gfx::Size
& size
) {
491 const uint8 kBlackY
= 0x00;
492 const uint8 kBlackUV
= 0x80;
493 const base::TimeDelta kZero
;
494 return CreateColorFrame(size
, kBlackY
, kBlackUV
, kBlackUV
, kZero
);
498 scoped_refptr
<VideoFrame
> VideoFrame::CreateTransparentFrame(
499 const gfx::Size
& size
) {
500 const uint8 kBlackY
= 0x00;
501 const uint8 kBlackUV
= 0x00;
502 const uint8 kTransparentA
= 0x00;
503 const base::TimeDelta kZero
;
504 scoped_refptr
<VideoFrame
> frame
=
505 CreateFrame(YV12A
, size
, gfx::Rect(size
), size
, kZero
);
506 FillYUVA(frame
.get(), kBlackY
, kBlackUV
, kBlackUV
, kTransparentA
);
510 #if defined(VIDEO_HOLE)
511 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
512 // maintained by the general compositor team. Please contact
513 // wonsik@chromium.org .
516 scoped_refptr
<VideoFrame
> VideoFrame::CreateHoleFrame(
517 const gfx::Size
& size
) {
518 DCHECK(IsValidConfig(UNKNOWN
, STORAGE_HOLE
, size
, gfx::Rect(size
), size
));
519 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
520 UNKNOWN
, STORAGE_HOLE
, size
, gfx::Rect(size
), size
, base::TimeDelta()));
523 #endif // defined(VIDEO_HOLE)
526 size_t VideoFrame::NumPlanes(Format format
) {
531 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
545 NOTREACHED() << "Unsupported video frame format: " << format
;
550 size_t VideoFrame::AllocationSize(Format format
, const gfx::Size
& coded_size
) {
552 for (size_t i
= 0; i
< NumPlanes(format
); ++i
)
553 total
+= PlaneAllocationSize(format
, i
, coded_size
);
558 gfx::Size
VideoFrame::PlaneSize(Format format
,
560 const gfx::Size
& coded_size
) {
561 DCHECK(IsValidPlane(plane
, format
));
563 int width
= coded_size
.width();
564 int height
= coded_size
.height();
565 if (format
!= ARGB
) {
566 // Align to multiple-of-two size overall. This ensures that non-subsampled
567 // planes can be addressed by pixel with the same scaling as the subsampled
569 width
= RoundUp(width
, 2);
570 height
= RoundUp(height
, 2);
573 const gfx::Size subsample
= SampleSize(format
, plane
);
574 DCHECK(width
% subsample
.width() == 0);
575 DCHECK(height
% subsample
.height() == 0);
576 return gfx::Size(BytesPerElement(format
, plane
) * width
/ subsample
.width(),
577 height
/ subsample
.height());
580 size_t VideoFrame::PlaneAllocationSize(Format format
,
582 const gfx::Size
& coded_size
) {
583 return PlaneSize(format
, plane
, coded_size
).GetArea();
587 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format
, size_t plane
) {
588 DCHECK(IsValidPlane(plane
, format
));
589 const int bits_per_element
= 8 * BytesPerElement(format
, plane
);
590 const int horiz_pixels_per_element
= SampleSize(format
, plane
).width();
591 DCHECK_EQ(bits_per_element
% horiz_pixels_per_element
, 0);
592 return bits_per_element
/ horiz_pixels_per_element
;
596 int VideoFrame::PlaneBitsPerPixel(Format format
, size_t plane
) {
597 DCHECK(IsValidPlane(plane
, format
));
598 return PlaneHorizontalBitsPerPixel(format
, plane
) /
599 SampleSize(format
, plane
).height();
602 VideoFrame::VideoFrame(Format format
,
603 StorageType storage_type
,
604 const gfx::Size
& coded_size
,
605 const gfx::Rect
& visible_rect
,
606 const gfx::Size
& natural_size
,
607 base::TimeDelta timestamp
)
609 storage_type_(storage_type
),
610 coded_size_(coded_size
),
611 visible_rect_(visible_rect
),
612 natural_size_(natural_size
),
613 shared_memory_handle_(base::SharedMemory::NULLHandle()),
614 shared_memory_offset_(0),
615 timestamp_(timestamp
),
616 release_sync_point_(0),
617 allow_overlay_(false) {
618 DCHECK(IsValidConfig(format_
, storage_type
, coded_size_
, visible_rect_
,
620 memset(&mailbox_holders_
, 0, sizeof(mailbox_holders_
));
621 memset(&strides_
, 0, sizeof(strides_
));
622 memset(&data_
, 0, sizeof(data_
));
625 VideoFrame::VideoFrame(Format format
,
626 StorageType storage_type
,
627 const gfx::Size
& coded_size
,
628 const gfx::Rect
& visible_rect
,
629 const gfx::Size
& natural_size
,
630 base::TimeDelta timestamp
,
631 base::SharedMemoryHandle handle
,
632 size_t shared_memory_offset
)
639 shared_memory_handle_
= handle
;
640 shared_memory_offset_
= shared_memory_offset
;
643 VideoFrame::VideoFrame(Format format
,
644 StorageType storage_type
,
645 const gfx::Size
& coded_size
,
646 const gfx::Rect
& visible_rect
,
647 const gfx::Size
& natural_size
,
648 const gpu::MailboxHolder(&mailbox_holders
)[kMaxPlanes
],
649 base::TimeDelta timestamp
)
656 memcpy(&mailbox_holders_
, mailbox_holders
, sizeof(mailbox_holders_
));
659 VideoFrame::~VideoFrame() {
660 if (!mailbox_holders_release_cb_
.is_null()) {
661 uint32 release_sync_point
;
663 // To ensure that changes to |release_sync_point_| are visible on this
664 // thread (imply a memory barrier).
665 base::AutoLock
locker(release_sync_point_lock_
);
666 release_sync_point
= release_sync_point_
;
668 base::ResetAndReturn(&mailbox_holders_release_cb_
).Run(release_sync_point
);
671 for (auto& callback
: done_callbacks_
)
672 base::ResetAndReturn(&callback
).Run();
676 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalStorage(
678 StorageType storage_type
,
679 const gfx::Size
& coded_size
,
680 const gfx::Rect
& visible_rect
,
681 const gfx::Size
& natural_size
,
684 base::TimeDelta timestamp
,
685 base::SharedMemoryHandle handle
,
686 size_t data_offset
) {
687 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
689 if (!IsValidConfig(format
, storage_type
, new_coded_size
, visible_rect
,
691 data_size
< AllocationSize(format
, new_coded_size
)) {
694 DLOG_IF(ERROR
, format
!= I420
) << "Only I420 format supported: "
695 << FormatToString(format
);
699 scoped_refptr
<VideoFrame
> frame
;
700 if (storage_type
== STORAGE_SHMEM
) {
701 frame
= new VideoFrame(format
, storage_type
, new_coded_size
, visible_rect
,
702 natural_size
, timestamp
, handle
, data_offset
);
704 frame
= new VideoFrame(format
, storage_type
, new_coded_size
, visible_rect
,
705 natural_size
, timestamp
);
707 frame
->strides_
[kYPlane
] = new_coded_size
.width();
708 frame
->strides_
[kUPlane
] = new_coded_size
.width() / 2;
709 frame
->strides_
[kVPlane
] = new_coded_size
.width() / 2;
710 frame
->data_
[kYPlane
] = data
;
711 frame
->data_
[kUPlane
] = data
+ new_coded_size
.GetArea();
712 frame
->data_
[kVPlane
] = data
+ (new_coded_size
.GetArea() * 5 / 4);
716 void VideoFrame::AllocateYUV() {
717 DCHECK_EQ(storage_type_
, STORAGE_OWNED_MEMORY
);
718 static_assert(0 == kYPlane
, "y plane data must be index 0");
720 size_t data_size
= 0;
721 size_t offset
[kMaxPlanes
];
722 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
) {
723 // The *2 in alignment for height is because some formats (e.g. h264) allow
724 // interlaced coding, and then the size needs to be a multiple of two
725 // macroblocks (vertically). See
726 // libavcodec/utils.c:avcodec_align_dimensions2().
727 const size_t height
= RoundUp(rows(plane
), kFrameSizeAlignment
* 2);
728 strides_
[plane
] = RoundUp(row_bytes(plane
), kFrameSizeAlignment
);
729 offset
[plane
] = data_size
;
730 data_size
+= height
* strides_
[plane
];
733 // The extra line of UV being allocated is because h264 chroma MC
734 // overreads by one line in some cases, see libavcodec/utils.c:
735 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
736 // put_h264_chroma_mc4_ssse3().
737 DCHECK(IsValidPlane(kUPlane
, format_
));
738 data_size
+= strides_
[kUPlane
] + kFrameSizePadding
;
740 // FFmpeg expects the initialize allocation to be zero-initialized. Failure
741 // to do so can lead to unitialized value usage. See http://crbug.com/390941
742 uint8
* data
= reinterpret_cast<uint8
*>(
743 base::AlignedAlloc(data_size
, kFrameAddressAlignment
));
744 memset(data
, 0, data_size
);
746 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
)
747 data_
[plane
] = data
+ offset
[plane
];
749 AddDestructionObserver(base::Bind(&ReleaseData
, data
));
753 bool VideoFrame::IsValidPlane(size_t plane
, Format format
) {
754 return (plane
< NumPlanes(format
));
757 int VideoFrame::stride(size_t plane
) const {
758 DCHECK(IsValidPlane(plane
, format_
));
759 return strides_
[plane
];
763 size_t VideoFrame::RowBytes(size_t plane
, Format format
, int width
) {
764 DCHECK(IsValidPlane(plane
, format
));
765 return BytesPerElement(format
, plane
) * Columns(plane
, format
, width
);
768 int VideoFrame::row_bytes(size_t plane
) const {
769 return RowBytes(plane
, format_
, coded_size_
.width());
773 size_t VideoFrame::Rows(size_t plane
, Format format
, int height
) {
774 DCHECK(IsValidPlane(plane
, format
));
775 const int sample_height
= SampleSize(format
, plane
).height();
776 return RoundUp(height
, sample_height
) / sample_height
;
780 size_t VideoFrame::Columns(size_t plane
, Format format
, int width
) {
781 DCHECK(IsValidPlane(plane
, format
));
782 const int sample_width
= SampleSize(format
, plane
).width();
783 return RoundUp(width
, sample_width
) / sample_width
;
786 int VideoFrame::rows(size_t plane
) const {
787 return Rows(plane
, format_
, coded_size_
.height());
790 const uint8
* VideoFrame::data(size_t plane
) const {
791 DCHECK(IsValidPlane(plane
, format_
));
792 DCHECK(IsMappable(storage_type_
));
796 uint8
* VideoFrame::data(size_t plane
) {
797 DCHECK(IsValidPlane(plane
, format_
));
798 DCHECK(IsMappable(storage_type_
));
802 const uint8
* VideoFrame::visible_data(size_t plane
) const {
803 DCHECK(IsValidPlane(plane
, format_
));
804 DCHECK(IsMappable(storage_type_
));
806 // Calculate an offset that is properly aligned for all planes.
807 const gfx::Size alignment
= CommonAlignment(format_
);
808 const gfx::Point
offset(RoundDown(visible_rect_
.x(), alignment
.width()),
809 RoundDown(visible_rect_
.y(), alignment
.height()));
811 const gfx::Size subsample
= SampleSize(format_
, plane
);
812 DCHECK(offset
.x() % subsample
.width() == 0);
813 DCHECK(offset
.y() % subsample
.height() == 0);
815 stride(plane
) * (offset
.y() / subsample
.height()) + // Row offset.
816 BytesPerElement(format_
, plane
) * // Column offset.
817 (offset
.x() / subsample
.width());
820 uint8
* VideoFrame::visible_data(size_t plane
) {
821 return const_cast<uint8
*>(
822 static_cast<const VideoFrame
*>(this)->visible_data(plane
));
825 const gpu::MailboxHolder
&
826 VideoFrame::mailbox_holder(size_t texture_index
) const {
827 #if defined(OS_LINUX)
828 DCHECK(storage_type_
== STORAGE_TEXTURE
|| storage_type_
== STORAGE_DMABUFS
);
830 DCHECK(storage_type_
== STORAGE_TEXTURE
);
832 DCHECK_LT(texture_index
, NumPlanes(format_
));
833 return mailbox_holders_
[texture_index
];
836 base::SharedMemoryHandle
VideoFrame::shared_memory_handle() const {
837 DCHECK_EQ(storage_type_
, STORAGE_SHMEM
);
838 DCHECK(shared_memory_handle_
!= base::SharedMemory::NULLHandle());
839 return shared_memory_handle_
;
842 size_t VideoFrame::shared_memory_offset() const {
843 DCHECK_EQ(storage_type_
, STORAGE_SHMEM
);
844 DCHECK(shared_memory_handle_
!= base::SharedMemory::NULLHandle());
845 return shared_memory_offset_
;
848 void VideoFrame::AddDestructionObserver(const base::Closure
& callback
) {
849 DCHECK(!callback
.is_null());
850 done_callbacks_
.push_back(callback
);
853 bool VideoFrame::IsEndOfStream() const {
855 return metadata_
.GetBoolean(VideoFrameMetadata::END_OF_STREAM
,
860 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient
* client
) {
861 #if defined(OS_LINUX)
862 DCHECK(storage_type_
== STORAGE_TEXTURE
|| storage_type_
== STORAGE_DMABUFS
);
864 DCHECK(storage_type_
== STORAGE_TEXTURE
);
866 base::AutoLock
locker(release_sync_point_lock_
);
867 // Must wait on the previous sync point before inserting a new sync point so
868 // that |mailbox_holders_release_cb_| guarantees the previous sync point
869 // occurred when it waits on |release_sync_point_|.
870 if (release_sync_point_
)
871 client
->WaitSyncPoint(release_sync_point_
);
872 release_sync_point_
= client
->InsertSyncPoint();
875 #if defined(OS_LINUX)
876 int VideoFrame::dmabuf_fd(size_t plane
) const {
877 DCHECK_EQ(storage_type_
, STORAGE_DMABUFS
);
878 return dmabuf_fds_
[plane
].get();
882 #if defined(OS_MACOSX)
883 CVPixelBufferRef
VideoFrame::cv_pixel_buffer() const {
884 return cv_pixel_buffer_
.get();
888 void VideoFrame::HashFrameForTesting(base::MD5Context
* context
) {
889 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
) {
890 for (int row
= 0; row
< rows(plane
); ++row
) {
891 base::MD5Update(context
, base::StringPiece(
892 reinterpret_cast<char*>(data(plane
) + stride(plane
) * row
),