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 "third_party/skia/include/core/SkBitmap.h"
21 static inline size_t RoundUp(size_t value, size_t alignment) {
22 // Check that |alignment| is a power of 2.
23 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1)));
24 return ((value + (alignment - 1)) & ~(alignment - 1));
27 // Rounds up |coded_size| if necessary for |format|.
28 static gfx::Size AdjustCodedSize(VideoFrame::Format format,
29 const gfx::Size& coded_size) {
30 gfx::Size new_coded_size(coded_size);
32 case VideoFrame::YV12:
33 case VideoFrame::YV12A:
34 case VideoFrame::I420:
35 case VideoFrame::YV12J:
36 new_coded_size.set_height(RoundUp(new_coded_size.height(), 2));
38 case VideoFrame::YV16:
39 new_coded_size.set_width(RoundUp(new_coded_size.width(), 2));
44 return new_coded_size;
48 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
49 VideoFrame::Format format,
50 const gfx::Size& coded_size,
51 const gfx::Rect& visible_rect,
52 const gfx::Size& natural_size,
53 base::TimeDelta timestamp) {
54 DCHECK(format != VideoFrame::UNKNOWN &&
55 format != VideoFrame::NV12 &&
56 format != VideoFrame::NATIVE_TEXTURE);
57 #if defined(VIDEO_HOLE)
58 DCHECK(format != VideoFrame::HOLE);
59 #endif // defined(VIDEO_HOLE)
61 // Since we're creating a new YUV frame (and allocating memory for it
62 // ourselves), we can pad the requested |coded_size| if necessary if the
63 // request does not line up on sample boundaries.
64 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
65 DCHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
67 scoped_refptr<VideoFrame> frame(
68 new VideoFrame(format,
72 scoped_ptr<gpu::MailboxHolder>(),
80 std::string VideoFrame::FormatToString(VideoFrame::Format format) {
82 case VideoFrame::UNKNOWN:
84 case VideoFrame::YV12:
86 case VideoFrame::YV16:
88 case VideoFrame::I420:
90 case VideoFrame::NATIVE_TEXTURE:
91 return "NATIVE_TEXTURE";
92 #if defined(VIDEO_HOLE)
93 case VideoFrame::HOLE:
95 #endif // defined(VIDEO_HOLE)
96 case VideoFrame::YV12A:
98 case VideoFrame::YV12J:
100 case VideoFrame::NV12:
102 case VideoFrame::YV24:
105 NOTREACHED() << "Invalid videoframe format provided: " << format;
110 bool VideoFrame::IsValidConfig(VideoFrame::Format format,
111 const gfx::Size& coded_size,
112 const gfx::Rect& visible_rect,
113 const gfx::Size& natural_size) {
114 // Check maximum limits for all formats.
115 if (coded_size.GetArea() > limits::kMaxCanvas ||
116 coded_size.width() > limits::kMaxDimension ||
117 coded_size.height() > limits::kMaxDimension ||
118 visible_rect.x() < 0 || visible_rect.y() < 0 ||
119 visible_rect.right() > coded_size.width() ||
120 visible_rect.bottom() > coded_size.height() ||
121 natural_size.GetArea() > limits::kMaxCanvas ||
122 natural_size.width() > limits::kMaxDimension ||
123 natural_size.height() > limits::kMaxDimension)
126 // Check format-specific width/height requirements.
128 case VideoFrame::UNKNOWN:
129 return (coded_size.IsEmpty() && visible_rect.IsEmpty() &&
130 natural_size.IsEmpty());
131 case VideoFrame::YV24:
133 case VideoFrame::YV12:
134 case VideoFrame::YV12J:
135 case VideoFrame::I420:
136 case VideoFrame::YV12A:
137 case VideoFrame::NV12:
138 // Subsampled YUV formats have width/height requirements.
139 if (static_cast<size_t>(coded_size.height()) <
140 RoundUp(visible_rect.bottom(), 2))
143 case VideoFrame::YV16:
144 if (static_cast<size_t>(coded_size.width()) <
145 RoundUp(visible_rect.right(), 2))
148 case VideoFrame::NATIVE_TEXTURE:
149 #if defined(VIDEO_HOLE)
150 case VideoFrame::HOLE:
151 #endif // defined(VIDEO_HOLE)
152 // NATIVE_TEXTURE and HOLE have no software-allocated buffers and are
153 // allowed to skip the below check and be empty.
157 // Check that software-allocated buffer formats are not empty.
158 return (!coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
159 !natural_size.IsEmpty());
163 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
164 scoped_ptr<gpu::MailboxHolder> mailbox_holder,
165 const ReleaseMailboxCB& mailbox_holder_release_cb,
166 const gfx::Size& coded_size,
167 const gfx::Rect& visible_rect,
168 const gfx::Size& natural_size,
169 base::TimeDelta timestamp,
170 const ReadPixelsCB& read_pixels_cb) {
171 scoped_refptr<VideoFrame> frame(new VideoFrame(NATIVE_TEXTURE,
175 mailbox_holder.Pass(),
178 frame->mailbox_holder_release_cb_ = mailbox_holder_release_cb;
179 frame->read_pixels_cb_ = read_pixels_cb;
184 void VideoFrame::ReadPixelsFromNativeTexture(const SkBitmap& pixels) {
185 DCHECK_EQ(format_, NATIVE_TEXTURE);
186 if (!read_pixels_cb_.is_null())
187 read_pixels_cb_.Run(pixels);
191 scoped_refptr<VideoFrame> VideoFrame::WrapExternalPackedMemory(
193 const gfx::Size& coded_size,
194 const gfx::Rect& visible_rect,
195 const gfx::Size& natural_size,
198 base::SharedMemoryHandle handle,
199 base::TimeDelta timestamp,
200 const base::Closure& no_longer_needed_cb) {
201 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
203 if (!IsValidConfig(format, new_coded_size, visible_rect, natural_size))
205 if (data_size < AllocationSize(format, new_coded_size))
209 case VideoFrame::I420: {
210 scoped_refptr<VideoFrame> frame(
211 new VideoFrame(format,
215 scoped_ptr<gpu::MailboxHolder>(),
218 frame->shared_memory_handle_ = handle;
219 frame->strides_[kYPlane] = new_coded_size.width();
220 frame->strides_[kUPlane] = new_coded_size.width() / 2;
221 frame->strides_[kVPlane] = new_coded_size.width() / 2;
222 frame->data_[kYPlane] = data;
223 frame->data_[kUPlane] = data + new_coded_size.GetArea();
224 frame->data_[kVPlane] = data + (new_coded_size.GetArea() * 5 / 4);
225 frame->no_longer_needed_cb_ = no_longer_needed_cb;
234 #if defined(OS_POSIX)
236 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
238 const gfx::Size& coded_size,
239 const gfx::Rect& visible_rect,
240 const gfx::Size& natural_size,
241 const std::vector<int> dmabuf_fds,
242 base::TimeDelta timestamp,
243 const base::Closure& no_longer_needed_cb) {
244 if (!IsValidConfig(format, coded_size, visible_rect, natural_size))
247 if (dmabuf_fds.size() != NumPlanes(format)) {
248 LOG(FATAL) << "Not enough dmabuf fds provided!";
252 scoped_refptr<VideoFrame> frame(
253 new VideoFrame(format,
257 scoped_ptr<gpu::MailboxHolder>(),
261 for (size_t i = 0; i < dmabuf_fds.size(); ++i) {
262 int duped_fd = HANDLE_EINTR(dup(dmabuf_fds[i]));
263 if (duped_fd == -1) {
264 // The already-duped in previous iterations fds will be closed when
265 // the partially-created frame drops out of scope here.
266 DLOG(ERROR) << "Failed duplicating a dmabuf fd";
270 frame->dmabuf_fds_[i].reset(duped_fd);
271 // Data is accessible only via fds.
272 frame->data_[i] = NULL;
273 frame->strides_[i] = 0;
276 frame->no_longer_needed_cb_ = no_longer_needed_cb;
282 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
284 const gfx::Size& coded_size,
285 const gfx::Rect& visible_rect,
286 const gfx::Size& natural_size,
293 base::TimeDelta timestamp,
294 const base::Closure& no_longer_needed_cb) {
295 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
296 CHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
298 scoped_refptr<VideoFrame> frame(
299 new VideoFrame(format,
303 scoped_ptr<gpu::MailboxHolder>(),
306 frame->strides_[kYPlane] = y_stride;
307 frame->strides_[kUPlane] = u_stride;
308 frame->strides_[kVPlane] = v_stride;
309 frame->data_[kYPlane] = y_data;
310 frame->data_[kUPlane] = u_data;
311 frame->data_[kVPlane] = v_data;
312 frame->no_longer_needed_cb_ = no_longer_needed_cb;
317 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
318 const scoped_refptr<VideoFrame>& frame,
319 const gfx::Rect& visible_rect,
320 const gfx::Size& natural_size,
321 const base::Closure& no_longer_needed_cb) {
322 // NATIVE_TEXTURE frames need mailbox info propagated, and there's no support
323 // for that here yet, see http://crbug/362521.
324 CHECK_NE(frame->format(), NATIVE_TEXTURE);
326 DCHECK(frame->visible_rect().Contains(visible_rect));
327 scoped_refptr<VideoFrame> wrapped_frame(
328 new VideoFrame(frame->format(),
332 scoped_ptr<gpu::MailboxHolder>(),
334 frame->end_of_stream()));
336 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
337 wrapped_frame->strides_[i] = frame->stride(i);
338 wrapped_frame->data_[i] = frame->data(i);
341 wrapped_frame->no_longer_needed_cb_ = no_longer_needed_cb;
342 return wrapped_frame;
346 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
347 return new VideoFrame(VideoFrame::UNKNOWN,
351 scoped_ptr<gpu::MailboxHolder>(),
357 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
358 const gfx::Size& size,
359 uint8 y, uint8 u, uint8 v,
360 base::TimeDelta timestamp) {
361 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
362 VideoFrame::YV12, size, gfx::Rect(size), size, timestamp);
363 FillYUV(frame.get(), y, u, v);
368 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
369 const uint8 kBlackY = 0x00;
370 const uint8 kBlackUV = 0x80;
371 const base::TimeDelta kZero;
372 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
376 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
377 const gfx::Size& size) {
378 const uint8 kBlackY = 0x00;
379 const uint8 kBlackUV = 0x00;
380 const uint8 kTransparentA = 0x00;
381 const base::TimeDelta kZero;
382 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
383 VideoFrame::YV12A, size, gfx::Rect(size), size, kZero);
384 FillYUVA(frame, kBlackY, kBlackUV, kBlackUV, kTransparentA);
388 #if defined(VIDEO_HOLE)
389 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
390 // maintained by the general compositor team. Please contact the following
393 // wonsik@chromium.org
394 // ycheo@chromium.org
397 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
398 const gfx::Size& size) {
399 DCHECK(IsValidConfig(VideoFrame::HOLE, size, gfx::Rect(size), size));
400 scoped_refptr<VideoFrame> frame(
401 new VideoFrame(VideoFrame::HOLE,
405 scoped_ptr<gpu::MailboxHolder>(),
410 #endif // defined(VIDEO_HOLE)
413 size_t VideoFrame::NumPlanes(Format format) {
415 case VideoFrame::NATIVE_TEXTURE:
416 #if defined(VIDEO_HOLE)
417 case VideoFrame::HOLE:
418 #endif // defined(VIDEO_HOLE)
420 case VideoFrame::NV12:
422 case VideoFrame::YV12:
423 case VideoFrame::YV16:
424 case VideoFrame::I420:
425 case VideoFrame::YV12J:
426 case VideoFrame::YV24:
428 case VideoFrame::YV12A:
430 case VideoFrame::UNKNOWN:
433 NOTREACHED() << "Unsupported video frame format: " << format;
439 size_t VideoFrame::AllocationSize(Format format, const gfx::Size& coded_size) {
441 for (size_t i = 0; i < NumPlanes(format); ++i)
442 total += PlaneAllocationSize(format, i, coded_size);
447 gfx::Size VideoFrame::PlaneSize(Format format,
449 const gfx::Size& coded_size) {
450 // Align to multiple-of-two size overall. This ensures that non-subsampled
451 // planes can be addressed by pixel with the same scaling as the subsampled
453 const int width = RoundUp(coded_size.width(), 2);
454 const int height = RoundUp(coded_size.height(), 2);
456 case VideoFrame::YV24:
458 case VideoFrame::kYPlane:
459 case VideoFrame::kUPlane:
460 case VideoFrame::kVPlane:
461 return gfx::Size(width, height);
466 case VideoFrame::YV12:
467 case VideoFrame::YV12J:
468 case VideoFrame::I420:
470 case VideoFrame::kYPlane:
471 return gfx::Size(width, height);
472 case VideoFrame::kUPlane:
473 case VideoFrame::kVPlane:
474 return gfx::Size(width / 2, height / 2);
479 case VideoFrame::YV12A:
481 case VideoFrame::kYPlane:
482 case VideoFrame::kAPlane:
483 return gfx::Size(width, height);
484 case VideoFrame::kUPlane:
485 case VideoFrame::kVPlane:
486 return gfx::Size(width / 2, height / 2);
491 case VideoFrame::YV16:
493 case VideoFrame::kYPlane:
494 return gfx::Size(width, height);
495 case VideoFrame::kUPlane:
496 case VideoFrame::kVPlane:
497 return gfx::Size(width / 2, height);
502 case VideoFrame::NV12:
504 case VideoFrame::kYPlane:
505 return gfx::Size(width, height);
506 case VideoFrame::kUVPlane:
507 return gfx::Size(width, height / 2);
512 case VideoFrame::UNKNOWN:
513 case VideoFrame::NATIVE_TEXTURE:
514 #if defined(VIDEO_HOLE)
515 case VideoFrame::HOLE:
516 #endif // defined(VIDEO_HOLE)
519 NOTREACHED() << "Unsupported video frame format/plane: "
520 << format << "/" << plane;
524 size_t VideoFrame::PlaneAllocationSize(Format format,
526 const gfx::Size& coded_size) {
527 // VideoFrame formats are (so far) all YUV and 1 byte per sample.
528 return PlaneSize(format, plane, coded_size).GetArea();
532 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format, size_t plane) {
534 case VideoFrame::YV24:
544 case VideoFrame::YV12:
545 case VideoFrame::YV16:
546 case VideoFrame::I420:
547 case VideoFrame::YV12J:
558 case VideoFrame::YV12A:
570 case VideoFrame::NV12:
580 case VideoFrame::UNKNOWN:
581 #if defined(VIDEO_HOLE)
582 case VideoFrame::HOLE:
583 #endif // defined(VIDEO_HOLE)
584 case VideoFrame::NATIVE_TEXTURE:
587 NOTREACHED() << "Unsupported video frame format/plane: "
588 << format << "/" << plane;
592 // Release data allocated by AllocateYUV().
593 static void ReleaseData(uint8* data) {
595 base::AlignedFree(data);
598 void VideoFrame::AllocateYUV() {
599 DCHECK(format_ == VideoFrame::YV12 || format_ == VideoFrame::YV16 ||
600 format_ == VideoFrame::YV12A || format_ == VideoFrame::I420 ||
601 format_ == VideoFrame::YV12J || format_ == VideoFrame::YV24);
602 // Align Y rows at least at 16 byte boundaries. The stride for both
603 // YV12 and YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for
604 // U and V applies to two rows of Y (one byte of UV for 4 bytes of Y), so in
605 // the case of YV12 the strides are identical for the same width surface, but
606 // the number of bytes allocated for YV12 is 1/2 the amount for U & V as
607 // YV16. We also round the height of the surface allocated to be an even
608 // number to avoid any potential of faulting by code that attempts to access
609 // the Y values of the final row, but assumes that the last row of U & V
610 // applies to a full two rows of Y. YV12A is the same as YV12, but with an
611 // additional alpha plane that has the same size and alignment as the Y plane.
612 size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane),
613 kFrameSizeAlignment);
614 size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane),
615 kFrameSizeAlignment);
617 // The *2 here is because some formats (e.g. h264) allow interlaced coding,
618 // and then the size needs to be a multiple of two macroblocks (vertically).
619 // See libavcodec/utils.c:avcodec_align_dimensions2().
620 size_t y_height = RoundUp(coded_size_.height(), kFrameSizeAlignment * 2);
622 (format_ == VideoFrame::YV12 || format_ == VideoFrame::YV12A ||
623 format_ == VideoFrame::I420)
626 size_t y_bytes = y_height * y_stride;
627 size_t uv_bytes = uv_height * uv_stride;
628 size_t a_bytes = format_ == VideoFrame::YV12A ? y_bytes : 0;
630 // The extra line of UV being allocated is because h264 chroma MC
631 // overreads by one line in some cases, see libavcodec/utils.c:
632 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
633 // put_h264_chroma_mc4_ssse3().
634 const size_t data_size =
635 y_bytes + (uv_bytes * 2 + uv_stride) + a_bytes + kFrameSizePadding;
636 uint8* data = reinterpret_cast<uint8*>(
637 base::AlignedAlloc(data_size, kFrameAddressAlignment));
638 // FFmpeg expects the initialize allocation to be zero-initialized. Failure
639 // to do so can lead to unitialized value usage. See http://crbug.com/390941
640 memset(data, 0, data_size);
641 no_longer_needed_cb_ = base::Bind(&ReleaseData, data);
642 COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0);
643 data_[VideoFrame::kYPlane] = data;
644 data_[VideoFrame::kUPlane] = data + y_bytes;
645 data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes;
646 strides_[VideoFrame::kYPlane] = y_stride;
647 strides_[VideoFrame::kUPlane] = uv_stride;
648 strides_[VideoFrame::kVPlane] = uv_stride;
649 if (format_ == YV12A) {
650 data_[VideoFrame::kAPlane] = data + y_bytes + (2 * uv_bytes);
651 strides_[VideoFrame::kAPlane] = y_stride;
655 VideoFrame::VideoFrame(VideoFrame::Format format,
656 const gfx::Size& coded_size,
657 const gfx::Rect& visible_rect,
658 const gfx::Size& natural_size,
659 scoped_ptr<gpu::MailboxHolder> mailbox_holder,
660 base::TimeDelta timestamp,
663 coded_size_(coded_size),
664 visible_rect_(visible_rect),
665 natural_size_(natural_size),
666 mailbox_holder_(mailbox_holder.Pass()),
667 shared_memory_handle_(base::SharedMemory::NULLHandle()),
668 timestamp_(timestamp),
669 release_sync_point_(0),
670 end_of_stream_(end_of_stream) {
671 DCHECK(IsValidConfig(format_, coded_size_, visible_rect_, natural_size_));
673 memset(&strides_, 0, sizeof(strides_));
674 memset(&data_, 0, sizeof(data_));
677 VideoFrame::~VideoFrame() {
678 if (!mailbox_holder_release_cb_.is_null()) {
679 uint32 release_sync_point;
681 // To ensure that changes to |release_sync_point_| are visible on this
682 // thread (imply a memory barrier).
683 base::AutoLock locker(release_sync_point_lock_);
684 release_sync_point = release_sync_point_;
686 base::ResetAndReturn(&mailbox_holder_release_cb_).Run(release_sync_point);
688 if (!no_longer_needed_cb_.is_null())
689 base::ResetAndReturn(&no_longer_needed_cb_).Run();
692 bool VideoFrame::IsValidPlane(size_t plane) const {
693 return (plane < NumPlanes(format_));
696 int VideoFrame::stride(size_t plane) const {
697 DCHECK(IsValidPlane(plane));
698 return strides_[plane];
701 int VideoFrame::row_bytes(size_t plane) const {
702 DCHECK(IsValidPlane(plane));
703 int width = coded_size_.width();
705 case VideoFrame::YV24:
715 case VideoFrame::YV12:
716 case VideoFrame::YV16:
717 case VideoFrame::I420:
718 case VideoFrame::YV12J:
724 return RoundUp(width, 2) / 2;
729 case VideoFrame::YV12A:
736 return RoundUp(width, 2) / 2;
741 case VideoFrame::NV12:
750 case VideoFrame::UNKNOWN:
751 #if defined(VIDEO_HOLE)
752 case VideoFrame::HOLE:
753 #endif // defined(VIDEO_HOLE)
754 case VideoFrame::NATIVE_TEXTURE:
757 NOTREACHED() << "Unsupported video frame format/plane: "
758 << format_ << "/" << plane;
762 int VideoFrame::rows(size_t plane) const {
763 DCHECK(IsValidPlane(plane));
764 int height = coded_size_.height();
766 case VideoFrame::YV24:
767 case VideoFrame::YV16:
777 case VideoFrame::YV12:
778 case VideoFrame::YV12J:
779 case VideoFrame::I420:
785 return RoundUp(height, 2) / 2;
790 case VideoFrame::YV12A:
797 return RoundUp(height, 2) / 2;
802 case VideoFrame::NV12:
807 return RoundUp(height, 2) / 2;
812 case VideoFrame::UNKNOWN:
813 #if defined(VIDEO_HOLE)
814 case VideoFrame::HOLE:
815 #endif // defined(VIDEO_HOLE)
816 case VideoFrame::NATIVE_TEXTURE:
819 NOTREACHED() << "Unsupported video frame format/plane: "
820 << format_ << "/" << plane;
824 uint8* VideoFrame::data(size_t plane) const {
825 DCHECK(IsValidPlane(plane));
829 const gpu::MailboxHolder* VideoFrame::mailbox_holder() const {
830 DCHECK_EQ(format_, NATIVE_TEXTURE);
831 return mailbox_holder_.get();
834 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
835 return shared_memory_handle_;
838 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
839 DCHECK_EQ(format_, NATIVE_TEXTURE);
840 base::AutoLock locker(release_sync_point_lock_);
841 // Must wait on the previous sync point before inserting a new sync point so
842 // that |mailbox_holder_release_cb_| guarantees the previous sync point
843 // occurred when it waits on |release_sync_point_|.
844 if (release_sync_point_)
845 client->WaitSyncPoint(release_sync_point_);
846 release_sync_point_ = client->InsertSyncPoint();
849 #if defined(OS_POSIX)
850 int VideoFrame::dmabuf_fd(size_t plane) const {
851 return dmabuf_fds_[plane].get();
855 void VideoFrame::HashFrameForTesting(base::MD5Context* context) {
856 for (int plane = 0; plane < kMaxPlanes; ++plane) {
857 if (!IsValidPlane(plane))
859 for (int row = 0; row < rows(plane); ++row) {
860 base::MD5Update(context, base::StringPiece(
861 reinterpret_cast<char*>(data(plane) + stride(plane) * row),