Revert "Reland c91b178b07b0d - Delete dead signin code (SigninGlobalError)"
[chromium-blink-merge.git] / media / base / video_frame.cc
blob662d6aa1c59edc31bdc55eca32df81001d43ec85
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/video_util.h"
17 #include "ui/gfx/geometry/point.h"
19 namespace media {
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.
54 // static
55 static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type) {
56 return
57 #if defined(OS_LINUX)
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 &&
61 #endif
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));
72 switch (plane) {
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:
79 switch (format) {
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 case PIXEL_FORMAT_NV12:
90 return gfx::Size(2, 2);
92 case PIXEL_FORMAT_UNKNOWN:
93 case PIXEL_FORMAT_ARGB:
94 case PIXEL_FORMAT_XRGB:
95 case PIXEL_FORMAT_UYVY:
96 break;
99 NOTREACHED();
100 return gfx::Size();
103 // Return the alignment for the whole frame, calculated as the max of the
104 // alignment for each individual plane.
105 static gfx::Size CommonAlignment(VideoPixelFormat format) {
106 int max_sample_width = 0;
107 int max_sample_height = 0;
108 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) {
109 const gfx::Size sample_size = SampleSize(format, plane);
110 max_sample_width = std::max(max_sample_width, sample_size.width());
111 max_sample_height = std::max(max_sample_height, sample_size.height());
113 return gfx::Size(max_sample_width, max_sample_height);
116 // Returns the number of bytes per element for given |plane| and |format|.
117 static int BytesPerElement(VideoPixelFormat format, size_t plane) {
118 DCHECK(IsValidPlane(plane, format));
119 switch (format) {
120 case PIXEL_FORMAT_ARGB:
121 case PIXEL_FORMAT_XRGB:
122 return 4;
123 case PIXEL_FORMAT_UYVY:
124 return 2;
125 case PIXEL_FORMAT_NV12: {
126 static const int bytes_per_element[] = {1, 2};
127 DCHECK_LT(plane, arraysize(bytes_per_element));
128 return bytes_per_element[plane];
130 case PIXEL_FORMAT_YV12:
131 case PIXEL_FORMAT_I420:
132 case PIXEL_FORMAT_YV16:
133 case PIXEL_FORMAT_YV12A:
134 case PIXEL_FORMAT_YV24:
135 return 1;
136 case PIXEL_FORMAT_UNKNOWN:
137 break;
139 NOTREACHED();
140 return 0;
143 // static
144 bool VideoFrame::IsValidConfig(VideoPixelFormat format,
145 StorageType storage_type,
146 const gfx::Size& coded_size,
147 const gfx::Rect& visible_rect,
148 const gfx::Size& natural_size) {
149 // Check maximum limits for all formats.
150 if (coded_size.GetArea() > limits::kMaxCanvas ||
151 coded_size.width() > limits::kMaxDimension ||
152 coded_size.height() > limits::kMaxDimension ||
153 visible_rect.x() < 0 || visible_rect.y() < 0 ||
154 visible_rect.right() > coded_size.width() ||
155 visible_rect.bottom() > coded_size.height() ||
156 natural_size.GetArea() > limits::kMaxCanvas ||
157 natural_size.width() > limits::kMaxDimension ||
158 natural_size.height() > limits::kMaxDimension)
159 return false;
161 // TODO(mcasas): Remove parameter |storage_type| when the opaque storage types
162 // comply with the checks below. Right now we skip them.
163 if (!IsStorageTypeMappable(storage_type))
164 return true;
166 // Make sure new formats are properly accounted for in the method.
167 static_assert(PIXEL_FORMAT_MAX == 9,
168 "Added pixel format, please review IsValidConfig()");
170 if (format == PIXEL_FORMAT_UNKNOWN) {
171 return coded_size.IsEmpty() && visible_rect.IsEmpty() &&
172 natural_size.IsEmpty();
175 // Check that software-allocated buffer formats are not empty.
176 return !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
177 !natural_size.IsEmpty();
180 // static
181 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(VideoPixelFormat format,
182 const gfx::Size& coded_size,
183 const gfx::Rect& visible_rect,
184 const gfx::Size& natural_size,
185 base::TimeDelta timestamp) {
186 if (!IsYuvPlanar(format)) {
187 NOTIMPLEMENTED();
188 return nullptr;
191 // Since we're creating a new YUV frame (and allocating memory for it
192 // ourselves), we can pad the requested |coded_size| if necessary if the
193 // request does not line up on sample boundaries. See discussion at
194 // http://crrev.com/1240833003
195 const gfx::Size alignment = CommonAlignment(format);
196 const gfx::Size new_coded_size =
197 gfx::Size(RoundUp(coded_size.width(), alignment.width()),
198 RoundUp(coded_size.height(), alignment.height()));
199 DCHECK((new_coded_size.width() % alignment.width() == 0) &&
200 (new_coded_size.height() % alignment.height() == 0));
202 const StorageType storage = STORAGE_OWNED_MEMORY;
203 if (!IsValidConfig(format, storage, new_coded_size, visible_rect,
204 natural_size)) {
205 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
206 << ConfigToString(format, storage, coded_size, visible_rect,
207 natural_size);
208 return nullptr;
211 scoped_refptr<VideoFrame> frame(new VideoFrame(
212 format, storage, new_coded_size, visible_rect, natural_size, timestamp));
213 frame->AllocateYUV();
214 return frame;
217 // static
218 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
219 VideoPixelFormat format,
220 const gpu::MailboxHolder& mailbox_holder,
221 const ReleaseMailboxCB& mailbox_holder_release_cb,
222 const gfx::Size& coded_size,
223 const gfx::Rect& visible_rect,
224 const gfx::Size& natural_size,
225 base::TimeDelta timestamp) {
226 if (format != PIXEL_FORMAT_ARGB && format != PIXEL_FORMAT_UYVY) {
227 DLOG(ERROR) << "Unsupported pixel format supported, got "
228 << VideoPixelFormatToString(format);
229 return nullptr;
231 const StorageType storage = STORAGE_OPAQUE;
232 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
233 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
234 << ConfigToString(format, storage, coded_size, visible_rect,
235 natural_size);
236 return nullptr;
239 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
240 mailbox_holders[kARGBPlane] = mailbox_holder;
241 return new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
242 mailbox_holders, mailbox_holder_release_cb, timestamp);
245 // static
246 scoped_refptr<VideoFrame> VideoFrame::WrapYUV420NativeTextures(
247 const gpu::MailboxHolder& y_mailbox_holder,
248 const gpu::MailboxHolder& u_mailbox_holder,
249 const gpu::MailboxHolder& v_mailbox_holder,
250 const ReleaseMailboxCB& mailbox_holder_release_cb,
251 const gfx::Size& coded_size,
252 const gfx::Rect& visible_rect,
253 const gfx::Size& natural_size,
254 base::TimeDelta timestamp) {
255 const StorageType storage = STORAGE_OPAQUE;
256 VideoPixelFormat format = PIXEL_FORMAT_I420;
257 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
258 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
259 << ConfigToString(format, storage, coded_size, visible_rect,
260 natural_size);
261 return nullptr;
264 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
265 mailbox_holders[kYPlane] = y_mailbox_holder;
266 mailbox_holders[kUPlane] = u_mailbox_holder;
267 mailbox_holders[kVPlane] = v_mailbox_holder;
268 return new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
269 mailbox_holders, mailbox_holder_release_cb, timestamp);
272 // static
273 scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
274 VideoPixelFormat format,
275 const gfx::Size& coded_size,
276 const gfx::Rect& visible_rect,
277 const gfx::Size& natural_size,
278 uint8* data,
279 size_t data_size,
280 base::TimeDelta timestamp) {
281 return WrapExternalStorage(format, STORAGE_UNOWNED_MEMORY, coded_size,
282 visible_rect, natural_size, data, data_size,
283 timestamp, base::SharedMemory::NULLHandle(), 0);
286 // static
287 scoped_refptr<VideoFrame> VideoFrame::WrapExternalSharedMemory(
288 VideoPixelFormat format,
289 const gfx::Size& coded_size,
290 const gfx::Rect& visible_rect,
291 const gfx::Size& natural_size,
292 uint8* data,
293 size_t data_size,
294 base::SharedMemoryHandle handle,
295 size_t data_offset,
296 base::TimeDelta timestamp) {
297 return WrapExternalStorage(format, STORAGE_SHMEM, coded_size, visible_rect,
298 natural_size, data, data_size, timestamp, handle,
299 data_offset);
302 // static
303 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
304 VideoPixelFormat format,
305 const gfx::Size& coded_size,
306 const gfx::Rect& visible_rect,
307 const gfx::Size& natural_size,
308 int32 y_stride,
309 int32 u_stride,
310 int32 v_stride,
311 uint8* y_data,
312 uint8* u_data,
313 uint8* v_data,
314 base::TimeDelta timestamp) {
315 const StorageType storage = STORAGE_UNOWNED_MEMORY;
316 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
317 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
318 << ConfigToString(format, storage, coded_size, visible_rect,
319 natural_size);
320 return nullptr;
323 scoped_refptr<VideoFrame> frame(new VideoFrame(
324 format, storage, coded_size, visible_rect, natural_size, timestamp));
325 frame->strides_[kYPlane] = y_stride;
326 frame->strides_[kUPlane] = u_stride;
327 frame->strides_[kVPlane] = v_stride;
328 frame->data_[kYPlane] = y_data;
329 frame->data_[kUPlane] = u_data;
330 frame->data_[kVPlane] = v_data;
331 return frame;
334 #if defined(OS_LINUX)
335 // static
336 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
337 VideoPixelFormat format,
338 const gfx::Size& coded_size,
339 const gfx::Rect& visible_rect,
340 const gfx::Size& natural_size,
341 const std::vector<int>& dmabuf_fds,
342 base::TimeDelta timestamp) {
343 #if defined(OS_CHROMEOS)
344 DCHECK_EQ(format, PIXEL_FORMAT_NV12);
345 #endif
347 const StorageType storage = STORAGE_DMABUFS;
348 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
349 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
350 << ConfigToString(format, storage, coded_size, visible_rect,
351 natural_size);
352 return nullptr;
355 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
356 scoped_refptr<VideoFrame> frame =
357 new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
358 mailbox_holders, ReleaseMailboxCB(), timestamp);
359 if (!frame || !frame->DuplicateFileDescriptors(dmabuf_fds))
360 return nullptr;
361 return frame;
363 #endif
365 #if defined(OS_MACOSX)
366 // static
367 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
368 CVPixelBufferRef cv_pixel_buffer,
369 base::TimeDelta timestamp) {
370 DCHECK(cv_pixel_buffer);
371 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
373 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
374 VideoPixelFormat format;
375 // There are very few compatible CV pixel formats, so just check each.
376 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
377 format = PIXEL_FORMAT_I420;
378 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
379 format = PIXEL_FORMAT_YV24;
380 } else if (cv_format == '420v') {
381 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
382 // minimum OS X and iOS SDKs permits it.
383 format = PIXEL_FORMAT_NV12;
384 } else {
385 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
386 return nullptr;
389 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
390 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
391 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
392 const StorageType storage = STORAGE_UNOWNED_MEMORY;
394 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
395 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
396 << ConfigToString(format, storage, coded_size, visible_rect,
397 natural_size);
398 return nullptr;
401 scoped_refptr<VideoFrame> frame(new VideoFrame(
402 format, storage, coded_size, visible_rect, natural_size, timestamp));
404 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
405 return frame;
407 #endif
409 // static
410 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
411 const scoped_refptr<VideoFrame>& frame,
412 const gfx::Rect& visible_rect,
413 const gfx::Size& natural_size) {
414 // Frames with textures need mailbox info propagated, and there's no support
415 // for that here yet, see http://crbug/362521.
416 CHECK(!frame->HasTextures());
418 DCHECK(frame->visible_rect().Contains(visible_rect));
420 if (!IsValidConfig(frame->format(), frame->storage_type(),
421 frame->coded_size(), visible_rect, natural_size)) {
422 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
423 << ConfigToString(frame->format(), frame->storage_type(),
424 frame->coded_size(), visible_rect,
425 natural_size);
426 return nullptr;
429 scoped_refptr<VideoFrame> wrapping_frame(new VideoFrame(
430 frame->format(), frame->storage_type(), frame->coded_size(), visible_rect,
431 natural_size, frame->timestamp()));
432 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
433 wrapping_frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM,
434 true);
437 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
438 wrapping_frame->strides_[i] = frame->stride(i);
439 wrapping_frame->data_[i] = frame->data(i);
442 #if defined(OS_LINUX)
443 // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
444 if (frame->storage_type() == STORAGE_DMABUFS) {
445 std::vector<int> original_fds;
446 for (size_t i = 0; i < kMaxPlanes; ++i)
447 original_fds.push_back(frame->dmabuf_fd(i));
448 if (!wrapping_frame->DuplicateFileDescriptors(original_fds))
449 return nullptr;
451 #endif
453 return wrapping_frame;
456 // static
457 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
458 scoped_refptr<VideoFrame> frame =
459 new VideoFrame(PIXEL_FORMAT_UNKNOWN, STORAGE_UNKNOWN, gfx::Size(),
460 gfx::Rect(), gfx::Size(), kNoTimestamp());
461 frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM, true);
462 return frame;
465 // static
466 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
467 const gfx::Size& size,
468 uint8 y, uint8 u, uint8 v,
469 base::TimeDelta timestamp) {
470 scoped_refptr<VideoFrame> frame =
471 CreateFrame(PIXEL_FORMAT_YV12, size, gfx::Rect(size), size, timestamp);
472 FillYUV(frame.get(), y, u, v);
473 return frame;
476 // static
477 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
478 const uint8 kBlackY = 0x00;
479 const uint8 kBlackUV = 0x80;
480 const base::TimeDelta kZero;
481 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
484 // static
485 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
486 const gfx::Size& size) {
487 const uint8 kBlackY = 0x00;
488 const uint8 kBlackUV = 0x00;
489 const uint8 kTransparentA = 0x00;
490 const base::TimeDelta kZero;
491 scoped_refptr<VideoFrame> frame =
492 CreateFrame(PIXEL_FORMAT_YV12A, size, gfx::Rect(size), size, kZero);
493 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
494 return frame;
497 #if defined(VIDEO_HOLE)
498 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
499 // maintained by the general compositor team. Please contact
500 // wonsik@chromium.org .
502 // static
503 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
504 const gfx::Size& size) {
505 const VideoPixelFormat format = PIXEL_FORMAT_UNKNOWN;
506 const StorageType storage = STORAGE_HOLE;
507 const gfx::Rect visible_rect = gfx::Rect(size);
508 if (!IsValidConfig(format, storage, size, visible_rect, size)) {
509 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
510 << ConfigToString(format, storage, size, visible_rect, size);
511 return nullptr;
513 scoped_refptr<VideoFrame> frame(new VideoFrame(
514 format, storage, size, gfx::Rect(size), size, base::TimeDelta()));
515 return frame;
517 #endif // defined(VIDEO_HOLE)
519 // static
520 size_t VideoFrame::NumPlanes(VideoPixelFormat format) {
521 switch (format) {
522 case PIXEL_FORMAT_ARGB:
523 case PIXEL_FORMAT_XRGB:
524 case PIXEL_FORMAT_UYVY:
525 return 1;
526 case PIXEL_FORMAT_NV12:
527 return 2;
528 case PIXEL_FORMAT_YV12:
529 case PIXEL_FORMAT_YV16:
530 case PIXEL_FORMAT_I420:
531 case PIXEL_FORMAT_YV24:
532 return 3;
533 case PIXEL_FORMAT_YV12A:
534 return 4;
535 case PIXEL_FORMAT_UNKNOWN:
536 break;
538 NOTREACHED() << "Unsupported video frame format: " << format;
539 return 0;
542 // static
543 size_t VideoFrame::AllocationSize(VideoPixelFormat format,
544 const gfx::Size& coded_size) {
545 size_t total = 0;
546 for (size_t i = 0; i < NumPlanes(format); ++i)
547 total += PlaneSize(format, i, coded_size).GetArea();
548 return total;
551 // static
552 gfx::Size VideoFrame::PlaneSize(VideoPixelFormat format,
553 size_t plane,
554 const gfx::Size& coded_size) {
555 DCHECK(IsValidPlane(plane, format));
557 int width = coded_size.width();
558 int height = coded_size.height();
559 if (format != PIXEL_FORMAT_ARGB) {
560 // Align to multiple-of-two size overall. This ensures that non-subsampled
561 // planes can be addressed by pixel with the same scaling as the subsampled
562 // planes.
563 width = RoundUp(width, 2);
564 height = RoundUp(height, 2);
567 const gfx::Size subsample = SampleSize(format, plane);
568 DCHECK(width % subsample.width() == 0);
569 DCHECK(height % subsample.height() == 0);
570 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
571 height / subsample.height());
574 // static
575 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format,
576 size_t plane) {
577 DCHECK(IsValidPlane(plane, format));
578 const int bits_per_element = 8 * BytesPerElement(format, plane);
579 const int horiz_pixels_per_element = SampleSize(format, plane).width();
580 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
581 return bits_per_element / horiz_pixels_per_element;
584 // static
585 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format, size_t plane) {
586 DCHECK(IsValidPlane(plane, format));
587 return PlaneHorizontalBitsPerPixel(format, plane) /
588 SampleSize(format, plane).height();
591 // static
592 size_t VideoFrame::RowBytes(size_t plane, VideoPixelFormat format, int width) {
593 DCHECK(IsValidPlane(plane, format));
594 return BytesPerElement(format, plane) * Columns(plane, format, width);
597 // static
598 size_t VideoFrame::Rows(size_t plane, VideoPixelFormat format, int height) {
599 DCHECK(IsValidPlane(plane, format));
600 const int sample_height = SampleSize(format, plane).height();
601 return RoundUp(height, sample_height) / sample_height;
604 // static
605 size_t VideoFrame::Columns(size_t plane, VideoPixelFormat format, int width) {
606 DCHECK(IsValidPlane(plane, format));
607 const int sample_width = SampleSize(format, plane).width();
608 return RoundUp(width, sample_width) / sample_width;
611 // static
612 void VideoFrame::HashFrameForTesting(base::MD5Context* context,
613 const scoped_refptr<VideoFrame>& frame) {
614 DCHECK(context);
615 for (size_t plane = 0; plane < NumPlanes(frame->format()); ++plane) {
616 for (int row = 0; row < frame->rows(plane); ++row) {
617 base::MD5Update(
618 context,
619 base::StringPiece(reinterpret_cast<char*>(frame->data(plane) +
620 frame->stride(plane) * row),
621 frame->row_bytes(plane)));
626 bool VideoFrame::IsMappable() const {
627 return IsStorageTypeMappable(storage_type_);
630 bool VideoFrame::HasTextures() const {
631 return !mailbox_holders_[0].mailbox.IsZero();
634 int VideoFrame::stride(size_t plane) const {
635 DCHECK(IsValidPlane(plane, format_));
636 return strides_[plane];
639 int VideoFrame::row_bytes(size_t plane) const {
640 return RowBytes(plane, format_, coded_size_.width());
643 int VideoFrame::rows(size_t plane) const {
644 return Rows(plane, format_, coded_size_.height());
647 const uint8* VideoFrame::data(size_t plane) const {
648 DCHECK(IsValidPlane(plane, format_));
649 DCHECK(IsMappable());
650 return data_[plane];
653 uint8* VideoFrame::data(size_t plane) {
654 DCHECK(IsValidPlane(plane, format_));
655 DCHECK(IsMappable());
656 return data_[plane];
659 const uint8* VideoFrame::visible_data(size_t plane) const {
660 DCHECK(IsValidPlane(plane, format_));
661 DCHECK(IsMappable());
663 // Calculate an offset that is properly aligned for all planes.
664 const gfx::Size alignment = CommonAlignment(format_);
665 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
666 RoundDown(visible_rect_.y(), alignment.height()));
668 const gfx::Size subsample = SampleSize(format_, plane);
669 DCHECK(offset.x() % subsample.width() == 0);
670 DCHECK(offset.y() % subsample.height() == 0);
671 return data(plane) +
672 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
673 BytesPerElement(format_, plane) * // Column offset.
674 (offset.x() / subsample.width());
677 uint8* VideoFrame::visible_data(size_t plane) {
678 return const_cast<uint8*>(
679 static_cast<const VideoFrame*>(this)->visible_data(plane));
682 const gpu::MailboxHolder&
683 VideoFrame::mailbox_holder(size_t texture_index) const {
684 DCHECK(HasTextures());
685 DCHECK(IsValidPlane(texture_index, format_));
686 return mailbox_holders_[texture_index];
689 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
690 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
691 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
692 return shared_memory_handle_;
695 size_t VideoFrame::shared_memory_offset() const {
696 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
697 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
698 return shared_memory_offset_;
701 #if defined(OS_LINUX)
702 int VideoFrame::dmabuf_fd(size_t plane) const {
703 DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
704 DCHECK(IsValidPlane(plane, format_));
705 return dmabuf_fds_[plane].get();
708 bool VideoFrame::DuplicateFileDescriptors(const std::vector<int>& in_fds) {
709 // TODO(mcasas): Support offsets for e.g. multiplanar inside a single |in_fd|.
711 storage_type_ = STORAGE_DMABUFS;
712 // TODO(posciak): This is not exactly correct, it's possible for one
713 // buffer to contain more than one plane.
714 if (in_fds.size() != NumPlanes(format_)) {
715 LOG(FATAL) << "Not enough dmabuf fds provided, got: " << in_fds.size()
716 << ", expected: " << NumPlanes(format_);
717 return false;
720 // Make sure that all fds are closed if any dup() fails,
721 base::ScopedFD temp_dmabuf_fds[kMaxPlanes];
722 for (size_t i = 0; i < in_fds.size(); ++i) {
723 temp_dmabuf_fds[i] = base::ScopedFD(HANDLE_EINTR(dup(in_fds[i])));
724 if (!temp_dmabuf_fds[i].is_valid()) {
725 DPLOG(ERROR) << "Failed duplicating a dmabuf fd";
726 return false;
729 for (size_t i = 0; i < kMaxPlanes; ++i)
730 dmabuf_fds_[i].reset(temp_dmabuf_fds[i].release());
732 return true;
734 #endif
736 void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle) {
737 storage_type_ = STORAGE_SHMEM;
738 shared_memory_handle_ = handle;
741 #if defined(OS_MACOSX)
742 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
743 return cv_pixel_buffer_.get();
745 #endif
747 void VideoFrame::AddDestructionObserver(const base::Closure& callback) {
748 DCHECK(!callback.is_null());
749 done_callbacks_.push_back(callback);
752 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
753 DCHECK(HasTextures());
754 base::AutoLock locker(release_sync_point_lock_);
755 // Must wait on the previous sync point before inserting a new sync point so
756 // that |mailbox_holders_release_cb_| guarantees the previous sync point
757 // occurred when it waits on |release_sync_point_|.
758 if (release_sync_point_)
759 client->WaitSyncPoint(release_sync_point_);
760 release_sync_point_ = client->InsertSyncPoint();
763 // static
764 scoped_refptr<VideoFrame> VideoFrame::WrapExternalStorage(
765 VideoPixelFormat format,
766 StorageType storage_type,
767 const gfx::Size& coded_size,
768 const gfx::Rect& visible_rect,
769 const gfx::Size& natural_size,
770 uint8* data,
771 size_t data_size,
772 base::TimeDelta timestamp,
773 base::SharedMemoryHandle handle,
774 size_t data_offset) {
775 DCHECK(IsStorageTypeMappable(storage_type));
777 if (format != PIXEL_FORMAT_I420) {
778 DLOG(ERROR) << "Only PIXEL_FORMAT_I420 format supported: "
779 << VideoPixelFormatToString(format);
780 return nullptr;
783 if (!IsValidConfig(format, storage_type, coded_size, visible_rect,
784 natural_size)) {
785 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
786 << ConfigToString(format, storage_type, coded_size,
787 visible_rect, natural_size);
788 return nullptr;
791 scoped_refptr<VideoFrame> frame;
792 if (storage_type == STORAGE_SHMEM) {
793 frame = new VideoFrame(format, storage_type, coded_size, visible_rect,
794 natural_size, timestamp, handle, data_offset);
795 } else {
796 frame = new VideoFrame(format, storage_type, coded_size, visible_rect,
797 natural_size, timestamp);
799 frame->strides_[kYPlane] = coded_size.width();
800 frame->strides_[kUPlane] = coded_size.width() / 2;
801 frame->strides_[kVPlane] = coded_size.width() / 2;
802 frame->data_[kYPlane] = data;
803 frame->data_[kUPlane] = data + coded_size.GetArea();
804 frame->data_[kVPlane] = data + (coded_size.GetArea() * 5 / 4);
805 return frame;
808 VideoFrame::VideoFrame(VideoPixelFormat format,
809 StorageType storage_type,
810 const gfx::Size& coded_size,
811 const gfx::Rect& visible_rect,
812 const gfx::Size& natural_size,
813 base::TimeDelta timestamp)
814 : format_(format),
815 storage_type_(storage_type),
816 coded_size_(coded_size),
817 visible_rect_(visible_rect),
818 natural_size_(natural_size),
819 shared_memory_handle_(base::SharedMemory::NULLHandle()),
820 shared_memory_offset_(0),
821 timestamp_(timestamp),
822 release_sync_point_(0) {
823 DCHECK(IsValidConfig(format_, storage_type, coded_size_, visible_rect_,
824 natural_size_));
825 memset(&mailbox_holders_, 0, sizeof(mailbox_holders_));
826 memset(&strides_, 0, sizeof(strides_));
827 memset(&data_, 0, sizeof(data_));
830 VideoFrame::VideoFrame(VideoPixelFormat format,
831 StorageType storage_type,
832 const gfx::Size& coded_size,
833 const gfx::Rect& visible_rect,
834 const gfx::Size& natural_size,
835 base::TimeDelta timestamp,
836 base::SharedMemoryHandle handle,
837 size_t shared_memory_offset)
838 : VideoFrame(format,
839 storage_type,
840 coded_size,
841 visible_rect,
842 natural_size,
843 timestamp) {
844 DCHECK_EQ(storage_type, STORAGE_SHMEM);
845 AddSharedMemoryHandle(handle);
846 shared_memory_offset_ = shared_memory_offset;
849 VideoFrame::VideoFrame(VideoPixelFormat format,
850 StorageType storage_type,
851 const gfx::Size& coded_size,
852 const gfx::Rect& visible_rect,
853 const gfx::Size& natural_size,
854 const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
855 const ReleaseMailboxCB& mailbox_holder_release_cb,
856 base::TimeDelta timestamp)
857 : VideoFrame(format,
858 storage_type,
859 coded_size,
860 visible_rect,
861 natural_size,
862 timestamp) {
863 memcpy(&mailbox_holders_, mailbox_holders, sizeof(mailbox_holders_));
864 mailbox_holders_release_cb_ = mailbox_holder_release_cb;
867 VideoFrame::~VideoFrame() {
868 if (!mailbox_holders_release_cb_.is_null()) {
869 uint32 release_sync_point;
871 // To ensure that changes to |release_sync_point_| are visible on this
872 // thread (imply a memory barrier).
873 base::AutoLock locker(release_sync_point_lock_);
874 release_sync_point = release_sync_point_;
876 base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_point);
879 for (auto& callback : done_callbacks_)
880 base::ResetAndReturn(&callback).Run();
883 void VideoFrame::AllocateYUV() {
884 DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
885 static_assert(0 == kYPlane, "y plane data must be index 0");
887 size_t data_size = 0;
888 size_t offset[kMaxPlanes];
889 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
890 // The *2 in alignment for height is because some formats (e.g. h264) allow
891 // interlaced coding, and then the size needs to be a multiple of two
892 // macroblocks (vertically). See
893 // libavcodec/utils.c:avcodec_align_dimensions2().
894 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2);
895 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment);
896 offset[plane] = data_size;
897 data_size += height * strides_[plane];
900 // The extra line of UV being allocated is because h264 chroma MC
901 // overreads by one line in some cases, see libavcodec/utils.c:
902 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
903 // put_h264_chroma_mc4_ssse3().
904 DCHECK(IsValidPlane(kUPlane, format_));
905 data_size += strides_[kUPlane] + kFrameSizePadding;
907 uint8* data = reinterpret_cast<uint8*>(
908 base::AlignedAlloc(data_size, kFrameAddressAlignment));
910 for (size_t plane = 0; plane < NumPlanes(format_); ++plane)
911 data_[plane] = data + offset[plane];
913 AddDestructionObserver(base::Bind(&base::AlignedFree, data));
916 } // namespace media