Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / media / base / mac / video_frame_mac.cc
blobe532ddc7511089fd3eb2bd8a1589a3dd07b2ec31
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/mac/video_frame_mac.h"
7 #include <algorithm>
9 #include "media/base/mac/corevideo_glue.h"
10 #include "media/base/video_frame.h"
12 namespace media {
14 namespace {
16 // Maximum number of planes supported by this implementation.
17 const int kMaxPlanes = 3;
19 // CVPixelBuffer release callback. See |GetCvPixelBufferRepresentation()|.
20 void CvPixelBufferReleaseCallback(void* frame_ref,
21 const void* data,
22 size_t size,
23 size_t num_planes,
24 const void* planes[]) {
25 free(const_cast<void*>(data));
26 reinterpret_cast<const VideoFrame*>(frame_ref)->Release();
29 } // namespace
31 MEDIA_EXPORT base::ScopedCFTypeRef<CVPixelBufferRef>
32 WrapVideoFrameInCVPixelBuffer(const VideoFrame& frame) {
33 base::ScopedCFTypeRef<CVPixelBufferRef> pixel_buffer;
35 // If the frame is backed by a pixel buffer, just return that buffer.
36 if (frame.cv_pixel_buffer()) {
37 pixel_buffer.reset(frame.cv_pixel_buffer(), base::scoped_policy::RETAIN);
38 return pixel_buffer;
41 // VideoFrame only supports YUV formats and most of them are 'YVU' ordered,
42 // which CVPixelBuffer does not support. This means we effectively can only
43 // represent I420 and NV12 frames. In addition, VideoFrame does not carry
44 // colorimetric information, so this function assumes standard video range
45 // and ITU Rec 709 primaries.
46 VideoFrame::Format video_frame_format = frame.format();
47 OSType cv_format;
48 if (video_frame_format == VideoFrame::Format::I420) {
49 cv_format = kCVPixelFormatType_420YpCbCr8Planar;
50 } else if (video_frame_format == VideoFrame::Format::NV12) {
51 cv_format = CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
52 } else {
53 DLOG(ERROR) << " unsupported frame format: " << video_frame_format;
54 return pixel_buffer;
57 int num_planes = VideoFrame::NumPlanes(video_frame_format);
58 DCHECK_LE(num_planes, kMaxPlanes);
59 gfx::Size coded_size = frame.coded_size();
61 // TODO(jfroy): Support extended pixels (i.e. padding).
62 if (coded_size != frame.visible_rect().size()) {
63 DLOG(ERROR) << " frame with extended pixels not supported: "
64 << " coded_size: " << coded_size.ToString()
65 << ", visible_rect: " << frame.visible_rect().ToString();
66 return pixel_buffer;
69 // Build arrays for each plane's data pointer, dimensions and byte alignment.
70 void* plane_ptrs[kMaxPlanes];
71 size_t plane_widths[kMaxPlanes];
72 size_t plane_heights[kMaxPlanes];
73 size_t plane_bytes_per_row[kMaxPlanes];
74 for (int plane_i = 0; plane_i < num_planes; ++plane_i) {
75 plane_ptrs[plane_i] = const_cast<uint8*>(frame.data(plane_i));
76 gfx::Size plane_size =
77 VideoFrame::PlaneSize(video_frame_format, plane_i, coded_size);
78 plane_widths[plane_i] = plane_size.width();
79 plane_heights[plane_i] = plane_size.height();
80 plane_bytes_per_row[plane_i] = frame.stride(plane_i);
83 // CVPixelBufferCreateWithPlanarBytes needs a dummy plane descriptor or the
84 // release callback will not execute. The descriptor is freed in the callback.
85 void* descriptor = calloc(
87 std::max(sizeof(CVPlanarPixelBufferInfo_YCbCrPlanar),
88 sizeof(CoreVideoGlue::CVPlanarPixelBufferInfo_YCbCrBiPlanar)));
90 // Wrap the frame's data in a CVPixelBuffer. Because this is a C API, we can't
91 // give it a smart pointer to the frame, so instead pass a raw pointer and
92 // increment the frame's reference count manually.
93 CVReturn result = CVPixelBufferCreateWithPlanarBytes(
94 kCFAllocatorDefault, coded_size.width(), coded_size.height(), cv_format,
95 descriptor, 0, num_planes, plane_ptrs, plane_widths, plane_heights,
96 plane_bytes_per_row, &CvPixelBufferReleaseCallback,
97 const_cast<VideoFrame*>(&frame), nullptr, pixel_buffer.InitializeInto());
98 if (result != kCVReturnSuccess) {
99 DLOG(ERROR) << " CVPixelBufferCreateWithPlanarBytes failed: " << result;
100 return base::ScopedCFTypeRef<CVPixelBufferRef>(nullptr);
103 // The CVPixelBuffer now references the data of the frame, so increment its
104 // reference count manually. The release callback set on the pixel buffer will
105 // release the frame.
106 frame.AddRef();
108 // Apply required colorimetric attachments.
109 CVBufferSetAttachment(pixel_buffer, kCVImageBufferColorPrimariesKey,
110 kCVImageBufferColorPrimaries_ITU_R_709_2,
111 kCVAttachmentMode_ShouldPropagate);
112 CVBufferSetAttachment(pixel_buffer, kCVImageBufferTransferFunctionKey,
113 kCVImageBufferTransferFunction_ITU_R_709_2,
114 kCVAttachmentMode_ShouldPropagate);
115 CVBufferSetAttachment(pixel_buffer, kCVImageBufferYCbCrMatrixKey,
116 kCVImageBufferYCbCrMatrix_ITU_R_709_2,
117 kCVAttachmentMode_ShouldPropagate);
119 return pixel_buffer;
122 } // namespace media