Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / media / base / video_frame.cc
blobc837159958739edb5d4d3708d1dab8af0bc45562
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 "gpu/command_buffer/common/mailbox_holder.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 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
36 // for the U-plane in I420.
37 static gfx::Size SampleSize(VideoFrame::Format format, size_t plane) {
38 DCHECK(VideoFrame::IsValidPlane(plane, format));
40 switch (plane) {
41 case VideoFrame::kYPlane:
42 case VideoFrame::kAPlane:
43 return gfx::Size(1, 1);
45 case VideoFrame::kUPlane: // and VideoFrame::kUVPlane:
46 case VideoFrame::kVPlane:
47 switch (format) {
48 case VideoFrame::YV24:
49 return gfx::Size(1, 1);
51 case VideoFrame::YV16:
52 return gfx::Size(2, 1);
54 case VideoFrame::YV12:
55 case VideoFrame::YV12J:
56 case VideoFrame::YV12HD:
57 case VideoFrame::I420:
58 case VideoFrame::YV12A:
59 case VideoFrame::NV12:
60 return gfx::Size(2, 2);
62 case VideoFrame::UNKNOWN:
63 #if defined(VIDEO_HOLE)
64 case VideoFrame::HOLE:
65 #endif // defined(VIDEO_HOLE)
66 case VideoFrame::NATIVE_TEXTURE:
67 case VideoFrame::ARGB:
68 break;
71 NOTREACHED();
72 return gfx::Size();
75 // Return the alignment for the whole frame, calculated as the max of the
76 // alignment for each individual plane.
77 static gfx::Size CommonAlignment(VideoFrame::Format format) {
78 int max_sample_width = 0;
79 int max_sample_height = 0;
80 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) {
81 const gfx::Size sample_size = SampleSize(format, plane);
82 max_sample_width = std::max(max_sample_width, sample_size.width());
83 max_sample_height = std::max(max_sample_height, sample_size.height());
85 return gfx::Size(max_sample_width, max_sample_height);
88 // Returns the number of bytes per element for given |plane| and |format|. E.g.
89 // 2 for the UV plane in NV12.
90 static int BytesPerElement(VideoFrame::Format format, size_t plane) {
91 DCHECK(VideoFrame::IsValidPlane(plane, format));
92 if (format == VideoFrame::ARGB)
93 return 4;
95 if (format == VideoFrame::NV12 && plane == VideoFrame::kUVPlane)
96 return 2;
98 return 1;
101 // Rounds up |coded_size| if necessary for |format|.
102 static gfx::Size AdjustCodedSize(VideoFrame::Format format,
103 const gfx::Size& coded_size) {
104 const gfx::Size alignment = CommonAlignment(format);
105 return gfx::Size(RoundUp(coded_size.width(), alignment.width()),
106 RoundUp(coded_size.height(), alignment.height()));
109 // static
110 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
111 VideoFrame::Format format,
112 const gfx::Size& coded_size,
113 const gfx::Rect& visible_rect,
114 const gfx::Size& natural_size,
115 base::TimeDelta timestamp) {
116 switch (format) {
117 case VideoFrame::YV12:
118 case VideoFrame::YV16:
119 case VideoFrame::I420:
120 case VideoFrame::YV12A:
121 case VideoFrame::YV12J:
122 case VideoFrame::YV24:
123 case VideoFrame::YV12HD:
124 break;
126 case VideoFrame::UNKNOWN:
127 case VideoFrame::NV12:
128 case VideoFrame::NATIVE_TEXTURE:
129 #if defined(VIDEO_HOLE)
130 case VideoFrame::HOLE:
131 #endif // defined(VIDEO_HOLE)
132 case VideoFrame::ARGB:
133 NOTIMPLEMENTED();
134 return nullptr;
137 // Since we're creating a new YUV frame (and allocating memory for it
138 // ourselves), we can pad the requested |coded_size| if necessary if the
139 // request does not line up on sample boundaries.
140 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
141 DCHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
143 scoped_refptr<VideoFrame> frame(
144 new VideoFrame(format,
145 new_coded_size,
146 visible_rect,
147 natural_size,
148 scoped_ptr<gpu::MailboxHolder>(),
149 timestamp,
150 false));
151 frame->AllocateYUV();
152 return frame;
155 // static
156 std::string VideoFrame::FormatToString(VideoFrame::Format format) {
157 switch (format) {
158 case VideoFrame::UNKNOWN:
159 return "UNKNOWN";
160 case VideoFrame::YV12:
161 return "YV12";
162 case VideoFrame::YV16:
163 return "YV16";
164 case VideoFrame::I420:
165 return "I420";
166 case VideoFrame::NATIVE_TEXTURE:
167 return "NATIVE_TEXTURE";
168 #if defined(VIDEO_HOLE)
169 case VideoFrame::HOLE:
170 return "HOLE";
171 #endif // defined(VIDEO_HOLE)
172 case VideoFrame::YV12A:
173 return "YV12A";
174 case VideoFrame::YV12J:
175 return "YV12J";
176 case VideoFrame::NV12:
177 return "NV12";
178 case VideoFrame::YV24:
179 return "YV24";
180 case VideoFrame::ARGB:
181 return "ARGB";
182 case VideoFrame::YV12HD:
183 return "YV12HD";
185 NOTREACHED() << "Invalid videoframe format provided: " << format;
186 return "";
189 // static
190 bool VideoFrame::IsValidConfig(VideoFrame::Format format,
191 const gfx::Size& coded_size,
192 const gfx::Rect& visible_rect,
193 const gfx::Size& natural_size) {
194 // Check maximum limits for all formats.
195 if (coded_size.GetArea() > limits::kMaxCanvas ||
196 coded_size.width() > limits::kMaxDimension ||
197 coded_size.height() > limits::kMaxDimension ||
198 visible_rect.x() < 0 || visible_rect.y() < 0 ||
199 visible_rect.right() > coded_size.width() ||
200 visible_rect.bottom() > coded_size.height() ||
201 natural_size.GetArea() > limits::kMaxCanvas ||
202 natural_size.width() > limits::kMaxDimension ||
203 natural_size.height() > limits::kMaxDimension)
204 return false;
206 // Check format-specific width/height requirements.
207 switch (format) {
208 case VideoFrame::UNKNOWN:
209 return (coded_size.IsEmpty() && visible_rect.IsEmpty() &&
210 natural_size.IsEmpty());
212 // NATIVE_TEXTURE and HOLE have no software-allocated buffers and are
213 // allowed to skip the below check.
214 case VideoFrame::NATIVE_TEXTURE:
215 #if defined(VIDEO_HOLE)
216 case VideoFrame::HOLE:
217 #endif // defined(VIDEO_HOLE)
218 return true;
220 case VideoFrame::YV24:
221 case VideoFrame::YV12:
222 case VideoFrame::YV12J:
223 case VideoFrame::I420:
224 case VideoFrame::YV12A:
225 case VideoFrame::NV12:
226 case VideoFrame::YV12HD:
227 case VideoFrame::YV16:
228 case VideoFrame::ARGB:
229 // Check that software-allocated buffer formats are aligned correctly and
230 // not empty.
231 const gfx::Size alignment = CommonAlignment(format);
232 return RoundUp(visible_rect.right(), alignment.width()) <=
233 static_cast<size_t>(coded_size.width()) &&
234 RoundUp(visible_rect.bottom(), alignment.height()) <=
235 static_cast<size_t>(coded_size.height()) &&
236 !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
237 !natural_size.IsEmpty();
240 NOTREACHED();
241 return false;
244 // static
245 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
246 scoped_ptr<gpu::MailboxHolder> mailbox_holder,
247 const ReleaseMailboxCB& mailbox_holder_release_cb,
248 const gfx::Size& coded_size,
249 const gfx::Rect& visible_rect,
250 const gfx::Size& natural_size,
251 base::TimeDelta timestamp,
252 bool allow_overlay) {
253 scoped_refptr<VideoFrame> frame(new VideoFrame(NATIVE_TEXTURE,
254 coded_size,
255 visible_rect,
256 natural_size,
257 mailbox_holder.Pass(),
258 timestamp,
259 false));
260 frame->mailbox_holder_release_cb_ = mailbox_holder_release_cb;
261 frame->allow_overlay_ = allow_overlay;
263 return frame;
266 // static
267 scoped_refptr<VideoFrame> VideoFrame::WrapExternalPackedMemory(
268 Format format,
269 const gfx::Size& coded_size,
270 const gfx::Rect& visible_rect,
271 const gfx::Size& natural_size,
272 uint8* data,
273 size_t data_size,
274 base::SharedMemoryHandle handle,
275 size_t data_offset,
276 base::TimeDelta timestamp,
277 const base::Closure& no_longer_needed_cb) {
278 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
280 if (!IsValidConfig(format, new_coded_size, visible_rect, natural_size))
281 return NULL;
282 if (data_size < AllocationSize(format, new_coded_size))
283 return NULL;
285 switch (format) {
286 case VideoFrame::I420: {
287 scoped_refptr<VideoFrame> frame(
288 new VideoFrame(format,
289 new_coded_size,
290 visible_rect,
291 natural_size,
292 scoped_ptr<gpu::MailboxHolder>(),
293 timestamp,
294 false));
295 frame->shared_memory_handle_ = handle;
296 frame->shared_memory_offset_ = data_offset;
297 frame->strides_[kYPlane] = new_coded_size.width();
298 frame->strides_[kUPlane] = new_coded_size.width() / 2;
299 frame->strides_[kVPlane] = new_coded_size.width() / 2;
300 frame->data_[kYPlane] = data;
301 frame->data_[kUPlane] = data + new_coded_size.GetArea();
302 frame->data_[kVPlane] = data + (new_coded_size.GetArea() * 5 / 4);
303 frame->no_longer_needed_cb_ = no_longer_needed_cb;
304 return frame;
306 default:
307 NOTIMPLEMENTED();
308 return NULL;
312 // static
313 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
314 Format format,
315 const gfx::Size& coded_size,
316 const gfx::Rect& visible_rect,
317 const gfx::Size& natural_size,
318 int32 y_stride,
319 int32 u_stride,
320 int32 v_stride,
321 uint8* y_data,
322 uint8* u_data,
323 uint8* v_data,
324 base::TimeDelta timestamp,
325 const base::Closure& no_longer_needed_cb) {
326 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
327 CHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
329 scoped_refptr<VideoFrame> frame(
330 new VideoFrame(format,
331 new_coded_size,
332 visible_rect,
333 natural_size,
334 scoped_ptr<gpu::MailboxHolder>(),
335 timestamp,
336 false));
337 frame->strides_[kYPlane] = y_stride;
338 frame->strides_[kUPlane] = u_stride;
339 frame->strides_[kVPlane] = v_stride;
340 frame->data_[kYPlane] = y_data;
341 frame->data_[kUPlane] = u_data;
342 frame->data_[kVPlane] = v_data;
343 frame->no_longer_needed_cb_ = no_longer_needed_cb;
344 return frame;
347 #if defined(OS_POSIX)
348 // static
349 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
350 Format format,
351 const gfx::Size& coded_size,
352 const gfx::Rect& visible_rect,
353 const gfx::Size& natural_size,
354 const std::vector<int> dmabuf_fds,
355 base::TimeDelta timestamp,
356 const base::Closure& no_longer_needed_cb) {
357 if (!IsValidConfig(format, coded_size, visible_rect, natural_size))
358 return NULL;
360 // TODO(posciak): This is not exactly correct, it's possible for one
361 // buffer to contain more than one plane.
362 if (dmabuf_fds.size() != NumPlanes(format)) {
363 LOG(FATAL) << "Not enough dmabuf fds provided!";
364 return NULL;
367 scoped_refptr<VideoFrame> frame(
368 new VideoFrame(format,
369 coded_size,
370 visible_rect,
371 natural_size,
372 scoped_ptr<gpu::MailboxHolder>(),
373 timestamp,
374 false));
376 for (size_t i = 0; i < dmabuf_fds.size(); ++i) {
377 int duped_fd = HANDLE_EINTR(dup(dmabuf_fds[i]));
378 if (duped_fd == -1) {
379 // The already-duped in previous iterations fds will be closed when
380 // the partially-created frame drops out of scope here.
381 DLOG(ERROR) << "Failed duplicating a dmabuf fd";
382 return NULL;
385 frame->dmabuf_fds_[i].reset(duped_fd);
386 // Data is accessible only via fds.
387 frame->data_[i] = NULL;
388 frame->strides_[i] = 0;
391 frame->no_longer_needed_cb_ = no_longer_needed_cb;
392 return frame;
394 #endif
396 #if defined(OS_MACOSX)
397 // static
398 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
399 CVPixelBufferRef cv_pixel_buffer,
400 base::TimeDelta timestamp) {
401 DCHECK(cv_pixel_buffer);
402 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
404 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
405 Format format;
406 // There are very few compatible CV pixel formats, so just check each.
407 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
408 format = Format::I420;
409 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
410 format = Format::YV24;
411 } else if (cv_format == '420v') {
412 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
413 // minimum OS X and iOS SDKs permits it.
414 format = Format::NV12;
415 } else {
416 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
417 return NULL;
420 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
421 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
422 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
424 if (!IsValidConfig(format, coded_size, visible_rect, natural_size))
425 return NULL;
427 scoped_refptr<VideoFrame> frame(
428 new VideoFrame(format,
429 coded_size,
430 visible_rect,
431 natural_size,
432 scoped_ptr<gpu::MailboxHolder>(),
433 timestamp,
434 false));
436 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
437 return frame;
439 #endif
441 // static
442 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
443 const scoped_refptr<VideoFrame>& frame,
444 const gfx::Rect& visible_rect,
445 const gfx::Size& natural_size,
446 const base::Closure& no_longer_needed_cb) {
447 // NATIVE_TEXTURE frames need mailbox info propagated, and there's no support
448 // for that here yet, see http://crbug/362521.
449 CHECK_NE(frame->format(), NATIVE_TEXTURE);
451 DCHECK(frame->visible_rect().Contains(visible_rect));
452 scoped_refptr<VideoFrame> wrapped_frame(
453 new VideoFrame(frame->format(),
454 frame->coded_size(),
455 visible_rect,
456 natural_size,
457 scoped_ptr<gpu::MailboxHolder>(),
458 frame->timestamp(),
459 frame->end_of_stream()));
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 wrapped_frame->no_longer_needed_cb_ = no_longer_needed_cb;
467 return wrapped_frame;
470 // static
471 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
472 return new VideoFrame(VideoFrame::UNKNOWN,
473 gfx::Size(),
474 gfx::Rect(),
475 gfx::Size(),
476 scoped_ptr<gpu::MailboxHolder>(),
477 kNoTimestamp(),
478 true);
481 // static
482 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
483 const gfx::Size& size,
484 uint8 y, uint8 u, uint8 v,
485 base::TimeDelta timestamp) {
486 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
487 VideoFrame::YV12, size, gfx::Rect(size), size, timestamp);
488 FillYUV(frame.get(), y, u, v);
489 return frame;
492 // static
493 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
494 const uint8 kBlackY = 0x00;
495 const uint8 kBlackUV = 0x80;
496 const base::TimeDelta kZero;
497 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
500 // static
501 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
502 const gfx::Size& size) {
503 const uint8 kBlackY = 0x00;
504 const uint8 kBlackUV = 0x00;
505 const uint8 kTransparentA = 0x00;
506 const base::TimeDelta kZero;
507 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
508 VideoFrame::YV12A, size, gfx::Rect(size), size, kZero);
509 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
510 return frame;
513 #if defined(VIDEO_HOLE)
514 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
515 // maintained by the general compositor team. Please contact the following
516 // people instead:
518 // wonsik@chromium.org
519 // ycheo@chromium.org
521 // static
522 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
523 const gfx::Size& size) {
524 DCHECK(IsValidConfig(VideoFrame::HOLE, size, gfx::Rect(size), size));
525 scoped_refptr<VideoFrame> frame(
526 new VideoFrame(VideoFrame::HOLE,
527 size,
528 gfx::Rect(size),
529 size,
530 scoped_ptr<gpu::MailboxHolder>(),
531 base::TimeDelta(),
532 false));
533 return frame;
535 #endif // defined(VIDEO_HOLE)
537 // static
538 size_t VideoFrame::NumPlanes(Format format) {
539 switch (format) {
540 case VideoFrame::NATIVE_TEXTURE:
541 #if defined(VIDEO_HOLE)
542 case VideoFrame::HOLE:
543 #endif // defined(VIDEO_HOLE)
544 return 0;
545 case VideoFrame::ARGB:
546 return 1;
547 case VideoFrame::NV12:
548 return 2;
549 case VideoFrame::YV12:
550 case VideoFrame::YV16:
551 case VideoFrame::I420:
552 case VideoFrame::YV12J:
553 case VideoFrame::YV12HD:
554 case VideoFrame::YV24:
555 return 3;
556 case VideoFrame::YV12A:
557 return 4;
558 case VideoFrame::UNKNOWN:
559 break;
561 NOTREACHED() << "Unsupported video frame format: " << format;
562 return 0;
566 // static
567 size_t VideoFrame::AllocationSize(Format format, const gfx::Size& coded_size) {
568 size_t total = 0;
569 for (size_t i = 0; i < NumPlanes(format); ++i)
570 total += PlaneAllocationSize(format, i, coded_size);
571 return total;
574 // static
575 gfx::Size VideoFrame::PlaneSize(Format format,
576 size_t plane,
577 const gfx::Size& coded_size) {
578 DCHECK(IsValidPlane(plane, format));
580 int width = coded_size.width();
581 int height = coded_size.height();
582 if (format != VideoFrame::ARGB) {
583 // Align to multiple-of-two size overall. This ensures that non-subsampled
584 // planes can be addressed by pixel with the same scaling as the subsampled
585 // planes.
586 width = RoundUp(width, 2);
587 height = RoundUp(height, 2);
590 const gfx::Size subsample = SampleSize(format, plane);
591 DCHECK(width % subsample.width() == 0);
592 DCHECK(height % subsample.height() == 0);
593 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
594 height / subsample.height());
597 size_t VideoFrame::PlaneAllocationSize(Format format,
598 size_t plane,
599 const gfx::Size& coded_size) {
600 return PlaneSize(format, plane, coded_size).GetArea();
603 // static
604 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format, size_t plane) {
605 DCHECK(IsValidPlane(plane, format));
606 const int bits_per_element = 8 * BytesPerElement(format, plane);
607 const int horiz_pixels_per_element = SampleSize(format, plane).width();
608 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
609 return bits_per_element / horiz_pixels_per_element;
612 // static
613 int VideoFrame::PlaneBitsPerPixel(Format format, size_t plane) {
614 DCHECK(IsValidPlane(plane, format));
615 return PlaneHorizontalBitsPerPixel(format, plane) /
616 SampleSize(format, plane).height();
619 // Release data allocated by AllocateYUV().
620 static void ReleaseData(uint8* data) {
621 DCHECK(data);
622 base::AlignedFree(data);
625 void VideoFrame::AllocateYUV() {
626 DCHECK(format_ == YV12 || format_ == YV16 || format_ == YV12A ||
627 format_ == I420 || format_ == YV12J || format_ == YV24 ||
628 format_ == YV12HD);
629 static_assert(0 == kYPlane, "y plane data must be index 0");
631 size_t data_size = 0;
632 size_t offset[kMaxPlanes];
633 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format_); ++plane) {
634 // The *2 in alignment for height is because some formats (e.g. h264) allow
635 // interlaced coding, and then the size needs to be a multiple of two
636 // macroblocks (vertically). See
637 // libavcodec/utils.c:avcodec_align_dimensions2().
638 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2);
639 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment);
640 offset[plane] = data_size;
641 data_size += height * strides_[plane];
644 // The extra line of UV being allocated is because h264 chroma MC
645 // overreads by one line in some cases, see libavcodec/utils.c:
646 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
647 // put_h264_chroma_mc4_ssse3().
648 DCHECK(IsValidPlane(kUPlane, format_));
649 data_size += strides_[kUPlane] + kFrameSizePadding;
651 // FFmpeg expects the initialize allocation to be zero-initialized. Failure
652 // to do so can lead to unitialized value usage. See http://crbug.com/390941
653 uint8* data = reinterpret_cast<uint8*>(
654 base::AlignedAlloc(data_size, kFrameAddressAlignment));
655 memset(data, 0, data_size);
657 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format_); ++plane)
658 data_[plane] = data + offset[plane];
660 no_longer_needed_cb_ = base::Bind(&ReleaseData, data);
663 VideoFrame::VideoFrame(VideoFrame::Format format,
664 const gfx::Size& coded_size,
665 const gfx::Rect& visible_rect,
666 const gfx::Size& natural_size,
667 scoped_ptr<gpu::MailboxHolder> mailbox_holder,
668 base::TimeDelta timestamp,
669 bool end_of_stream)
670 : format_(format),
671 coded_size_(coded_size),
672 visible_rect_(visible_rect),
673 natural_size_(natural_size),
674 mailbox_holder_(mailbox_holder.Pass()),
675 shared_memory_handle_(base::SharedMemory::NULLHandle()),
676 shared_memory_offset_(0),
677 timestamp_(timestamp),
678 release_sync_point_(0),
679 end_of_stream_(end_of_stream),
680 allow_overlay_(false) {
681 DCHECK(IsValidConfig(format_, coded_size_, visible_rect_, natural_size_));
683 memset(&strides_, 0, sizeof(strides_));
684 memset(&data_, 0, sizeof(data_));
687 VideoFrame::~VideoFrame() {
688 if (!mailbox_holder_release_cb_.is_null()) {
689 uint32 release_sync_point;
691 // To ensure that changes to |release_sync_point_| are visible on this
692 // thread (imply a memory barrier).
693 base::AutoLock locker(release_sync_point_lock_);
694 release_sync_point = release_sync_point_;
696 base::ResetAndReturn(&mailbox_holder_release_cb_).Run(release_sync_point);
698 if (!no_longer_needed_cb_.is_null())
699 base::ResetAndReturn(&no_longer_needed_cb_).Run();
702 // static
703 bool VideoFrame::IsValidPlane(size_t plane, VideoFrame::Format format) {
704 return (plane < NumPlanes(format));
707 int VideoFrame::stride(size_t plane) const {
708 DCHECK(IsValidPlane(plane, format_));
709 return strides_[plane];
712 // static
713 size_t VideoFrame::RowBytes(size_t plane,
714 VideoFrame::Format format,
715 int width) {
716 DCHECK(IsValidPlane(plane, format));
717 return BytesPerElement(format, plane) * Columns(plane, format, width);
720 int VideoFrame::row_bytes(size_t plane) const {
721 return RowBytes(plane, format_, coded_size_.width());
724 // static
725 size_t VideoFrame::Rows(size_t plane, VideoFrame::Format format, int height) {
726 DCHECK(IsValidPlane(plane, format));
727 const int sample_height = SampleSize(format, plane).height();
728 return RoundUp(height, sample_height) / sample_height;
731 // static
732 size_t VideoFrame::Columns(size_t plane, Format format, int width) {
733 DCHECK(IsValidPlane(plane, format));
734 const int sample_width = SampleSize(format, plane).width();
735 return RoundUp(width, sample_width) / sample_width;
738 int VideoFrame::rows(size_t plane) const {
739 return Rows(plane, format_, coded_size_.height());
742 const uint8* VideoFrame::data(size_t plane) const {
743 DCHECK(IsValidPlane(plane, format_));
744 return data_[plane];
747 uint8* VideoFrame::data(size_t plane) {
748 DCHECK(IsValidPlane(plane, format_));
749 return data_[plane];
752 const uint8* VideoFrame::visible_data(size_t plane) const {
753 DCHECK(IsValidPlane(plane, format_));
755 // Calculate an offset that is properly aligned for all planes.
756 const gfx::Size alignment = CommonAlignment(format_);
757 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
758 RoundDown(visible_rect_.y(), alignment.height()));
760 const gfx::Size subsample = SampleSize(format_, plane);
761 DCHECK(offset.x() % subsample.width() == 0);
762 DCHECK(offset.y() % subsample.height() == 0);
763 return data(plane) +
764 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
765 BytesPerElement(format_, plane) * // Column offset.
766 (offset.x() / subsample.width());
769 uint8* VideoFrame::visible_data(size_t plane) {
770 return const_cast<uint8*>(
771 static_cast<const VideoFrame*>(this)->visible_data(plane));
774 const gpu::MailboxHolder* VideoFrame::mailbox_holder() const {
775 DCHECK_EQ(format_, NATIVE_TEXTURE);
776 return mailbox_holder_.get();
779 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
780 return shared_memory_handle_;
783 size_t VideoFrame::shared_memory_offset() const {
784 return shared_memory_offset_;
787 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
788 DCHECK_EQ(format_, NATIVE_TEXTURE);
789 base::AutoLock locker(release_sync_point_lock_);
790 // Must wait on the previous sync point before inserting a new sync point so
791 // that |mailbox_holder_release_cb_| guarantees the previous sync point
792 // occurred when it waits on |release_sync_point_|.
793 if (release_sync_point_)
794 client->WaitSyncPoint(release_sync_point_);
795 release_sync_point_ = client->InsertSyncPoint();
798 #if defined(OS_POSIX)
799 int VideoFrame::dmabuf_fd(size_t plane) const {
800 return dmabuf_fds_[plane].get();
802 #endif
804 #if defined(OS_MACOSX)
805 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
806 return cv_pixel_buffer_.get();
808 #endif
810 void VideoFrame::HashFrameForTesting(base::MD5Context* context) {
811 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
812 for (int row = 0; row < rows(plane); ++row) {
813 base::MD5Update(context, base::StringPiece(
814 reinterpret_cast<char*>(data(plane) + stride(plane) * row),
815 row_bytes(plane)));
820 } // namespace media