[BackgroundSync] Clean up some tests
[chromium-blink-merge.git] / media / base / video_frame.cc
blobaac26e520067ee2a53681edbcaf51c90515d15c6
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 the pixel size per element for given |plane| and |format|. E.g. 2x2
35 // for the U-plane in I420.
36 static gfx::Size SampleSize(VideoFrame::Format format, size_t plane) {
37 DCHECK(VideoFrame::IsValidPlane(plane, format));
39 switch (plane) {
40 case VideoFrame::kYPlane:
41 case VideoFrame::kAPlane:
42 return gfx::Size(1, 1);
44 case VideoFrame::kUPlane: // and VideoFrame::kUVPlane:
45 case VideoFrame::kVPlane:
46 switch (format) {
47 case VideoFrame::YV24:
48 return gfx::Size(1, 1);
50 case VideoFrame::YV16:
51 return gfx::Size(2, 1);
53 case VideoFrame::YV12:
54 case VideoFrame::I420:
55 case VideoFrame::YV12A:
56 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
57 case VideoFrame::NV12:
58 #endif
59 return gfx::Size(2, 2);
61 case VideoFrame::UNKNOWN:
62 case VideoFrame::ARGB:
63 case VideoFrame::XRGB:
64 break;
67 NOTREACHED();
68 return gfx::Size();
71 // Return the alignment for the whole frame, calculated as the max of the
72 // alignment for each individual plane.
73 static gfx::Size CommonAlignment(VideoFrame::Format format) {
74 int max_sample_width = 0;
75 int max_sample_height = 0;
76 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) {
77 const gfx::Size sample_size = SampleSize(format, plane);
78 max_sample_width = std::max(max_sample_width, sample_size.width());
79 max_sample_height = std::max(max_sample_height, sample_size.height());
81 return gfx::Size(max_sample_width, max_sample_height);
84 // Returns the number of bytes per element for given |plane| and |format|.
85 static int BytesPerElement(VideoFrame::Format format, size_t plane) {
86 DCHECK(VideoFrame::IsValidPlane(plane, format));
87 if (format == VideoFrame::ARGB || format == VideoFrame::XRGB)
88 return 4;
90 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
91 if (format == VideoFrame::NV12 && plane == VideoFrame::kUVPlane)
92 return 2;
93 #endif
95 return 1;
98 // Rounds up |coded_size| if necessary for |format|.
99 static gfx::Size AdjustCodedSize(VideoFrame::Format format,
100 const gfx::Size& coded_size) {
101 const gfx::Size alignment = CommonAlignment(format);
102 return gfx::Size(RoundUp(coded_size.width(), alignment.width()),
103 RoundUp(coded_size.height(), alignment.height()));
106 // Release data allocated by AllocateYUV().
107 static void ReleaseData(uint8* data) {
108 DCHECK(data);
109 base::AlignedFree(data);
112 //static
113 bool VideoFrame::IsYuvPlanar(Format format) {
114 switch (format) {
115 case YV12:
116 case I420:
117 case YV16:
118 case YV12A:
119 case YV24:
120 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
121 case NV12:
122 #endif
123 return true;
125 case UNKNOWN:
126 case ARGB:
127 case XRGB:
128 return false;
130 return false;
133 //static
134 bool VideoFrame::IsMappable(StorageType storage_type) {
135 return storage_type == STORAGE_SHMEM ||
136 storage_type == STORAGE_OWNED_MEMORY ||
137 storage_type == STORAGE_UNOWNED_MEMORY;
140 // static
141 std::string VideoFrame::FormatToString(Format format) {
142 switch (format) {
143 case UNKNOWN:
144 return "UNKNOWN";
145 case YV12:
146 return "YV12";
147 case YV16:
148 return "YV16";
149 case I420:
150 return "I420";
151 case YV12A:
152 return "YV12A";
153 case YV24:
154 return "YV24";
155 case ARGB:
156 return "ARGB";
157 case XRGB:
158 return "XRGB";
159 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
160 case NV12:
161 return "NV12";
162 #endif
164 NOTREACHED() << "Invalid VideoFrame format provided: " << format;
165 return "";
168 // static
169 bool VideoFrame::IsValidConfig(Format format,
170 StorageType storage_type,
171 const gfx::Size& coded_size,
172 const gfx::Rect& visible_rect,
173 const gfx::Size& natural_size) {
174 // Check maximum limits for all formats.
175 if (coded_size.GetArea() > limits::kMaxCanvas ||
176 coded_size.width() > limits::kMaxDimension ||
177 coded_size.height() > limits::kMaxDimension ||
178 visible_rect.x() < 0 || visible_rect.y() < 0 ||
179 visible_rect.right() > coded_size.width() ||
180 visible_rect.bottom() > coded_size.height() ||
181 natural_size.GetArea() > limits::kMaxCanvas ||
182 natural_size.width() > limits::kMaxDimension ||
183 natural_size.height() > limits::kMaxDimension)
184 return false;
186 // TODO(mcasas): Remove parameter |storage_type| when STORAGE_HOLE and
187 // STORAGE_TEXTURE comply with the checks below. Right now we skip them.
188 #if defined(VIDEO_HOLE)
189 if (storage_type == STORAGE_HOLE)
190 return true;
191 #endif
192 if(storage_type == STORAGE_TEXTURE)
193 return true;
195 // Check format-specific width/height requirements.
196 switch (format) {
197 case UNKNOWN:
198 return (coded_size.IsEmpty() && visible_rect.IsEmpty() &&
199 natural_size.IsEmpty());
200 case YV24:
201 case YV12:
202 case I420:
203 case YV12A:
204 case YV16:
205 case ARGB:
206 case XRGB:
207 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
208 case NV12:
209 #endif
210 // Check that software-allocated buffer formats are aligned correctly and
211 // not empty.
212 const gfx::Size alignment = CommonAlignment(format);
213 return RoundUp(visible_rect.right(), alignment.width()) <=
214 static_cast<size_t>(coded_size.width()) &&
215 RoundUp(visible_rect.bottom(), alignment.height()) <=
216 static_cast<size_t>(coded_size.height()) &&
217 !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
218 !natural_size.IsEmpty();
221 // TODO(mcasas): Check that storage type and underlying mailboxes/dataptr are
222 // matching.
223 NOTREACHED();
224 return false;
227 // static
228 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
229 Format format,
230 const gfx::Size& coded_size,
231 const gfx::Rect& visible_rect,
232 const gfx::Size& natural_size,
233 base::TimeDelta timestamp) {
234 if (!IsYuvPlanar(format)) {
235 NOTIMPLEMENTED();
236 return nullptr;
239 // Since we're creating a new YUV frame (and allocating memory for it
240 // ourselves), we can pad the requested |coded_size| if necessary if the
241 // request does not line up on sample boundaries.
242 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
243 DCHECK(IsValidConfig(format, STORAGE_OWNED_MEMORY, new_coded_size,
244 visible_rect, natural_size));
246 scoped_refptr<VideoFrame> frame(new VideoFrame(format, STORAGE_OWNED_MEMORY,
247 new_coded_size, visible_rect,
248 natural_size, timestamp));
249 frame->AllocateYUV();
250 return frame;
253 // static
254 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
255 const gpu::MailboxHolder& mailbox_holder,
256 const ReleaseMailboxCB& mailbox_holder_release_cb,
257 const gfx::Size& coded_size,
258 const gfx::Rect& visible_rect,
259 const gfx::Size& natural_size,
260 base::TimeDelta timestamp,
261 bool allow_overlay,
262 bool has_alpha) {
263 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
264 mailbox_holders[kARGBPlane] = mailbox_holder;
265 Format texture_format = has_alpha ? ARGB : XRGB;
266 scoped_refptr<VideoFrame> frame(
267 new VideoFrame(texture_format, STORAGE_TEXTURE, coded_size, visible_rect,
268 natural_size, mailbox_holders, timestamp));
269 frame->mailbox_holders_release_cb_ = mailbox_holder_release_cb;
270 frame->allow_overlay_ = allow_overlay;
271 return frame;
274 // static
275 scoped_refptr<VideoFrame> VideoFrame::WrapYUV420NativeTextures(
276 const gpu::MailboxHolder& y_mailbox_holder,
277 const gpu::MailboxHolder& u_mailbox_holder,
278 const gpu::MailboxHolder& v_mailbox_holder,
279 const ReleaseMailboxCB& mailbox_holder_release_cb,
280 const gfx::Size& coded_size,
281 const gfx::Rect& visible_rect,
282 const gfx::Size& natural_size,
283 base::TimeDelta timestamp,
284 bool allow_overlay) {
285 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
286 mailbox_holders[kYPlane] = y_mailbox_holder;
287 mailbox_holders[kUPlane] = u_mailbox_holder;
288 mailbox_holders[kVPlane] = v_mailbox_holder;
289 scoped_refptr<VideoFrame> frame(
290 new VideoFrame(I420, STORAGE_TEXTURE, coded_size, visible_rect,
291 natural_size, mailbox_holders, timestamp));
292 frame->mailbox_holders_release_cb_ = mailbox_holder_release_cb;
293 frame->allow_overlay_ = allow_overlay;
294 return frame;
297 // static
298 scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
299 Format format,
300 const gfx::Size& coded_size,
301 const gfx::Rect& visible_rect,
302 const gfx::Size& natural_size,
303 uint8* data,
304 size_t data_size,
305 base::TimeDelta timestamp) {
306 return WrapExternalStorage(format, STORAGE_UNOWNED_MEMORY, coded_size,
307 visible_rect, natural_size, data, data_size,
308 timestamp, base::SharedMemory::NULLHandle(), 0);
311 // static
312 scoped_refptr<VideoFrame> VideoFrame::WrapExternalSharedMemory(
313 Format format,
314 const gfx::Size& coded_size,
315 const gfx::Rect& visible_rect,
316 const gfx::Size& natural_size,
317 uint8* data,
318 size_t data_size,
319 base::SharedMemoryHandle handle,
320 size_t data_offset,
321 base::TimeDelta timestamp) {
322 return WrapExternalStorage(format, STORAGE_SHMEM, coded_size, visible_rect,
323 natural_size, data, data_size, timestamp, handle,
324 data_offset);
327 // static
328 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
329 Format format,
330 const gfx::Size& coded_size,
331 const gfx::Rect& visible_rect,
332 const gfx::Size& natural_size,
333 int32 y_stride,
334 int32 u_stride,
335 int32 v_stride,
336 uint8* y_data,
337 uint8* u_data,
338 uint8* v_data,
339 base::TimeDelta timestamp) {
340 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
341 CHECK(IsValidConfig(format, STORAGE_UNOWNED_MEMORY, new_coded_size,
342 visible_rect, natural_size));
344 scoped_refptr<VideoFrame> frame(new VideoFrame(format, STORAGE_UNOWNED_MEMORY,
345 new_coded_size, visible_rect,
346 natural_size, timestamp));
347 frame->strides_[kYPlane] = y_stride;
348 frame->strides_[kUPlane] = u_stride;
349 frame->strides_[kVPlane] = v_stride;
350 frame->data_[kYPlane] = y_data;
351 frame->data_[kUPlane] = u_data;
352 frame->data_[kVPlane] = v_data;
353 return frame;
356 #if defined(OS_LINUX)
357 // static
358 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
359 Format format,
360 const gfx::Size& coded_size,
361 const gfx::Rect& visible_rect,
362 const gfx::Size& natural_size,
363 const std::vector<int> dmabuf_fds,
364 base::TimeDelta timestamp) {
365 if (!IsValidConfig(format, STORAGE_DMABUFS, coded_size, visible_rect,
366 natural_size)) {
367 return NULL;
370 // TODO(posciak): This is not exactly correct, it's possible for one
371 // buffer to contain more than one plane.
372 if (dmabuf_fds.size() != NumPlanes(format)) {
373 LOG(FATAL) << "Not enough dmabuf fds provided!";
374 return NULL;
377 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
378 DCHECK_EQ(format, NV12);
379 #endif
380 scoped_refptr<VideoFrame> frame(new VideoFrame(format, STORAGE_DMABUFS,
381 coded_size, visible_rect,
382 natural_size, timestamp));
384 for (size_t i = 0; i < dmabuf_fds.size(); ++i) {
385 int duped_fd = HANDLE_EINTR(dup(dmabuf_fds[i]));
386 if (duped_fd == -1) {
387 // The already-duped in previous iterations fds will be closed when
388 // the partially-created frame drops out of scope here.
389 DLOG(ERROR) << "Failed duplicating a dmabuf fd";
390 return NULL;
393 frame->dmabuf_fds_[i].reset(duped_fd);
394 // Data is accessible only via fds.
395 frame->data_[i] = NULL;
396 frame->strides_[i] = 0;
399 return frame;
401 #endif
403 #if defined(OS_MACOSX)
404 // static
405 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
406 CVPixelBufferRef cv_pixel_buffer,
407 base::TimeDelta timestamp) {
408 DCHECK(cv_pixel_buffer);
409 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
411 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
412 Format format;
413 // There are very few compatible CV pixel formats, so just check each.
414 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
415 format = I420;
416 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
417 format = YV24;
418 } else if (cv_format == '420v') {
419 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
420 // minimum OS X and iOS SDKs permits it.
421 format = NV12;
422 } else {
423 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
424 return NULL;
427 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
428 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
429 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
431 if (!IsValidConfig(format, STORAGE_UNOWNED_MEMORY, coded_size, visible_rect,
432 natural_size)) {
433 return NULL;
436 scoped_refptr<VideoFrame> frame(new VideoFrame(format, STORAGE_UNOWNED_MEMORY,
437 coded_size, visible_rect,
438 natural_size, timestamp));
440 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
441 return frame;
443 #endif
445 // static
446 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
447 const scoped_refptr<VideoFrame>& frame,
448 const gfx::Rect& visible_rect,
449 const gfx::Size& natural_size) {
450 // STORAGE_TEXTURE frames need mailbox info propagated, and there's no support
451 // for that here yet, see http://crbug/362521.
452 CHECK_NE(frame->storage_type(), STORAGE_TEXTURE);
454 DCHECK(frame->visible_rect().Contains(visible_rect));
455 scoped_refptr<VideoFrame> wrapped_frame(new VideoFrame(
456 frame->format(), frame->storage_type(), frame->coded_size(), visible_rect,
457 natural_size, frame->timestamp()));
458 if (frame->IsEndOfStream())
459 frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM, true);
461 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
462 wrapped_frame->strides_[i] = frame->stride(i);
463 wrapped_frame->data_[i] = frame->data(i);
466 return wrapped_frame;
469 // static
470 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
471 scoped_refptr<VideoFrame> frame =
472 new VideoFrame(UNKNOWN, STORAGE_UNKNOWN, gfx::Size(), gfx::Rect(),
473 gfx::Size(), kNoTimestamp());
474 frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM, true);
475 return frame;
478 // static
479 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
480 const gfx::Size& size,
481 uint8 y, uint8 u, uint8 v,
482 base::TimeDelta timestamp) {
483 scoped_refptr<VideoFrame> frame =
484 CreateFrame(YV12, size, gfx::Rect(size), size, timestamp);
485 FillYUV(frame.get(), y, u, v);
486 return frame;
489 // static
490 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
491 const uint8 kBlackY = 0x00;
492 const uint8 kBlackUV = 0x80;
493 const base::TimeDelta kZero;
494 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
497 // static
498 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
499 const gfx::Size& size) {
500 const uint8 kBlackY = 0x00;
501 const uint8 kBlackUV = 0x00;
502 const uint8 kTransparentA = 0x00;
503 const base::TimeDelta kZero;
504 scoped_refptr<VideoFrame> frame =
505 CreateFrame(YV12A, size, gfx::Rect(size), size, kZero);
506 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
507 return frame;
510 #if defined(VIDEO_HOLE)
511 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
512 // maintained by the general compositor team. Please contact
513 // wonsik@chromium.org .
515 // static
516 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
517 const gfx::Size& size) {
518 DCHECK(IsValidConfig(UNKNOWN, STORAGE_HOLE, size, gfx::Rect(size), size));
519 scoped_refptr<VideoFrame> frame(new VideoFrame(
520 UNKNOWN, STORAGE_HOLE, size, gfx::Rect(size), size, base::TimeDelta()));
521 return frame;
523 #endif // defined(VIDEO_HOLE)
525 // static
526 size_t VideoFrame::NumPlanes(Format format) {
527 switch (format) {
528 case ARGB:
529 case XRGB:
530 return 1;
531 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
532 case NV12:
533 return 2;
534 #endif
535 case YV12:
536 case YV16:
537 case I420:
538 case YV24:
539 return 3;
540 case YV12A:
541 return 4;
542 case UNKNOWN:
543 break;
545 NOTREACHED() << "Unsupported video frame format: " << format;
546 return 0;
549 // static
550 size_t VideoFrame::AllocationSize(Format format, const gfx::Size& coded_size) {
551 size_t total = 0;
552 for (size_t i = 0; i < NumPlanes(format); ++i)
553 total += PlaneAllocationSize(format, i, coded_size);
554 return total;
557 // static
558 gfx::Size VideoFrame::PlaneSize(Format format,
559 size_t plane,
560 const gfx::Size& coded_size) {
561 DCHECK(IsValidPlane(plane, format));
563 int width = coded_size.width();
564 int height = coded_size.height();
565 if (format != ARGB) {
566 // Align to multiple-of-two size overall. This ensures that non-subsampled
567 // planes can be addressed by pixel with the same scaling as the subsampled
568 // planes.
569 width = RoundUp(width, 2);
570 height = RoundUp(height, 2);
573 const gfx::Size subsample = SampleSize(format, plane);
574 DCHECK(width % subsample.width() == 0);
575 DCHECK(height % subsample.height() == 0);
576 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
577 height / subsample.height());
580 size_t VideoFrame::PlaneAllocationSize(Format format,
581 size_t plane,
582 const gfx::Size& coded_size) {
583 return PlaneSize(format, plane, coded_size).GetArea();
586 // static
587 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format, size_t plane) {
588 DCHECK(IsValidPlane(plane, format));
589 const int bits_per_element = 8 * BytesPerElement(format, plane);
590 const int horiz_pixels_per_element = SampleSize(format, plane).width();
591 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
592 return bits_per_element / horiz_pixels_per_element;
595 // static
596 int VideoFrame::PlaneBitsPerPixel(Format format, size_t plane) {
597 DCHECK(IsValidPlane(plane, format));
598 return PlaneHorizontalBitsPerPixel(format, plane) /
599 SampleSize(format, plane).height();
602 VideoFrame::VideoFrame(Format format,
603 StorageType storage_type,
604 const gfx::Size& coded_size,
605 const gfx::Rect& visible_rect,
606 const gfx::Size& natural_size,
607 base::TimeDelta timestamp)
608 : format_(format),
609 storage_type_(storage_type),
610 coded_size_(coded_size),
611 visible_rect_(visible_rect),
612 natural_size_(natural_size),
613 shared_memory_handle_(base::SharedMemory::NULLHandle()),
614 shared_memory_offset_(0),
615 timestamp_(timestamp),
616 release_sync_point_(0),
617 allow_overlay_(false) {
618 DCHECK(IsValidConfig(format_, storage_type, coded_size_, visible_rect_,
619 natural_size_));
620 memset(&mailbox_holders_, 0, sizeof(mailbox_holders_));
621 memset(&strides_, 0, sizeof(strides_));
622 memset(&data_, 0, sizeof(data_));
625 VideoFrame::VideoFrame(Format format,
626 StorageType storage_type,
627 const gfx::Size& coded_size,
628 const gfx::Rect& visible_rect,
629 const gfx::Size& natural_size,
630 base::TimeDelta timestamp,
631 base::SharedMemoryHandle handle,
632 size_t shared_memory_offset)
633 : VideoFrame(format,
634 storage_type,
635 coded_size,
636 visible_rect,
637 natural_size,
638 timestamp) {
639 shared_memory_handle_ = handle;
640 shared_memory_offset_ = shared_memory_offset;
643 VideoFrame::VideoFrame(Format format,
644 StorageType storage_type,
645 const gfx::Size& coded_size,
646 const gfx::Rect& visible_rect,
647 const gfx::Size& natural_size,
648 const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
649 base::TimeDelta timestamp)
650 : VideoFrame(format,
651 storage_type,
652 coded_size,
653 visible_rect,
654 natural_size,
655 timestamp) {
656 memcpy(&mailbox_holders_, mailbox_holders, sizeof(mailbox_holders_));
659 VideoFrame::~VideoFrame() {
660 if (!mailbox_holders_release_cb_.is_null()) {
661 uint32 release_sync_point;
663 // To ensure that changes to |release_sync_point_| are visible on this
664 // thread (imply a memory barrier).
665 base::AutoLock locker(release_sync_point_lock_);
666 release_sync_point = release_sync_point_;
668 base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_point);
671 for (auto& callback : done_callbacks_)
672 base::ResetAndReturn(&callback).Run();
675 // static
676 scoped_refptr<VideoFrame> VideoFrame::WrapExternalStorage(
677 Format format,
678 StorageType storage_type,
679 const gfx::Size& coded_size,
680 const gfx::Rect& visible_rect,
681 const gfx::Size& natural_size,
682 uint8* data,
683 size_t data_size,
684 base::TimeDelta timestamp,
685 base::SharedMemoryHandle handle,
686 size_t data_offset) {
687 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
689 if (!IsValidConfig(format, storage_type, new_coded_size, visible_rect,
690 natural_size) ||
691 data_size < AllocationSize(format, new_coded_size)) {
692 return NULL;
694 DLOG_IF(ERROR, format != I420) << "Only I420 format supported: "
695 << FormatToString(format);
696 if (format != I420)
697 return NULL;
699 scoped_refptr<VideoFrame> frame;
700 if (storage_type == STORAGE_SHMEM) {
701 frame = new VideoFrame(format, storage_type, new_coded_size, visible_rect,
702 natural_size, timestamp, handle, data_offset);
703 } else {
704 frame = new VideoFrame(format, storage_type, new_coded_size, visible_rect,
705 natural_size, timestamp);
707 frame->strides_[kYPlane] = new_coded_size.width();
708 frame->strides_[kUPlane] = new_coded_size.width() / 2;
709 frame->strides_[kVPlane] = new_coded_size.width() / 2;
710 frame->data_[kYPlane] = data;
711 frame->data_[kUPlane] = data + new_coded_size.GetArea();
712 frame->data_[kVPlane] = data + (new_coded_size.GetArea() * 5 / 4);
713 return frame;
716 void VideoFrame::AllocateYUV() {
717 DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
718 static_assert(0 == kYPlane, "y plane data must be index 0");
720 size_t data_size = 0;
721 size_t offset[kMaxPlanes];
722 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
723 // The *2 in alignment for height is because some formats (e.g. h264) allow
724 // interlaced coding, and then the size needs to be a multiple of two
725 // macroblocks (vertically). See
726 // libavcodec/utils.c:avcodec_align_dimensions2().
727 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2);
728 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment);
729 offset[plane] = data_size;
730 data_size += height * strides_[plane];
733 // The extra line of UV being allocated is because h264 chroma MC
734 // overreads by one line in some cases, see libavcodec/utils.c:
735 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
736 // put_h264_chroma_mc4_ssse3().
737 DCHECK(IsValidPlane(kUPlane, format_));
738 data_size += strides_[kUPlane] + kFrameSizePadding;
740 // FFmpeg expects the initialize allocation to be zero-initialized. Failure
741 // to do so can lead to unitialized value usage. See http://crbug.com/390941
742 uint8* data = reinterpret_cast<uint8*>(
743 base::AlignedAlloc(data_size, kFrameAddressAlignment));
744 memset(data, 0, data_size);
746 for (size_t plane = 0; plane < NumPlanes(format_); ++plane)
747 data_[plane] = data + offset[plane];
749 AddDestructionObserver(base::Bind(&ReleaseData, data));
752 // static
753 bool VideoFrame::IsValidPlane(size_t plane, Format format) {
754 return (plane < NumPlanes(format));
757 int VideoFrame::stride(size_t plane) const {
758 DCHECK(IsValidPlane(plane, format_));
759 return strides_[plane];
762 // static
763 size_t VideoFrame::RowBytes(size_t plane, Format format, int width) {
764 DCHECK(IsValidPlane(plane, format));
765 return BytesPerElement(format, plane) * Columns(plane, format, width);
768 int VideoFrame::row_bytes(size_t plane) const {
769 return RowBytes(plane, format_, coded_size_.width());
772 // static
773 size_t VideoFrame::Rows(size_t plane, Format format, int height) {
774 DCHECK(IsValidPlane(plane, format));
775 const int sample_height = SampleSize(format, plane).height();
776 return RoundUp(height, sample_height) / sample_height;
779 // static
780 size_t VideoFrame::Columns(size_t plane, Format format, int width) {
781 DCHECK(IsValidPlane(plane, format));
782 const int sample_width = SampleSize(format, plane).width();
783 return RoundUp(width, sample_width) / sample_width;
786 int VideoFrame::rows(size_t plane) const {
787 return Rows(plane, format_, coded_size_.height());
790 const uint8* VideoFrame::data(size_t plane) const {
791 DCHECK(IsValidPlane(plane, format_));
792 DCHECK(IsMappable(storage_type_));
793 return data_[plane];
796 uint8* VideoFrame::data(size_t plane) {
797 DCHECK(IsValidPlane(plane, format_));
798 DCHECK(IsMappable(storage_type_));
799 return data_[plane];
802 const uint8* VideoFrame::visible_data(size_t plane) const {
803 DCHECK(IsValidPlane(plane, format_));
804 DCHECK(IsMappable(storage_type_));
806 // Calculate an offset that is properly aligned for all planes.
807 const gfx::Size alignment = CommonAlignment(format_);
808 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
809 RoundDown(visible_rect_.y(), alignment.height()));
811 const gfx::Size subsample = SampleSize(format_, plane);
812 DCHECK(offset.x() % subsample.width() == 0);
813 DCHECK(offset.y() % subsample.height() == 0);
814 return data(plane) +
815 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
816 BytesPerElement(format_, plane) * // Column offset.
817 (offset.x() / subsample.width());
820 uint8* VideoFrame::visible_data(size_t plane) {
821 return const_cast<uint8*>(
822 static_cast<const VideoFrame*>(this)->visible_data(plane));
825 const gpu::MailboxHolder&
826 VideoFrame::mailbox_holder(size_t texture_index) const {
827 #if defined(OS_LINUX)
828 DCHECK(storage_type_ == STORAGE_TEXTURE || storage_type_ == STORAGE_DMABUFS);
829 #else
830 DCHECK(storage_type_ == STORAGE_TEXTURE);
831 #endif
832 DCHECK_LT(texture_index, NumPlanes(format_));
833 return mailbox_holders_[texture_index];
836 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
837 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
838 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
839 return shared_memory_handle_;
842 size_t VideoFrame::shared_memory_offset() const {
843 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
844 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
845 return shared_memory_offset_;
848 void VideoFrame::AddDestructionObserver(const base::Closure& callback) {
849 DCHECK(!callback.is_null());
850 done_callbacks_.push_back(callback);
853 bool VideoFrame::IsEndOfStream() const {
854 bool end_of_stream;
855 return metadata_.GetBoolean(VideoFrameMetadata::END_OF_STREAM,
856 &end_of_stream) &&
857 end_of_stream;
860 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
861 #if defined(OS_LINUX)
862 DCHECK(storage_type_ == STORAGE_TEXTURE || storage_type_ == STORAGE_DMABUFS);
863 #else
864 DCHECK(storage_type_ == STORAGE_TEXTURE);
865 #endif
866 base::AutoLock locker(release_sync_point_lock_);
867 // Must wait on the previous sync point before inserting a new sync point so
868 // that |mailbox_holders_release_cb_| guarantees the previous sync point
869 // occurred when it waits on |release_sync_point_|.
870 if (release_sync_point_)
871 client->WaitSyncPoint(release_sync_point_);
872 release_sync_point_ = client->InsertSyncPoint();
875 #if defined(OS_LINUX)
876 int VideoFrame::dmabuf_fd(size_t plane) const {
877 DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
878 return dmabuf_fds_[plane].get();
880 #endif
882 #if defined(OS_MACOSX)
883 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
884 return cv_pixel_buffer_.get();
886 #endif
888 void VideoFrame::HashFrameForTesting(base::MD5Context* context) {
889 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
890 for (int row = 0; row < rows(plane); ++row) {
891 base::MD5Update(context, base::StringPiece(
892 reinterpret_cast<char*>(data(plane) + stride(plane) * row),
893 row_bytes(plane)));
898 } // namespace media