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));
28 scoped_refptr
<VideoFrame
> VideoFrame::CreateFrame(
29 VideoFrame::Format format
,
30 const gfx::Size
& coded_size
,
31 const gfx::Rect
& visible_rect
,
32 const gfx::Size
& natural_size
,
33 base::TimeDelta timestamp
) {
34 // Since we're creating a new YUV frame (and allocating memory for it
35 // ourselves), we can pad the requested |coded_size| if necessary if the
36 // request does not line up on sample boundaries.
37 gfx::Size
new_coded_size(coded_size
);
39 case VideoFrame::YV24
:
41 case VideoFrame::YV12
:
42 case VideoFrame::YV12A
:
43 case VideoFrame::I420
:
44 case VideoFrame::YV12J
:
45 new_coded_size
.set_height((new_coded_size
.height() + 1) / 2 * 2);
47 case VideoFrame::YV16
:
48 new_coded_size
.set_width((new_coded_size
.width() + 1) / 2 * 2);
50 case VideoFrame::UNKNOWN
:
51 case VideoFrame::NV12
:
52 #if defined(VIDEO_HOLE)
53 case VideoFrame::HOLE
:
54 #endif // defined(VIDEO_HOLE)
55 case VideoFrame::NATIVE_TEXTURE
:
56 LOG(FATAL
) << "Only YUV formats supported: " << format
;
59 DCHECK(IsValidConfig(format
, new_coded_size
, visible_rect
, natural_size
));
60 scoped_refptr
<VideoFrame
> frame(
61 new VideoFrame(format
,
65 scoped_ptr
<gpu::MailboxHolder
>(),
73 std::string
VideoFrame::FormatToString(VideoFrame::Format format
) {
75 case VideoFrame::UNKNOWN
:
77 case VideoFrame::YV12
:
79 case VideoFrame::YV16
:
81 case VideoFrame::I420
:
83 case VideoFrame::NATIVE_TEXTURE
:
84 return "NATIVE_TEXTURE";
85 #if defined(VIDEO_HOLE)
86 case VideoFrame::HOLE
:
88 #endif // defined(VIDEO_HOLE)
89 case VideoFrame::YV12A
:
91 case VideoFrame::YV12J
:
93 case VideoFrame::NV12
:
95 case VideoFrame::YV24
:
98 NOTREACHED() << "Invalid videoframe format provided: " << format
;
103 bool VideoFrame::IsValidConfig(VideoFrame::Format format
,
104 const gfx::Size
& coded_size
,
105 const gfx::Rect
& visible_rect
,
106 const gfx::Size
& natural_size
) {
107 // Check maximum limits for all formats.
108 if (coded_size
.GetArea() > limits::kMaxCanvas
||
109 coded_size
.width() > limits::kMaxDimension
||
110 coded_size
.height() > limits::kMaxDimension
||
111 visible_rect
.x() < 0 || visible_rect
.y() < 0 ||
112 visible_rect
.right() > coded_size
.width() ||
113 visible_rect
.bottom() > coded_size
.height() ||
114 natural_size
.GetArea() > limits::kMaxCanvas
||
115 natural_size
.width() > limits::kMaxDimension
||
116 natural_size
.height() > limits::kMaxDimension
)
119 // Check format-specific width/height requirements.
121 case VideoFrame::UNKNOWN
:
122 return (coded_size
.IsEmpty() && visible_rect
.IsEmpty() &&
123 natural_size
.IsEmpty());
124 case VideoFrame::YV24
:
126 case VideoFrame::YV12
:
127 case VideoFrame::YV12J
:
128 case VideoFrame::I420
:
129 case VideoFrame::YV12A
:
130 case VideoFrame::NV12
:
131 // Subsampled YUV formats have width/height requirements.
132 if (static_cast<size_t>(coded_size
.height()) <
133 RoundUp(visible_rect
.bottom(), 2))
136 case VideoFrame::YV16
:
137 if (static_cast<size_t>(coded_size
.width()) <
138 RoundUp(visible_rect
.right(), 2))
141 case VideoFrame::NATIVE_TEXTURE
:
142 #if defined(VIDEO_HOLE)
143 case VideoFrame::HOLE
:
144 #endif // defined(VIDEO_HOLE)
145 // NATIVE_TEXTURE and HOLE have no software-allocated buffers and are
146 // allowed to skip the below check and be empty.
150 // Check that software-allocated buffer formats are not empty.
151 return (!coded_size
.IsEmpty() && !visible_rect
.IsEmpty() &&
152 !natural_size
.IsEmpty());
156 scoped_refptr
<VideoFrame
> VideoFrame::WrapNativeTexture(
157 scoped_ptr
<gpu::MailboxHolder
> mailbox_holder
,
158 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
159 const gfx::Size
& coded_size
,
160 const gfx::Rect
& visible_rect
,
161 const gfx::Size
& natural_size
,
162 base::TimeDelta timestamp
,
163 const ReadPixelsCB
& read_pixels_cb
) {
164 scoped_refptr
<VideoFrame
> frame(new VideoFrame(NATIVE_TEXTURE
,
168 mailbox_holder
.Pass(),
171 frame
->mailbox_holder_release_cb_
= mailbox_holder_release_cb
;
172 frame
->read_pixels_cb_
= read_pixels_cb
;
177 void VideoFrame::ReadPixelsFromNativeTexture(const SkBitmap
& pixels
) {
178 DCHECK_EQ(format_
, NATIVE_TEXTURE
);
179 if (!read_pixels_cb_
.is_null())
180 read_pixels_cb_
.Run(pixels
);
184 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalPackedMemory(
186 const gfx::Size
& coded_size
,
187 const gfx::Rect
& visible_rect
,
188 const gfx::Size
& natural_size
,
191 base::SharedMemoryHandle handle
,
192 base::TimeDelta timestamp
,
193 const base::Closure
& no_longer_needed_cb
) {
194 if (!IsValidConfig(format
, coded_size
, visible_rect
, natural_size
))
196 if (data_size
< AllocationSize(format
, coded_size
))
200 case VideoFrame::I420
: {
201 scoped_refptr
<VideoFrame
> frame(
202 new VideoFrame(format
,
206 scoped_ptr
<gpu::MailboxHolder
>(),
209 frame
->shared_memory_handle_
= handle
;
210 frame
->strides_
[kYPlane
] = coded_size
.width();
211 frame
->strides_
[kUPlane
] = coded_size
.width() / 2;
212 frame
->strides_
[kVPlane
] = coded_size
.width() / 2;
213 frame
->data_
[kYPlane
] = data
;
214 frame
->data_
[kUPlane
] = data
+ coded_size
.GetArea();
215 frame
->data_
[kVPlane
] = data
+ (coded_size
.GetArea() * 5 / 4);
216 frame
->no_longer_needed_cb_
= no_longer_needed_cb
;
225 #if defined(OS_POSIX)
227 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalDmabufs(
229 const gfx::Size
& coded_size
,
230 const gfx::Rect
& visible_rect
,
231 const gfx::Size
& natural_size
,
232 const std::vector
<int> dmabuf_fds
,
233 base::TimeDelta timestamp
,
234 const base::Closure
& no_longer_needed_cb
) {
235 if (!IsValidConfig(format
, coded_size
, visible_rect
, natural_size
))
238 if (dmabuf_fds
.size() != NumPlanes(format
)) {
239 LOG(FATAL
) << "Not enough dmabuf fds provided!";
243 scoped_refptr
<VideoFrame
> frame(
244 new VideoFrame(format
,
248 scoped_ptr
<gpu::MailboxHolder
>(),
252 for (size_t i
= 0; i
< dmabuf_fds
.size(); ++i
) {
253 int duped_fd
= HANDLE_EINTR(dup(dmabuf_fds
[i
]));
254 if (duped_fd
== -1) {
255 // The already-duped in previous iterations fds will be closed when
256 // the partially-created frame drops out of scope here.
257 DLOG(ERROR
) << "Failed duplicating a dmabuf fd";
261 frame
->dmabuf_fds_
[i
].reset(duped_fd
);
262 // Data is accessible only via fds.
263 frame
->data_
[i
] = NULL
;
264 frame
->strides_
[i
] = 0;
267 frame
->no_longer_needed_cb_
= no_longer_needed_cb
;
273 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalYuvData(
275 const gfx::Size
& coded_size
,
276 const gfx::Rect
& visible_rect
,
277 const gfx::Size
& natural_size
,
284 base::TimeDelta timestamp
,
285 const base::Closure
& no_longer_needed_cb
) {
286 if (!IsValidConfig(format
, coded_size
, visible_rect
, natural_size
))
289 scoped_refptr
<VideoFrame
> frame(
290 new VideoFrame(format
,
294 scoped_ptr
<gpu::MailboxHolder
>(),
297 frame
->strides_
[kYPlane
] = y_stride
;
298 frame
->strides_
[kUPlane
] = u_stride
;
299 frame
->strides_
[kVPlane
] = v_stride
;
300 frame
->data_
[kYPlane
] = y_data
;
301 frame
->data_
[kUPlane
] = u_data
;
302 frame
->data_
[kVPlane
] = v_data
;
303 frame
->no_longer_needed_cb_
= no_longer_needed_cb
;
308 scoped_refptr
<VideoFrame
> VideoFrame::WrapVideoFrame(
309 const scoped_refptr
<VideoFrame
>& frame
,
310 const gfx::Rect
& visible_rect
,
311 const gfx::Size
& natural_size
,
312 const base::Closure
& no_longer_needed_cb
) {
313 // NATIVE_TEXTURE frames need mailbox info propagated, and there's no support
314 // for that here yet, see http://crbug/362521.
315 CHECK(frame
->format() != NATIVE_TEXTURE
);
317 DCHECK(frame
->visible_rect().Contains(visible_rect
));
318 scoped_refptr
<VideoFrame
> wrapped_frame(
319 new VideoFrame(frame
->format(),
323 scoped_ptr
<gpu::MailboxHolder
>(),
325 frame
->end_of_stream()));
327 for (size_t i
= 0; i
< NumPlanes(frame
->format()); ++i
) {
328 wrapped_frame
->strides_
[i
] = frame
->stride(i
);
329 wrapped_frame
->data_
[i
] = frame
->data(i
);
332 wrapped_frame
->no_longer_needed_cb_
= no_longer_needed_cb
;
333 return wrapped_frame
;
337 scoped_refptr
<VideoFrame
> VideoFrame::CreateEOSFrame() {
338 return new VideoFrame(VideoFrame::UNKNOWN
,
342 scoped_ptr
<gpu::MailboxHolder
>(),
348 scoped_refptr
<VideoFrame
> VideoFrame::CreateColorFrame(
349 const gfx::Size
& size
,
350 uint8 y
, uint8 u
, uint8 v
,
351 base::TimeDelta timestamp
) {
352 scoped_refptr
<VideoFrame
> frame
= VideoFrame::CreateFrame(
353 VideoFrame::YV12
, size
, gfx::Rect(size
), size
, timestamp
);
354 FillYUV(frame
.get(), y
, u
, v
);
359 scoped_refptr
<VideoFrame
> VideoFrame::CreateBlackFrame(const gfx::Size
& size
) {
360 const uint8 kBlackY
= 0x00;
361 const uint8 kBlackUV
= 0x80;
362 const base::TimeDelta kZero
;
363 return CreateColorFrame(size
, kBlackY
, kBlackUV
, kBlackUV
, kZero
);
366 #if defined(VIDEO_HOLE)
367 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
368 // maintained by the general compositor team. Please contact the following
371 // wonsik@chromium.org
372 // ycheo@chromium.org
375 scoped_refptr
<VideoFrame
> VideoFrame::CreateHoleFrame(
376 const gfx::Size
& size
) {
377 DCHECK(IsValidConfig(VideoFrame::HOLE
, size
, gfx::Rect(size
), size
));
378 scoped_refptr
<VideoFrame
> frame(
379 new VideoFrame(VideoFrame::HOLE
,
383 scoped_ptr
<gpu::MailboxHolder
>(),
388 #endif // defined(VIDEO_HOLE)
391 size_t VideoFrame::NumPlanes(Format format
) {
393 case VideoFrame::NATIVE_TEXTURE
:
394 #if defined(VIDEO_HOLE)
395 case VideoFrame::HOLE
:
396 #endif // defined(VIDEO_HOLE)
398 case VideoFrame::NV12
:
400 case VideoFrame::YV12
:
401 case VideoFrame::YV16
:
402 case VideoFrame::I420
:
403 case VideoFrame::YV12J
:
404 case VideoFrame::YV24
:
406 case VideoFrame::YV12A
:
408 case VideoFrame::UNKNOWN
:
411 NOTREACHED() << "Unsupported video frame format: " << format
;
417 size_t VideoFrame::AllocationSize(Format format
, const gfx::Size
& coded_size
) {
419 for (size_t i
= 0; i
< NumPlanes(format
); ++i
)
420 total
+= PlaneAllocationSize(format
, i
, coded_size
);
425 gfx::Size
VideoFrame::PlaneSize(Format format
,
427 const gfx::Size
& coded_size
) {
428 // Align to multiple-of-two size overall. This ensures that non-subsampled
429 // planes can be addressed by pixel with the same scaling as the subsampled
431 const int width
= RoundUp(coded_size
.width(), 2);
432 const int height
= RoundUp(coded_size
.height(), 2);
434 case VideoFrame::YV24
:
436 case VideoFrame::kYPlane
:
437 case VideoFrame::kUPlane
:
438 case VideoFrame::kVPlane
:
439 return gfx::Size(width
, height
);
444 case VideoFrame::YV12
:
445 case VideoFrame::YV12J
:
446 case VideoFrame::I420
:
448 case VideoFrame::kYPlane
:
449 return gfx::Size(width
, height
);
450 case VideoFrame::kUPlane
:
451 case VideoFrame::kVPlane
:
452 return gfx::Size(width
/ 2, height
/ 2);
457 case VideoFrame::YV12A
:
459 case VideoFrame::kYPlane
:
460 case VideoFrame::kAPlane
:
461 return gfx::Size(width
, height
);
462 case VideoFrame::kUPlane
:
463 case VideoFrame::kVPlane
:
464 return gfx::Size(width
/ 2, height
/ 2);
469 case VideoFrame::YV16
:
471 case VideoFrame::kYPlane
:
472 return gfx::Size(width
, height
);
473 case VideoFrame::kUPlane
:
474 case VideoFrame::kVPlane
:
475 return gfx::Size(width
/ 2, height
);
480 case VideoFrame::NV12
:
482 case VideoFrame::kYPlane
:
483 return gfx::Size(width
, height
);
484 case VideoFrame::kUVPlane
:
485 return gfx::Size(width
, height
/ 2);
490 case VideoFrame::UNKNOWN
:
491 case VideoFrame::NATIVE_TEXTURE
:
492 #if defined(VIDEO_HOLE)
493 case VideoFrame::HOLE
:
494 #endif // defined(VIDEO_HOLE)
497 NOTREACHED() << "Unsupported video frame format/plane: "
498 << format
<< "/" << plane
;
502 size_t VideoFrame::PlaneAllocationSize(Format format
,
504 const gfx::Size
& coded_size
) {
505 // VideoFrame formats are (so far) all YUV and 1 byte per sample.
506 return PlaneSize(format
, plane
, coded_size
).GetArea();
510 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format
, size_t plane
) {
512 case VideoFrame::YV24
:
522 case VideoFrame::YV12
:
523 case VideoFrame::YV16
:
524 case VideoFrame::I420
:
525 case VideoFrame::YV12J
:
536 case VideoFrame::YV12A
:
548 case VideoFrame::NV12
:
558 case VideoFrame::UNKNOWN
:
559 #if defined(VIDEO_HOLE)
560 case VideoFrame::HOLE
:
561 #endif // defined(VIDEO_HOLE)
562 case VideoFrame::NATIVE_TEXTURE
:
565 NOTREACHED() << "Unsupported video frame format/plane: "
566 << format
<< "/" << plane
;
570 // Release data allocated by AllocateYUV().
571 static void ReleaseData(uint8
* data
) {
573 base::AlignedFree(data
);
576 void VideoFrame::AllocateYUV() {
577 DCHECK(format_
== VideoFrame::YV12
|| format_
== VideoFrame::YV16
||
578 format_
== VideoFrame::YV12A
|| format_
== VideoFrame::I420
||
579 format_
== VideoFrame::YV12J
|| format_
== VideoFrame::YV24
);
580 // Align Y rows at least at 16 byte boundaries. The stride for both
581 // YV12 and YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for
582 // U and V applies to two rows of Y (one byte of UV for 4 bytes of Y), so in
583 // the case of YV12 the strides are identical for the same width surface, but
584 // the number of bytes allocated for YV12 is 1/2 the amount for U & V as
585 // YV16. We also round the height of the surface allocated to be an even
586 // number to avoid any potential of faulting by code that attempts to access
587 // the Y values of the final row, but assumes that the last row of U & V
588 // applies to a full two rows of Y. YV12A is the same as YV12, but with an
589 // additional alpha plane that has the same size and alignment as the Y plane.
590 size_t y_stride
= RoundUp(row_bytes(VideoFrame::kYPlane
),
591 kFrameSizeAlignment
);
592 size_t uv_stride
= RoundUp(row_bytes(VideoFrame::kUPlane
),
593 kFrameSizeAlignment
);
595 // The *2 here is because some formats (e.g. h264) allow interlaced coding,
596 // and then the size needs to be a multiple of two macroblocks (vertically).
597 // See libavcodec/utils.c:avcodec_align_dimensions2().
598 size_t y_height
= RoundUp(coded_size_
.height(), kFrameSizeAlignment
* 2);
600 (format_
== VideoFrame::YV12
|| format_
== VideoFrame::YV12A
||
601 format_
== VideoFrame::I420
)
604 size_t y_bytes
= y_height
* y_stride
;
605 size_t uv_bytes
= uv_height
* uv_stride
;
606 size_t a_bytes
= format_
== VideoFrame::YV12A
? y_bytes
: 0;
608 // The extra line of UV being allocated is because h264 chroma MC
609 // overreads by one line in some cases, see libavcodec/utils.c:
610 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
611 // put_h264_chroma_mc4_ssse3().
612 uint8
* data
= reinterpret_cast<uint8
*>(
614 y_bytes
+ (uv_bytes
* 2 + uv_stride
) + a_bytes
+ kFrameSizePadding
,
615 kFrameAddressAlignment
));
616 no_longer_needed_cb_
= base::Bind(&ReleaseData
, data
);
617 COMPILE_ASSERT(0 == VideoFrame::kYPlane
, y_plane_data_must_be_index_0
);
618 data_
[VideoFrame::kYPlane
] = data
;
619 data_
[VideoFrame::kUPlane
] = data
+ y_bytes
;
620 data_
[VideoFrame::kVPlane
] = data
+ y_bytes
+ uv_bytes
;
621 strides_
[VideoFrame::kYPlane
] = y_stride
;
622 strides_
[VideoFrame::kUPlane
] = uv_stride
;
623 strides_
[VideoFrame::kVPlane
] = uv_stride
;
624 if (format_
== YV12A
) {
625 data_
[VideoFrame::kAPlane
] = data
+ y_bytes
+ (2 * uv_bytes
);
626 strides_
[VideoFrame::kAPlane
] = y_stride
;
630 VideoFrame::VideoFrame(VideoFrame::Format format
,
631 const gfx::Size
& coded_size
,
632 const gfx::Rect
& visible_rect
,
633 const gfx::Size
& natural_size
,
634 scoped_ptr
<gpu::MailboxHolder
> mailbox_holder
,
635 base::TimeDelta timestamp
,
638 coded_size_(coded_size
),
639 visible_rect_(visible_rect
),
640 natural_size_(natural_size
),
641 mailbox_holder_(mailbox_holder
.Pass()),
642 shared_memory_handle_(base::SharedMemory::NULLHandle()),
643 timestamp_(timestamp
),
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 std::vector
<uint32
> release_sync_points
;
655 base::AutoLock
locker(release_sync_point_lock_
);
656 release_sync_points_
.swap(release_sync_points
);
658 base::ResetAndReturn(&mailbox_holder_release_cb_
).Run(release_sync_points
);
660 if (!no_longer_needed_cb_
.is_null())
661 base::ResetAndReturn(&no_longer_needed_cb_
).Run();
664 bool VideoFrame::IsValidPlane(size_t plane
) const {
665 return (plane
< NumPlanes(format_
));
668 int VideoFrame::stride(size_t plane
) const {
669 DCHECK(IsValidPlane(plane
));
670 return strides_
[plane
];
673 int VideoFrame::row_bytes(size_t plane
) const {
674 DCHECK(IsValidPlane(plane
));
675 int width
= coded_size_
.width();
677 case VideoFrame::YV24
:
687 case VideoFrame::YV12
:
688 case VideoFrame::YV16
:
689 case VideoFrame::I420
:
690 case VideoFrame::YV12J
:
696 return RoundUp(width
, 2) / 2;
701 case VideoFrame::YV12A
:
708 return RoundUp(width
, 2) / 2;
713 case VideoFrame::NV12
:
722 case VideoFrame::UNKNOWN
:
723 #if defined(VIDEO_HOLE)
724 case VideoFrame::HOLE
:
725 #endif // defined(VIDEO_HOLE)
726 case VideoFrame::NATIVE_TEXTURE
:
729 NOTREACHED() << "Unsupported video frame format/plane: "
730 << format_
<< "/" << plane
;
734 int VideoFrame::rows(size_t plane
) const {
735 DCHECK(IsValidPlane(plane
));
736 int height
= coded_size_
.height();
738 case VideoFrame::YV24
:
739 case VideoFrame::YV16
:
749 case VideoFrame::YV12
:
750 case VideoFrame::YV12J
:
751 case VideoFrame::I420
:
757 return RoundUp(height
, 2) / 2;
762 case VideoFrame::YV12A
:
769 return RoundUp(height
, 2) / 2;
774 case VideoFrame::NV12
:
779 return RoundUp(height
, 2) / 2;
784 case VideoFrame::UNKNOWN
:
785 #if defined(VIDEO_HOLE)
786 case VideoFrame::HOLE
:
787 #endif // defined(VIDEO_HOLE)
788 case VideoFrame::NATIVE_TEXTURE
:
791 NOTREACHED() << "Unsupported video frame format/plane: "
792 << format_
<< "/" << plane
;
796 uint8
* VideoFrame::data(size_t plane
) const {
797 DCHECK(IsValidPlane(plane
));
801 const gpu::MailboxHolder
* VideoFrame::mailbox_holder() const {
802 DCHECK_EQ(format_
, NATIVE_TEXTURE
);
803 return mailbox_holder_
.get();
806 base::SharedMemoryHandle
VideoFrame::shared_memory_handle() const {
807 return shared_memory_handle_
;
810 void VideoFrame::AppendReleaseSyncPoint(uint32 sync_point
) {
811 DCHECK_EQ(format_
, NATIVE_TEXTURE
);
814 base::AutoLock
locker(release_sync_point_lock_
);
815 release_sync_points_
.push_back(sync_point
);
818 #if defined(OS_POSIX)
819 int VideoFrame::dmabuf_fd(size_t plane
) const {
820 return dmabuf_fds_
[plane
].get();
824 void VideoFrame::HashFrameForTesting(base::MD5Context
* context
) {
825 for (int plane
= 0; plane
< kMaxPlanes
; ++plane
) {
826 if (!IsValidPlane(plane
))
828 for (int row
= 0; row
< rows(plane
); ++row
) {
829 base::MD5Update(context
, base::StringPiece(
830 reinterpret_cast<char*>(data(plane
) + stride(plane
) * row
),