Work around WebKeyboardEvent.domCode not being set on OS X.
[chromium-blink-merge.git] / media / base / video_frame.cc
blob8e323a6bd4cc9d10267e569b110a35d835bcb7f1
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;
89 return 1;
92 // Rounds up |coded_size| if necessary for |format|.
93 static gfx::Size AdjustCodedSize(VideoFrame::Format format,
94 const gfx::Size& coded_size) {
95 const gfx::Size alignment = CommonAlignment(format);
96 return gfx::Size(RoundUp(coded_size.width(), alignment.width()),
97 RoundUp(coded_size.height(), alignment.height()));
100 // Release data allocated by AllocateYUV().
101 static void ReleaseData(uint8* data) {
102 DCHECK(data);
103 base::AlignedFree(data);
106 //static
107 bool VideoFrame::IsYuvPlanar(Format format) {
108 switch (format) {
109 case YV12:
110 case I420:
111 case YV16:
112 case YV12A:
113 case YV24:
114 return true;
115 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
116 case NV12:
117 #endif
118 case UNKNOWN:
119 case ARGB:
120 case XRGB:
121 return false;
123 return false;
126 //static
127 bool VideoFrame::IsMappable(StorageType storage_type) {
128 return storage_type == STORAGE_SHMEM ||
129 storage_type == STORAGE_OWNED_MEMORY ||
130 storage_type == STORAGE_UNOWNED_MEMORY;
133 // static
134 std::string VideoFrame::FormatToString(Format format) {
135 switch (format) {
136 case UNKNOWN:
137 return "UNKNOWN";
138 case YV12:
139 return "YV12";
140 case YV16:
141 return "YV16";
142 case I420:
143 return "I420";
144 case YV12A:
145 return "YV12A";
146 case YV24:
147 return "YV24";
148 case ARGB:
149 return "ARGB";
150 case XRGB:
151 return "XRGB";
152 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
153 case NV12:
154 return "NV12";
155 #endif
157 NOTREACHED() << "Invalid VideoFrame format provided: " << format;
158 return "";
161 // static
162 bool VideoFrame::IsValidConfig(Format format,
163 StorageType storage_type,
164 const gfx::Size& coded_size,
165 const gfx::Rect& visible_rect,
166 const gfx::Size& natural_size) {
167 // Check maximum limits for all formats.
168 if (coded_size.GetArea() > limits::kMaxCanvas ||
169 coded_size.width() > limits::kMaxDimension ||
170 coded_size.height() > limits::kMaxDimension ||
171 visible_rect.x() < 0 || visible_rect.y() < 0 ||
172 visible_rect.right() > coded_size.width() ||
173 visible_rect.bottom() > coded_size.height() ||
174 natural_size.GetArea() > limits::kMaxCanvas ||
175 natural_size.width() > limits::kMaxDimension ||
176 natural_size.height() > limits::kMaxDimension)
177 return false;
179 // TODO(mcasas): Remove parameter |storage_type| when STORAGE_HOLE and
180 // STORAGE_TEXTURE comply with the checks below. Right now we skip them.
181 #if defined(VIDEO_HOLE)
182 if (storage_type == STORAGE_HOLE)
183 return true;
184 #endif
185 if(storage_type == STORAGE_TEXTURE)
186 return true;
188 // Check format-specific width/height requirements.
189 switch (format) {
190 case UNKNOWN:
191 return (coded_size.IsEmpty() && visible_rect.IsEmpty() &&
192 natural_size.IsEmpty());
193 case YV24:
194 case YV12:
195 case I420:
196 case YV12A:
197 case YV16:
198 case ARGB:
199 case XRGB:
200 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
201 case NV12:
202 #endif
203 // Check that software-allocated buffer formats are aligned correctly and
204 // not empty.
205 const gfx::Size alignment = CommonAlignment(format);
206 return RoundUp(visible_rect.right(), alignment.width()) <=
207 static_cast<size_t>(coded_size.width()) &&
208 RoundUp(visible_rect.bottom(), alignment.height()) <=
209 static_cast<size_t>(coded_size.height()) &&
210 !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
211 !natural_size.IsEmpty();
214 // TODO(mcasas): Check that storage type and underlying mailboxes/dataptr are
215 // matching.
216 NOTREACHED();
217 return false;
220 // static
221 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
222 Format format,
223 const gfx::Size& coded_size,
224 const gfx::Rect& visible_rect,
225 const gfx::Size& natural_size,
226 base::TimeDelta timestamp) {
227 if (!IsYuvPlanar(format)) {
228 NOTIMPLEMENTED();
229 return nullptr;
232 // Since we're creating a new YUV frame (and allocating memory for it
233 // ourselves), we can pad the requested |coded_size| if necessary if the
234 // request does not line up on sample boundaries.
235 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
236 DCHECK(IsValidConfig(format, STORAGE_OWNED_MEMORY, new_coded_size,
237 visible_rect, natural_size));
239 scoped_refptr<VideoFrame> frame(
240 new VideoFrame(format, STORAGE_OWNED_MEMORY, new_coded_size, visible_rect,
241 natural_size, timestamp, false));
242 frame->AllocateYUV();
243 return frame;
246 // static
247 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
248 const gpu::MailboxHolder& mailbox_holder,
249 const ReleaseMailboxCB& mailbox_holder_release_cb,
250 const gfx::Size& coded_size,
251 const gfx::Rect& visible_rect,
252 const gfx::Size& natural_size,
253 base::TimeDelta timestamp,
254 bool allow_overlay,
255 bool has_alpha) {
256 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
257 mailbox_holders[kARGBPlane] = mailbox_holder;
258 Format texture_format = has_alpha ? ARGB : XRGB;
259 scoped_refptr<VideoFrame> frame(
260 new VideoFrame(texture_format, STORAGE_TEXTURE, coded_size, visible_rect,
261 natural_size, mailbox_holders, timestamp, false));
262 frame->mailbox_holders_release_cb_ = mailbox_holder_release_cb;
263 frame->allow_overlay_ = allow_overlay;
264 return frame;
267 // static
268 scoped_refptr<VideoFrame> VideoFrame::WrapYUV420NativeTextures(
269 const gpu::MailboxHolder& y_mailbox_holder,
270 const gpu::MailboxHolder& u_mailbox_holder,
271 const gpu::MailboxHolder& v_mailbox_holder,
272 const ReleaseMailboxCB& mailbox_holder_release_cb,
273 const gfx::Size& coded_size,
274 const gfx::Rect& visible_rect,
275 const gfx::Size& natural_size,
276 base::TimeDelta timestamp,
277 bool allow_overlay) {
278 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
279 mailbox_holders[kYPlane] = y_mailbox_holder;
280 mailbox_holders[kUPlane] = u_mailbox_holder;
281 mailbox_holders[kVPlane] = v_mailbox_holder;
282 scoped_refptr<VideoFrame> frame(
283 new VideoFrame(I420, STORAGE_TEXTURE, coded_size, visible_rect,
284 natural_size, mailbox_holders, timestamp, false));
285 frame->mailbox_holders_release_cb_ = mailbox_holder_release_cb;
286 frame->allow_overlay_ = allow_overlay;
287 return frame;
290 // static
291 scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
292 Format format,
293 const gfx::Size& coded_size,
294 const gfx::Rect& visible_rect,
295 const gfx::Size& natural_size,
296 uint8* data,
297 size_t data_size,
298 base::TimeDelta timestamp) {
299 return WrapExternalStorage(format, STORAGE_UNOWNED_MEMORY, coded_size,
300 visible_rect, natural_size, data, data_size,
301 timestamp, base::SharedMemory::NULLHandle(), 0);
304 // static
305 scoped_refptr<VideoFrame> VideoFrame::WrapExternalSharedMemory(
306 Format format,
307 const gfx::Size& coded_size,
308 const gfx::Rect& visible_rect,
309 const gfx::Size& natural_size,
310 uint8* data,
311 size_t data_size,
312 base::SharedMemoryHandle handle,
313 size_t data_offset,
314 base::TimeDelta timestamp) {
315 return WrapExternalStorage(format, STORAGE_SHMEM, coded_size, visible_rect,
316 natural_size, data, data_size, timestamp, handle,
317 data_offset);
320 // static
321 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
322 Format format,
323 const gfx::Size& coded_size,
324 const gfx::Rect& visible_rect,
325 const gfx::Size& natural_size,
326 int32 y_stride,
327 int32 u_stride,
328 int32 v_stride,
329 uint8* y_data,
330 uint8* u_data,
331 uint8* v_data,
332 base::TimeDelta timestamp) {
333 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
334 CHECK(IsValidConfig(format, STORAGE_UNOWNED_MEMORY, new_coded_size,
335 visible_rect, natural_size));
337 scoped_refptr<VideoFrame> frame(
338 new VideoFrame(format, STORAGE_UNOWNED_MEMORY, new_coded_size,
339 visible_rect, natural_size, timestamp, false));
340 frame->strides_[kYPlane] = y_stride;
341 frame->strides_[kUPlane] = u_stride;
342 frame->strides_[kVPlane] = v_stride;
343 frame->data_[kYPlane] = y_data;
344 frame->data_[kUPlane] = u_data;
345 frame->data_[kVPlane] = v_data;
346 return frame;
349 #if defined(OS_LINUX)
350 // static
351 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
352 Format format,
353 const gfx::Size& coded_size,
354 const gfx::Rect& visible_rect,
355 const gfx::Size& natural_size,
356 const std::vector<int> dmabuf_fds,
357 base::TimeDelta timestamp) {
358 if (!IsValidConfig(format, STORAGE_DMABUFS, coded_size, visible_rect,
359 natural_size)) {
360 return NULL;
363 // TODO(posciak): This is not exactly correct, it's possible for one
364 // buffer to contain more than one plane.
365 if (dmabuf_fds.size() != NumPlanes(format)) {
366 LOG(FATAL) << "Not enough dmabuf fds provided!";
367 return NULL;
370 DCHECK_EQ(format, ARGB);
371 scoped_refptr<VideoFrame> frame(
372 new VideoFrame(format, STORAGE_DMABUFS, coded_size, visible_rect,
373 natural_size, timestamp, false));
375 for (size_t i = 0; i < dmabuf_fds.size(); ++i) {
376 int duped_fd = HANDLE_EINTR(dup(dmabuf_fds[i]));
377 if (duped_fd == -1) {
378 // The already-duped in previous iterations fds will be closed when
379 // the partially-created frame drops out of scope here.
380 DLOG(ERROR) << "Failed duplicating a dmabuf fd";
381 return NULL;
384 frame->dmabuf_fds_[i].reset(duped_fd);
385 // Data is accessible only via fds.
386 frame->data_[i] = NULL;
387 frame->strides_[i] = 0;
390 return frame;
392 #endif
394 #if defined(OS_MACOSX)
395 // static
396 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
397 CVPixelBufferRef cv_pixel_buffer,
398 base::TimeDelta timestamp) {
399 DCHECK(cv_pixel_buffer);
400 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
402 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
403 Format format;
404 // There are very few compatible CV pixel formats, so just check each.
405 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
406 format = I420;
407 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
408 format = YV24;
409 } else if (cv_format == '420v') {
410 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
411 // minimum OS X and iOS SDKs permits it.
412 format = NV12;
413 } else {
414 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
415 return NULL;
418 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
419 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
420 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
422 if (!IsValidConfig(format, STORAGE_UNOWNED_MEMORY, coded_size, visible_rect,
423 natural_size)) {
424 return NULL;
427 scoped_refptr<VideoFrame> frame(
428 new VideoFrame(format, STORAGE_UNOWNED_MEMORY, coded_size, visible_rect,
429 natural_size, timestamp, false));
431 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
432 return frame;
434 #endif
436 // static
437 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
438 const scoped_refptr<VideoFrame>& frame,
439 const gfx::Rect& visible_rect,
440 const gfx::Size& natural_size) {
441 // STORAGE_TEXTURE frames need mailbox info propagated, and there's no support
442 // for that here yet, see http://crbug/362521.
443 CHECK_NE(frame->storage_type(), STORAGE_TEXTURE);
445 DCHECK(frame->visible_rect().Contains(visible_rect));
446 scoped_refptr<VideoFrame> wrapped_frame(
447 new VideoFrame(frame->format(), frame->storage_type(),
448 frame->coded_size(), visible_rect, natural_size,
449 frame->timestamp(), frame->end_of_stream()));
451 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
452 wrapped_frame->strides_[i] = frame->stride(i);
453 wrapped_frame->data_[i] = frame->data(i);
456 return wrapped_frame;
459 // static
460 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
461 return new VideoFrame(UNKNOWN, STORAGE_UNKNOWN, gfx::Size(), gfx::Rect(),
462 gfx::Size(), kNoTimestamp(), true);
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(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(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 DCHECK(IsValidConfig(UNKNOWN, STORAGE_HOLE, size, gfx::Rect(size), size));
506 scoped_refptr<VideoFrame> frame(
507 new VideoFrame(UNKNOWN, STORAGE_HOLE, size, gfx::Rect(size), size,
508 base::TimeDelta(), false));
509 return frame;
511 #endif // defined(VIDEO_HOLE)
513 // static
514 size_t VideoFrame::NumPlanes(Format format) {
515 switch (format) {
516 case ARGB:
517 case XRGB:
518 return 1;
519 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
520 case NV12:
521 return 2;
522 #endif
523 case YV12:
524 case YV16:
525 case I420:
526 case YV24:
527 return 3;
528 case YV12A:
529 return 4;
530 case UNKNOWN:
531 break;
533 NOTREACHED() << "Unsupported video frame format: " << format;
534 return 0;
537 // static
538 size_t VideoFrame::AllocationSize(Format format, const gfx::Size& coded_size) {
539 size_t total = 0;
540 for (size_t i = 0; i < NumPlanes(format); ++i)
541 total += PlaneAllocationSize(format, i, coded_size);
542 return total;
545 // static
546 gfx::Size VideoFrame::PlaneSize(Format format,
547 size_t plane,
548 const gfx::Size& coded_size) {
549 DCHECK(IsValidPlane(plane, format));
551 int width = coded_size.width();
552 int height = coded_size.height();
553 if (format != ARGB) {
554 // Align to multiple-of-two size overall. This ensures that non-subsampled
555 // planes can be addressed by pixel with the same scaling as the subsampled
556 // planes.
557 width = RoundUp(width, 2);
558 height = RoundUp(height, 2);
561 const gfx::Size subsample = SampleSize(format, plane);
562 DCHECK(width % subsample.width() == 0);
563 DCHECK(height % subsample.height() == 0);
564 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
565 height / subsample.height());
568 size_t VideoFrame::PlaneAllocationSize(Format format,
569 size_t plane,
570 const gfx::Size& coded_size) {
571 return PlaneSize(format, plane, coded_size).GetArea();
574 // static
575 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format, size_t plane) {
576 DCHECK(IsValidPlane(plane, format));
577 const int bits_per_element = 8 * BytesPerElement(format, plane);
578 const int horiz_pixels_per_element = SampleSize(format, plane).width();
579 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
580 return bits_per_element / horiz_pixels_per_element;
583 // static
584 int VideoFrame::PlaneBitsPerPixel(Format format, size_t plane) {
585 DCHECK(IsValidPlane(plane, format));
586 return PlaneHorizontalBitsPerPixel(format, plane) /
587 SampleSize(format, plane).height();
590 void VideoFrame::AllocateYUV() {
591 DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
592 static_assert(0 == kYPlane, "y plane data must be index 0");
594 size_t data_size = 0;
595 size_t offset[kMaxPlanes];
596 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
597 // The *2 in alignment for height is because some formats (e.g. h264) allow
598 // interlaced coding, and then the size needs to be a multiple of two
599 // macroblocks (vertically). See
600 // libavcodec/utils.c:avcodec_align_dimensions2().
601 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2);
602 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment);
603 offset[plane] = data_size;
604 data_size += height * strides_[plane];
607 // The extra line of UV being allocated is because h264 chroma MC
608 // overreads by one line in some cases, see libavcodec/utils.c:
609 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
610 // put_h264_chroma_mc4_ssse3().
611 DCHECK(IsValidPlane(kUPlane, format_));
612 data_size += strides_[kUPlane] + kFrameSizePadding;
614 // FFmpeg expects the initialize allocation to be zero-initialized. Failure
615 // to do so can lead to unitialized value usage. See http://crbug.com/390941
616 uint8* data = reinterpret_cast<uint8*>(
617 base::AlignedAlloc(data_size, kFrameAddressAlignment));
618 memset(data, 0, data_size);
620 for (size_t plane = 0; plane < NumPlanes(format_); ++plane)
621 data_[plane] = data + offset[plane];
623 AddDestructionObserver(base::Bind(&ReleaseData, data));
626 VideoFrame::VideoFrame(Format format,
627 StorageType storage_type,
628 const gfx::Size& coded_size,
629 const gfx::Rect& visible_rect,
630 const gfx::Size& natural_size,
631 base::TimeDelta timestamp,
632 bool end_of_stream)
633 : format_(format),
634 storage_type_(storage_type),
635 coded_size_(coded_size),
636 visible_rect_(visible_rect),
637 natural_size_(natural_size),
638 shared_memory_handle_(base::SharedMemory::NULLHandle()),
639 shared_memory_offset_(0),
640 timestamp_(timestamp),
641 release_sync_point_(0),
642 end_of_stream_(end_of_stream),
643 allow_overlay_(false) {
644 DCHECK(IsValidConfig(format_, storage_type, coded_size_, visible_rect_,
645 natural_size_));
646 memset(&mailbox_holders_, 0, sizeof(mailbox_holders_));
647 memset(&strides_, 0, sizeof(strides_));
648 memset(&data_, 0, sizeof(data_));
651 VideoFrame::VideoFrame(Format format,
652 StorageType storage_type,
653 const gfx::Size& coded_size,
654 const gfx::Rect& visible_rect,
655 const gfx::Size& natural_size,
656 base::TimeDelta timestamp,
657 bool end_of_stream,
658 base::SharedMemoryHandle handle,
659 size_t shared_memory_offset)
660 : VideoFrame(format, storage_type, coded_size, visible_rect, natural_size,
661 timestamp, end_of_stream) {
662 shared_memory_handle_ = handle;
663 shared_memory_offset_ = shared_memory_offset;
666 VideoFrame::VideoFrame(Format format,
667 StorageType storage_type,
668 const gfx::Size& coded_size,
669 const gfx::Rect& visible_rect,
670 const gfx::Size& natural_size,
671 const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
672 base::TimeDelta timestamp,
673 bool end_of_stream)
674 : VideoFrame(format, storage_type, coded_size, visible_rect, natural_size,
675 timestamp, end_of_stream) {
676 memcpy(&mailbox_holders_, mailbox_holders, sizeof(mailbox_holders_));
679 VideoFrame::~VideoFrame() {
680 if (!mailbox_holders_release_cb_.is_null()) {
681 uint32 release_sync_point;
683 // To ensure that changes to |release_sync_point_| are visible on this
684 // thread (imply a memory barrier).
685 base::AutoLock locker(release_sync_point_lock_);
686 release_sync_point = release_sync_point_;
688 base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_point);
691 for (auto& callback : done_callbacks_)
692 base::ResetAndReturn(&callback).Run();
695 // static
696 scoped_refptr<VideoFrame> VideoFrame::WrapExternalStorage(
697 Format format,
698 StorageType storage_type,
699 const gfx::Size& coded_size,
700 const gfx::Rect& visible_rect,
701 const gfx::Size& natural_size,
702 uint8* data,
703 size_t data_size,
704 base::TimeDelta timestamp,
705 base::SharedMemoryHandle handle,
706 size_t data_offset) {
707 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
709 if (!IsValidConfig(format, storage_type, new_coded_size, visible_rect,
710 natural_size) ||
711 data_size < AllocationSize(format, new_coded_size)) {
712 return NULL;
714 DLOG_IF(ERROR, format != I420) << "Only I420 format supported: "
715 << FormatToString(format);
716 if (format != I420)
717 return NULL;
719 scoped_refptr<VideoFrame> frame;
720 if (storage_type == STORAGE_SHMEM) {
721 frame = new VideoFrame(format, storage_type, new_coded_size, visible_rect,
722 natural_size, timestamp, false, handle, data_offset);
723 } else {
724 frame = new VideoFrame(format, storage_type, new_coded_size, visible_rect,
725 natural_size, timestamp, false);
727 frame->strides_[kYPlane] = new_coded_size.width();
728 frame->strides_[kUPlane] = new_coded_size.width() / 2;
729 frame->strides_[kVPlane] = new_coded_size.width() / 2;
730 frame->data_[kYPlane] = data;
731 frame->data_[kUPlane] = data + new_coded_size.GetArea();
732 frame->data_[kVPlane] = data + (new_coded_size.GetArea() * 5 / 4);
733 return frame;
736 // static
737 bool VideoFrame::IsValidPlane(size_t plane, Format format) {
738 return (plane < NumPlanes(format));
741 int VideoFrame::stride(size_t plane) const {
742 DCHECK(IsValidPlane(plane, format_));
743 return strides_[plane];
746 // static
747 size_t VideoFrame::RowBytes(size_t plane, Format format, int width) {
748 DCHECK(IsValidPlane(plane, format));
749 return BytesPerElement(format, plane) * Columns(plane, format, width);
752 int VideoFrame::row_bytes(size_t plane) const {
753 return RowBytes(plane, format_, coded_size_.width());
756 // static
757 size_t VideoFrame::Rows(size_t plane, Format format, int height) {
758 DCHECK(IsValidPlane(plane, format));
759 const int sample_height = SampleSize(format, plane).height();
760 return RoundUp(height, sample_height) / sample_height;
763 // static
764 size_t VideoFrame::Columns(size_t plane, Format format, int width) {
765 DCHECK(IsValidPlane(plane, format));
766 const int sample_width = SampleSize(format, plane).width();
767 return RoundUp(width, sample_width) / sample_width;
770 int VideoFrame::rows(size_t plane) const {
771 return Rows(plane, format_, coded_size_.height());
774 const uint8* VideoFrame::data(size_t plane) const {
775 DCHECK(IsValidPlane(plane, format_));
776 DCHECK(IsMappable(storage_type_));
777 return data_[plane];
780 uint8* VideoFrame::data(size_t plane) {
781 DCHECK(IsValidPlane(plane, format_));
782 DCHECK(IsMappable(storage_type_));
783 return data_[plane];
786 const uint8* VideoFrame::visible_data(size_t plane) const {
787 DCHECK(IsValidPlane(plane, format_));
788 DCHECK(IsMappable(storage_type_));
790 // Calculate an offset that is properly aligned for all planes.
791 const gfx::Size alignment = CommonAlignment(format_);
792 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
793 RoundDown(visible_rect_.y(), alignment.height()));
795 const gfx::Size subsample = SampleSize(format_, plane);
796 DCHECK(offset.x() % subsample.width() == 0);
797 DCHECK(offset.y() % subsample.height() == 0);
798 return data(plane) +
799 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
800 BytesPerElement(format_, plane) * // Column offset.
801 (offset.x() / subsample.width());
804 uint8* VideoFrame::visible_data(size_t plane) {
805 return const_cast<uint8*>(
806 static_cast<const VideoFrame*>(this)->visible_data(plane));
809 const gpu::MailboxHolder&
810 VideoFrame::mailbox_holder(size_t texture_index) const {
811 #if defined(OS_LINUX)
812 DCHECK(storage_type_ == STORAGE_TEXTURE || storage_type_ == STORAGE_DMABUFS);
813 #else
814 DCHECK(storage_type_ == STORAGE_TEXTURE);
815 #endif
816 DCHECK_LT(texture_index, NumPlanes(format_));
817 return mailbox_holders_[texture_index];
820 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
821 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
822 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
823 return shared_memory_handle_;
826 size_t VideoFrame::shared_memory_offset() const {
827 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
828 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
829 return shared_memory_offset_;
832 void VideoFrame::AddDestructionObserver(const base::Closure& callback) {
833 DCHECK(!callback.is_null());
834 done_callbacks_.push_back(callback);
837 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
838 #if defined(OS_LINUX)
839 DCHECK(storage_type_ == STORAGE_TEXTURE || storage_type_ == STORAGE_DMABUFS);
840 #else
841 DCHECK(storage_type_ == STORAGE_TEXTURE);
842 #endif
843 base::AutoLock locker(release_sync_point_lock_);
844 // Must wait on the previous sync point before inserting a new sync point so
845 // that |mailbox_holders_release_cb_| guarantees the previous sync point
846 // occurred when it waits on |release_sync_point_|.
847 if (release_sync_point_)
848 client->WaitSyncPoint(release_sync_point_);
849 release_sync_point_ = client->InsertSyncPoint();
852 #if defined(OS_LINUX)
853 int VideoFrame::dmabuf_fd(size_t plane) const {
854 DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
855 return dmabuf_fds_[plane].get();
857 #endif
859 #if defined(OS_MACOSX)
860 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
861 return cv_pixel_buffer_.get();
863 #endif
865 void VideoFrame::HashFrameForTesting(base::MD5Context* context) {
866 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
867 for (int row = 0; row < rows(plane); ++row) {
868 base::MD5Update(context, base::StringPiece(
869 reinterpret_cast<char*>(data(plane) + stride(plane) * row),
870 row_bytes(plane)));
875 } // namespace media