| // 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 "base/logging.h" | 
 | #include "media/base/video_frame.h" | 
 | #include "ui/gfx/gpu_memory_buffer.h" | 
 | #include "ui/gfx/mac/io_surface.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[]) { | 
 |   free(const_cast<void*>(data)); | 
 |   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; | 
 |   } | 
 |  | 
 |   // If the frame has a GMB, yank out its IOSurface if possible. | 
 |   if (frame.GetGpuMemoryBuffer()) { | 
 |     gfx::GpuMemoryBufferHandle handle = | 
 |         frame.GetGpuMemoryBuffer()->CloneHandle(); | 
 |     if (handle.type == gfx::GpuMemoryBufferType::IO_SURFACE_BUFFER) { | 
 |       gfx::ScopedIOSurface io_surface = handle.io_surface; | 
 |       if (io_surface) { | 
 |         const CVReturn cv_return = CVPixelBufferCreateWithIOSurface( | 
 |             nullptr, io_surface, nullptr, pixel_buffer.InitializeInto()); | 
 |         if (cv_return == kCVReturnSuccess) { | 
 |           VLOG(3) << "Returning IOSurface-based CVPixelBuffer."; | 
 |           return pixel_buffer; | 
 |         } | 
 |         pixel_buffer.reset(); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   VLOG(3) << "Returning RAM based CVPixelBuffer."; | 
 |  | 
 |   // 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), | 
 |                          sizeof(CVPlanarPixelBufferInfo_YCbCrBiPlanar))); | 
 |  | 
 |   // 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. | 
 |   frame.AddRef(); | 
 |  | 
 |   // Apply required colorimetric attachments. | 
 |   CVBufferSetAttachment(pixel_buffer, kCVImageBufferColorPrimariesKey, | 
 |                         kCVImageBufferColorPrimaries_ITU_R_709_2, | 
 |                         kCVAttachmentMode_ShouldPropagate); | 
 |   CVBufferSetAttachment(pixel_buffer, kCVImageBufferTransferFunctionKey, | 
 |                         kCVImageBufferTransferFunction_ITU_R_709_2, | 
 |                         kCVAttachmentMode_ShouldPropagate); | 
 |   CVBufferSetAttachment(pixel_buffer, kCVImageBufferYCbCrMatrixKey, | 
 |                         kCVImageBufferYCbCrMatrix_ITU_R_709_2, | 
 |                         kCVAttachmentMode_ShouldPropagate); | 
 |  | 
 |   return pixel_buffer; | 
 | } | 
 |  | 
 | }  // namespace media |