Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / media / base / video_frame.cc
blob12b46d56d3f98fefae980e047eccd68a87b61070
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 case VideoFrame::NV12:
57 return gfx::Size(2, 2);
59 case VideoFrame::UNKNOWN:
60 #if defined(VIDEO_HOLE)
61 case VideoFrame::HOLE:
62 #endif // defined(VIDEO_HOLE)
63 case VideoFrame::NATIVE_TEXTURE:
64 case VideoFrame::ARGB:
65 break;
68 NOTREACHED();
69 return gfx::Size();
72 // Return the alignment for the whole frame, calculated as the max of the
73 // alignment for each individual plane.
74 static gfx::Size CommonAlignment(VideoFrame::Format format) {
75 int max_sample_width = 0;
76 int max_sample_height = 0;
77 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) {
78 const gfx::Size sample_size = SampleSize(format, plane);
79 max_sample_width = std::max(max_sample_width, sample_size.width());
80 max_sample_height = std::max(max_sample_height, sample_size.height());
82 return gfx::Size(max_sample_width, max_sample_height);
85 // Returns the number of bytes per element for given |plane| and |format|. E.g.
86 // 2 for the UV plane in NV12.
87 static int BytesPerElement(VideoFrame::Format format, size_t plane) {
88 DCHECK(VideoFrame::IsValidPlane(plane, format));
89 if (format == VideoFrame::ARGB)
90 return 4;
92 if (format == VideoFrame::NV12 && plane == VideoFrame::kUVPlane)
93 return 2;
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 // static
107 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
108 VideoFrame::Format format,
109 const gfx::Size& coded_size,
110 const gfx::Rect& visible_rect,
111 const gfx::Size& natural_size,
112 base::TimeDelta timestamp) {
113 switch (format) {
114 case VideoFrame::YV12:
115 case VideoFrame::YV16:
116 case VideoFrame::I420:
117 case VideoFrame::YV12A:
118 case VideoFrame::YV24:
119 break;
121 case VideoFrame::UNKNOWN:
122 case VideoFrame::NV12:
123 case VideoFrame::NATIVE_TEXTURE:
124 #if defined(VIDEO_HOLE)
125 case VideoFrame::HOLE:
126 #endif // defined(VIDEO_HOLE)
127 case VideoFrame::ARGB:
128 NOTIMPLEMENTED();
129 return nullptr;
132 // Since we're creating a new YUV frame (and allocating memory for it
133 // ourselves), we can pad the requested |coded_size| if necessary if the
134 // request does not line up on sample boundaries.
135 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
136 DCHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
138 gpu::MailboxHolder mailboxes[kMaxPlanes];
139 scoped_refptr<VideoFrame> frame(
140 new VideoFrame(format, new_coded_size, visible_rect, natural_size,
141 mailboxes, TEXTURE_RGBA, timestamp, false));
142 frame->AllocateYUV();
143 return frame;
146 // static
147 std::string VideoFrame::FormatToString(VideoFrame::Format format) {
148 switch (format) {
149 case VideoFrame::UNKNOWN:
150 return "UNKNOWN";
151 case VideoFrame::YV12:
152 return "YV12";
153 case VideoFrame::YV16:
154 return "YV16";
155 case VideoFrame::I420:
156 return "I420";
157 case VideoFrame::NATIVE_TEXTURE:
158 return "NATIVE_TEXTURE";
159 #if defined(VIDEO_HOLE)
160 case VideoFrame::HOLE:
161 return "HOLE";
162 #endif // defined(VIDEO_HOLE)
163 case VideoFrame::YV12A:
164 return "YV12A";
165 case VideoFrame::NV12:
166 return "NV12";
167 case VideoFrame::YV24:
168 return "YV24";
169 case VideoFrame::ARGB:
170 return "ARGB";
172 NOTREACHED() << "Invalid videoframe format provided: " << format;
173 return "";
176 // static
177 bool VideoFrame::IsValidConfig(VideoFrame::Format format,
178 const gfx::Size& coded_size,
179 const gfx::Rect& visible_rect,
180 const gfx::Size& natural_size) {
181 // Check maximum limits for all formats.
182 if (coded_size.GetArea() > limits::kMaxCanvas ||
183 coded_size.width() > limits::kMaxDimension ||
184 coded_size.height() > limits::kMaxDimension ||
185 visible_rect.x() < 0 || visible_rect.y() < 0 ||
186 visible_rect.right() > coded_size.width() ||
187 visible_rect.bottom() > coded_size.height() ||
188 natural_size.GetArea() > limits::kMaxCanvas ||
189 natural_size.width() > limits::kMaxDimension ||
190 natural_size.height() > limits::kMaxDimension)
191 return false;
193 // Check format-specific width/height requirements.
194 switch (format) {
195 case VideoFrame::UNKNOWN:
196 return (coded_size.IsEmpty() && visible_rect.IsEmpty() &&
197 natural_size.IsEmpty());
199 // NATIVE_TEXTURE and HOLE have no software-allocated buffers and are
200 // allowed to skip the below check.
201 case VideoFrame::NATIVE_TEXTURE:
202 #if defined(VIDEO_HOLE)
203 case VideoFrame::HOLE:
204 #endif // defined(VIDEO_HOLE)
205 return true;
207 case VideoFrame::YV24:
208 case VideoFrame::YV12:
209 case VideoFrame::I420:
210 case VideoFrame::YV12A:
211 case VideoFrame::NV12:
212 case VideoFrame::YV16:
213 case VideoFrame::ARGB:
214 // Check that software-allocated buffer formats are aligned correctly and
215 // not empty.
216 const gfx::Size alignment = CommonAlignment(format);
217 return RoundUp(visible_rect.right(), alignment.width()) <=
218 static_cast<size_t>(coded_size.width()) &&
219 RoundUp(visible_rect.bottom(), alignment.height()) <=
220 static_cast<size_t>(coded_size.height()) &&
221 !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
222 !natural_size.IsEmpty();
225 NOTREACHED();
226 return false;
229 // static
230 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
231 const gpu::MailboxHolder& mailbox_holder,
232 const ReleaseMailboxCB& mailbox_holder_release_cb,
233 const gfx::Size& coded_size,
234 const gfx::Rect& visible_rect,
235 const gfx::Size& natural_size,
236 base::TimeDelta timestamp,
237 bool allow_overlay,
238 bool has_alpha) {
239 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
240 mailbox_holders[kARGBPlane] = mailbox_holder;
241 TextureFormat texture_format = has_alpha ? TEXTURE_RGBA : TEXTURE_RGB;
242 scoped_refptr<VideoFrame> frame(
243 new VideoFrame(NATIVE_TEXTURE, coded_size, visible_rect, natural_size,
244 mailbox_holders, texture_format, timestamp, false));
245 frame->mailbox_holders_release_cb_ = mailbox_holder_release_cb;
246 frame->allow_overlay_ = allow_overlay;
247 return frame;
250 // static
251 scoped_refptr<VideoFrame> VideoFrame::WrapYUV420NativeTextures(
252 const gpu::MailboxHolder& y_mailbox_holder,
253 const gpu::MailboxHolder& u_mailbox_holder,
254 const gpu::MailboxHolder& v_mailbox_holder,
255 const ReleaseMailboxCB& mailbox_holder_release_cb,
256 const gfx::Size& coded_size,
257 const gfx::Rect& visible_rect,
258 const gfx::Size& natural_size,
259 base::TimeDelta timestamp,
260 bool allow_overlay) {
261 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
262 mailbox_holders[kYPlane] = y_mailbox_holder;
263 mailbox_holders[kUPlane] = u_mailbox_holder;
264 mailbox_holders[kVPlane] = v_mailbox_holder;
265 scoped_refptr<VideoFrame> frame(
266 new VideoFrame(NATIVE_TEXTURE, coded_size, visible_rect, natural_size,
267 mailbox_holders, TEXTURE_YUV_420, timestamp, false));
268 frame->mailbox_holders_release_cb_ = mailbox_holder_release_cb;
269 frame->allow_overlay_ = allow_overlay;
270 return frame;
273 // static
274 scoped_refptr<VideoFrame> VideoFrame::WrapExternalPackedMemory(
275 Format format,
276 const gfx::Size& coded_size,
277 const gfx::Rect& visible_rect,
278 const gfx::Size& natural_size,
279 uint8* data,
280 size_t data_size,
281 base::SharedMemoryHandle handle,
282 size_t data_offset,
283 base::TimeDelta timestamp) {
284 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
286 if (!IsValidConfig(format, new_coded_size, visible_rect, natural_size))
287 return NULL;
288 if (data_size < AllocationSize(format, new_coded_size))
289 return NULL;
291 switch (format) {
292 case VideoFrame::I420: {
293 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
294 scoped_refptr<VideoFrame> frame(
295 new VideoFrame(format, new_coded_size, visible_rect, natural_size,
296 mailbox_holders, TEXTURE_RGBA, timestamp, false));
297 frame->shared_memory_handle_ = handle;
298 frame->shared_memory_offset_ = data_offset;
299 frame->strides_[kYPlane] = new_coded_size.width();
300 frame->strides_[kUPlane] = new_coded_size.width() / 2;
301 frame->strides_[kVPlane] = new_coded_size.width() / 2;
302 frame->data_[kYPlane] = data;
303 frame->data_[kUPlane] = data + new_coded_size.GetArea();
304 frame->data_[kVPlane] = data + (new_coded_size.GetArea() * 5 / 4);
305 return frame;
307 default:
308 NOTIMPLEMENTED();
309 return NULL;
313 // static
314 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
315 Format format,
316 const gfx::Size& coded_size,
317 const gfx::Rect& visible_rect,
318 const gfx::Size& natural_size,
319 int32 y_stride,
320 int32 u_stride,
321 int32 v_stride,
322 uint8* y_data,
323 uint8* u_data,
324 uint8* v_data,
325 base::TimeDelta timestamp) {
326 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
327 CHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
329 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
330 scoped_refptr<VideoFrame> frame(
331 new VideoFrame(format, new_coded_size, visible_rect, natural_size,
332 mailbox_holders, TEXTURE_RGBA, timestamp, false));
333 frame->strides_[kYPlane] = y_stride;
334 frame->strides_[kUPlane] = u_stride;
335 frame->strides_[kVPlane] = v_stride;
336 frame->data_[kYPlane] = y_data;
337 frame->data_[kUPlane] = u_data;
338 frame->data_[kVPlane] = v_data;
339 return frame;
342 #if defined(OS_POSIX)
343 // static
344 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
345 Format format,
346 const gfx::Size& coded_size,
347 const gfx::Rect& visible_rect,
348 const gfx::Size& natural_size,
349 const std::vector<int> dmabuf_fds,
350 base::TimeDelta timestamp) {
351 if (!IsValidConfig(format, coded_size, visible_rect, natural_size))
352 return NULL;
354 // TODO(posciak): This is not exactly correct, it's possible for one
355 // buffer to contain more than one plane.
356 if (dmabuf_fds.size() != NumPlanes(format)) {
357 LOG(FATAL) << "Not enough dmabuf fds provided!";
358 return NULL;
361 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
362 scoped_refptr<VideoFrame> frame(
363 new VideoFrame(format, coded_size, visible_rect, natural_size,
364 mailbox_holders, TEXTURE_RGBA, timestamp, false));
366 for (size_t i = 0; i < dmabuf_fds.size(); ++i) {
367 int duped_fd = HANDLE_EINTR(dup(dmabuf_fds[i]));
368 if (duped_fd == -1) {
369 // The already-duped in previous iterations fds will be closed when
370 // the partially-created frame drops out of scope here.
371 DLOG(ERROR) << "Failed duplicating a dmabuf fd";
372 return NULL;
375 frame->dmabuf_fds_[i].reset(duped_fd);
376 // Data is accessible only via fds.
377 frame->data_[i] = NULL;
378 frame->strides_[i] = 0;
381 return frame;
383 #endif
385 #if defined(OS_MACOSX)
386 // static
387 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
388 CVPixelBufferRef cv_pixel_buffer,
389 base::TimeDelta timestamp) {
390 DCHECK(cv_pixel_buffer);
391 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
393 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
394 Format format;
395 // There are very few compatible CV pixel formats, so just check each.
396 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
397 format = Format::I420;
398 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
399 format = Format::YV24;
400 } else if (cv_format == '420v') {
401 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
402 // minimum OS X and iOS SDKs permits it.
403 format = Format::NV12;
404 } else {
405 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
406 return NULL;
409 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
410 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
411 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
413 if (!IsValidConfig(format, coded_size, visible_rect, natural_size))
414 return NULL;
416 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
417 scoped_refptr<VideoFrame> frame(
418 new VideoFrame(format, coded_size, visible_rect, natural_size,
419 mailbox_holders, TEXTURE_RGBA, timestamp, false));
421 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
422 return frame;
424 #endif
426 // static
427 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
428 const scoped_refptr<VideoFrame>& frame,
429 const gfx::Rect& visible_rect,
430 const gfx::Size& natural_size) {
431 // NATIVE_TEXTURE frames need mailbox info propagated, and there's no support
432 // for that here yet, see http://crbug/362521.
433 CHECK_NE(frame->format(), NATIVE_TEXTURE);
435 DCHECK(frame->visible_rect().Contains(visible_rect));
436 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
437 scoped_refptr<VideoFrame> wrapped_frame(
438 new VideoFrame(frame->format(), frame->coded_size(), visible_rect,
439 natural_size, mailbox_holders, TEXTURE_RGBA,
440 frame->timestamp(), frame->end_of_stream()));
442 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
443 wrapped_frame->strides_[i] = frame->stride(i);
444 wrapped_frame->data_[i] = frame->data(i);
447 return wrapped_frame;
450 // static
451 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
452 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
453 return new VideoFrame(VideoFrame::UNKNOWN, gfx::Size(), gfx::Rect(),
454 gfx::Size(), mailbox_holders, TEXTURE_RGBA,
455 kNoTimestamp(), true);
458 // static
459 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
460 const gfx::Size& size,
461 uint8 y, uint8 u, uint8 v,
462 base::TimeDelta timestamp) {
463 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
464 VideoFrame::YV12, size, gfx::Rect(size), size, timestamp);
465 FillYUV(frame.get(), y, u, v);
466 return frame;
469 // static
470 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
471 const uint8 kBlackY = 0x00;
472 const uint8 kBlackUV = 0x80;
473 const base::TimeDelta kZero;
474 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
477 // static
478 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
479 const gfx::Size& size) {
480 const uint8 kBlackY = 0x00;
481 const uint8 kBlackUV = 0x00;
482 const uint8 kTransparentA = 0x00;
483 const base::TimeDelta kZero;
484 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
485 VideoFrame::YV12A, size, gfx::Rect(size), size, kZero);
486 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
487 return frame;
490 #if defined(VIDEO_HOLE)
491 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
492 // maintained by the general compositor team. Please contact the following
493 // people instead:
495 // wonsik@chromium.org
496 // ycheo@chromium.org
498 // static
499 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
500 const gfx::Size& size) {
501 DCHECK(IsValidConfig(VideoFrame::HOLE, size, gfx::Rect(size), size));
502 gpu::MailboxHolder mailboxes[kMaxPlanes];
503 scoped_refptr<VideoFrame> frame(
504 new VideoFrame(VideoFrame::HOLE, size, gfx::Rect(size), size, mailboxes,
505 TEXTURE_RGBA, base::TimeDelta(), false));
506 return frame;
508 #endif // defined(VIDEO_HOLE)
510 // static
511 size_t VideoFrame::NumPlanes(Format format) {
512 switch (format) {
513 case VideoFrame::NATIVE_TEXTURE:
514 #if defined(VIDEO_HOLE)
515 case VideoFrame::HOLE:
516 #endif // defined(VIDEO_HOLE)
517 return 0;
518 case VideoFrame::ARGB:
519 return 1;
520 case VideoFrame::NV12:
521 return 2;
522 case VideoFrame::YV12:
523 case VideoFrame::YV16:
524 case VideoFrame::I420:
525 case VideoFrame::YV24:
526 return 3;
527 case VideoFrame::YV12A:
528 return 4;
529 case VideoFrame::UNKNOWN:
530 break;
532 NOTREACHED() << "Unsupported video frame format: " << format;
533 return 0;
536 // static
537 size_t VideoFrame::NumTextures(TextureFormat texture_format) {
538 switch (texture_format) {
539 case TEXTURE_RGBA:
540 case TEXTURE_RGB:
541 return 1;
542 case TEXTURE_YUV_420:
543 return 3;
546 NOTREACHED();
547 return 0;
550 // static
551 size_t VideoFrame::AllocationSize(Format format, const gfx::Size& coded_size) {
552 size_t total = 0;
553 for (size_t i = 0; i < NumPlanes(format); ++i)
554 total += PlaneAllocationSize(format, i, coded_size);
555 return total;
558 // static
559 gfx::Size VideoFrame::PlaneSize(Format format,
560 size_t plane,
561 const gfx::Size& coded_size) {
562 DCHECK(IsValidPlane(plane, format));
564 int width = coded_size.width();
565 int height = coded_size.height();
566 if (format != VideoFrame::ARGB) {
567 // Align to multiple-of-two size overall. This ensures that non-subsampled
568 // planes can be addressed by pixel with the same scaling as the subsampled
569 // planes.
570 width = RoundUp(width, 2);
571 height = RoundUp(height, 2);
574 const gfx::Size subsample = SampleSize(format, plane);
575 DCHECK(width % subsample.width() == 0);
576 DCHECK(height % subsample.height() == 0);
577 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
578 height / subsample.height());
581 size_t VideoFrame::PlaneAllocationSize(Format format,
582 size_t plane,
583 const gfx::Size& coded_size) {
584 return PlaneSize(format, plane, coded_size).GetArea();
587 // static
588 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format, size_t plane) {
589 DCHECK(IsValidPlane(plane, format));
590 const int bits_per_element = 8 * BytesPerElement(format, plane);
591 const int horiz_pixels_per_element = SampleSize(format, plane).width();
592 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
593 return bits_per_element / horiz_pixels_per_element;
596 // static
597 int VideoFrame::PlaneBitsPerPixel(Format format, size_t plane) {
598 DCHECK(IsValidPlane(plane, format));
599 return PlaneHorizontalBitsPerPixel(format, plane) /
600 SampleSize(format, plane).height();
603 // Release data allocated by AllocateYUV().
604 static void ReleaseData(uint8* data) {
605 DCHECK(data);
606 base::AlignedFree(data);
609 void VideoFrame::AllocateYUV() {
610 DCHECK(format_ == YV12 || format_ == YV16 || format_ == YV12A ||
611 format_ == I420 || format_ == YV24);
612 static_assert(0 == kYPlane, "y plane data must be index 0");
614 size_t data_size = 0;
615 size_t offset[kMaxPlanes];
616 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format_); ++plane) {
617 // The *2 in alignment for height is because some formats (e.g. h264) allow
618 // interlaced coding, and then the size needs to be a multiple of two
619 // macroblocks (vertically). See
620 // libavcodec/utils.c:avcodec_align_dimensions2().
621 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2);
622 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment);
623 offset[plane] = data_size;
624 data_size += height * strides_[plane];
627 // The extra line of UV being allocated is because h264 chroma MC
628 // overreads by one line in some cases, see libavcodec/utils.c:
629 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
630 // put_h264_chroma_mc4_ssse3().
631 DCHECK(IsValidPlane(kUPlane, format_));
632 data_size += strides_[kUPlane] + kFrameSizePadding;
634 // FFmpeg expects the initialize allocation to be zero-initialized. Failure
635 // to do so can lead to unitialized value usage. See http://crbug.com/390941
636 uint8* data = reinterpret_cast<uint8*>(
637 base::AlignedAlloc(data_size, kFrameAddressAlignment));
638 memset(data, 0, data_size);
640 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format_); ++plane)
641 data_[plane] = data + offset[plane];
643 AddDestructionObserver(base::Bind(&ReleaseData, data));
646 VideoFrame::VideoFrame(VideoFrame::Format format,
647 const gfx::Size& coded_size,
648 const gfx::Rect& visible_rect,
649 const gfx::Size& natural_size,
650 const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
651 VideoFrame::TextureFormat texture_format,
652 base::TimeDelta timestamp,
653 bool end_of_stream)
654 : format_(format),
655 texture_format_(texture_format),
656 coded_size_(coded_size),
657 visible_rect_(visible_rect),
658 natural_size_(natural_size),
659 shared_memory_handle_(base::SharedMemory::NULLHandle()),
660 shared_memory_offset_(0),
661 timestamp_(timestamp),
662 release_sync_point_(0),
663 end_of_stream_(end_of_stream),
664 allow_overlay_(false) {
665 DCHECK(IsValidConfig(format_, coded_size_, visible_rect_, natural_size_));
666 memcpy(&mailbox_holders_, mailbox_holders, sizeof(mailbox_holders_));
667 memset(&strides_, 0, sizeof(strides_));
668 memset(&data_, 0, sizeof(data_));
671 VideoFrame::~VideoFrame() {
672 if (!mailbox_holders_release_cb_.is_null()) {
673 uint32 release_sync_point;
675 // To ensure that changes to |release_sync_point_| are visible on this
676 // thread (imply a memory barrier).
677 base::AutoLock locker(release_sync_point_lock_);
678 release_sync_point = release_sync_point_;
680 base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_point);
683 for (auto& callback : done_callbacks_)
684 base::ResetAndReturn(&callback).Run();
687 // static
688 bool VideoFrame::IsValidPlane(size_t plane, VideoFrame::Format format) {
689 return (plane < NumPlanes(format));
692 int VideoFrame::stride(size_t plane) const {
693 DCHECK(IsValidPlane(plane, format_));
694 return strides_[plane];
697 // static
698 size_t VideoFrame::RowBytes(size_t plane,
699 VideoFrame::Format format,
700 int width) {
701 DCHECK(IsValidPlane(plane, format));
702 return BytesPerElement(format, plane) * Columns(plane, format, width);
705 int VideoFrame::row_bytes(size_t plane) const {
706 return RowBytes(plane, format_, coded_size_.width());
709 // static
710 size_t VideoFrame::Rows(size_t plane, VideoFrame::Format format, int height) {
711 DCHECK(IsValidPlane(plane, format));
712 const int sample_height = SampleSize(format, plane).height();
713 return RoundUp(height, sample_height) / sample_height;
716 // static
717 size_t VideoFrame::Columns(size_t plane, Format format, int width) {
718 DCHECK(IsValidPlane(plane, format));
719 const int sample_width = SampleSize(format, plane).width();
720 return RoundUp(width, sample_width) / sample_width;
723 int VideoFrame::rows(size_t plane) const {
724 return Rows(plane, format_, coded_size_.height());
727 const uint8* VideoFrame::data(size_t plane) const {
728 DCHECK(IsValidPlane(plane, format_));
729 return data_[plane];
732 uint8* VideoFrame::data(size_t plane) {
733 DCHECK(IsValidPlane(plane, format_));
734 return data_[plane];
737 const uint8* VideoFrame::visible_data(size_t plane) const {
738 DCHECK(IsValidPlane(plane, format_));
740 // Calculate an offset that is properly aligned for all planes.
741 const gfx::Size alignment = CommonAlignment(format_);
742 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
743 RoundDown(visible_rect_.y(), alignment.height()));
745 const gfx::Size subsample = SampleSize(format_, plane);
746 DCHECK(offset.x() % subsample.width() == 0);
747 DCHECK(offset.y() % subsample.height() == 0);
748 return data(plane) +
749 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
750 BytesPerElement(format_, plane) * // Column offset.
751 (offset.x() / subsample.width());
754 uint8* VideoFrame::visible_data(size_t plane) {
755 return const_cast<uint8*>(
756 static_cast<const VideoFrame*>(this)->visible_data(plane));
759 const gpu::MailboxHolder& VideoFrame::mailbox_holder(size_t texture) const {
760 DCHECK_EQ(format_, NATIVE_TEXTURE);
761 DCHECK_LT(texture, NumTextures(texture_format_));
762 return mailbox_holders_[texture];
765 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
766 return shared_memory_handle_;
769 size_t VideoFrame::shared_memory_offset() const {
770 return shared_memory_offset_;
773 void VideoFrame::AddDestructionObserver(const base::Closure& callback) {
774 DCHECK(!callback.is_null());
775 done_callbacks_.push_back(callback);
778 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
779 DCHECK_EQ(format_, NATIVE_TEXTURE);
780 base::AutoLock locker(release_sync_point_lock_);
781 // Must wait on the previous sync point before inserting a new sync point so
782 // that |mailbox_holders_release_cb_| guarantees the previous sync point
783 // occurred when it waits on |release_sync_point_|.
784 if (release_sync_point_)
785 client->WaitSyncPoint(release_sync_point_);
786 release_sync_point_ = client->InsertSyncPoint();
789 #if defined(OS_POSIX)
790 int VideoFrame::dmabuf_fd(size_t plane) const {
791 return dmabuf_fds_[plane].get();
793 #endif
795 #if defined(OS_MACOSX)
796 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
797 return cv_pixel_buffer_.get();
799 #endif
801 void VideoFrame::HashFrameForTesting(base::MD5Context* context) {
802 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
803 for (int row = 0; row < rows(plane); ++row) {
804 base::MD5Update(context, base::StringPiece(
805 reinterpret_cast<char*>(data(plane) + stride(plane) * row),
806 row_bytes(plane)));
811 } // namespace media