Update V8 to version 4.7.56.
[chromium-blink-merge.git] / media / base / video_frame.cc
bloba8736de7a766936b934b471d4beff6f5134788b5
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"
7 #include <algorithm>
9 #include "base/bind.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/timestamp_constants.h"
17 #include "media/base/video_util.h"
18 #include "ui/gfx/geometry/point.h"
20 namespace media {
22 static bool IsPowerOfTwo(size_t x) {
23 return x != 0 && (x & (x - 1)) == 0;
26 static inline size_t RoundUp(size_t value, size_t alignment) {
27 DCHECK(IsPowerOfTwo(alignment));
28 return ((value + (alignment - 1)) & ~(alignment - 1));
31 static inline size_t RoundDown(size_t value, size_t alignment) {
32 DCHECK(IsPowerOfTwo(alignment));
33 return value & ~(alignment - 1);
36 static std::string ConfigToString(const VideoPixelFormat format,
37 const VideoFrame::StorageType storage_type,
38 const gfx::Size& coded_size,
39 const gfx::Rect& visible_rect,
40 const gfx::Size& natural_size) {
41 return base::StringPrintf(
42 "format:%s coded_size:%s visible_rect:%s natural_size:%s",
43 VideoPixelFormatToString(format).c_str(), coded_size.ToString().c_str(),
44 visible_rect.ToString().c_str(), natural_size.ToString().c_str());
47 // Returns true if |plane| is a valid plane index for the given |format|.
48 static bool IsValidPlane(size_t plane, VideoPixelFormat format) {
49 DCHECK_LE(VideoFrame::NumPlanes(format),
50 static_cast<size_t>(VideoFrame::kMaxPlanes));
51 return (plane < VideoFrame::NumPlanes(format));
54 // Returns true if |frame| is accesible mapped in the VideoFrame memory space.
55 // static
56 static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type) {
57 return
58 #if defined(OS_LINUX)
59 // This is not strictly needed but makes explicit that, at VideoFrame
60 // level, DmaBufs are not mappable from userspace.
61 storage_type != VideoFrame::STORAGE_DMABUFS &&
62 #endif
63 (storage_type == VideoFrame::STORAGE_UNOWNED_MEMORY ||
64 storage_type == VideoFrame::STORAGE_OWNED_MEMORY ||
65 storage_type == VideoFrame::STORAGE_SHMEM);
68 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
69 // for the U-plane in PIXEL_FORMAT_I420.
70 static gfx::Size SampleSize(VideoPixelFormat format, size_t plane) {
71 DCHECK(IsValidPlane(plane, format));
73 switch (plane) {
74 case VideoFrame::kYPlane:
75 case VideoFrame::kAPlane:
76 return gfx::Size(1, 1);
78 case VideoFrame::kUPlane: // and VideoFrame::kUVPlane:
79 case VideoFrame::kVPlane:
80 switch (format) {
81 case PIXEL_FORMAT_YV24:
82 return gfx::Size(1, 1);
84 case PIXEL_FORMAT_YV16:
85 return gfx::Size(2, 1);
87 case PIXEL_FORMAT_YV12:
88 case PIXEL_FORMAT_I420:
89 case PIXEL_FORMAT_YV12A:
90 case PIXEL_FORMAT_NV12:
91 case PIXEL_FORMAT_NV21:
92 case PIXEL_FORMAT_MT21:
93 return gfx::Size(2, 2);
95 case PIXEL_FORMAT_UNKNOWN:
96 case PIXEL_FORMAT_UYVY:
97 case PIXEL_FORMAT_YUY2:
98 case PIXEL_FORMAT_ARGB:
99 case PIXEL_FORMAT_XRGB:
100 case PIXEL_FORMAT_RGB24:
101 case PIXEL_FORMAT_RGB32:
102 case PIXEL_FORMAT_MJPEG:
103 break;
106 NOTREACHED();
107 return gfx::Size();
110 // Return the alignment for the whole frame, calculated as the max of the
111 // alignment for each individual plane.
112 static gfx::Size CommonAlignment(VideoPixelFormat format) {
113 int max_sample_width = 0;
114 int max_sample_height = 0;
115 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) {
116 const gfx::Size sample_size = SampleSize(format, plane);
117 max_sample_width = std::max(max_sample_width, sample_size.width());
118 max_sample_height = std::max(max_sample_height, sample_size.height());
120 return gfx::Size(max_sample_width, max_sample_height);
123 // Returns the number of bytes per element for given |plane| and |format|.
124 static int BytesPerElement(VideoPixelFormat format, size_t plane) {
125 DCHECK(IsValidPlane(plane, format));
126 switch (format) {
127 case PIXEL_FORMAT_ARGB:
128 case PIXEL_FORMAT_XRGB:
129 case PIXEL_FORMAT_RGB32:
130 return 4;
131 case PIXEL_FORMAT_RGB24:
132 return 3;
133 case PIXEL_FORMAT_UYVY:
134 case PIXEL_FORMAT_YUY2:
135 return 2;
136 case PIXEL_FORMAT_NV12:
137 case PIXEL_FORMAT_NV21:
138 case PIXEL_FORMAT_MT21: {
139 static const int bytes_per_element[] = {1, 2};
140 DCHECK_LT(plane, arraysize(bytes_per_element));
141 return bytes_per_element[plane];
143 case PIXEL_FORMAT_YV12:
144 case PIXEL_FORMAT_I420:
145 case PIXEL_FORMAT_YV16:
146 case PIXEL_FORMAT_YV12A:
147 case PIXEL_FORMAT_YV24:
148 return 1;
149 case PIXEL_FORMAT_MJPEG:
150 return 0;
151 case PIXEL_FORMAT_UNKNOWN:
152 break;
154 NOTREACHED();
155 return 0;
158 // static
159 bool VideoFrame::IsValidConfig(VideoPixelFormat format,
160 StorageType storage_type,
161 const gfx::Size& coded_size,
162 const gfx::Rect& visible_rect,
163 const gfx::Size& natural_size) {
164 // Check maximum limits for all formats.
165 if (coded_size.GetArea() > limits::kMaxCanvas ||
166 coded_size.width() > limits::kMaxDimension ||
167 coded_size.height() > limits::kMaxDimension ||
168 visible_rect.x() < 0 || visible_rect.y() < 0 ||
169 visible_rect.right() > coded_size.width() ||
170 visible_rect.bottom() > coded_size.height() ||
171 natural_size.GetArea() > limits::kMaxCanvas ||
172 natural_size.width() > limits::kMaxDimension ||
173 natural_size.height() > limits::kMaxDimension)
174 return false;
176 // TODO(mcasas): Remove parameter |storage_type| when the opaque storage types
177 // comply with the checks below. Right now we skip them.
178 if (!IsStorageTypeMappable(storage_type))
179 return true;
181 // Make sure new formats are properly accounted for in the method.
182 static_assert(PIXEL_FORMAT_MAX == 15,
183 "Added pixel format, please review IsValidConfig()");
185 if (format == PIXEL_FORMAT_UNKNOWN) {
186 return coded_size.IsEmpty() && visible_rect.IsEmpty() &&
187 natural_size.IsEmpty();
190 // Check that software-allocated buffer formats are not empty.
191 return !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
192 !natural_size.IsEmpty();
195 // static
196 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(VideoPixelFormat format,
197 const gfx::Size& coded_size,
198 const gfx::Rect& visible_rect,
199 const gfx::Size& natural_size,
200 base::TimeDelta timestamp) {
201 return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
202 timestamp, false);
205 // static
206 scoped_refptr<VideoFrame> VideoFrame::CreateZeroInitializedFrame(
207 VideoPixelFormat format,
208 const gfx::Size& coded_size,
209 const gfx::Rect& visible_rect,
210 const gfx::Size& natural_size,
211 base::TimeDelta timestamp) {
212 return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
213 timestamp, true);
216 // static
217 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
218 VideoPixelFormat format,
219 const gpu::MailboxHolder& mailbox_holder,
220 const ReleaseMailboxCB& mailbox_holder_release_cb,
221 const gfx::Size& coded_size,
222 const gfx::Rect& visible_rect,
223 const gfx::Size& natural_size,
224 base::TimeDelta timestamp) {
225 if (format != PIXEL_FORMAT_ARGB &&
226 format != PIXEL_FORMAT_UYVY &&
227 format != PIXEL_FORMAT_NV12) {
228 DLOG(ERROR) << "Unsupported pixel format supported, got "
229 << VideoPixelFormatToString(format);
230 return nullptr;
232 const StorageType storage = STORAGE_OPAQUE;
233 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
234 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
235 << ConfigToString(format, storage, coded_size, visible_rect,
236 natural_size);
237 return nullptr;
240 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
241 mailbox_holders[kARGBPlane] = mailbox_holder;
242 return new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
243 mailbox_holders, mailbox_holder_release_cb, timestamp);
246 // static
247 scoped_refptr<VideoFrame> VideoFrame::WrapYUV420NativeTextures(
248 const gpu::MailboxHolder& y_mailbox_holder,
249 const gpu::MailboxHolder& u_mailbox_holder,
250 const gpu::MailboxHolder& v_mailbox_holder,
251 const ReleaseMailboxCB& mailbox_holder_release_cb,
252 const gfx::Size& coded_size,
253 const gfx::Rect& visible_rect,
254 const gfx::Size& natural_size,
255 base::TimeDelta timestamp) {
256 const StorageType storage = STORAGE_OPAQUE;
257 VideoPixelFormat format = PIXEL_FORMAT_I420;
258 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
259 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
260 << ConfigToString(format, storage, coded_size, visible_rect,
261 natural_size);
262 return nullptr;
265 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
266 mailbox_holders[kYPlane] = y_mailbox_holder;
267 mailbox_holders[kUPlane] = u_mailbox_holder;
268 mailbox_holders[kVPlane] = v_mailbox_holder;
269 return new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
270 mailbox_holders, mailbox_holder_release_cb, timestamp);
273 // static
274 scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
275 VideoPixelFormat format,
276 const gfx::Size& coded_size,
277 const gfx::Rect& visible_rect,
278 const gfx::Size& natural_size,
279 uint8* data,
280 size_t data_size,
281 base::TimeDelta timestamp) {
282 return WrapExternalStorage(format, STORAGE_UNOWNED_MEMORY, coded_size,
283 visible_rect, natural_size, data, data_size,
284 timestamp, base::SharedMemory::NULLHandle(), 0);
287 // static
288 scoped_refptr<VideoFrame> VideoFrame::WrapExternalSharedMemory(
289 VideoPixelFormat format,
290 const gfx::Size& coded_size,
291 const gfx::Rect& visible_rect,
292 const gfx::Size& natural_size,
293 uint8* data,
294 size_t data_size,
295 base::SharedMemoryHandle handle,
296 size_t data_offset,
297 base::TimeDelta timestamp) {
298 return WrapExternalStorage(format, STORAGE_SHMEM, coded_size, visible_rect,
299 natural_size, data, data_size, timestamp, handle,
300 data_offset);
303 // static
304 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
305 VideoPixelFormat format,
306 const gfx::Size& coded_size,
307 const gfx::Rect& visible_rect,
308 const gfx::Size& natural_size,
309 int32 y_stride,
310 int32 u_stride,
311 int32 v_stride,
312 uint8* y_data,
313 uint8* u_data,
314 uint8* v_data,
315 base::TimeDelta timestamp) {
316 const StorageType storage = STORAGE_UNOWNED_MEMORY;
317 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
318 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
319 << ConfigToString(format, storage, coded_size, visible_rect,
320 natural_size);
321 return nullptr;
324 scoped_refptr<VideoFrame> frame(new VideoFrame(
325 format, storage, coded_size, visible_rect, natural_size, timestamp));
326 frame->strides_[kYPlane] = y_stride;
327 frame->strides_[kUPlane] = u_stride;
328 frame->strides_[kVPlane] = v_stride;
329 frame->data_[kYPlane] = y_data;
330 frame->data_[kUPlane] = u_data;
331 frame->data_[kVPlane] = v_data;
332 return frame;
335 #if defined(OS_LINUX)
336 // static
337 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
338 VideoPixelFormat format,
339 const gfx::Size& coded_size,
340 const gfx::Rect& visible_rect,
341 const gfx::Size& natural_size,
342 const std::vector<int>& dmabuf_fds,
343 base::TimeDelta timestamp) {
344 #if defined(OS_CHROMEOS)
345 DCHECK_EQ(format, PIXEL_FORMAT_NV12);
346 #endif
348 const StorageType storage = STORAGE_DMABUFS;
349 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
350 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
351 << ConfigToString(format, storage, coded_size, visible_rect,
352 natural_size);
353 return nullptr;
356 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
357 scoped_refptr<VideoFrame> frame =
358 new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
359 mailbox_holders, ReleaseMailboxCB(), timestamp);
360 if (!frame || !frame->DuplicateFileDescriptors(dmabuf_fds))
361 return nullptr;
362 return frame;
364 #endif
366 #if defined(OS_MACOSX)
367 // static
368 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
369 CVPixelBufferRef cv_pixel_buffer,
370 base::TimeDelta timestamp) {
371 DCHECK(cv_pixel_buffer);
372 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
374 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
375 VideoPixelFormat format;
376 // There are very few compatible CV pixel formats, so just check each.
377 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
378 format = PIXEL_FORMAT_I420;
379 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
380 format = PIXEL_FORMAT_YV24;
381 } else if (cv_format == '420v') {
382 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
383 // minimum OS X and iOS SDKs permits it.
384 format = PIXEL_FORMAT_NV12;
385 } else {
386 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
387 return nullptr;
390 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
391 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
392 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
393 const StorageType storage = STORAGE_UNOWNED_MEMORY;
395 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
396 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
397 << ConfigToString(format, storage, coded_size, visible_rect,
398 natural_size);
399 return nullptr;
402 scoped_refptr<VideoFrame> frame(new VideoFrame(
403 format, storage, coded_size, visible_rect, natural_size, timestamp));
405 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
406 return frame;
408 #endif
410 // static
411 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
412 const scoped_refptr<VideoFrame>& frame,
413 const gfx::Rect& visible_rect,
414 const gfx::Size& natural_size) {
415 // Frames with textures need mailbox info propagated, and there's no support
416 // for that here yet, see http://crbug/362521.
417 CHECK(!frame->HasTextures());
419 DCHECK(frame->visible_rect().Contains(visible_rect));
421 if (!IsValidConfig(frame->format(), frame->storage_type(),
422 frame->coded_size(), visible_rect, natural_size)) {
423 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
424 << ConfigToString(frame->format(), frame->storage_type(),
425 frame->coded_size(), visible_rect,
426 natural_size);
427 return nullptr;
430 scoped_refptr<VideoFrame> wrapping_frame(new VideoFrame(
431 frame->format(), frame->storage_type(), frame->coded_size(), visible_rect,
432 natural_size, frame->timestamp()));
433 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
434 wrapping_frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM,
435 true);
438 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
439 wrapping_frame->strides_[i] = frame->stride(i);
440 wrapping_frame->data_[i] = frame->data(i);
443 #if defined(OS_LINUX)
444 // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
445 if (frame->storage_type() == STORAGE_DMABUFS) {
446 std::vector<int> original_fds;
447 for (size_t i = 0; i < kMaxPlanes; ++i)
448 original_fds.push_back(frame->dmabuf_fd(i));
449 if (!wrapping_frame->DuplicateFileDescriptors(original_fds))
450 return nullptr;
452 #endif
454 return wrapping_frame;
457 // static
458 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
459 scoped_refptr<VideoFrame> frame =
460 new VideoFrame(PIXEL_FORMAT_UNKNOWN, STORAGE_UNKNOWN, gfx::Size(),
461 gfx::Rect(), gfx::Size(), kNoTimestamp());
462 frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM, true);
463 return frame;
466 // static
467 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
468 const gfx::Size& size,
469 uint8 y, uint8 u, uint8 v,
470 base::TimeDelta timestamp) {
471 scoped_refptr<VideoFrame> frame =
472 CreateFrame(PIXEL_FORMAT_YV12, size, gfx::Rect(size), size, timestamp);
473 FillYUV(frame.get(), y, u, v);
474 return frame;
477 // static
478 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
479 const uint8 kBlackY = 0x00;
480 const uint8 kBlackUV = 0x80;
481 const base::TimeDelta kZero;
482 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
485 // static
486 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
487 const gfx::Size& size) {
488 const uint8 kBlackY = 0x00;
489 const uint8 kBlackUV = 0x00;
490 const uint8 kTransparentA = 0x00;
491 const base::TimeDelta kZero;
492 scoped_refptr<VideoFrame> frame =
493 CreateFrame(PIXEL_FORMAT_YV12A, size, gfx::Rect(size), size, kZero);
494 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
495 return frame;
498 #if defined(VIDEO_HOLE)
499 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
500 // maintained by the general compositor team. Please contact
501 // wonsik@chromium.org .
503 // static
504 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
505 const gfx::Size& size) {
506 const VideoPixelFormat format = PIXEL_FORMAT_UNKNOWN;
507 const StorageType storage = STORAGE_HOLE;
508 const gfx::Rect visible_rect = gfx::Rect(size);
509 if (!IsValidConfig(format, storage, size, visible_rect, size)) {
510 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
511 << ConfigToString(format, storage, size, visible_rect, size);
512 return nullptr;
514 scoped_refptr<VideoFrame> frame(new VideoFrame(
515 format, storage, size, gfx::Rect(size), size, base::TimeDelta()));
516 return frame;
518 #endif // defined(VIDEO_HOLE)
520 // static
521 size_t VideoFrame::NumPlanes(VideoPixelFormat format) {
522 switch (format) {
523 case PIXEL_FORMAT_UYVY:
524 case PIXEL_FORMAT_YUY2:
525 case PIXEL_FORMAT_ARGB:
526 case PIXEL_FORMAT_XRGB:
527 case PIXEL_FORMAT_RGB24:
528 case PIXEL_FORMAT_RGB32:
529 case PIXEL_FORMAT_MJPEG:
530 return 1;
531 case PIXEL_FORMAT_NV12:
532 case PIXEL_FORMAT_NV21:
533 case PIXEL_FORMAT_MT21:
534 return 2;
535 case PIXEL_FORMAT_I420:
536 case PIXEL_FORMAT_YV12:
537 case PIXEL_FORMAT_YV16:
538 case PIXEL_FORMAT_YV24:
539 return 3;
540 case PIXEL_FORMAT_YV12A:
541 return 4;
542 case PIXEL_FORMAT_UNKNOWN:
543 break;
545 NOTREACHED() << "Unsupported video frame format: " << format;
546 return 0;
549 // static
550 size_t VideoFrame::AllocationSize(VideoPixelFormat format,
551 const gfx::Size& coded_size) {
552 size_t total = 0;
553 for (size_t i = 0; i < NumPlanes(format); ++i)
554 total += PlaneSize(format, i, coded_size).GetArea();
555 return total;
558 // static
559 gfx::Size VideoFrame::PlaneSize(VideoPixelFormat format,
560 size_t plane,
561 const gfx::Size& coded_size) {
562 DCHECK(IsValidPlane(plane, format));
564 int width = coded_size.width();
565 int height = coded_size.height();
566 if (format != PIXEL_FORMAT_ARGB) {
567 // Align to multiple-of-two size overall. This ensures that non-subsampled
568 // planes can be addressed by pixel with the same scaling as the subsampled
569 // planes.
570 width = RoundUp(width, 2);
571 height = RoundUp(height, 2);
574 const gfx::Size subsample = SampleSize(format, plane);
575 DCHECK(width % subsample.width() == 0);
576 DCHECK(height % subsample.height() == 0);
577 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
578 height / subsample.height());
581 // static
582 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format,
583 size_t plane) {
584 DCHECK(IsValidPlane(plane, format));
585 const int bits_per_element = 8 * BytesPerElement(format, plane);
586 const int horiz_pixels_per_element = SampleSize(format, plane).width();
587 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
588 return bits_per_element / horiz_pixels_per_element;
591 // static
592 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format, size_t plane) {
593 DCHECK(IsValidPlane(plane, format));
594 return PlaneHorizontalBitsPerPixel(format, plane) /
595 SampleSize(format, plane).height();
598 // static
599 size_t VideoFrame::RowBytes(size_t plane, VideoPixelFormat format, int width) {
600 DCHECK(IsValidPlane(plane, format));
601 return BytesPerElement(format, plane) * Columns(plane, format, width);
604 // static
605 size_t VideoFrame::Rows(size_t plane, VideoPixelFormat format, int height) {
606 DCHECK(IsValidPlane(plane, format));
607 const int sample_height = SampleSize(format, plane).height();
608 return RoundUp(height, sample_height) / sample_height;
611 // static
612 size_t VideoFrame::Columns(size_t plane, VideoPixelFormat format, int width) {
613 DCHECK(IsValidPlane(plane, format));
614 const int sample_width = SampleSize(format, plane).width();
615 return RoundUp(width, sample_width) / sample_width;
618 // static
619 void VideoFrame::HashFrameForTesting(base::MD5Context* context,
620 const scoped_refptr<VideoFrame>& frame) {
621 DCHECK(context);
622 for (size_t plane = 0; plane < NumPlanes(frame->format()); ++plane) {
623 for (int row = 0; row < frame->rows(plane); ++row) {
624 base::MD5Update(
625 context,
626 base::StringPiece(reinterpret_cast<char*>(frame->data(plane) +
627 frame->stride(plane) * row),
628 frame->row_bytes(plane)));
633 bool VideoFrame::IsMappable() const {
634 return IsStorageTypeMappable(storage_type_);
637 bool VideoFrame::HasTextures() const {
638 return !mailbox_holders_[0].mailbox.IsZero();
641 int VideoFrame::stride(size_t plane) const {
642 DCHECK(IsValidPlane(plane, format_));
643 return strides_[plane];
646 int VideoFrame::row_bytes(size_t plane) const {
647 return RowBytes(plane, format_, coded_size_.width());
650 int VideoFrame::rows(size_t plane) const {
651 return Rows(plane, format_, coded_size_.height());
654 const uint8* VideoFrame::data(size_t plane) const {
655 DCHECK(IsValidPlane(plane, format_));
656 DCHECK(IsMappable());
657 return data_[plane];
660 uint8* VideoFrame::data(size_t plane) {
661 DCHECK(IsValidPlane(plane, format_));
662 DCHECK(IsMappable());
663 return data_[plane];
666 const uint8* VideoFrame::visible_data(size_t plane) const {
667 DCHECK(IsValidPlane(plane, format_));
668 DCHECK(IsMappable());
670 // Calculate an offset that is properly aligned for all planes.
671 const gfx::Size alignment = CommonAlignment(format_);
672 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
673 RoundDown(visible_rect_.y(), alignment.height()));
675 const gfx::Size subsample = SampleSize(format_, plane);
676 DCHECK(offset.x() % subsample.width() == 0);
677 DCHECK(offset.y() % subsample.height() == 0);
678 return data(plane) +
679 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
680 BytesPerElement(format_, plane) * // Column offset.
681 (offset.x() / subsample.width());
684 uint8* VideoFrame::visible_data(size_t plane) {
685 return const_cast<uint8*>(
686 static_cast<const VideoFrame*>(this)->visible_data(plane));
689 const gpu::MailboxHolder&
690 VideoFrame::mailbox_holder(size_t texture_index) const {
691 DCHECK(HasTextures());
692 DCHECK(IsValidPlane(texture_index, format_));
693 return mailbox_holders_[texture_index];
696 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
697 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
698 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
699 return shared_memory_handle_;
702 size_t VideoFrame::shared_memory_offset() const {
703 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
704 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
705 return shared_memory_offset_;
708 #if defined(OS_LINUX)
709 int VideoFrame::dmabuf_fd(size_t plane) const {
710 DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
711 DCHECK(IsValidPlane(plane, format_));
712 return dmabuf_fds_[plane].get();
715 bool VideoFrame::DuplicateFileDescriptors(const std::vector<int>& in_fds) {
716 // TODO(mcasas): Support offsets for e.g. multiplanar inside a single |in_fd|.
718 storage_type_ = STORAGE_DMABUFS;
719 // TODO(posciak): This is not exactly correct, it's possible for one
720 // buffer to contain more than one plane.
721 if (in_fds.size() != NumPlanes(format_)) {
722 LOG(FATAL) << "Not enough dmabuf fds provided, got: " << in_fds.size()
723 << ", expected: " << NumPlanes(format_);
724 return false;
727 // Make sure that all fds are closed if any dup() fails,
728 base::ScopedFD temp_dmabuf_fds[kMaxPlanes];
729 for (size_t i = 0; i < in_fds.size(); ++i) {
730 temp_dmabuf_fds[i] = base::ScopedFD(HANDLE_EINTR(dup(in_fds[i])));
731 if (!temp_dmabuf_fds[i].is_valid()) {
732 DPLOG(ERROR) << "Failed duplicating a dmabuf fd";
733 return false;
736 for (size_t i = 0; i < kMaxPlanes; ++i)
737 dmabuf_fds_[i].reset(temp_dmabuf_fds[i].release());
739 return true;
741 #endif
743 void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle) {
744 storage_type_ = STORAGE_SHMEM;
745 shared_memory_handle_ = handle;
748 #if defined(OS_MACOSX)
749 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
750 return cv_pixel_buffer_.get();
752 #endif
754 void VideoFrame::AddDestructionObserver(const base::Closure& callback) {
755 DCHECK(!callback.is_null());
756 done_callbacks_.push_back(callback);
759 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
760 DCHECK(HasTextures());
761 base::AutoLock locker(release_sync_point_lock_);
762 // Must wait on the previous sync point before inserting a new sync point so
763 // that |mailbox_holders_release_cb_| guarantees the previous sync point
764 // occurred when it waits on |release_sync_point_|.
765 if (release_sync_point_)
766 client->WaitSyncPoint(release_sync_point_);
767 release_sync_point_ = client->InsertSyncPoint();
770 // static
771 scoped_refptr<VideoFrame> VideoFrame::WrapExternalStorage(
772 VideoPixelFormat format,
773 StorageType storage_type,
774 const gfx::Size& coded_size,
775 const gfx::Rect& visible_rect,
776 const gfx::Size& natural_size,
777 uint8* data,
778 size_t data_size,
779 base::TimeDelta timestamp,
780 base::SharedMemoryHandle handle,
781 size_t data_offset) {
782 DCHECK(IsStorageTypeMappable(storage_type));
784 if (format != PIXEL_FORMAT_I420) {
785 DLOG(ERROR) << "Only PIXEL_FORMAT_I420 format supported: "
786 << VideoPixelFormatToString(format);
787 return nullptr;
790 if (!IsValidConfig(format, storage_type, coded_size, visible_rect,
791 natural_size)) {
792 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
793 << ConfigToString(format, storage_type, coded_size,
794 visible_rect, natural_size);
795 return nullptr;
798 scoped_refptr<VideoFrame> frame;
799 if (storage_type == STORAGE_SHMEM) {
800 frame = new VideoFrame(format, storage_type, coded_size, visible_rect,
801 natural_size, timestamp, handle, data_offset);
802 } else {
803 frame = new VideoFrame(format, storage_type, coded_size, visible_rect,
804 natural_size, timestamp);
806 frame->strides_[kYPlane] = coded_size.width();
807 frame->strides_[kUPlane] = coded_size.width() / 2;
808 frame->strides_[kVPlane] = coded_size.width() / 2;
809 frame->data_[kYPlane] = data;
810 frame->data_[kUPlane] = data + coded_size.GetArea();
811 frame->data_[kVPlane] = data + (coded_size.GetArea() * 5 / 4);
812 return frame;
815 VideoFrame::VideoFrame(VideoPixelFormat format,
816 StorageType storage_type,
817 const gfx::Size& coded_size,
818 const gfx::Rect& visible_rect,
819 const gfx::Size& natural_size,
820 base::TimeDelta timestamp)
821 : format_(format),
822 storage_type_(storage_type),
823 coded_size_(coded_size),
824 visible_rect_(visible_rect),
825 natural_size_(natural_size),
826 shared_memory_handle_(base::SharedMemory::NULLHandle()),
827 shared_memory_offset_(0),
828 timestamp_(timestamp),
829 release_sync_point_(0) {
830 DCHECK(IsValidConfig(format_, storage_type, coded_size_, visible_rect_,
831 natural_size_));
832 memset(&mailbox_holders_, 0, sizeof(mailbox_holders_));
833 memset(&strides_, 0, sizeof(strides_));
834 memset(&data_, 0, sizeof(data_));
837 VideoFrame::VideoFrame(VideoPixelFormat format,
838 StorageType storage_type,
839 const gfx::Size& coded_size,
840 const gfx::Rect& visible_rect,
841 const gfx::Size& natural_size,
842 base::TimeDelta timestamp,
843 base::SharedMemoryHandle handle,
844 size_t shared_memory_offset)
845 : VideoFrame(format,
846 storage_type,
847 coded_size,
848 visible_rect,
849 natural_size,
850 timestamp) {
851 DCHECK_EQ(storage_type, STORAGE_SHMEM);
852 AddSharedMemoryHandle(handle);
853 shared_memory_offset_ = shared_memory_offset;
856 VideoFrame::VideoFrame(VideoPixelFormat format,
857 StorageType storage_type,
858 const gfx::Size& coded_size,
859 const gfx::Rect& visible_rect,
860 const gfx::Size& natural_size,
861 const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
862 const ReleaseMailboxCB& mailbox_holder_release_cb,
863 base::TimeDelta timestamp)
864 : VideoFrame(format,
865 storage_type,
866 coded_size,
867 visible_rect,
868 natural_size,
869 timestamp) {
870 memcpy(&mailbox_holders_, mailbox_holders, sizeof(mailbox_holders_));
871 mailbox_holders_release_cb_ = mailbox_holder_release_cb;
874 VideoFrame::~VideoFrame() {
875 if (!mailbox_holders_release_cb_.is_null()) {
876 uint32 release_sync_point;
878 // To ensure that changes to |release_sync_point_| are visible on this
879 // thread (imply a memory barrier).
880 base::AutoLock locker(release_sync_point_lock_);
881 release_sync_point = release_sync_point_;
883 base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_point);
886 for (auto& callback : done_callbacks_)
887 base::ResetAndReturn(&callback).Run();
890 // static
891 scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal(
892 VideoPixelFormat format,
893 const gfx::Size& coded_size,
894 const gfx::Rect& visible_rect,
895 const gfx::Size& natural_size,
896 base::TimeDelta timestamp,
897 bool zero_initialize_memory) {
898 if (!IsYuvPlanar(format)) {
899 NOTIMPLEMENTED();
900 return nullptr;
903 // Since we're creating a new YUV frame (and allocating memory for it
904 // ourselves), we can pad the requested |coded_size| if necessary if the
905 // request does not line up on sample boundaries. See discussion at
906 // http://crrev.com/1240833003
907 const gfx::Size alignment = CommonAlignment(format);
908 const gfx::Size new_coded_size =
909 gfx::Size(RoundUp(coded_size.width(), alignment.width()),
910 RoundUp(coded_size.height(), alignment.height()));
911 DCHECK((new_coded_size.width() % alignment.width() == 0) &&
912 (new_coded_size.height() % alignment.height() == 0));
914 const StorageType storage = STORAGE_OWNED_MEMORY;
915 if (!IsValidConfig(format, storage, new_coded_size, visible_rect,
916 natural_size)) {
917 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
918 << ConfigToString(format, storage, coded_size, visible_rect,
919 natural_size);
920 return nullptr;
923 scoped_refptr<VideoFrame> frame(new VideoFrame(
924 format, storage, new_coded_size, visible_rect, natural_size, timestamp));
925 frame->AllocateYUV(zero_initialize_memory);
926 return frame;
929 void VideoFrame::AllocateYUV(bool zero_initialize_memory) {
930 DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
931 static_assert(0 == kYPlane, "y plane data must be index 0");
933 size_t data_size = 0;
934 size_t offset[kMaxPlanes];
935 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
936 // The *2 in alignment for height is because some formats (e.g. h264) allow
937 // interlaced coding, and then the size needs to be a multiple of two
938 // macroblocks (vertically). See
939 // libavcodec/utils.c:avcodec_align_dimensions2().
940 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2);
941 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment);
942 offset[plane] = data_size;
943 data_size += height * strides_[plane];
946 // The extra line of UV being allocated is because h264 chroma MC
947 // overreads by one line in some cases, see libavcodec/utils.c:
948 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
949 // put_h264_chroma_mc4_ssse3().
950 DCHECK(IsValidPlane(kUPlane, format_));
951 data_size += strides_[kUPlane] + kFrameSizePadding;
953 uint8* data = reinterpret_cast<uint8*>(
954 base::AlignedAlloc(data_size, kFrameAddressAlignment));
955 if (zero_initialize_memory)
956 memset(data, 0, data_size);
958 for (size_t plane = 0; plane < NumPlanes(format_); ++plane)
959 data_[plane] = data + offset[plane];
961 AddDestructionObserver(base::Bind(&base::AlignedFree, data));
964 } // namespace media