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 "gpu/command_buffer/common/mailbox_holder.h"
15 #include "media/base/limits.h"
16 #include "media/base/video_util.h"
17 #include "ui/gfx/point.h"
19 #if !defined(MEDIA_FOR_CAST_IOS)
20 #include "third_party/skia/include/core/SkBitmap.h"
25 static bool IsPowerOfTwo(size_t x
) {
26 return x
!= 0 && (x
& (x
- 1)) == 0;
29 static inline size_t RoundUp(size_t value
, size_t alignment
) {
30 DCHECK(IsPowerOfTwo(alignment
));
31 return ((value
+ (alignment
- 1)) & ~(alignment
- 1));
34 static inline size_t RoundDown(size_t value
, size_t alignment
) {
35 DCHECK(IsPowerOfTwo(alignment
));
36 return value
& ~(alignment
- 1);
39 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
40 // for the U-plane in I420.
41 static gfx::Size
SampleSize(VideoFrame::Format format
, size_t plane
) {
42 DCHECK(VideoFrame::IsValidPlane(plane
, format
));
45 case VideoFrame::kYPlane
:
46 case VideoFrame::kAPlane
:
47 return gfx::Size(1, 1);
49 case VideoFrame::kUPlane
: // and VideoFrame::kUVPlane:
50 case VideoFrame::kVPlane
:
52 case VideoFrame::YV24
:
53 return gfx::Size(1, 1);
55 case VideoFrame::YV16
:
56 return gfx::Size(2, 1);
58 case VideoFrame::YV12
:
59 case VideoFrame::YV12J
:
60 case VideoFrame::I420
:
61 case VideoFrame::YV12A
:
62 case VideoFrame::NV12
:
63 return gfx::Size(2, 2);
65 case VideoFrame::UNKNOWN
:
66 #if defined(VIDEO_HOLE)
67 case VideoFrame::HOLE
:
68 #endif // defined(VIDEO_HOLE)
69 case VideoFrame::NATIVE_TEXTURE
:
77 // Return the alignment for the whole frame, calculated as the max of the
78 // alignment for each individual plane.
79 static gfx::Size
CommonAlignment(VideoFrame::Format format
) {
80 int max_sample_width
= 0;
81 int max_sample_height
= 0;
82 for (size_t plane
= 0; plane
< VideoFrame::NumPlanes(format
); ++plane
) {
83 const gfx::Size sample_size
= SampleSize(format
, plane
);
84 max_sample_width
= std::max(max_sample_width
, sample_size
.width());
85 max_sample_height
= std::max(max_sample_height
, sample_size
.height());
87 return gfx::Size(max_sample_width
, max_sample_height
);
90 // Returns the number of bytes per element for given |plane| and |format|. E.g.
91 // 2 for the UV plane in NV12.
92 static int BytesPerElement(VideoFrame::Format format
, size_t plane
) {
93 DCHECK(VideoFrame::IsValidPlane(plane
, format
));
94 return (format
== VideoFrame::NV12
&& plane
== VideoFrame::kUVPlane
) ? 2 : 1;
97 // Rounds up |coded_size| if necessary for |format|.
98 static gfx::Size
AdjustCodedSize(VideoFrame::Format format
,
99 const gfx::Size
& coded_size
) {
100 const gfx::Size alignment
= CommonAlignment(format
);
101 return gfx::Size(RoundUp(coded_size
.width(), alignment
.width()),
102 RoundUp(coded_size
.height(), alignment
.height()));
106 scoped_refptr
<VideoFrame
> VideoFrame::CreateFrame(
107 VideoFrame::Format format
,
108 const gfx::Size
& coded_size
,
109 const gfx::Rect
& visible_rect
,
110 const gfx::Size
& natural_size
,
111 base::TimeDelta timestamp
) {
112 DCHECK(format
!= VideoFrame::UNKNOWN
&&
113 format
!= VideoFrame::NV12
&&
114 format
!= VideoFrame::NATIVE_TEXTURE
);
115 #if defined(VIDEO_HOLE)
116 DCHECK(format
!= VideoFrame::HOLE
);
117 #endif // defined(VIDEO_HOLE)
119 // Since we're creating a new YUV frame (and allocating memory for it
120 // ourselves), we can pad the requested |coded_size| if necessary if the
121 // request does not line up on sample boundaries.
122 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
123 DCHECK(IsValidConfig(format
, new_coded_size
, visible_rect
, natural_size
));
125 scoped_refptr
<VideoFrame
> frame(
126 new VideoFrame(format
,
130 scoped_ptr
<gpu::MailboxHolder
>(),
133 frame
->AllocateYUV();
138 std::string
VideoFrame::FormatToString(VideoFrame::Format format
) {
140 case VideoFrame::UNKNOWN
:
142 case VideoFrame::YV12
:
144 case VideoFrame::YV16
:
146 case VideoFrame::I420
:
148 case VideoFrame::NATIVE_TEXTURE
:
149 return "NATIVE_TEXTURE";
150 #if defined(VIDEO_HOLE)
151 case VideoFrame::HOLE
:
153 #endif // defined(VIDEO_HOLE)
154 case VideoFrame::YV12A
:
156 case VideoFrame::YV12J
:
158 case VideoFrame::NV12
:
160 case VideoFrame::YV24
:
163 NOTREACHED() << "Invalid videoframe format provided: " << format
;
168 bool VideoFrame::IsValidConfig(VideoFrame::Format format
,
169 const gfx::Size
& coded_size
,
170 const gfx::Rect
& visible_rect
,
171 const gfx::Size
& natural_size
) {
172 // Check maximum limits for all formats.
173 if (coded_size
.GetArea() > limits::kMaxCanvas
||
174 coded_size
.width() > limits::kMaxDimension
||
175 coded_size
.height() > limits::kMaxDimension
||
176 visible_rect
.x() < 0 || visible_rect
.y() < 0 ||
177 visible_rect
.right() > coded_size
.width() ||
178 visible_rect
.bottom() > coded_size
.height() ||
179 natural_size
.GetArea() > limits::kMaxCanvas
||
180 natural_size
.width() > limits::kMaxDimension
||
181 natural_size
.height() > limits::kMaxDimension
)
184 // Check format-specific width/height requirements.
186 case VideoFrame::UNKNOWN
:
187 return (coded_size
.IsEmpty() && visible_rect
.IsEmpty() &&
188 natural_size
.IsEmpty());
190 // NATIVE_TEXTURE and HOLE have no software-allocated buffers and are
191 // allowed to skip the below check.
192 case VideoFrame::NATIVE_TEXTURE
:
193 #if defined(VIDEO_HOLE)
194 case VideoFrame::HOLE
:
195 #endif // defined(VIDEO_HOLE)
198 case VideoFrame::YV24
:
199 case VideoFrame::YV12
:
200 case VideoFrame::YV12J
:
201 case VideoFrame::I420
:
202 case VideoFrame::YV12A
:
203 case VideoFrame::NV12
:
204 case VideoFrame::YV16
:
205 // Check that software-allocated buffer formats are aligned correctly and
207 const gfx::Size alignment
= CommonAlignment(format
);
208 return RoundUp(visible_rect
.right(), alignment
.width()) <=
209 static_cast<size_t>(coded_size
.width()) &&
210 RoundUp(visible_rect
.bottom(), alignment
.height()) <=
211 static_cast<size_t>(coded_size
.height()) &&
212 !coded_size
.IsEmpty() && !visible_rect
.IsEmpty() &&
213 !natural_size
.IsEmpty();
221 scoped_refptr
<VideoFrame
> VideoFrame::WrapNativeTexture(
222 scoped_ptr
<gpu::MailboxHolder
> mailbox_holder
,
223 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
224 const gfx::Size
& coded_size
,
225 const gfx::Rect
& visible_rect
,
226 const gfx::Size
& natural_size
,
227 base::TimeDelta timestamp
,
228 const ReadPixelsCB
& read_pixels_cb
) {
229 scoped_refptr
<VideoFrame
> frame(new VideoFrame(NATIVE_TEXTURE
,
233 mailbox_holder
.Pass(),
236 frame
->mailbox_holder_release_cb_
= mailbox_holder_release_cb
;
237 frame
->read_pixels_cb_
= read_pixels_cb
;
242 #if !defined(MEDIA_FOR_CAST_IOS)
243 void VideoFrame::ReadPixelsFromNativeTexture(const SkBitmap
& pixels
) {
244 DCHECK_EQ(format_
, NATIVE_TEXTURE
);
245 if (!read_pixels_cb_
.is_null())
246 read_pixels_cb_
.Run(pixels
);
251 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalPackedMemory(
253 const gfx::Size
& coded_size
,
254 const gfx::Rect
& visible_rect
,
255 const gfx::Size
& natural_size
,
258 base::SharedMemoryHandle handle
,
259 base::TimeDelta timestamp
,
260 const base::Closure
& no_longer_needed_cb
) {
261 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
263 if (!IsValidConfig(format
, new_coded_size
, visible_rect
, natural_size
))
265 if (data_size
< AllocationSize(format
, new_coded_size
))
269 case VideoFrame::I420
: {
270 scoped_refptr
<VideoFrame
> frame(
271 new VideoFrame(format
,
275 scoped_ptr
<gpu::MailboxHolder
>(),
278 frame
->shared_memory_handle_
= handle
;
279 frame
->strides_
[kYPlane
] = new_coded_size
.width();
280 frame
->strides_
[kUPlane
] = new_coded_size
.width() / 2;
281 frame
->strides_
[kVPlane
] = new_coded_size
.width() / 2;
282 frame
->data_
[kYPlane
] = data
;
283 frame
->data_
[kUPlane
] = data
+ new_coded_size
.GetArea();
284 frame
->data_
[kVPlane
] = data
+ (new_coded_size
.GetArea() * 5 / 4);
285 frame
->no_longer_needed_cb_
= no_longer_needed_cb
;
294 #if defined(OS_POSIX)
296 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalDmabufs(
298 const gfx::Size
& coded_size
,
299 const gfx::Rect
& visible_rect
,
300 const gfx::Size
& natural_size
,
301 const std::vector
<int> dmabuf_fds
,
302 base::TimeDelta timestamp
,
303 const base::Closure
& no_longer_needed_cb
) {
304 if (!IsValidConfig(format
, coded_size
, visible_rect
, natural_size
))
307 if (dmabuf_fds
.size() != NumPlanes(format
)) {
308 LOG(FATAL
) << "Not enough dmabuf fds provided!";
312 scoped_refptr
<VideoFrame
> frame(
313 new VideoFrame(format
,
317 scoped_ptr
<gpu::MailboxHolder
>(),
321 for (size_t i
= 0; i
< dmabuf_fds
.size(); ++i
) {
322 int duped_fd
= HANDLE_EINTR(dup(dmabuf_fds
[i
]));
323 if (duped_fd
== -1) {
324 // The already-duped in previous iterations fds will be closed when
325 // the partially-created frame drops out of scope here.
326 DLOG(ERROR
) << "Failed duplicating a dmabuf fd";
330 frame
->dmabuf_fds_
[i
].reset(duped_fd
);
331 // Data is accessible only via fds.
332 frame
->data_
[i
] = NULL
;
333 frame
->strides_
[i
] = 0;
336 frame
->no_longer_needed_cb_
= no_longer_needed_cb
;
341 #if defined(OS_MACOSX)
343 scoped_refptr
<VideoFrame
> VideoFrame::WrapCVPixelBuffer(
344 CVPixelBufferRef cv_pixel_buffer
,
345 base::TimeDelta timestamp
) {
346 DCHECK(cv_pixel_buffer
);
347 DCHECK(CFGetTypeID(cv_pixel_buffer
) == CVPixelBufferGetTypeID());
349 const OSType cv_format
= CVPixelBufferGetPixelFormatType(cv_pixel_buffer
);
351 // There are very few compatible CV pixel formats, so just check each.
352 if (cv_format
== kCVPixelFormatType_420YpCbCr8Planar
) {
353 format
= Format::I420
;
354 } else if (cv_format
== kCVPixelFormatType_444YpCbCr8
) {
355 format
= Format::YV24
;
356 } else if (cv_format
== '420v') {
357 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
358 // minimum OS X and iOS SDKs permits it.
359 format
= Format::NV12
;
361 DLOG(ERROR
) << "CVPixelBuffer format not supported: " << cv_format
;
365 const gfx::Size
coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer
));
366 const gfx::Rect
visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer
));
367 const gfx::Size
natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer
));
369 if (!IsValidConfig(format
, coded_size
, visible_rect
, natural_size
))
372 scoped_refptr
<VideoFrame
> frame(
373 new VideoFrame(format
,
377 scoped_ptr
<gpu::MailboxHolder
>(),
381 frame
->cv_pixel_buffer_
.reset(cv_pixel_buffer
, base::scoped_policy::RETAIN
);
387 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalYuvData(
389 const gfx::Size
& coded_size
,
390 const gfx::Rect
& visible_rect
,
391 const gfx::Size
& natural_size
,
398 base::TimeDelta timestamp
,
399 const base::Closure
& no_longer_needed_cb
) {
400 const gfx::Size new_coded_size
= AdjustCodedSize(format
, coded_size
);
401 CHECK(IsValidConfig(format
, new_coded_size
, visible_rect
, natural_size
));
403 scoped_refptr
<VideoFrame
> frame(
404 new VideoFrame(format
,
408 scoped_ptr
<gpu::MailboxHolder
>(),
411 frame
->strides_
[kYPlane
] = y_stride
;
412 frame
->strides_
[kUPlane
] = u_stride
;
413 frame
->strides_
[kVPlane
] = v_stride
;
414 frame
->data_
[kYPlane
] = y_data
;
415 frame
->data_
[kUPlane
] = u_data
;
416 frame
->data_
[kVPlane
] = v_data
;
417 frame
->no_longer_needed_cb_
= no_longer_needed_cb
;
422 scoped_refptr
<VideoFrame
> VideoFrame::WrapVideoFrame(
423 const scoped_refptr
<VideoFrame
>& frame
,
424 const gfx::Rect
& visible_rect
,
425 const gfx::Size
& natural_size
,
426 const base::Closure
& no_longer_needed_cb
) {
427 // NATIVE_TEXTURE frames need mailbox info propagated, and there's no support
428 // for that here yet, see http://crbug/362521.
429 CHECK_NE(frame
->format(), NATIVE_TEXTURE
);
431 DCHECK(frame
->visible_rect().Contains(visible_rect
));
432 scoped_refptr
<VideoFrame
> wrapped_frame(
433 new VideoFrame(frame
->format(),
437 scoped_ptr
<gpu::MailboxHolder
>(),
439 frame
->end_of_stream()));
441 for (size_t i
= 0; i
< NumPlanes(frame
->format()); ++i
) {
442 wrapped_frame
->strides_
[i
] = frame
->stride(i
);
443 wrapped_frame
->data_
[i
] = frame
->data(i
);
446 wrapped_frame
->no_longer_needed_cb_
= no_longer_needed_cb
;
447 return wrapped_frame
;
451 scoped_refptr
<VideoFrame
> VideoFrame::CreateEOSFrame() {
452 return new VideoFrame(VideoFrame::UNKNOWN
,
456 scoped_ptr
<gpu::MailboxHolder
>(),
462 scoped_refptr
<VideoFrame
> VideoFrame::CreateColorFrame(
463 const gfx::Size
& size
,
464 uint8 y
, uint8 u
, uint8 v
,
465 base::TimeDelta timestamp
) {
466 scoped_refptr
<VideoFrame
> frame
= VideoFrame::CreateFrame(
467 VideoFrame::YV12
, size
, gfx::Rect(size
), size
, timestamp
);
468 FillYUV(frame
.get(), y
, u
, v
);
473 scoped_refptr
<VideoFrame
> VideoFrame::CreateBlackFrame(const gfx::Size
& size
) {
474 const uint8 kBlackY
= 0x00;
475 const uint8 kBlackUV
= 0x80;
476 const base::TimeDelta kZero
;
477 return CreateColorFrame(size
, kBlackY
, kBlackUV
, kBlackUV
, kZero
);
481 scoped_refptr
<VideoFrame
> VideoFrame::CreateTransparentFrame(
482 const gfx::Size
& size
) {
483 const uint8 kBlackY
= 0x00;
484 const uint8 kBlackUV
= 0x00;
485 const uint8 kTransparentA
= 0x00;
486 const base::TimeDelta kZero
;
487 scoped_refptr
<VideoFrame
> frame
= VideoFrame::CreateFrame(
488 VideoFrame::YV12A
, size
, gfx::Rect(size
), size
, kZero
);
489 FillYUVA(frame
.get(), kBlackY
, kBlackUV
, kBlackUV
, kTransparentA
);
493 #if defined(VIDEO_HOLE)
494 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
495 // maintained by the general compositor team. Please contact the following
498 // wonsik@chromium.org
499 // ycheo@chromium.org
502 scoped_refptr
<VideoFrame
> VideoFrame::CreateHoleFrame(
503 const gfx::Size
& size
) {
504 DCHECK(IsValidConfig(VideoFrame::HOLE
, size
, gfx::Rect(size
), size
));
505 scoped_refptr
<VideoFrame
> frame(
506 new VideoFrame(VideoFrame::HOLE
,
510 scoped_ptr
<gpu::MailboxHolder
>(),
515 #endif // defined(VIDEO_HOLE)
518 size_t VideoFrame::NumPlanes(Format format
) {
520 case VideoFrame::NATIVE_TEXTURE
:
521 #if defined(VIDEO_HOLE)
522 case VideoFrame::HOLE
:
523 #endif // defined(VIDEO_HOLE)
525 case VideoFrame::NV12
:
527 case VideoFrame::YV12
:
528 case VideoFrame::YV16
:
529 case VideoFrame::I420
:
530 case VideoFrame::YV12J
:
531 case VideoFrame::YV24
:
533 case VideoFrame::YV12A
:
535 case VideoFrame::UNKNOWN
:
538 NOTREACHED() << "Unsupported video frame format: " << format
;
544 size_t VideoFrame::AllocationSize(Format format
, const gfx::Size
& coded_size
) {
546 for (size_t i
= 0; i
< NumPlanes(format
); ++i
)
547 total
+= PlaneAllocationSize(format
, i
, coded_size
);
552 gfx::Size
VideoFrame::PlaneSize(Format format
,
554 const gfx::Size
& coded_size
) {
555 DCHECK(IsValidPlane(plane
, format
));
557 // Align to multiple-of-two size overall. This ensures that non-subsampled
558 // planes can be addressed by pixel with the same scaling as the subsampled
560 const int width
= RoundUp(coded_size
.width(), 2);
561 const int height
= RoundUp(coded_size
.height(), 2);
563 const gfx::Size subsample
= SampleSize(format
, plane
);
564 DCHECK(width
% subsample
.width() == 0);
565 DCHECK(height
% subsample
.height() == 0);
566 return gfx::Size(BytesPerElement(format
, plane
) * width
/ subsample
.width(),
567 height
/ subsample
.height());
570 size_t VideoFrame::PlaneAllocationSize(Format format
,
572 const gfx::Size
& coded_size
) {
573 // VideoFrame formats are (so far) all YUV and 1 byte per sample.
574 return PlaneSize(format
, plane
, coded_size
).GetArea();
578 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format
, size_t plane
) {
579 DCHECK(IsValidPlane(plane
, format
));
580 const int bits_per_element
= 8 * BytesPerElement(format
, plane
);
581 const int pixels_per_element
= SampleSize(format
, plane
).GetArea();
582 DCHECK(bits_per_element
% pixels_per_element
== 0);
583 return bits_per_element
/ pixels_per_element
;
586 // Release data allocated by AllocateYUV().
587 static void ReleaseData(uint8
* data
) {
589 base::AlignedFree(data
);
592 void VideoFrame::AllocateYUV() {
593 DCHECK(format_
== YV12
|| format_
== YV16
|| format_
== YV12A
||
594 format_
== I420
|| format_
== YV12J
|| format_
== YV24
);
595 COMPILE_ASSERT(0 == kYPlane
, y_plane_data_must_be_index_0
);
597 size_t data_size
= 0;
598 size_t offset
[kMaxPlanes
];
599 for (size_t plane
= 0; plane
< VideoFrame::NumPlanes(format_
); ++plane
) {
600 // The *2 in alignment for height is because some formats (e.g. h264) allow
601 // interlaced coding, and then the size needs to be a multiple of two
602 // macroblocks (vertically). See
603 // libavcodec/utils.c:avcodec_align_dimensions2().
604 const size_t height
= RoundUp(rows(plane
), kFrameSizeAlignment
* 2);
605 strides_
[plane
] = RoundUp(row_bytes(plane
), kFrameSizeAlignment
);
606 offset
[plane
] = data_size
;
607 data_size
+= height
* strides_
[plane
];
610 // The extra line of UV being allocated is because h264 chroma MC
611 // overreads by one line in some cases, see libavcodec/utils.c:
612 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
613 // put_h264_chroma_mc4_ssse3().
614 DCHECK(IsValidPlane(kUPlane
, format_
));
615 data_size
+= strides_
[kUPlane
] + kFrameSizePadding
;
617 // FFmpeg expects the initialize allocation to be zero-initialized. Failure
618 // to do so can lead to unitialized value usage. See http://crbug.com/390941
619 uint8
* data
= reinterpret_cast<uint8
*>(
620 base::AlignedAlloc(data_size
, kFrameAddressAlignment
));
621 memset(data
, 0, data_size
);
623 for (size_t plane
= 0; plane
< VideoFrame::NumPlanes(format_
); ++plane
)
624 data_
[plane
] = data
+ offset
[plane
];
626 no_longer_needed_cb_
= base::Bind(&ReleaseData
, data
);
629 VideoFrame::VideoFrame(VideoFrame::Format format
,
630 const gfx::Size
& coded_size
,
631 const gfx::Rect
& visible_rect
,
632 const gfx::Size
& natural_size
,
633 scoped_ptr
<gpu::MailboxHolder
> mailbox_holder
,
634 base::TimeDelta timestamp
,
637 coded_size_(coded_size
),
638 visible_rect_(visible_rect
),
639 natural_size_(natural_size
),
640 mailbox_holder_(mailbox_holder
.Pass()),
641 shared_memory_handle_(base::SharedMemory::NULLHandle()),
642 timestamp_(timestamp
),
643 release_sync_point_(0),
644 end_of_stream_(end_of_stream
) {
645 DCHECK(IsValidConfig(format_
, coded_size_
, visible_rect_
, natural_size_
));
647 memset(&strides_
, 0, sizeof(strides_
));
648 memset(&data_
, 0, sizeof(data_
));
651 VideoFrame::~VideoFrame() {
652 if (!mailbox_holder_release_cb_
.is_null()) {
653 uint32 release_sync_point
;
655 // To ensure that changes to |release_sync_point_| are visible on this
656 // thread (imply a memory barrier).
657 base::AutoLock
locker(release_sync_point_lock_
);
658 release_sync_point
= release_sync_point_
;
660 base::ResetAndReturn(&mailbox_holder_release_cb_
).Run(release_sync_point
);
662 if (!no_longer_needed_cb_
.is_null())
663 base::ResetAndReturn(&no_longer_needed_cb_
).Run();
667 bool VideoFrame::IsValidPlane(size_t plane
, VideoFrame::Format format
) {
668 return (plane
< NumPlanes(format
));
671 int VideoFrame::stride(size_t plane
) const {
672 DCHECK(IsValidPlane(plane
, format_
));
673 return strides_
[plane
];
677 size_t VideoFrame::RowBytes(size_t plane
,
678 VideoFrame::Format format
,
680 DCHECK(IsValidPlane(plane
, format
));
681 return BytesPerElement(format
, plane
) * Columns(plane
, format
, width
);
684 int VideoFrame::row_bytes(size_t plane
) const {
685 return RowBytes(plane
, format_
, coded_size_
.width());
689 size_t VideoFrame::Rows(size_t plane
, VideoFrame::Format format
, int height
) {
690 DCHECK(IsValidPlane(plane
, format
));
691 const int sample_height
= SampleSize(format
, plane
).height();
692 return RoundUp(height
, sample_height
) / sample_height
;
696 size_t VideoFrame::Columns(size_t plane
, Format format
, int width
) {
697 DCHECK(IsValidPlane(plane
, format
));
698 const int sample_width
= SampleSize(format
, plane
).width();
699 return RoundUp(width
, sample_width
) / sample_width
;
702 int VideoFrame::rows(size_t plane
) const {
703 return Rows(plane
, format_
, coded_size_
.height());
706 const uint8
* VideoFrame::data(size_t plane
) const {
707 DCHECK(IsValidPlane(plane
, format_
));
711 uint8
* VideoFrame::data(size_t plane
) {
712 DCHECK(IsValidPlane(plane
, format_
));
716 const uint8
* VideoFrame::visible_data(size_t plane
) const {
717 DCHECK(IsValidPlane(plane
, format_
));
719 // Calculate an offset that is properly aligned for all planes.
720 const gfx::Size alignment
= CommonAlignment(format_
);
721 const gfx::Point
offset(RoundDown(visible_rect_
.x(), alignment
.width()),
722 RoundDown(visible_rect_
.y(), alignment
.height()));
724 const gfx::Size subsample
= SampleSize(format_
, plane
);
725 DCHECK(offset
.x() % subsample
.width() == 0);
726 DCHECK(offset
.y() % subsample
.height() == 0);
728 stride(plane
) * (offset
.y() / subsample
.height()) + // Row offset.
729 BytesPerElement(format_
, plane
) * // Column offset.
730 (offset
.x() / subsample
.width());
733 uint8
* VideoFrame::visible_data(size_t plane
) {
734 return const_cast<uint8
*>(
735 static_cast<const VideoFrame
*>(this)->visible_data(plane
));
738 const gpu::MailboxHolder
* VideoFrame::mailbox_holder() const {
739 DCHECK_EQ(format_
, NATIVE_TEXTURE
);
740 return mailbox_holder_
.get();
743 base::SharedMemoryHandle
VideoFrame::shared_memory_handle() const {
744 return shared_memory_handle_
;
747 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient
* client
) {
748 DCHECK_EQ(format_
, NATIVE_TEXTURE
);
749 base::AutoLock
locker(release_sync_point_lock_
);
750 // Must wait on the previous sync point before inserting a new sync point so
751 // that |mailbox_holder_release_cb_| guarantees the previous sync point
752 // occurred when it waits on |release_sync_point_|.
753 if (release_sync_point_
)
754 client
->WaitSyncPoint(release_sync_point_
);
755 release_sync_point_
= client
->InsertSyncPoint();
758 #if defined(OS_POSIX)
759 int VideoFrame::dmabuf_fd(size_t plane
) const {
760 return dmabuf_fds_
[plane
].get();
764 #if defined(OS_MACOSX)
765 CVPixelBufferRef
VideoFrame::cv_pixel_buffer() const {
766 return cv_pixel_buffer_
.get();
770 void VideoFrame::HashFrameForTesting(base::MD5Context
* context
) {
771 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
) {
772 for (int row
= 0; row
< rows(plane
); ++row
) {
773 base::MD5Update(context
, base::StringPiece(
774 reinterpret_cast<char*>(data(plane
) + stride(plane
) * row
),