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/video_util.h"
17 #include "ui/gfx/geometry/point.h"
21 static bool IsPowerOfTwo(size_t x
) {
22 return x
!= 0 && (x
& (x
- 1)) == 0;
25 static inline size_t RoundUp(size_t value
, size_t alignment
) {
26 DCHECK(IsPowerOfTwo(alignment
));
27 return ((value
+ (alignment
- 1)) & ~(alignment
- 1));
30 static inline size_t RoundDown(size_t value
, size_t alignment
) {
31 DCHECK(IsPowerOfTwo(alignment
));
32 return value
& ~(alignment
- 1);
35 static std::string
ConfigToString(const VideoPixelFormat format
,
36 const VideoFrame::StorageType storage_type
,
37 const gfx::Size
& coded_size
,
38 const gfx::Rect
& visible_rect
,
39 const gfx::Size
& natural_size
) {
40 return base::StringPrintf(
41 "format:%s coded_size:%s visible_rect:%s natural_size:%s",
42 VideoPixelFormatToString(format
).c_str(), coded_size
.ToString().c_str(),
43 visible_rect
.ToString().c_str(), natural_size
.ToString().c_str());
46 // Returns true if |plane| is a valid plane index for the given |format|.
47 static bool IsValidPlane(size_t plane
, VideoPixelFormat format
) {
48 DCHECK_LE(VideoFrame::NumPlanes(format
),
49 static_cast<size_t>(VideoFrame::kMaxPlanes
));
50 return (plane
< VideoFrame::NumPlanes(format
));
53 // Returns true if |frame| is accesible mapped in the VideoFrame memory space.
55 static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type
) {
58 // This is not strictly needed but makes explicit that, at VideoFrame
59 // level, DmaBufs are not mappable from userspace.
60 storage_type
!= VideoFrame::STORAGE_DMABUFS
&&
62 (storage_type
== VideoFrame::STORAGE_UNOWNED_MEMORY
||
63 storage_type
== VideoFrame::STORAGE_OWNED_MEMORY
||
64 storage_type
== VideoFrame::STORAGE_SHMEM
);
67 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
68 // for the U-plane in PIXEL_FORMAT_I420.
69 static gfx::Size
SampleSize(VideoPixelFormat format
, size_t plane
) {
70 DCHECK(IsValidPlane(plane
, format
));
73 case VideoFrame::kYPlane
:
74 case VideoFrame::kAPlane
:
75 return gfx::Size(1, 1);
77 case VideoFrame::kUPlane
: // and VideoFrame::kUVPlane:
78 case VideoFrame::kVPlane
:
80 case PIXEL_FORMAT_YV24
:
81 return gfx::Size(1, 1);
83 case PIXEL_FORMAT_YV16
:
84 return gfx::Size(2, 1);
86 case PIXEL_FORMAT_YV12
:
87 case PIXEL_FORMAT_I420
:
88 case PIXEL_FORMAT_YV12A
:
89 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
90 case PIXEL_FORMAT_NV12
:
92 return gfx::Size(2, 2);
94 case PIXEL_FORMAT_UNKNOWN
:
95 case PIXEL_FORMAT_ARGB
:
96 case PIXEL_FORMAT_XRGB
:
104 // Return the alignment for the whole frame, calculated as the max of the
105 // alignment for each individual plane.
106 static gfx::Size
CommonAlignment(VideoPixelFormat format
) {
107 int max_sample_width
= 0;
108 int max_sample_height
= 0;
109 for (size_t plane
= 0; plane
< VideoFrame::NumPlanes(format
); ++plane
) {
110 const gfx::Size sample_size
= SampleSize(format
, plane
);
111 max_sample_width
= std::max(max_sample_width
, sample_size
.width());
112 max_sample_height
= std::max(max_sample_height
, sample_size
.height());
114 return gfx::Size(max_sample_width
, max_sample_height
);
117 // Returns the number of bytes per element for given |plane| and |format|.
118 static int BytesPerElement(VideoPixelFormat format
, size_t plane
) {
119 DCHECK(IsValidPlane(plane
, format
));
120 if (format
== PIXEL_FORMAT_ARGB
|| format
== PIXEL_FORMAT_XRGB
)
123 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
124 if (format
== PIXEL_FORMAT_NV12
&& plane
== VideoFrame::kUVPlane
)
132 bool VideoFrame::IsValidConfig(VideoPixelFormat format
,
133 StorageType storage_type
,
134 const gfx::Size
& coded_size
,
135 const gfx::Rect
& visible_rect
,
136 const gfx::Size
& natural_size
) {
137 // Check maximum limits for all formats.
138 if (coded_size
.GetArea() > limits::kMaxCanvas
||
139 coded_size
.width() > limits::kMaxDimension
||
140 coded_size
.height() > limits::kMaxDimension
||
141 visible_rect
.x() < 0 || visible_rect
.y() < 0 ||
142 visible_rect
.right() > coded_size
.width() ||
143 visible_rect
.bottom() > coded_size
.height() ||
144 natural_size
.GetArea() > limits::kMaxCanvas
||
145 natural_size
.width() > limits::kMaxDimension
||
146 natural_size
.height() > limits::kMaxDimension
)
149 // TODO(mcasas): Remove parameter |storage_type| when the opaque storage types
150 // comply with the checks below. Right now we skip them.
151 if (!IsStorageTypeMappable(storage_type
))
154 // Make sure new formats are properly accounted for in the method.
155 static_assert(PIXEL_FORMAT_MAX
== 8,
156 "Added pixel format, please review IsValidConfig()");
158 if (format
== PIXEL_FORMAT_UNKNOWN
) {
159 return coded_size
.IsEmpty() && visible_rect
.IsEmpty() &&
160 natural_size
.IsEmpty();
163 // Check that software-allocated buffer formats are not empty.
164 return !coded_size
.IsEmpty() && !visible_rect
.IsEmpty() &&
165 !natural_size
.IsEmpty();
169 scoped_refptr
<VideoFrame
> VideoFrame::CreateFrame(VideoPixelFormat format
,
170 const gfx::Size
& coded_size
,
171 const gfx::Rect
& visible_rect
,
172 const gfx::Size
& natural_size
,
173 base::TimeDelta timestamp
) {
174 if (!IsYuvPlanar(format
)) {
179 // Since we're creating a new YUV frame (and allocating memory for it
180 // ourselves), we can pad the requested |coded_size| if necessary if the
181 // request does not line up on sample boundaries. See discussion at
182 // http://crrev.com/1240833003
183 const gfx::Size alignment
= CommonAlignment(format
);
184 const gfx::Size new_coded_size
=
185 gfx::Size(RoundUp(coded_size
.width(), alignment
.width()),
186 RoundUp(coded_size
.height(), alignment
.height()));
187 DCHECK((new_coded_size
.width() % alignment
.width() == 0) &&
188 (new_coded_size
.height() % alignment
.height() == 0));
190 const StorageType storage
= STORAGE_OWNED_MEMORY
;
191 if (!IsValidConfig(format
, storage
, new_coded_size
, visible_rect
,
193 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
194 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
199 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
200 format
, storage
, new_coded_size
, visible_rect
, natural_size
, timestamp
));
201 frame
->AllocateYUV();
206 scoped_refptr
<VideoFrame
> VideoFrame::WrapNativeTexture(
207 VideoPixelFormat format
,
208 const gpu::MailboxHolder
& mailbox_holder
,
209 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
210 const gfx::Size
& coded_size
,
211 const gfx::Rect
& visible_rect
,
212 const gfx::Size
& natural_size
,
213 base::TimeDelta timestamp
) {
214 if (format
!= PIXEL_FORMAT_ARGB
) {
215 DLOG(ERROR
) << "Only ARGB pixel format supported, got "
216 << VideoPixelFormatToString(format
);
219 const StorageType storage
= STORAGE_OPAQUE
;
220 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
221 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
222 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
227 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
228 mailbox_holders
[kARGBPlane
] = mailbox_holder
;
229 return new VideoFrame(format
, storage
, coded_size
, visible_rect
, natural_size
,
230 mailbox_holders
, mailbox_holder_release_cb
, timestamp
);
234 scoped_refptr
<VideoFrame
> VideoFrame::WrapYUV420NativeTextures(
235 const gpu::MailboxHolder
& y_mailbox_holder
,
236 const gpu::MailboxHolder
& u_mailbox_holder
,
237 const gpu::MailboxHolder
& v_mailbox_holder
,
238 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
239 const gfx::Size
& coded_size
,
240 const gfx::Rect
& visible_rect
,
241 const gfx::Size
& natural_size
,
242 base::TimeDelta timestamp
) {
243 const StorageType storage
= STORAGE_OPAQUE
;
244 VideoPixelFormat format
= PIXEL_FORMAT_I420
;
245 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
246 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
247 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
252 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
253 mailbox_holders
[kYPlane
] = y_mailbox_holder
;
254 mailbox_holders
[kUPlane
] = u_mailbox_holder
;
255 mailbox_holders
[kVPlane
] = v_mailbox_holder
;
256 return new VideoFrame(format
, storage
, coded_size
, visible_rect
, natural_size
,
257 mailbox_holders
, mailbox_holder_release_cb
, timestamp
);
261 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalData(
262 VideoPixelFormat format
,
263 const gfx::Size
& coded_size
,
264 const gfx::Rect
& visible_rect
,
265 const gfx::Size
& natural_size
,
268 base::TimeDelta timestamp
) {
269 return WrapExternalStorage(format
, STORAGE_UNOWNED_MEMORY
, coded_size
,
270 visible_rect
, natural_size
, data
, data_size
,
271 timestamp
, base::SharedMemory::NULLHandle(), 0);
275 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalSharedMemory(
276 VideoPixelFormat format
,
277 const gfx::Size
& coded_size
,
278 const gfx::Rect
& visible_rect
,
279 const gfx::Size
& natural_size
,
282 base::SharedMemoryHandle handle
,
284 base::TimeDelta timestamp
) {
285 return WrapExternalStorage(format
, STORAGE_SHMEM
, coded_size
, visible_rect
,
286 natural_size
, data
, data_size
, timestamp
, handle
,
291 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalYuvData(
292 VideoPixelFormat format
,
293 const gfx::Size
& coded_size
,
294 const gfx::Rect
& visible_rect
,
295 const gfx::Size
& natural_size
,
302 base::TimeDelta timestamp
) {
303 const StorageType storage
= STORAGE_UNOWNED_MEMORY
;
304 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
305 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
306 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
311 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
312 format
, storage
, coded_size
, visible_rect
, natural_size
, timestamp
));
313 frame
->strides_
[kYPlane
] = y_stride
;
314 frame
->strides_
[kUPlane
] = u_stride
;
315 frame
->strides_
[kVPlane
] = v_stride
;
316 frame
->data_
[kYPlane
] = y_data
;
317 frame
->data_
[kUPlane
] = u_data
;
318 frame
->data_
[kVPlane
] = v_data
;
322 #if defined(OS_LINUX)
324 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalDmabufs(
325 VideoPixelFormat format
,
326 const gfx::Size
& coded_size
,
327 const gfx::Rect
& visible_rect
,
328 const gfx::Size
& natural_size
,
329 const std::vector
<int>& dmabuf_fds
,
330 base::TimeDelta timestamp
) {
331 #if defined(OS_CHROMEOS)
332 DCHECK_EQ(format
, PIXEL_FORMAT_NV12
);
335 const StorageType storage
= STORAGE_DMABUFS
;
336 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
337 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
338 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
343 gpu::MailboxHolder mailbox_holders
[kMaxPlanes
];
344 scoped_refptr
<VideoFrame
> frame
=
345 new VideoFrame(format
, storage
, coded_size
, visible_rect
, natural_size
,
346 mailbox_holders
, ReleaseMailboxCB(), timestamp
);
347 if (!frame
|| !frame
->DuplicateFileDescriptors(dmabuf_fds
))
353 #if defined(OS_MACOSX)
355 scoped_refptr
<VideoFrame
> VideoFrame::WrapCVPixelBuffer(
356 CVPixelBufferRef cv_pixel_buffer
,
357 base::TimeDelta timestamp
) {
358 DCHECK(cv_pixel_buffer
);
359 DCHECK(CFGetTypeID(cv_pixel_buffer
) == CVPixelBufferGetTypeID());
361 const OSType cv_format
= CVPixelBufferGetPixelFormatType(cv_pixel_buffer
);
362 VideoPixelFormat format
;
363 // There are very few compatible CV pixel formats, so just check each.
364 if (cv_format
== kCVPixelFormatType_420YpCbCr8Planar
) {
365 format
= PIXEL_FORMAT_I420
;
366 } else if (cv_format
== kCVPixelFormatType_444YpCbCr8
) {
367 format
= PIXEL_FORMAT_YV24
;
368 } else if (cv_format
== '420v') {
369 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
370 // minimum OS X and iOS SDKs permits it.
371 format
= PIXEL_FORMAT_NV12
;
373 DLOG(ERROR
) << "CVPixelBuffer format not supported: " << cv_format
;
377 const gfx::Size
coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer
));
378 const gfx::Rect
visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer
));
379 const gfx::Size
natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer
));
380 const StorageType storage
= STORAGE_UNOWNED_MEMORY
;
382 if (!IsValidConfig(format
, storage
, coded_size
, visible_rect
, natural_size
)) {
383 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
384 << ConfigToString(format
, storage
, coded_size
, visible_rect
,
389 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
390 format
, storage
, coded_size
, visible_rect
, natural_size
, timestamp
));
392 frame
->cv_pixel_buffer_
.reset(cv_pixel_buffer
, base::scoped_policy::RETAIN
);
398 scoped_refptr
<VideoFrame
> VideoFrame::WrapVideoFrame(
399 const scoped_refptr
<VideoFrame
>& frame
,
400 const gfx::Rect
& visible_rect
,
401 const gfx::Size
& natural_size
) {
402 // Frames with textures need mailbox info propagated, and there's no support
403 // for that here yet, see http://crbug/362521.
404 CHECK(!frame
->HasTextures());
406 DCHECK(frame
->visible_rect().Contains(visible_rect
));
408 if (!IsValidConfig(frame
->format(), frame
->storage_type(),
409 frame
->coded_size(), visible_rect
, natural_size
)) {
410 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
411 << ConfigToString(frame
->format(), frame
->storage_type(),
412 frame
->coded_size(), visible_rect
,
417 scoped_refptr
<VideoFrame
> wrapping_frame(new VideoFrame(
418 frame
->format(), frame
->storage_type(), frame
->coded_size(), visible_rect
,
419 natural_size
, frame
->timestamp()));
420 if (frame
->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM
)) {
421 wrapping_frame
->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM
,
425 for (size_t i
= 0; i
< NumPlanes(frame
->format()); ++i
) {
426 wrapping_frame
->strides_
[i
] = frame
->stride(i
);
427 wrapping_frame
->data_
[i
] = frame
->data(i
);
430 #if defined(OS_LINUX)
431 // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
432 if (frame
->storage_type() == STORAGE_DMABUFS
) {
433 std::vector
<int> original_fds
;
434 for (size_t i
= 0; i
< kMaxPlanes
; ++i
)
435 original_fds
.push_back(frame
->dmabuf_fd(i
));
436 if (!wrapping_frame
->DuplicateFileDescriptors(original_fds
))
441 return wrapping_frame
;
445 scoped_refptr
<VideoFrame
> VideoFrame::CreateEOSFrame() {
446 scoped_refptr
<VideoFrame
> frame
=
447 new VideoFrame(PIXEL_FORMAT_UNKNOWN
, STORAGE_UNKNOWN
, gfx::Size(),
448 gfx::Rect(), gfx::Size(), kNoTimestamp());
449 frame
->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM
, true);
454 scoped_refptr
<VideoFrame
> VideoFrame::CreateColorFrame(
455 const gfx::Size
& size
,
456 uint8 y
, uint8 u
, uint8 v
,
457 base::TimeDelta timestamp
) {
458 scoped_refptr
<VideoFrame
> frame
=
459 CreateFrame(PIXEL_FORMAT_YV12
, size
, gfx::Rect(size
), size
, timestamp
);
460 FillYUV(frame
.get(), y
, u
, v
);
465 scoped_refptr
<VideoFrame
> VideoFrame::CreateBlackFrame(const gfx::Size
& size
) {
466 const uint8 kBlackY
= 0x00;
467 const uint8 kBlackUV
= 0x80;
468 const base::TimeDelta kZero
;
469 return CreateColorFrame(size
, kBlackY
, kBlackUV
, kBlackUV
, kZero
);
473 scoped_refptr
<VideoFrame
> VideoFrame::CreateTransparentFrame(
474 const gfx::Size
& size
) {
475 const uint8 kBlackY
= 0x00;
476 const uint8 kBlackUV
= 0x00;
477 const uint8 kTransparentA
= 0x00;
478 const base::TimeDelta kZero
;
479 scoped_refptr
<VideoFrame
> frame
=
480 CreateFrame(PIXEL_FORMAT_YV12A
, size
, gfx::Rect(size
), size
, kZero
);
481 FillYUVA(frame
.get(), kBlackY
, kBlackUV
, kBlackUV
, kTransparentA
);
485 #if defined(VIDEO_HOLE)
486 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
487 // maintained by the general compositor team. Please contact
488 // wonsik@chromium.org .
491 scoped_refptr
<VideoFrame
> VideoFrame::CreateHoleFrame(
492 const gfx::Size
& size
) {
493 const VideoPixelFormat format
= PIXEL_FORMAT_UNKNOWN
;
494 const StorageType storage
= STORAGE_HOLE
;
495 const gfx::Rect visible_rect
= gfx::Rect(size
);
496 if (!IsValidConfig(format
, storage
, size
, visible_rect
, size
)) {
497 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
498 << ConfigToString(format
, storage
, size
, visible_rect
, size
);
501 scoped_refptr
<VideoFrame
> frame(new VideoFrame(
502 format
, storage
, size
, gfx::Rect(size
), size
, base::TimeDelta()));
505 #endif // defined(VIDEO_HOLE)
508 size_t VideoFrame::NumPlanes(VideoPixelFormat format
) {
510 case PIXEL_FORMAT_ARGB
:
511 case PIXEL_FORMAT_XRGB
:
513 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
514 case PIXEL_FORMAT_NV12
:
517 case PIXEL_FORMAT_YV12
:
518 case PIXEL_FORMAT_YV16
:
519 case PIXEL_FORMAT_I420
:
520 case PIXEL_FORMAT_YV24
:
522 case PIXEL_FORMAT_YV12A
:
524 case PIXEL_FORMAT_UNKNOWN
:
527 NOTREACHED() << "Unsupported video frame format: " << format
;
532 size_t VideoFrame::AllocationSize(VideoPixelFormat format
,
533 const gfx::Size
& coded_size
) {
535 for (size_t i
= 0; i
< NumPlanes(format
); ++i
)
536 total
+= PlaneSize(format
, i
, coded_size
).GetArea();
541 gfx::Size
VideoFrame::PlaneSize(VideoPixelFormat format
,
543 const gfx::Size
& coded_size
) {
544 DCHECK(IsValidPlane(plane
, format
));
546 int width
= coded_size
.width();
547 int height
= coded_size
.height();
548 if (format
!= PIXEL_FORMAT_ARGB
) {
549 // Align to multiple-of-two size overall. This ensures that non-subsampled
550 // planes can be addressed by pixel with the same scaling as the subsampled
552 width
= RoundUp(width
, 2);
553 height
= RoundUp(height
, 2);
556 const gfx::Size subsample
= SampleSize(format
, plane
);
557 DCHECK(width
% subsample
.width() == 0);
558 DCHECK(height
% subsample
.height() == 0);
559 return gfx::Size(BytesPerElement(format
, plane
) * width
/ subsample
.width(),
560 height
/ subsample
.height());
564 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format
,
566 DCHECK(IsValidPlane(plane
, format
));
567 const int bits_per_element
= 8 * BytesPerElement(format
, plane
);
568 const int horiz_pixels_per_element
= SampleSize(format
, plane
).width();
569 DCHECK_EQ(bits_per_element
% horiz_pixels_per_element
, 0);
570 return bits_per_element
/ horiz_pixels_per_element
;
574 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format
, size_t plane
) {
575 DCHECK(IsValidPlane(plane
, format
));
576 return PlaneHorizontalBitsPerPixel(format
, plane
) /
577 SampleSize(format
, plane
).height();
581 size_t VideoFrame::RowBytes(size_t plane
, VideoPixelFormat format
, int width
) {
582 DCHECK(IsValidPlane(plane
, format
));
583 return BytesPerElement(format
, plane
) * Columns(plane
, format
, width
);
587 size_t VideoFrame::Rows(size_t plane
, VideoPixelFormat format
, int height
) {
588 DCHECK(IsValidPlane(plane
, format
));
589 const int sample_height
= SampleSize(format
, plane
).height();
590 return RoundUp(height
, sample_height
) / sample_height
;
594 size_t VideoFrame::Columns(size_t plane
, VideoPixelFormat format
, int width
) {
595 DCHECK(IsValidPlane(plane
, format
));
596 const int sample_width
= SampleSize(format
, plane
).width();
597 return RoundUp(width
, sample_width
) / sample_width
;
601 void VideoFrame::HashFrameForTesting(base::MD5Context
* context
,
602 const scoped_refptr
<VideoFrame
>& frame
) {
604 for (size_t plane
= 0; plane
< NumPlanes(frame
->format()); ++plane
) {
605 for (int row
= 0; row
< frame
->rows(plane
); ++row
) {
608 base::StringPiece(reinterpret_cast<char*>(frame
->data(plane
) +
609 frame
->stride(plane
) * row
),
610 frame
->row_bytes(plane
)));
615 bool VideoFrame::IsMappable() const {
616 return IsStorageTypeMappable(storage_type_
);
619 bool VideoFrame::HasTextures() const {
620 return !mailbox_holders_
[0].mailbox
.IsZero();
623 int VideoFrame::stride(size_t plane
) const {
624 DCHECK(IsValidPlane(plane
, format_
));
625 return strides_
[plane
];
628 int VideoFrame::row_bytes(size_t plane
) const {
629 return RowBytes(plane
, format_
, coded_size_
.width());
632 int VideoFrame::rows(size_t plane
) const {
633 return Rows(plane
, format_
, coded_size_
.height());
636 const uint8
* VideoFrame::data(size_t plane
) const {
637 DCHECK(IsValidPlane(plane
, format_
));
638 DCHECK(IsMappable());
642 uint8
* VideoFrame::data(size_t plane
) {
643 DCHECK(IsValidPlane(plane
, format_
));
644 DCHECK(IsMappable());
648 const uint8
* VideoFrame::visible_data(size_t plane
) const {
649 DCHECK(IsValidPlane(plane
, format_
));
650 DCHECK(IsMappable());
652 // Calculate an offset that is properly aligned for all planes.
653 const gfx::Size alignment
= CommonAlignment(format_
);
654 const gfx::Point
offset(RoundDown(visible_rect_
.x(), alignment
.width()),
655 RoundDown(visible_rect_
.y(), alignment
.height()));
657 const gfx::Size subsample
= SampleSize(format_
, plane
);
658 DCHECK(offset
.x() % subsample
.width() == 0);
659 DCHECK(offset
.y() % subsample
.height() == 0);
661 stride(plane
) * (offset
.y() / subsample
.height()) + // Row offset.
662 BytesPerElement(format_
, plane
) * // Column offset.
663 (offset
.x() / subsample
.width());
666 uint8
* VideoFrame::visible_data(size_t plane
) {
667 return const_cast<uint8
*>(
668 static_cast<const VideoFrame
*>(this)->visible_data(plane
));
671 const gpu::MailboxHolder
&
672 VideoFrame::mailbox_holder(size_t texture_index
) const {
673 DCHECK(HasTextures());
674 DCHECK(IsValidPlane(texture_index
, format_
));
675 return mailbox_holders_
[texture_index
];
678 base::SharedMemoryHandle
VideoFrame::shared_memory_handle() const {
679 DCHECK_EQ(storage_type_
, STORAGE_SHMEM
);
680 DCHECK(shared_memory_handle_
!= base::SharedMemory::NULLHandle());
681 return shared_memory_handle_
;
684 size_t VideoFrame::shared_memory_offset() const {
685 DCHECK_EQ(storage_type_
, STORAGE_SHMEM
);
686 DCHECK(shared_memory_handle_
!= base::SharedMemory::NULLHandle());
687 return shared_memory_offset_
;
690 #if defined(OS_LINUX)
691 int VideoFrame::dmabuf_fd(size_t plane
) const {
692 DCHECK_EQ(storage_type_
, STORAGE_DMABUFS
);
693 DCHECK(IsValidPlane(plane
, format_
));
694 return dmabuf_fds_
[plane
].get();
697 bool VideoFrame::DuplicateFileDescriptors(const std::vector
<int>& in_fds
) {
698 // TODO(mcasas): Support offsets for e.g. multiplanar inside a single |in_fd|.
700 storage_type_
= STORAGE_DMABUFS
;
701 // TODO(posciak): This is not exactly correct, it's possible for one
702 // buffer to contain more than one plane.
703 if (in_fds
.size() != NumPlanes(format_
)) {
704 LOG(FATAL
) << "Not enough dmabuf fds provided, got: " << in_fds
.size()
705 << ", expected: " << NumPlanes(format_
);
709 // Make sure that all fds are closed if any dup() fails,
710 base::ScopedFD temp_dmabuf_fds
[kMaxPlanes
];
711 for (size_t i
= 0; i
< in_fds
.size(); ++i
) {
712 temp_dmabuf_fds
[i
] = base::ScopedFD(HANDLE_EINTR(dup(in_fds
[i
])));
713 if (!temp_dmabuf_fds
[i
].is_valid()) {
714 DPLOG(ERROR
) << "Failed duplicating a dmabuf fd";
718 for (size_t i
= 0; i
< kMaxPlanes
; ++i
)
719 dmabuf_fds_
[i
].reset(temp_dmabuf_fds
[i
].release());
725 void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle
) {
726 storage_type_
= STORAGE_SHMEM
;
727 shared_memory_handle_
= handle
;
730 #if defined(OS_MACOSX)
731 CVPixelBufferRef
VideoFrame::cv_pixel_buffer() const {
732 return cv_pixel_buffer_
.get();
736 void VideoFrame::AddDestructionObserver(const base::Closure
& callback
) {
737 DCHECK(!callback
.is_null());
738 done_callbacks_
.push_back(callback
);
741 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient
* client
) {
742 DCHECK(HasTextures());
743 base::AutoLock
locker(release_sync_point_lock_
);
744 // Must wait on the previous sync point before inserting a new sync point so
745 // that |mailbox_holders_release_cb_| guarantees the previous sync point
746 // occurred when it waits on |release_sync_point_|.
747 if (release_sync_point_
)
748 client
->WaitSyncPoint(release_sync_point_
);
749 release_sync_point_
= client
->InsertSyncPoint();
753 scoped_refptr
<VideoFrame
> VideoFrame::WrapExternalStorage(
754 VideoPixelFormat format
,
755 StorageType storage_type
,
756 const gfx::Size
& coded_size
,
757 const gfx::Rect
& visible_rect
,
758 const gfx::Size
& natural_size
,
761 base::TimeDelta timestamp
,
762 base::SharedMemoryHandle handle
,
763 size_t data_offset
) {
764 DCHECK(IsStorageTypeMappable(storage_type
));
766 if (format
!= PIXEL_FORMAT_I420
) {
767 DLOG(ERROR
) << "Only PIXEL_FORMAT_I420 format supported: "
768 << VideoPixelFormatToString(format
);
772 if (!IsValidConfig(format
, storage_type
, coded_size
, visible_rect
,
774 DLOG(ERROR
) << __FUNCTION__
<< " Invalid config."
775 << ConfigToString(format
, storage_type
, coded_size
,
776 visible_rect
, natural_size
);
780 scoped_refptr
<VideoFrame
> frame
;
781 if (storage_type
== STORAGE_SHMEM
) {
782 frame
= new VideoFrame(format
, storage_type
, coded_size
, visible_rect
,
783 natural_size
, timestamp
, handle
, data_offset
);
785 frame
= new VideoFrame(format
, storage_type
, coded_size
, visible_rect
,
786 natural_size
, timestamp
);
788 frame
->strides_
[kYPlane
] = coded_size
.width();
789 frame
->strides_
[kUPlane
] = coded_size
.width() / 2;
790 frame
->strides_
[kVPlane
] = coded_size
.width() / 2;
791 frame
->data_
[kYPlane
] = data
;
792 frame
->data_
[kUPlane
] = data
+ coded_size
.GetArea();
793 frame
->data_
[kVPlane
] = data
+ (coded_size
.GetArea() * 5 / 4);
797 VideoFrame::VideoFrame(VideoPixelFormat format
,
798 StorageType storage_type
,
799 const gfx::Size
& coded_size
,
800 const gfx::Rect
& visible_rect
,
801 const gfx::Size
& natural_size
,
802 base::TimeDelta timestamp
)
804 storage_type_(storage_type
),
805 coded_size_(coded_size
),
806 visible_rect_(visible_rect
),
807 natural_size_(natural_size
),
808 shared_memory_handle_(base::SharedMemory::NULLHandle()),
809 shared_memory_offset_(0),
810 timestamp_(timestamp
),
811 release_sync_point_(0) {
812 DCHECK(IsValidConfig(format_
, storage_type
, coded_size_
, visible_rect_
,
814 memset(&mailbox_holders_
, 0, sizeof(mailbox_holders_
));
815 memset(&strides_
, 0, sizeof(strides_
));
816 memset(&data_
, 0, sizeof(data_
));
819 VideoFrame::VideoFrame(VideoPixelFormat format
,
820 StorageType storage_type
,
821 const gfx::Size
& coded_size
,
822 const gfx::Rect
& visible_rect
,
823 const gfx::Size
& natural_size
,
824 base::TimeDelta timestamp
,
825 base::SharedMemoryHandle handle
,
826 size_t shared_memory_offset
)
833 DCHECK_EQ(storage_type
, STORAGE_SHMEM
);
834 AddSharedMemoryHandle(handle
);
835 shared_memory_offset_
= shared_memory_offset
;
838 VideoFrame::VideoFrame(VideoPixelFormat format
,
839 StorageType storage_type
,
840 const gfx::Size
& coded_size
,
841 const gfx::Rect
& visible_rect
,
842 const gfx::Size
& natural_size
,
843 const gpu::MailboxHolder(&mailbox_holders
)[kMaxPlanes
],
844 const ReleaseMailboxCB
& mailbox_holder_release_cb
,
845 base::TimeDelta timestamp
)
852 memcpy(&mailbox_holders_
, mailbox_holders
, sizeof(mailbox_holders_
));
853 mailbox_holders_release_cb_
= mailbox_holder_release_cb
;
856 VideoFrame::~VideoFrame() {
857 if (!mailbox_holders_release_cb_
.is_null()) {
858 uint32 release_sync_point
;
860 // To ensure that changes to |release_sync_point_| are visible on this
861 // thread (imply a memory barrier).
862 base::AutoLock
locker(release_sync_point_lock_
);
863 release_sync_point
= release_sync_point_
;
865 base::ResetAndReturn(&mailbox_holders_release_cb_
).Run(release_sync_point
);
868 for (auto& callback
: done_callbacks_
)
869 base::ResetAndReturn(&callback
).Run();
872 void VideoFrame::AllocateYUV() {
873 DCHECK_EQ(storage_type_
, STORAGE_OWNED_MEMORY
);
874 static_assert(0 == kYPlane
, "y plane data must be index 0");
876 size_t data_size
= 0;
877 size_t offset
[kMaxPlanes
];
878 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
) {
879 // The *2 in alignment for height is because some formats (e.g. h264) allow
880 // interlaced coding, and then the size needs to be a multiple of two
881 // macroblocks (vertically). See
882 // libavcodec/utils.c:avcodec_align_dimensions2().
883 const size_t height
= RoundUp(rows(plane
), kFrameSizeAlignment
* 2);
884 strides_
[plane
] = RoundUp(row_bytes(plane
), kFrameSizeAlignment
);
885 offset
[plane
] = data_size
;
886 data_size
+= height
* strides_
[plane
];
889 // The extra line of UV being allocated is because h264 chroma MC
890 // overreads by one line in some cases, see libavcodec/utils.c:
891 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
892 // put_h264_chroma_mc4_ssse3().
893 DCHECK(IsValidPlane(kUPlane
, format_
));
894 data_size
+= strides_
[kUPlane
] + kFrameSizePadding
;
896 uint8
* data
= reinterpret_cast<uint8
*>(
897 base::AlignedAlloc(data_size
, kFrameAddressAlignment
));
899 for (size_t plane
= 0; plane
< NumPlanes(format_
); ++plane
)
900 data_
[plane
] = data
+ offset
[plane
];
902 AddDestructionObserver(base::Bind(&base::AlignedFree
, data
));