Add Smart Lock warm welcome on Windows.
[chromium-blink-merge.git] / media / base / video_frame.cc
blobb69f4f59dddaac8e983309eda2f73f93e682f2b4
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/base/video_frame.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/memory/aligned_memory.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/stringprintf.h"
15 #include "media/base/limits.h"
16 #include "media/base/timestamp_constants.h"
17 #include "media/base/video_util.h"
18 #include "ui/gfx/geometry/point.h"
20 namespace media {
22 static bool IsPowerOfTwo(size_t x) {
23 return x != 0 && (x & (x - 1)) == 0;
26 static inline size_t RoundUp(size_t value, size_t alignment) {
27 DCHECK(IsPowerOfTwo(alignment));
28 return ((value + (alignment - 1)) & ~(alignment - 1));
31 static inline size_t RoundDown(size_t value, size_t alignment) {
32 DCHECK(IsPowerOfTwo(alignment));
33 return value & ~(alignment - 1);
36 static std::string ConfigToString(const VideoPixelFormat format,
37 const VideoFrame::StorageType storage_type,
38 const gfx::Size& coded_size,
39 const gfx::Rect& visible_rect,
40 const gfx::Size& natural_size) {
41 return base::StringPrintf(
42 "format:%s coded_size:%s visible_rect:%s natural_size:%s",
43 VideoPixelFormatToString(format).c_str(), coded_size.ToString().c_str(),
44 visible_rect.ToString().c_str(), natural_size.ToString().c_str());
47 // Returns true if |plane| is a valid plane index for the given |format|.
48 static bool IsValidPlane(size_t plane, VideoPixelFormat format) {
49 DCHECK_LE(VideoFrame::NumPlanes(format),
50 static_cast<size_t>(VideoFrame::kMaxPlanes));
51 return (plane < VideoFrame::NumPlanes(format));
54 // Returns true if |frame| is accesible mapped in the VideoFrame memory space.
55 // static
56 static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type) {
57 return
58 #if defined(OS_LINUX)
59 // This is not strictly needed but makes explicit that, at VideoFrame
60 // level, DmaBufs are not mappable from userspace.
61 storage_type != VideoFrame::STORAGE_DMABUFS &&
62 #endif
63 (storage_type == VideoFrame::STORAGE_UNOWNED_MEMORY ||
64 storage_type == VideoFrame::STORAGE_OWNED_MEMORY ||
65 storage_type == VideoFrame::STORAGE_SHMEM);
68 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
69 // for the U-plane in PIXEL_FORMAT_I420.
70 static gfx::Size SampleSize(VideoPixelFormat format, size_t plane) {
71 DCHECK(IsValidPlane(plane, format));
73 switch (plane) {
74 case VideoFrame::kYPlane:
75 case VideoFrame::kAPlane:
76 return gfx::Size(1, 1);
78 case VideoFrame::kUPlane: // and VideoFrame::kUVPlane:
79 case VideoFrame::kVPlane:
80 switch (format) {
81 case PIXEL_FORMAT_YV24:
82 return gfx::Size(1, 1);
84 case PIXEL_FORMAT_YV16:
85 return gfx::Size(2, 1);
87 case PIXEL_FORMAT_YV12:
88 case PIXEL_FORMAT_I420:
89 case PIXEL_FORMAT_YV12A:
90 case PIXEL_FORMAT_NV12:
91 case PIXEL_FORMAT_NV21:
92 return gfx::Size(2, 2);
94 case PIXEL_FORMAT_UNKNOWN:
95 case PIXEL_FORMAT_UYVY:
96 case PIXEL_FORMAT_YUY2:
97 case PIXEL_FORMAT_ARGB:
98 case PIXEL_FORMAT_XRGB:
99 case PIXEL_FORMAT_RGB24:
100 case PIXEL_FORMAT_RGB32:
101 case PIXEL_FORMAT_MJPEG:
102 break;
105 NOTREACHED();
106 return gfx::Size();
109 // Return the alignment for the whole frame, calculated as the max of the
110 // alignment for each individual plane.
111 static gfx::Size CommonAlignment(VideoPixelFormat format) {
112 int max_sample_width = 0;
113 int max_sample_height = 0;
114 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) {
115 const gfx::Size sample_size = SampleSize(format, plane);
116 max_sample_width = std::max(max_sample_width, sample_size.width());
117 max_sample_height = std::max(max_sample_height, sample_size.height());
119 return gfx::Size(max_sample_width, max_sample_height);
122 // Returns the number of bytes per element for given |plane| and |format|.
123 static int BytesPerElement(VideoPixelFormat format, size_t plane) {
124 DCHECK(IsValidPlane(plane, format));
125 switch (format) {
126 case PIXEL_FORMAT_ARGB:
127 case PIXEL_FORMAT_XRGB:
128 case PIXEL_FORMAT_RGB32:
129 return 4;
130 case PIXEL_FORMAT_RGB24:
131 return 3;
132 case PIXEL_FORMAT_UYVY:
133 case PIXEL_FORMAT_YUY2:
134 return 2;
135 case PIXEL_FORMAT_NV12:
136 case PIXEL_FORMAT_NV21: {
137 static const int bytes_per_element[] = {1, 2};
138 DCHECK_LT(plane, arraysize(bytes_per_element));
139 return bytes_per_element[plane];
141 case PIXEL_FORMAT_YV12:
142 case PIXEL_FORMAT_I420:
143 case PIXEL_FORMAT_YV16:
144 case PIXEL_FORMAT_YV12A:
145 case PIXEL_FORMAT_YV24:
146 return 1;
147 case PIXEL_FORMAT_MJPEG:
148 return 0;
149 case PIXEL_FORMAT_UNKNOWN:
150 break;
152 NOTREACHED();
153 return 0;
156 // static
157 bool VideoFrame::IsValidConfig(VideoPixelFormat format,
158 StorageType storage_type,
159 const gfx::Size& coded_size,
160 const gfx::Rect& visible_rect,
161 const gfx::Size& natural_size) {
162 // Check maximum limits for all formats.
163 if (coded_size.GetArea() > limits::kMaxCanvas ||
164 coded_size.width() > limits::kMaxDimension ||
165 coded_size.height() > limits::kMaxDimension ||
166 visible_rect.x() < 0 || visible_rect.y() < 0 ||
167 visible_rect.right() > coded_size.width() ||
168 visible_rect.bottom() > coded_size.height() ||
169 natural_size.GetArea() > limits::kMaxCanvas ||
170 natural_size.width() > limits::kMaxDimension ||
171 natural_size.height() > limits::kMaxDimension)
172 return false;
174 // TODO(mcasas): Remove parameter |storage_type| when the opaque storage types
175 // comply with the checks below. Right now we skip them.
176 if (!IsStorageTypeMappable(storage_type))
177 return true;
179 // Make sure new formats are properly accounted for in the method.
180 static_assert(PIXEL_FORMAT_MAX == 14,
181 "Added pixel format, please review IsValidConfig()");
183 if (format == PIXEL_FORMAT_UNKNOWN) {
184 return coded_size.IsEmpty() && visible_rect.IsEmpty() &&
185 natural_size.IsEmpty();
188 // Check that software-allocated buffer formats are not empty.
189 return !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
190 !natural_size.IsEmpty();
193 // static
194 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(VideoPixelFormat format,
195 const gfx::Size& coded_size,
196 const gfx::Rect& visible_rect,
197 const gfx::Size& natural_size,
198 base::TimeDelta timestamp) {
199 return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
200 timestamp, false);
203 // static
204 scoped_refptr<VideoFrame> VideoFrame::CreateZeroInitializedFrame(
205 VideoPixelFormat format,
206 const gfx::Size& coded_size,
207 const gfx::Rect& visible_rect,
208 const gfx::Size& natural_size,
209 base::TimeDelta timestamp) {
210 return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
211 timestamp, true);
214 // static
215 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
216 VideoPixelFormat format,
217 const gpu::MailboxHolder& mailbox_holder,
218 const ReleaseMailboxCB& mailbox_holder_release_cb,
219 const gfx::Size& coded_size,
220 const gfx::Rect& visible_rect,
221 const gfx::Size& natural_size,
222 base::TimeDelta timestamp) {
223 if (format != PIXEL_FORMAT_ARGB &&
224 format != PIXEL_FORMAT_UYVY &&
225 format != PIXEL_FORMAT_NV12) {
226 DLOG(ERROR) << "Unsupported pixel format supported, got "
227 << VideoPixelFormatToString(format);
228 return nullptr;
230 const StorageType storage = STORAGE_OPAQUE;
231 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
232 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
233 << ConfigToString(format, storage, coded_size, visible_rect,
234 natural_size);
235 return nullptr;
238 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
239 mailbox_holders[kARGBPlane] = mailbox_holder;
240 return new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
241 mailbox_holders, mailbox_holder_release_cb, timestamp);
244 // static
245 scoped_refptr<VideoFrame> VideoFrame::WrapYUV420NativeTextures(
246 const gpu::MailboxHolder& y_mailbox_holder,
247 const gpu::MailboxHolder& u_mailbox_holder,
248 const gpu::MailboxHolder& v_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 const StorageType storage = STORAGE_OPAQUE;
255 VideoPixelFormat format = PIXEL_FORMAT_I420;
256 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
257 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
258 << ConfigToString(format, storage, coded_size, visible_rect,
259 natural_size);
260 return nullptr;
263 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
264 mailbox_holders[kYPlane] = y_mailbox_holder;
265 mailbox_holders[kUPlane] = u_mailbox_holder;
266 mailbox_holders[kVPlane] = v_mailbox_holder;
267 return new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
268 mailbox_holders, mailbox_holder_release_cb, timestamp);
271 // static
272 scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
273 VideoPixelFormat format,
274 const gfx::Size& coded_size,
275 const gfx::Rect& visible_rect,
276 const gfx::Size& natural_size,
277 uint8* data,
278 size_t data_size,
279 base::TimeDelta timestamp) {
280 return WrapExternalStorage(format, STORAGE_UNOWNED_MEMORY, coded_size,
281 visible_rect, natural_size, data, data_size,
282 timestamp, base::SharedMemory::NULLHandle(), 0);
285 // static
286 scoped_refptr<VideoFrame> VideoFrame::WrapExternalSharedMemory(
287 VideoPixelFormat format,
288 const gfx::Size& coded_size,
289 const gfx::Rect& visible_rect,
290 const gfx::Size& natural_size,
291 uint8* data,
292 size_t data_size,
293 base::SharedMemoryHandle handle,
294 size_t data_offset,
295 base::TimeDelta timestamp) {
296 return WrapExternalStorage(format, STORAGE_SHMEM, coded_size, visible_rect,
297 natural_size, data, data_size, timestamp, handle,
298 data_offset);
301 // static
302 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
303 VideoPixelFormat format,
304 const gfx::Size& coded_size,
305 const gfx::Rect& visible_rect,
306 const gfx::Size& natural_size,
307 int32 y_stride,
308 int32 u_stride,
309 int32 v_stride,
310 uint8* y_data,
311 uint8* u_data,
312 uint8* v_data,
313 base::TimeDelta timestamp) {
314 const StorageType storage = STORAGE_UNOWNED_MEMORY;
315 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
316 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
317 << ConfigToString(format, storage, coded_size, visible_rect,
318 natural_size);
319 return nullptr;
322 scoped_refptr<VideoFrame> frame(new VideoFrame(
323 format, storage, coded_size, visible_rect, natural_size, timestamp));
324 frame->strides_[kYPlane] = y_stride;
325 frame->strides_[kUPlane] = u_stride;
326 frame->strides_[kVPlane] = v_stride;
327 frame->data_[kYPlane] = y_data;
328 frame->data_[kUPlane] = u_data;
329 frame->data_[kVPlane] = v_data;
330 return frame;
333 #if defined(OS_LINUX)
334 // static
335 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
336 VideoPixelFormat format,
337 const gfx::Size& coded_size,
338 const gfx::Rect& visible_rect,
339 const gfx::Size& natural_size,
340 const std::vector<int>& dmabuf_fds,
341 base::TimeDelta timestamp) {
342 #if defined(OS_CHROMEOS)
343 DCHECK_EQ(format, PIXEL_FORMAT_NV12);
344 #endif
346 const StorageType storage = STORAGE_DMABUFS;
347 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
348 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
349 << ConfigToString(format, storage, coded_size, visible_rect,
350 natural_size);
351 return nullptr;
354 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
355 scoped_refptr<VideoFrame> frame =
356 new VideoFrame(format, storage, coded_size, visible_rect, natural_size,
357 mailbox_holders, ReleaseMailboxCB(), timestamp);
358 if (!frame || !frame->DuplicateFileDescriptors(dmabuf_fds))
359 return nullptr;
360 return frame;
362 #endif
364 #if defined(OS_MACOSX)
365 // static
366 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
367 CVPixelBufferRef cv_pixel_buffer,
368 base::TimeDelta timestamp) {
369 DCHECK(cv_pixel_buffer);
370 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
372 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
373 VideoPixelFormat format;
374 // There are very few compatible CV pixel formats, so just check each.
375 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
376 format = PIXEL_FORMAT_I420;
377 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
378 format = PIXEL_FORMAT_YV24;
379 } else if (cv_format == '420v') {
380 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
381 // minimum OS X and iOS SDKs permits it.
382 format = PIXEL_FORMAT_NV12;
383 } else {
384 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
385 return nullptr;
388 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
389 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
390 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
391 const StorageType storage = STORAGE_UNOWNED_MEMORY;
393 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
394 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
395 << ConfigToString(format, storage, coded_size, visible_rect,
396 natural_size);
397 return nullptr;
400 scoped_refptr<VideoFrame> frame(new VideoFrame(
401 format, storage, coded_size, visible_rect, natural_size, timestamp));
403 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
404 return frame;
406 #endif
408 // static
409 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
410 const scoped_refptr<VideoFrame>& frame,
411 const gfx::Rect& visible_rect,
412 const gfx::Size& natural_size) {
413 // Frames with textures need mailbox info propagated, and there's no support
414 // for that here yet, see http://crbug/362521.
415 CHECK(!frame->HasTextures());
417 DCHECK(frame->visible_rect().Contains(visible_rect));
419 if (!IsValidConfig(frame->format(), frame->storage_type(),
420 frame->coded_size(), visible_rect, natural_size)) {
421 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
422 << ConfigToString(frame->format(), frame->storage_type(),
423 frame->coded_size(), visible_rect,
424 natural_size);
425 return nullptr;
428 scoped_refptr<VideoFrame> wrapping_frame(new VideoFrame(
429 frame->format(), frame->storage_type(), frame->coded_size(), visible_rect,
430 natural_size, frame->timestamp()));
431 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
432 wrapping_frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM,
433 true);
436 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
437 wrapping_frame->strides_[i] = frame->stride(i);
438 wrapping_frame->data_[i] = frame->data(i);
441 #if defined(OS_LINUX)
442 // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
443 if (frame->storage_type() == STORAGE_DMABUFS) {
444 std::vector<int> original_fds;
445 for (size_t i = 0; i < kMaxPlanes; ++i)
446 original_fds.push_back(frame->dmabuf_fd(i));
447 if (!wrapping_frame->DuplicateFileDescriptors(original_fds))
448 return nullptr;
450 #endif
452 return wrapping_frame;
455 // static
456 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
457 scoped_refptr<VideoFrame> frame =
458 new VideoFrame(PIXEL_FORMAT_UNKNOWN, STORAGE_UNKNOWN, gfx::Size(),
459 gfx::Rect(), gfx::Size(), kNoTimestamp());
460 frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM, true);
461 return frame;
464 // static
465 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
466 const gfx::Size& size,
467 uint8 y, uint8 u, uint8 v,
468 base::TimeDelta timestamp) {
469 scoped_refptr<VideoFrame> frame =
470 CreateFrame(PIXEL_FORMAT_YV12, size, gfx::Rect(size), size, timestamp);
471 FillYUV(frame.get(), y, u, v);
472 return frame;
475 // static
476 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
477 const uint8 kBlackY = 0x00;
478 const uint8 kBlackUV = 0x80;
479 const base::TimeDelta kZero;
480 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
483 // static
484 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
485 const gfx::Size& size) {
486 const uint8 kBlackY = 0x00;
487 const uint8 kBlackUV = 0x00;
488 const uint8 kTransparentA = 0x00;
489 const base::TimeDelta kZero;
490 scoped_refptr<VideoFrame> frame =
491 CreateFrame(PIXEL_FORMAT_YV12A, size, gfx::Rect(size), size, kZero);
492 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
493 return frame;
496 #if defined(VIDEO_HOLE)
497 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
498 // maintained by the general compositor team. Please contact
499 // wonsik@chromium.org .
501 // static
502 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
503 const gfx::Size& size) {
504 const VideoPixelFormat format = PIXEL_FORMAT_UNKNOWN;
505 const StorageType storage = STORAGE_HOLE;
506 const gfx::Rect visible_rect = gfx::Rect(size);
507 if (!IsValidConfig(format, storage, size, visible_rect, size)) {
508 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
509 << ConfigToString(format, storage, size, visible_rect, size);
510 return nullptr;
512 scoped_refptr<VideoFrame> frame(new VideoFrame(
513 format, storage, size, gfx::Rect(size), size, base::TimeDelta()));
514 return frame;
516 #endif // defined(VIDEO_HOLE)
518 // static
519 size_t VideoFrame::NumPlanes(VideoPixelFormat format) {
520 switch (format) {
521 case PIXEL_FORMAT_UYVY:
522 case PIXEL_FORMAT_YUY2:
523 case PIXEL_FORMAT_ARGB:
524 case PIXEL_FORMAT_XRGB:
525 case PIXEL_FORMAT_RGB24:
526 case PIXEL_FORMAT_RGB32:
527 case PIXEL_FORMAT_MJPEG:
528 return 1;
529 case PIXEL_FORMAT_NV12:
530 case PIXEL_FORMAT_NV21:
531 return 2;
532 case PIXEL_FORMAT_I420:
533 case PIXEL_FORMAT_YV12:
534 case PIXEL_FORMAT_YV16:
535 case PIXEL_FORMAT_YV24:
536 return 3;
537 case PIXEL_FORMAT_YV12A:
538 return 4;
539 case PIXEL_FORMAT_UNKNOWN:
540 break;
542 NOTREACHED() << "Unsupported video frame format: " << format;
543 return 0;
546 // static
547 size_t VideoFrame::AllocationSize(VideoPixelFormat format,
548 const gfx::Size& coded_size) {
549 size_t total = 0;
550 for (size_t i = 0; i < NumPlanes(format); ++i)
551 total += PlaneSize(format, i, coded_size).GetArea();
552 return total;
555 // static
556 gfx::Size VideoFrame::PlaneSize(VideoPixelFormat format,
557 size_t plane,
558 const gfx::Size& coded_size) {
559 DCHECK(IsValidPlane(plane, format));
561 int width = coded_size.width();
562 int height = coded_size.height();
563 if (format != PIXEL_FORMAT_ARGB) {
564 // Align to multiple-of-two size overall. This ensures that non-subsampled
565 // planes can be addressed by pixel with the same scaling as the subsampled
566 // planes.
567 width = RoundUp(width, 2);
568 height = RoundUp(height, 2);
571 const gfx::Size subsample = SampleSize(format, plane);
572 DCHECK(width % subsample.width() == 0);
573 DCHECK(height % subsample.height() == 0);
574 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
575 height / subsample.height());
578 // static
579 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format,
580 size_t plane) {
581 DCHECK(IsValidPlane(plane, format));
582 const int bits_per_element = 8 * BytesPerElement(format, plane);
583 const int horiz_pixels_per_element = SampleSize(format, plane).width();
584 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
585 return bits_per_element / horiz_pixels_per_element;
588 // static
589 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format, size_t plane) {
590 DCHECK(IsValidPlane(plane, format));
591 return PlaneHorizontalBitsPerPixel(format, plane) /
592 SampleSize(format, plane).height();
595 // static
596 size_t VideoFrame::RowBytes(size_t plane, VideoPixelFormat format, int width) {
597 DCHECK(IsValidPlane(plane, format));
598 return BytesPerElement(format, plane) * Columns(plane, format, width);
601 // static
602 size_t VideoFrame::Rows(size_t plane, VideoPixelFormat format, int height) {
603 DCHECK(IsValidPlane(plane, format));
604 const int sample_height = SampleSize(format, plane).height();
605 return RoundUp(height, sample_height) / sample_height;
608 // static
609 size_t VideoFrame::Columns(size_t plane, VideoPixelFormat format, int width) {
610 DCHECK(IsValidPlane(plane, format));
611 const int sample_width = SampleSize(format, plane).width();
612 return RoundUp(width, sample_width) / sample_width;
615 // static
616 void VideoFrame::HashFrameForTesting(base::MD5Context* context,
617 const scoped_refptr<VideoFrame>& frame) {
618 DCHECK(context);
619 for (size_t plane = 0; plane < NumPlanes(frame->format()); ++plane) {
620 for (int row = 0; row < frame->rows(plane); ++row) {
621 base::MD5Update(
622 context,
623 base::StringPiece(reinterpret_cast<char*>(frame->data(plane) +
624 frame->stride(plane) * row),
625 frame->row_bytes(plane)));
630 bool VideoFrame::IsMappable() const {
631 return IsStorageTypeMappable(storage_type_);
634 bool VideoFrame::HasTextures() const {
635 return !mailbox_holders_[0].mailbox.IsZero();
638 int VideoFrame::stride(size_t plane) const {
639 DCHECK(IsValidPlane(plane, format_));
640 return strides_[plane];
643 int VideoFrame::row_bytes(size_t plane) const {
644 return RowBytes(plane, format_, coded_size_.width());
647 int VideoFrame::rows(size_t plane) const {
648 return Rows(plane, format_, coded_size_.height());
651 const uint8* VideoFrame::data(size_t plane) const {
652 DCHECK(IsValidPlane(plane, format_));
653 DCHECK(IsMappable());
654 return data_[plane];
657 uint8* VideoFrame::data(size_t plane) {
658 DCHECK(IsValidPlane(plane, format_));
659 DCHECK(IsMappable());
660 return data_[plane];
663 const uint8* VideoFrame::visible_data(size_t plane) const {
664 DCHECK(IsValidPlane(plane, format_));
665 DCHECK(IsMappable());
667 // Calculate an offset that is properly aligned for all planes.
668 const gfx::Size alignment = CommonAlignment(format_);
669 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
670 RoundDown(visible_rect_.y(), alignment.height()));
672 const gfx::Size subsample = SampleSize(format_, plane);
673 DCHECK(offset.x() % subsample.width() == 0);
674 DCHECK(offset.y() % subsample.height() == 0);
675 return data(plane) +
676 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
677 BytesPerElement(format_, plane) * // Column offset.
678 (offset.x() / subsample.width());
681 uint8* VideoFrame::visible_data(size_t plane) {
682 return const_cast<uint8*>(
683 static_cast<const VideoFrame*>(this)->visible_data(plane));
686 const gpu::MailboxHolder&
687 VideoFrame::mailbox_holder(size_t texture_index) const {
688 DCHECK(HasTextures());
689 DCHECK(IsValidPlane(texture_index, format_));
690 return mailbox_holders_[texture_index];
693 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
694 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
695 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
696 return shared_memory_handle_;
699 size_t VideoFrame::shared_memory_offset() const {
700 DCHECK_EQ(storage_type_, STORAGE_SHMEM);
701 DCHECK(shared_memory_handle_ != base::SharedMemory::NULLHandle());
702 return shared_memory_offset_;
705 #if defined(OS_LINUX)
706 int VideoFrame::dmabuf_fd(size_t plane) const {
707 DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
708 DCHECK(IsValidPlane(plane, format_));
709 return dmabuf_fds_[plane].get();
712 bool VideoFrame::DuplicateFileDescriptors(const std::vector<int>& in_fds) {
713 // TODO(mcasas): Support offsets for e.g. multiplanar inside a single |in_fd|.
715 storage_type_ = STORAGE_DMABUFS;
716 // TODO(posciak): This is not exactly correct, it's possible for one
717 // buffer to contain more than one plane.
718 if (in_fds.size() != NumPlanes(format_)) {
719 LOG(FATAL) << "Not enough dmabuf fds provided, got: " << in_fds.size()
720 << ", expected: " << NumPlanes(format_);
721 return false;
724 // Make sure that all fds are closed if any dup() fails,
725 base::ScopedFD temp_dmabuf_fds[kMaxPlanes];
726 for (size_t i = 0; i < in_fds.size(); ++i) {
727 temp_dmabuf_fds[i] = base::ScopedFD(HANDLE_EINTR(dup(in_fds[i])));
728 if (!temp_dmabuf_fds[i].is_valid()) {
729 DPLOG(ERROR) << "Failed duplicating a dmabuf fd";
730 return false;
733 for (size_t i = 0; i < kMaxPlanes; ++i)
734 dmabuf_fds_[i].reset(temp_dmabuf_fds[i].release());
736 return true;
738 #endif
740 void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle) {
741 storage_type_ = STORAGE_SHMEM;
742 shared_memory_handle_ = handle;
745 #if defined(OS_MACOSX)
746 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
747 return cv_pixel_buffer_.get();
749 #endif
751 void VideoFrame::AddDestructionObserver(const base::Closure& callback) {
752 DCHECK(!callback.is_null());
753 done_callbacks_.push_back(callback);
756 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
757 DCHECK(HasTextures());
758 base::AutoLock locker(release_sync_point_lock_);
759 // Must wait on the previous sync point before inserting a new sync point so
760 // that |mailbox_holders_release_cb_| guarantees the previous sync point
761 // occurred when it waits on |release_sync_point_|.
762 if (release_sync_point_)
763 client->WaitSyncPoint(release_sync_point_);
764 release_sync_point_ = client->InsertSyncPoint();
767 // static
768 scoped_refptr<VideoFrame> VideoFrame::WrapExternalStorage(
769 VideoPixelFormat format,
770 StorageType storage_type,
771 const gfx::Size& coded_size,
772 const gfx::Rect& visible_rect,
773 const gfx::Size& natural_size,
774 uint8* data,
775 size_t data_size,
776 base::TimeDelta timestamp,
777 base::SharedMemoryHandle handle,
778 size_t data_offset) {
779 DCHECK(IsStorageTypeMappable(storage_type));
781 if (format != PIXEL_FORMAT_I420) {
782 DLOG(ERROR) << "Only PIXEL_FORMAT_I420 format supported: "
783 << VideoPixelFormatToString(format);
784 return nullptr;
787 if (!IsValidConfig(format, storage_type, coded_size, visible_rect,
788 natural_size)) {
789 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
790 << ConfigToString(format, storage_type, coded_size,
791 visible_rect, natural_size);
792 return nullptr;
795 scoped_refptr<VideoFrame> frame;
796 if (storage_type == STORAGE_SHMEM) {
797 frame = new VideoFrame(format, storage_type, coded_size, visible_rect,
798 natural_size, timestamp, handle, data_offset);
799 } else {
800 frame = new VideoFrame(format, storage_type, coded_size, visible_rect,
801 natural_size, timestamp);
803 frame->strides_[kYPlane] = coded_size.width();
804 frame->strides_[kUPlane] = coded_size.width() / 2;
805 frame->strides_[kVPlane] = coded_size.width() / 2;
806 frame->data_[kYPlane] = data;
807 frame->data_[kUPlane] = data + coded_size.GetArea();
808 frame->data_[kVPlane] = data + (coded_size.GetArea() * 5 / 4);
809 return frame;
812 VideoFrame::VideoFrame(VideoPixelFormat format,
813 StorageType storage_type,
814 const gfx::Size& coded_size,
815 const gfx::Rect& visible_rect,
816 const gfx::Size& natural_size,
817 base::TimeDelta timestamp)
818 : format_(format),
819 storage_type_(storage_type),
820 coded_size_(coded_size),
821 visible_rect_(visible_rect),
822 natural_size_(natural_size),
823 shared_memory_handle_(base::SharedMemory::NULLHandle()),
824 shared_memory_offset_(0),
825 timestamp_(timestamp),
826 release_sync_point_(0) {
827 DCHECK(IsValidConfig(format_, storage_type, coded_size_, visible_rect_,
828 natural_size_));
829 memset(&mailbox_holders_, 0, sizeof(mailbox_holders_));
830 memset(&strides_, 0, sizeof(strides_));
831 memset(&data_, 0, sizeof(data_));
834 VideoFrame::VideoFrame(VideoPixelFormat format,
835 StorageType storage_type,
836 const gfx::Size& coded_size,
837 const gfx::Rect& visible_rect,
838 const gfx::Size& natural_size,
839 base::TimeDelta timestamp,
840 base::SharedMemoryHandle handle,
841 size_t shared_memory_offset)
842 : VideoFrame(format,
843 storage_type,
844 coded_size,
845 visible_rect,
846 natural_size,
847 timestamp) {
848 DCHECK_EQ(storage_type, STORAGE_SHMEM);
849 AddSharedMemoryHandle(handle);
850 shared_memory_offset_ = shared_memory_offset;
853 VideoFrame::VideoFrame(VideoPixelFormat format,
854 StorageType storage_type,
855 const gfx::Size& coded_size,
856 const gfx::Rect& visible_rect,
857 const gfx::Size& natural_size,
858 const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
859 const ReleaseMailboxCB& mailbox_holder_release_cb,
860 base::TimeDelta timestamp)
861 : VideoFrame(format,
862 storage_type,
863 coded_size,
864 visible_rect,
865 natural_size,
866 timestamp) {
867 memcpy(&mailbox_holders_, mailbox_holders, sizeof(mailbox_holders_));
868 mailbox_holders_release_cb_ = mailbox_holder_release_cb;
871 VideoFrame::~VideoFrame() {
872 if (!mailbox_holders_release_cb_.is_null()) {
873 uint32 release_sync_point;
875 // To ensure that changes to |release_sync_point_| are visible on this
876 // thread (imply a memory barrier).
877 base::AutoLock locker(release_sync_point_lock_);
878 release_sync_point = release_sync_point_;
880 base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_point);
883 for (auto& callback : done_callbacks_)
884 base::ResetAndReturn(&callback).Run();
887 // static
888 scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal(
889 VideoPixelFormat format,
890 const gfx::Size& coded_size,
891 const gfx::Rect& visible_rect,
892 const gfx::Size& natural_size,
893 base::TimeDelta timestamp,
894 bool zero_initialize_memory) {
895 if (!IsYuvPlanar(format)) {
896 NOTIMPLEMENTED();
897 return nullptr;
900 // Since we're creating a new YUV frame (and allocating memory for it
901 // ourselves), we can pad the requested |coded_size| if necessary if the
902 // request does not line up on sample boundaries. See discussion at
903 // http://crrev.com/1240833003
904 const gfx::Size alignment = CommonAlignment(format);
905 const gfx::Size new_coded_size =
906 gfx::Size(RoundUp(coded_size.width(), alignment.width()),
907 RoundUp(coded_size.height(), alignment.height()));
908 DCHECK((new_coded_size.width() % alignment.width() == 0) &&
909 (new_coded_size.height() % alignment.height() == 0));
911 const StorageType storage = STORAGE_OWNED_MEMORY;
912 if (!IsValidConfig(format, storage, new_coded_size, visible_rect,
913 natural_size)) {
914 DLOG(ERROR) << __FUNCTION__ << " Invalid config."
915 << ConfigToString(format, storage, coded_size, visible_rect,
916 natural_size);
917 return nullptr;
920 scoped_refptr<VideoFrame> frame(new VideoFrame(
921 format, storage, new_coded_size, visible_rect, natural_size, timestamp));
922 frame->AllocateYUV(zero_initialize_memory);
923 return frame;
926 void VideoFrame::AllocateYUV(bool zero_initialize_memory) {
927 DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
928 static_assert(0 == kYPlane, "y plane data must be index 0");
930 size_t data_size = 0;
931 size_t offset[kMaxPlanes];
932 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) {
933 // The *2 in alignment for height is because some formats (e.g. h264) allow
934 // interlaced coding, and then the size needs to be a multiple of two
935 // macroblocks (vertically). See
936 // libavcodec/utils.c:avcodec_align_dimensions2().
937 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2);
938 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment);
939 offset[plane] = data_size;
940 data_size += height * strides_[plane];
943 // The extra line of UV being allocated is because h264 chroma MC
944 // overreads by one line in some cases, see libavcodec/utils.c:
945 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
946 // put_h264_chroma_mc4_ssse3().
947 DCHECK(IsValidPlane(kUPlane, format_));
948 data_size += strides_[kUPlane] + kFrameSizePadding;
950 uint8* data = reinterpret_cast<uint8*>(
951 base::AlignedAlloc(data_size, kFrameAddressAlignment));
952 if (zero_initialize_memory)
953 memset(data, 0, data_size);
955 for (size_t plane = 0; plane < NumPlanes(format_); ++plane)
956 data_[plane] = data + offset[plane];
958 AddDestructionObserver(base::Bind(&base::AlignedFree, data));
961 } // namespace media