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