Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / media / base / video_frame.cc
blob51269a8b6076736e3b1c03e6fd7e46635901c956
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 "media/base/limits.h"
15 #include "media/base/video_util.h"
16 #include "ui/gfx/geometry/point.h"
18 namespace media {
20 static bool IsPowerOfTwo(size_t x) {
21 return x != 0 && (x & (x - 1)) == 0;
24 static inline size_t RoundUp(size_t value, size_t alignment) {
25 DCHECK(IsPowerOfTwo(alignment));
26 return ((value + (alignment - 1)) & ~(alignment - 1));
29 static inline size_t RoundDown(size_t value, size_t alignment) {
30 DCHECK(IsPowerOfTwo(alignment));
31 return value & ~(alignment - 1);
34 // Returns true if |plane| is a valid plane index for the given |format|.
35 static bool IsValidPlane(size_t plane, VideoPixelFormat format) {
36 DCHECK_LE(VideoFrame::NumPlanes(format),
37 static_cast<size_t>(VideoFrame::kMaxPlanes));
38 return (plane < VideoFrame::NumPlanes(format));
41 // Returns true if |frame| is accesible mapped in the VideoFrame memory space.
42 // static
43 static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type) {
44 return
45 #if defined(OS_LINUX)
46 // This is not strictly needed but makes explicit that, at VideoFrame
47 // level, DmaBufs are not mappable from userspace.
48 storage_type != VideoFrame::STORAGE_DMABUFS &&
49 #endif
50 (storage_type == VideoFrame::STORAGE_UNOWNED_MEMORY ||
51 storage_type == VideoFrame::STORAGE_OWNED_MEMORY ||
52 storage_type == VideoFrame::STORAGE_SHMEM);
55 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
56 // for the U-plane in PIXEL_FORMAT_I420.
57 static gfx::Size SampleSize(VideoPixelFormat format, size_t plane) {
58 DCHECK(IsValidPlane(plane, format));
60 switch (plane) {
61 case VideoFrame::kYPlane:
62 case VideoFrame::kAPlane:
63 return gfx::Size(1, 1);
65 case VideoFrame::kUPlane: // and VideoFrame::kUVPlane:
66 case VideoFrame::kVPlane:
67 switch (format) {
68 case PIXEL_FORMAT_YV24:
69 return gfx::Size(1, 1);
71 case PIXEL_FORMAT_YV16:
72 return gfx::Size(2, 1);
74 case PIXEL_FORMAT_YV12:
75 case PIXEL_FORMAT_I420:
76 case PIXEL_FORMAT_YV12A:
77 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
78 case PIXEL_FORMAT_NV12:
79 #endif
80 return gfx::Size(2, 2);
82 case PIXEL_FORMAT_UNKNOWN:
83 case PIXEL_FORMAT_ARGB:
84 case PIXEL_FORMAT_XRGB:
85 break;
88 NOTREACHED();
89 return gfx::Size();
92 // Return the alignment for the whole frame, calculated as the max of the
93 // alignment for each individual plane.
94 static gfx::Size CommonAlignment(VideoPixelFormat format) {
95 int max_sample_width = 0;
96 int max_sample_height = 0;
97 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) {
98 const gfx::Size sample_size = SampleSize(format, plane);
99 max_sample_width = std::max(max_sample_width, sample_size.width());
100 max_sample_height = std::max(max_sample_height, sample_size.height());
102 return gfx::Size(max_sample_width, max_sample_height);
105 // Returns the number of bytes per element for given |plane| and |format|.
106 static int BytesPerElement(VideoPixelFormat format, size_t plane) {
107 DCHECK(IsValidPlane(plane, format));
108 if (format == PIXEL_FORMAT_ARGB || format == PIXEL_FORMAT_XRGB)
109 return 4;
111 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
112 if (format == PIXEL_FORMAT_NV12 && plane == VideoFrame::kUVPlane)
113 return 2;
114 #endif
116 return 1;
119 // Rounds up |coded_size| if necessary for |format|.
120 static gfx::Size AdjustCodedSize(VideoPixelFormat format,
121 const gfx::Size& coded_size) {
122 const gfx::Size alignment = CommonAlignment(format);
123 return gfx::Size(RoundUp(coded_size.width(), alignment.width()),
124 RoundUp(coded_size.height(), alignment.height()));
127 // static
128 bool VideoFrame::IsValidConfig(VideoPixelFormat format,
129 StorageType storage_type,
130 const gfx::Size& coded_size,
131 const gfx::Rect& visible_rect,
132 const gfx::Size& natural_size) {
133 // Check maximum limits for all formats.
134 if (coded_size.GetArea() > limits::kMaxCanvas ||
135 coded_size.width() > limits::kMaxDimension ||
136 coded_size.height() > limits::kMaxDimension ||
137 visible_rect.x() < 0 || visible_rect.y() < 0 ||
138 visible_rect.right() > coded_size.width() ||
139 visible_rect.bottom() > coded_size.height() ||
140 natural_size.GetArea() > limits::kMaxCanvas ||
141 natural_size.width() > limits::kMaxDimension ||
142 natural_size.height() > limits::kMaxDimension)
143 return false;
145 // TODO(mcasas): Remove parameter |storage_type| when the opaque storage types
146 // comply with the checks below. Right now we skip them.
147 if (!IsStorageTypeMappable(storage_type))
148 return true;
150 // Check format-specific width/height requirements.
151 switch (format) {
152 case PIXEL_FORMAT_UNKNOWN:
153 return (coded_size.IsEmpty() && visible_rect.IsEmpty() &&
154 natural_size.IsEmpty());
155 case PIXEL_FORMAT_YV24:
156 case PIXEL_FORMAT_YV12:
157 case PIXEL_FORMAT_I420:
158 case PIXEL_FORMAT_YV12A:
159 case PIXEL_FORMAT_YV16:
160 case PIXEL_FORMAT_ARGB:
161 case PIXEL_FORMAT_XRGB:
162 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
163 case PIXEL_FORMAT_NV12:
164 #endif
165 // Check that software-allocated buffer formats are aligned correctly and
166 // not empty.
167 const gfx::Size alignment = CommonAlignment(format);
168 return RoundUp(visible_rect.right(), alignment.width()) <=
169 static_cast<size_t>(coded_size.width()) &&
170 RoundUp(visible_rect.bottom(), alignment.height()) <=
171 static_cast<size_t>(coded_size.height()) &&
172 !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
173 !natural_size.IsEmpty();
176 // TODO(mcasas): Check that storage type and underlying mailboxes/dataptr are
177 // matching.
178 NOTREACHED();
179 return false;
182 // static
183 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(VideoPixelFormat format,
184 const gfx::Size& coded_size,
185 const gfx::Rect& visible_rect,
186 const gfx::Size& natural_size,
187 base::TimeDelta timestamp) {
188 if (!IsYuvPlanar(format)) {
189 NOTIMPLEMENTED();
190 return nullptr;
193 // Since we're creating a new YUV frame (and allocating memory for it
194 // ourselves), we can pad the requested |coded_size| if necessary if the
195 // request does not line up on sample boundaries.
196 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
197 DCHECK(IsValidConfig(format, STORAGE_OWNED_MEMORY, new_coded_size,
198 visible_rect, natural_size));
200 scoped_refptr<VideoFrame> frame(new VideoFrame(format, STORAGE_OWNED_MEMORY,
201 new_coded_size, visible_rect,
202 natural_size, timestamp));
203 frame->AllocateYUV();
204 return frame;
207 // static
208 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
209 VideoPixelFormat format,
210 const gpu::MailboxHolder& mailbox_holder,
211 const ReleaseMailboxCB& mailbox_holder_release_cb,
212 const gfx::Size& coded_size,
213 const gfx::Rect& visible_rect,
214 const gfx::Size& natural_size,
215 base::TimeDelta timestamp) {
216 if (format != PIXEL_FORMAT_ARGB) {
217 DLOG(ERROR) << "Only ARGB pixel format supported, got "
218 << VideoPixelFormatToString(format);
219 return nullptr;
221 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
222 mailbox_holders[kARGBPlane] = mailbox_holder;
223 return new VideoFrame(format, STORAGE_OPAQUE, coded_size,
224 visible_rect, natural_size, mailbox_holders,
225 mailbox_holder_release_cb, timestamp);
228 // static
229 scoped_refptr<VideoFrame> VideoFrame::WrapYUV420NativeTextures(
230 const gpu::MailboxHolder& y_mailbox_holder,
231 const gpu::MailboxHolder& u_mailbox_holder,
232 const gpu::MailboxHolder& v_mailbox_holder,
233 const ReleaseMailboxCB& mailbox_holder_release_cb,
234 const gfx::Size& coded_size,
235 const gfx::Rect& visible_rect,
236 const gfx::Size& natural_size,
237 base::TimeDelta timestamp) {
238 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
239 mailbox_holders[kYPlane] = y_mailbox_holder;
240 mailbox_holders[kUPlane] = u_mailbox_holder;
241 mailbox_holders[kVPlane] = v_mailbox_holder;
242 return new VideoFrame(PIXEL_FORMAT_I420, STORAGE_OPAQUE, coded_size,
243 visible_rect, natural_size, mailbox_holders,
244 mailbox_holder_release_cb, timestamp);
247 // static
248 scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
249 VideoPixelFormat format,
250 const gfx::Size& coded_size,
251 const gfx::Rect& visible_rect,
252 const gfx::Size& natural_size,
253 uint8* data,
254 size_t data_size,
255 base::TimeDelta timestamp) {
256 return WrapExternalStorage(format, STORAGE_UNOWNED_MEMORY, coded_size,
257 visible_rect, natural_size, data, data_size,
258 timestamp, base::SharedMemory::NULLHandle(), 0);
261 // static
262 scoped_refptr<VideoFrame> VideoFrame::WrapExternalSharedMemory(
263 VideoPixelFormat format,
264 const gfx::Size& coded_size,
265 const gfx::Rect& visible_rect,
266 const gfx::Size& natural_size,
267 uint8* data,
268 size_t data_size,
269 base::SharedMemoryHandle handle,
270 size_t data_offset,
271 base::TimeDelta timestamp) {
272 return WrapExternalStorage(format, STORAGE_SHMEM, coded_size, visible_rect,
273 natural_size, data, data_size, timestamp, handle,
274 data_offset);
277 // static
278 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
279 VideoPixelFormat format,
280 const gfx::Size& coded_size,
281 const gfx::Rect& visible_rect,
282 const gfx::Size& natural_size,
283 int32 y_stride,
284 int32 u_stride,
285 int32 v_stride,
286 uint8* y_data,
287 uint8* u_data,
288 uint8* v_data,
289 base::TimeDelta timestamp) {
290 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
291 CHECK(IsValidConfig(format, STORAGE_UNOWNED_MEMORY, new_coded_size,
292 visible_rect, natural_size));
294 scoped_refptr<VideoFrame> frame(new VideoFrame(format, STORAGE_UNOWNED_MEMORY,
295 new_coded_size, visible_rect,
296 natural_size, timestamp));
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 return frame;
306 #if defined(OS_LINUX)
307 // static
308 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
309 VideoPixelFormat format,
310 const gfx::Size& coded_size,
311 const gfx::Rect& visible_rect,
312 const gfx::Size& natural_size,
313 const std::vector<int>& dmabuf_fds,
314 base::TimeDelta timestamp) {
315 #if defined(OS_CHROMEOS)
316 DCHECK_EQ(format, PIXEL_FORMAT_NV12);
317 #endif
319 if (!IsValidConfig(format, STORAGE_DMABUFS, coded_size, visible_rect,
320 natural_size)) {
321 return nullptr;
323 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
324 scoped_refptr<VideoFrame> frame =
325 new VideoFrame(format, STORAGE_DMABUFS, coded_size, visible_rect,
326 natural_size, mailbox_holders, ReleaseMailboxCB(),
327 timestamp);
328 if (!frame || !frame->DuplicateFileDescriptors(dmabuf_fds))
329 return nullptr;
330 return frame;
332 #endif
334 #if defined(OS_MACOSX)
335 // static
336 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
337 CVPixelBufferRef cv_pixel_buffer,
338 base::TimeDelta timestamp) {
339 DCHECK(cv_pixel_buffer);
340 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
342 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
343 VideoPixelFormat format;
344 // There are very few compatible CV pixel formats, so just check each.
345 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
346 format = PIXEL_FORMAT_I420;
347 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
348 format = PIXEL_FORMAT_YV24;
349 } else if (cv_format == '420v') {
350 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
351 // minimum OS X and iOS SDKs permits it.
352 format = PIXEL_FORMAT_NV12;
353 } else {
354 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
355 return NULL;
358 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
359 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
360 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
362 if (!IsValidConfig(format, STORAGE_UNOWNED_MEMORY,
363 coded_size, visible_rect, natural_size)) {
364 return NULL;
367 scoped_refptr<VideoFrame> frame(new VideoFrame(format, STORAGE_UNOWNED_MEMORY,
368 coded_size, visible_rect,
369 natural_size, timestamp));
371 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
372 return frame;
374 #endif
376 // static
377 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
378 const scoped_refptr<VideoFrame>& frame,
379 const gfx::Rect& visible_rect,
380 const gfx::Size& natural_size) {
381 // Frames with textures need mailbox info propagated, and there's no support
382 // for that here yet, see http://crbug/362521.
383 CHECK(!frame->HasTextures());
385 DCHECK(frame->visible_rect().Contains(visible_rect));
386 scoped_refptr<VideoFrame> wrapping_frame(new VideoFrame(
387 frame->format(), frame->storage_type(), frame->coded_size(), visible_rect,
388 natural_size, frame->timestamp()));
389 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
390 wrapping_frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM,
391 true);
394 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
395 wrapping_frame->strides_[i] = frame->stride(i);
396 wrapping_frame->data_[i] = frame->data(i);
399 #if defined(OS_LINUX)
400 // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
401 if (frame->storage_type() == STORAGE_DMABUFS) {
402 std::vector<int> original_fds;
403 for (size_t i = 0; i < kMaxPlanes; ++i)
404 original_fds.push_back(frame->dmabuf_fd(i));
405 if (!wrapping_frame->DuplicateFileDescriptors(original_fds))
406 return nullptr;
408 #endif
410 return wrapping_frame;
413 // static
414 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
415 scoped_refptr<VideoFrame> frame =
416 new VideoFrame(PIXEL_FORMAT_UNKNOWN, STORAGE_UNKNOWN, gfx::Size(),
417 gfx::Rect(), gfx::Size(), kNoTimestamp());
418 frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM, true);
419 return frame;
422 // static
423 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
424 const gfx::Size& size,
425 uint8 y, uint8 u, uint8 v,
426 base::TimeDelta timestamp) {
427 scoped_refptr<VideoFrame> frame =
428 CreateFrame(PIXEL_FORMAT_YV12, size, gfx::Rect(size), size, timestamp);
429 FillYUV(frame.get(), y, u, v);
430 return frame;
433 // static
434 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
435 const uint8 kBlackY = 0x00;
436 const uint8 kBlackUV = 0x80;
437 const base::TimeDelta kZero;
438 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
441 // static
442 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
443 const gfx::Size& size) {
444 const uint8 kBlackY = 0x00;
445 const uint8 kBlackUV = 0x00;
446 const uint8 kTransparentA = 0x00;
447 const base::TimeDelta kZero;
448 scoped_refptr<VideoFrame> frame =
449 CreateFrame(PIXEL_FORMAT_YV12A, size, gfx::Rect(size), size, kZero);
450 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
451 return frame;
454 #if defined(VIDEO_HOLE)
455 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
456 // maintained by the general compositor team. Please contact
457 // wonsik@chromium.org .
459 // static
460 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
461 const gfx::Size& size) {
462 DCHECK(IsValidConfig(PIXEL_FORMAT_UNKNOWN, STORAGE_HOLE, size,
463 gfx::Rect(size), size));
464 scoped_refptr<VideoFrame> frame(
465 new VideoFrame(PIXEL_FORMAT_UNKNOWN, STORAGE_HOLE, size, gfx::Rect(size),
466 size, base::TimeDelta()));
467 return frame;
469 #endif // defined(VIDEO_HOLE)
471 // static
472 size_t VideoFrame::NumPlanes(VideoPixelFormat format) {
473 switch (format) {
474 case PIXEL_FORMAT_ARGB:
475 case PIXEL_FORMAT_XRGB:
476 return 1;
477 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
478 case PIXEL_FORMAT_NV12:
479 return 2;
480 #endif
481 case PIXEL_FORMAT_YV12:
482 case PIXEL_FORMAT_YV16:
483 case PIXEL_FORMAT_I420:
484 case PIXEL_FORMAT_YV24:
485 return 3;
486 case PIXEL_FORMAT_YV12A:
487 return 4;
488 case PIXEL_FORMAT_UNKNOWN:
489 break;
491 NOTREACHED() << "Unsupported video frame format: " << format;
492 return 0;
495 // static
496 size_t VideoFrame::AllocationSize(VideoPixelFormat format,
497 const gfx::Size& coded_size) {
498 size_t total = 0;
499 for (size_t i = 0; i < NumPlanes(format); ++i)
500 total += PlaneSize(format, i, coded_size).GetArea();
501 return total;
504 // static
505 gfx::Size VideoFrame::PlaneSize(VideoPixelFormat format,
506 size_t plane,
507 const gfx::Size& coded_size) {
508 DCHECK(IsValidPlane(plane, format));
510 int width = coded_size.width();
511 int height = coded_size.height();
512 if (format != PIXEL_FORMAT_ARGB) {
513 // Align to multiple-of-two size overall. This ensures that non-subsampled
514 // planes can be addressed by pixel with the same scaling as the subsampled
515 // planes.
516 width = RoundUp(width, 2);
517 height = RoundUp(height, 2);
520 const gfx::Size subsample = SampleSize(format, plane);
521 DCHECK(width % subsample.width() == 0);
522 DCHECK(height % subsample.height() == 0);
523 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
524 height / subsample.height());
527 // static
528 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format,
529 size_t plane) {
530 DCHECK(IsValidPlane(plane, format));
531 const int bits_per_element = 8 * BytesPerElement(format, plane);
532 const int horiz_pixels_per_element = SampleSize(format, plane).width();
533 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
534 return bits_per_element / horiz_pixels_per_element;
537 // static
538 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format, size_t plane) {
539 DCHECK(IsValidPlane(plane, format));
540 return PlaneHorizontalBitsPerPixel(format, plane) /
541 SampleSize(format, plane).height();
544 // static
545 size_t VideoFrame::RowBytes(size_t plane, VideoPixelFormat format, int width) {
546 DCHECK(IsValidPlane(plane, format));
547 return BytesPerElement(format, plane) * Columns(plane, format, width);
550 // static
551 size_t VideoFrame::Rows(size_t plane, VideoPixelFormat format, int height) {
552 DCHECK(IsValidPlane(plane, format));
553 const int sample_height = SampleSize(format, plane).height();
554 return RoundUp(height, sample_height) / sample_height;
557 // static
558 size_t VideoFrame::Columns(size_t plane, VideoPixelFormat format, int width) {
559 DCHECK(IsValidPlane(plane, format));
560 const int sample_width = SampleSize(format, plane).width();
561 return RoundUp(width, sample_width) / sample_width;
564 // static
565 void VideoFrame::HashFrameForTesting(base::MD5Context* context,
566 const scoped_refptr<VideoFrame>& frame) {
567 DCHECK(context);
568 for (size_t plane = 0; plane < NumPlanes(frame->format()); ++plane) {
569 for (int row = 0; row < frame->rows(plane); ++row) {
570 base::MD5Update(
571 context,
572 base::StringPiece(reinterpret_cast<char*>(frame->data(plane) +
573 frame->stride(plane) * row),
574 frame->row_bytes(plane)));
579 bool VideoFrame::IsMappable() const {
580 return IsStorageTypeMappable(storage_type_);
583 bool VideoFrame::HasTextures() const {
584 return !mailbox_holders_[0].mailbox.IsZero();
587 int VideoFrame::stride(size_t plane) const {
588 DCHECK(IsValidPlane(plane, format_));
589 return strides_[plane];
592 int VideoFrame::row_bytes(size_t plane) const {
593 return RowBytes(plane, format_, coded_size_.width());
596 int VideoFrame::rows(size_t plane) const {
597 return Rows(plane, format_, coded_size_.height());
600 const uint8* VideoFrame::data(size_t plane) const {
601 DCHECK(IsValidPlane(plane, format_));
602 DCHECK(IsMappable());
603 return data_[plane];
606 uint8* VideoFrame::data(size_t plane) {
607 DCHECK(IsValidPlane(plane, format_));
608 DCHECK(IsMappable());
609 return data_[plane];
612 const uint8* VideoFrame::visible_data(size_t plane) const {
613 DCHECK(IsValidPlane(plane, format_));
614 DCHECK(IsMappable());
616 // Calculate an offset that is properly aligned for all planes.
617 const gfx::Size alignment = CommonAlignment(format_);
618 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
619 RoundDown(visible_rect_.y(), alignment.height()));
621 const gfx::Size subsample = SampleSize(format_, plane);
622 DCHECK(offset.x() % subsample.width() == 0);
623 DCHECK(offset.y() % subsample.height() == 0);
624 return data(plane) +
625 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
626 BytesPerElement(format_, plane) * // Column offset.
627 (offset.x() / subsample.width());
630 uint8* VideoFrame::visible_data(size_t plane) {
631 return const_cast<uint8*>(
632 static_cast<const VideoFrame*>(this)->visible_data(plane));
635 const gpu::MailboxHolder&
636 VideoFrame::mailbox_holder(size_t texture_index) const {
637 DCHECK(HasTextures());
638 DCHECK(IsValidPlane(texture_index, format_));
639 return mailbox_holders_[texture_index];
642 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
643 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
644 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
645 return shared_memory_handle_;
648 size_t VideoFrame::shared_memory_offset() const {
649 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
650 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
651 return shared_memory_offset_;
654 #if defined(OS_LINUX)
655 int VideoFrame::dmabuf_fd(size_t plane) const {
656 DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
657 DCHECK(IsValidPlane(plane, format_));
658 return dmabuf_fds_[plane].get();
661 bool VideoFrame::DuplicateFileDescriptors(const std::vector<int>& in_fds) {
662 // TODO(mcasas): Support offsets for e.g. multiplanar inside a single |in_fd|.
664 storage_type_ = STORAGE_DMABUFS;
665 // TODO(posciak): This is not exactly correct, it's possible for one
666 // buffer to contain more than one plane.
667 if (in_fds.size() != NumPlanes(format_)) {
668 LOG(FATAL) << "Not enough dmabuf fds provided, got: " << in_fds.size()
669 << ", expected: " << NumPlanes(format_);
670 return false;
673 // Make sure that all fds are closed if any dup() fails,
674 base::ScopedFD temp_dmabuf_fds[kMaxPlanes];
675 for (size_t i = 0; i < in_fds.size(); ++i) {
676 temp_dmabuf_fds[i] = base::ScopedFD(HANDLE_EINTR(dup(in_fds[i])));
677 if (!temp_dmabuf_fds[i].is_valid()) {
678 DPLOG(ERROR) << "Failed duplicating a dmabuf fd";
679 return false;
682 for (size_t i = 0; i < kMaxPlanes; ++i)
683 dmabuf_fds_[i].reset(temp_dmabuf_fds[i].release());
685 return true;
687 #endif
689 void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle) {
690 storage_type_ = STORAGE_SHMEM;
691 shared_memory_handle_ = handle;
694 #if defined(OS_MACOSX)
695 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
696 return cv_pixel_buffer_.get();
698 #endif
700 void VideoFrame::AddDestructionObserver(const base::Closure& callback) {
701 DCHECK(!callback.is_null());
702 done_callbacks_.push_back(callback);
705 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
706 DCHECK(HasTextures());
707 base::AutoLock locker(release_sync_point_lock_);
708 // Must wait on the previous sync point before inserting a new sync point so
709 // that |mailbox_holders_release_cb_| guarantees the previous sync point
710 // occurred when it waits on |release_sync_point_|.
711 if (release_sync_point_)
712 client->WaitSyncPoint(release_sync_point_);
713 release_sync_point_ = client->InsertSyncPoint();
716 // static
717 scoped_refptr<VideoFrame> VideoFrame::WrapExternalStorage(
718 VideoPixelFormat format,
719 StorageType storage_type,
720 const gfx::Size& coded_size,
721 const gfx::Rect& visible_rect,
722 const gfx::Size& natural_size,
723 uint8* data,
724 size_t data_size,
725 base::TimeDelta timestamp,
726 base::SharedMemoryHandle handle,
727 size_t data_offset) {
728 DCHECK(IsStorageTypeMappable(storage_type));
730 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
731 if (!IsValidConfig(format, storage_type, new_coded_size, visible_rect,
732 natural_size) ||
733 data_size < AllocationSize(format, new_coded_size)) {
734 return NULL;
736 DLOG_IF(ERROR, format != PIXEL_FORMAT_I420)
737 << "Only PIXEL_FORMAT_I420 format supported: "
738 << VideoPixelFormatToString(format);
739 if (format != PIXEL_FORMAT_I420)
740 return NULL;
742 scoped_refptr<VideoFrame> frame;
743 if (storage_type == STORAGE_SHMEM) {
744 frame = new VideoFrame(format, storage_type, new_coded_size,
745 visible_rect, natural_size, timestamp, handle,
746 data_offset);
747 } else {
748 frame = new VideoFrame(format, storage_type, new_coded_size,
749 visible_rect, natural_size, timestamp);
751 frame->strides_[kYPlane] = new_coded_size.width();
752 frame->strides_[kUPlane] = new_coded_size.width() / 2;
753 frame->strides_[kVPlane] = new_coded_size.width() / 2;
754 frame->data_[kYPlane] = data;
755 frame->data_[kUPlane] = data + new_coded_size.GetArea();
756 frame->data_[kVPlane] = data + (new_coded_size.GetArea() * 5 / 4);
757 return frame;
760 VideoFrame::VideoFrame(VideoPixelFormat format,
761 StorageType storage_type,
762 const gfx::Size& coded_size,
763 const gfx::Rect& visible_rect,
764 const gfx::Size& natural_size,
765 base::TimeDelta timestamp)
766 : format_(format),
767 storage_type_(storage_type),
768 coded_size_(coded_size),
769 visible_rect_(visible_rect),
770 natural_size_(natural_size),
771 shared_memory_handle_(base::SharedMemory::NULLHandle()),
772 shared_memory_offset_(0),
773 timestamp_(timestamp),
774 release_sync_point_(0) {
775 DCHECK(IsValidConfig(format_, storage_type, coded_size_, visible_rect_,
776 natural_size_));
777 memset(&mailbox_holders_, 0, sizeof(mailbox_holders_));
778 memset(&strides_, 0, sizeof(strides_));
779 memset(&data_, 0, sizeof(data_));
782 VideoFrame::VideoFrame(VideoPixelFormat format,
783 StorageType storage_type,
784 const gfx::Size& coded_size,
785 const gfx::Rect& visible_rect,
786 const gfx::Size& natural_size,
787 base::TimeDelta timestamp,
788 base::SharedMemoryHandle handle,
789 size_t shared_memory_offset)
790 : VideoFrame(format,
791 storage_type,
792 coded_size,
793 visible_rect,
794 natural_size,
795 timestamp) {
796 DCHECK_EQ(storage_type, STORAGE_SHMEM);
797 AddSharedMemoryHandle(handle);
798 shared_memory_offset_ = shared_memory_offset;
801 VideoFrame::VideoFrame(VideoPixelFormat format,
802 StorageType storage_type,
803 const gfx::Size& coded_size,
804 const gfx::Rect& visible_rect,
805 const gfx::Size& natural_size,
806 const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
807 const ReleaseMailboxCB& mailbox_holder_release_cb,
808 base::TimeDelta timestamp)
809 : VideoFrame(format,
810 storage_type,
811 coded_size,
812 visible_rect,
813 natural_size,
814 timestamp) {
815 memcpy(&mailbox_holders_, mailbox_holders, sizeof(mailbox_holders_));
816 mailbox_holders_release_cb_ = mailbox_holder_release_cb;
819 VideoFrame::~VideoFrame() {
820 if (!mailbox_holders_release_cb_.is_null()) {
821 uint32 release_sync_point;
823 // To ensure that changes to |release_sync_point_| are visible on this
824 // thread (imply a memory barrier).
825 base::AutoLock locker(release_sync_point_lock_);
826 release_sync_point = release_sync_point_;
828 base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_point);
831 for (auto& callback : done_callbacks_)
832 base::ResetAndReturn(&callback).Run();
835 void VideoFrame::AllocateYUV() {
836 DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
837 static_assert(0 == kYPlane, "y plane data must be index 0");
839 size_t data_size = 0;
840 size_t offset[kMaxPlanes];
841 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
842 // The *2 in alignment for height is because some formats (e.g. h264) allow
843 // interlaced coding, and then the size needs to be a multiple of two
844 // macroblocks (vertically). See
845 // libavcodec/utils.c:avcodec_align_dimensions2().
846 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2);
847 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment);
848 offset[plane] = data_size;
849 data_size += height * strides_[plane];
852 // The extra line of UV being allocated is because h264 chroma MC
853 // overreads by one line in some cases, see libavcodec/utils.c:
854 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
855 // put_h264_chroma_mc4_ssse3().
856 DCHECK(IsValidPlane(kUPlane, format_));
857 data_size += strides_[kUPlane] + kFrameSizePadding;
859 uint8* data = reinterpret_cast<uint8*>(
860 base::AlignedAlloc(data_size, kFrameAddressAlignment));
862 for (size_t plane = 0; plane < NumPlanes(format_); ++plane)
863 data_[plane] = data + offset[plane];
865 AddDestructionObserver(base::Bind(&base::AlignedFree, data));
868 } // namespace media