blob: e299bf2fadb3f77dae937898884cbd8f4f45d7be [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/base/mac/video_frame_mac.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include "media/base/video_frame.h"
namespace media {
namespace {
// Maximum number of planes supported by this implementation.
const int kMaxPlanes = 3;
// CVPixelBuffer release callback. See |GetCvPixelBufferRepresentation()|.
void CvPixelBufferReleaseCallback(void* frame_ref,
const void* data,
size_t size,
size_t num_planes,
const void* planes[]) {
reinterpret_cast<const VideoFrame*>(frame_ref)->Release();
} // namespace
MEDIA_EXPORT base::ScopedCFTypeRef<CVPixelBufferRef>
WrapVideoFrameInCVPixelBuffer(const VideoFrame& frame) {
base::ScopedCFTypeRef<CVPixelBufferRef> pixel_buffer;
// If the frame is backed by a pixel buffer, just return that buffer.
if (frame.CvPixelBuffer()) {
pixel_buffer.reset(frame.CvPixelBuffer(), base::scoped_policy::RETAIN);
return pixel_buffer;
// VideoFrame only supports YUV formats and most of them are 'YVU' ordered,
// which CVPixelBuffer does not support. This means we effectively can only
// represent I420 and NV12 frames. In addition, VideoFrame does not carry
// colorimetric information, so this function assumes standard video range
// and ITU Rec 709 primaries.
const VideoPixelFormat video_frame_format = frame.format();
OSType cv_format;
if (video_frame_format == PIXEL_FORMAT_I420) {
cv_format = kCVPixelFormatType_420YpCbCr8Planar;
} else if (video_frame_format == PIXEL_FORMAT_NV12) {
cv_format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
} else {
DLOG(ERROR) << " unsupported frame format: " << video_frame_format;
return pixel_buffer;
int num_planes = VideoFrame::NumPlanes(video_frame_format);
DCHECK_LE(num_planes, kMaxPlanes);
const gfx::Rect& visible_rect = frame.visible_rect();
// Build arrays for each plane's data pointer, dimensions and byte alignment.
void* plane_ptrs[kMaxPlanes];
size_t plane_widths[kMaxPlanes];
size_t plane_heights[kMaxPlanes];
size_t plane_bytes_per_row[kMaxPlanes];
for (int plane_i = 0; plane_i < num_planes; ++plane_i) {
plane_ptrs[plane_i] = const_cast<uint8_t*>(frame.visible_data(plane_i));
gfx::Size plane_size =
VideoFrame::PlaneSize(video_frame_format, plane_i, visible_rect.size());
plane_widths[plane_i] = plane_size.width();
plane_heights[plane_i] = plane_size.height();
plane_bytes_per_row[plane_i] = frame.stride(plane_i);
// CVPixelBufferCreateWithPlanarBytes needs a dummy plane descriptor or the
// release callback will not execute. The descriptor is freed in the callback.
void* descriptor =
calloc(1, std::max(sizeof(CVPlanarPixelBufferInfo_YCbCrPlanar),
// Wrap the frame's data in a CVPixelBuffer. Because this is a C API, we can't
// give it a smart pointer to the frame, so instead pass a raw pointer and
// increment the frame's reference count manually.
CVReturn result = CVPixelBufferCreateWithPlanarBytes(
kCFAllocatorDefault, visible_rect.width(), visible_rect.height(),
cv_format, descriptor, 0, num_planes, plane_ptrs, plane_widths,
plane_heights, plane_bytes_per_row, &CvPixelBufferReleaseCallback,
const_cast<VideoFrame*>(&frame), nullptr, pixel_buffer.InitializeInto());
if (result != kCVReturnSuccess) {
DLOG(ERROR) << " CVPixelBufferCreateWithPlanarBytes failed: " << result;
return base::ScopedCFTypeRef<CVPixelBufferRef>(nullptr);
// The CVPixelBuffer now references the data of the frame, so increment its
// reference count manually. The release callback set on the pixel buffer will
// release the frame.
// Apply required colorimetric attachments.
CVBufferSetAttachment(pixel_buffer, kCVImageBufferColorPrimariesKey,
CVBufferSetAttachment(pixel_buffer, kCVImageBufferTransferFunctionKey,
CVBufferSetAttachment(pixel_buffer, kCVImageBufferYCbCrMatrixKey,
return pixel_buffer;
} // namespace media