• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "examples/gav1_decode_cv_pixel_buffer_pool.h"
16 
17 #include <cassert>
18 #include <cstdint>
19 #include <cstdio>
20 #include <cstdlib>
21 #include <memory>
22 #include <new>
23 #include <type_traits>
24 
25 namespace {
26 
27 struct CFTypeDeleter {
operator ()__anon15300e610111::CFTypeDeleter28   void operator()(CFTypeRef cf) const { CFRelease(cf); }
29 };
30 
31 using UniqueCFNumberRef =
32     std::unique_ptr<std::remove_pointer<CFNumberRef>::type, CFTypeDeleter>;
33 
34 using UniqueCFDictionaryRef =
35     std::unique_ptr<std::remove_pointer<CFDictionaryRef>::type, CFTypeDeleter>;
36 
37 }  // namespace
38 
39 extern "C" {
40 
Gav1DecodeOnCVPixelBufferSizeChanged(void * callback_private_data,int bitdepth,libgav1::ImageFormat image_format,int width,int height,int left_border,int right_border,int top_border,int bottom_border,int stride_alignment)41 libgav1::StatusCode Gav1DecodeOnCVPixelBufferSizeChanged(
42     void* callback_private_data, int bitdepth,
43     libgav1::ImageFormat image_format, int width, int height, int left_border,
44     int right_border, int top_border, int bottom_border, int stride_alignment) {
45   auto* buffer_pool =
46       static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data);
47   return buffer_pool->OnCVPixelBufferSizeChanged(
48       bitdepth, image_format, width, height, left_border, right_border,
49       top_border, bottom_border, stride_alignment);
50 }
51 
Gav1DecodeGetCVPixelBuffer(void * callback_private_data,int bitdepth,libgav1::ImageFormat image_format,int width,int height,int left_border,int right_border,int top_border,int bottom_border,int stride_alignment,libgav1::FrameBuffer * frame_buffer)52 libgav1::StatusCode Gav1DecodeGetCVPixelBuffer(
53     void* callback_private_data, int bitdepth,
54     libgav1::ImageFormat image_format, int width, int height, int left_border,
55     int right_border, int top_border, int bottom_border, int stride_alignment,
56     libgav1::FrameBuffer* frame_buffer) {
57   auto* buffer_pool =
58       static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data);
59   return buffer_pool->GetCVPixelBuffer(
60       bitdepth, image_format, width, height, left_border, right_border,
61       top_border, bottom_border, stride_alignment, frame_buffer);
62 }
63 
Gav1DecodeReleaseCVPixelBuffer(void * callback_private_data,void * buffer_private_data)64 void Gav1DecodeReleaseCVPixelBuffer(void* callback_private_data,
65                                     void* buffer_private_data) {
66   auto* buffer_pool =
67       static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data);
68   buffer_pool->ReleaseCVPixelBuffer(buffer_private_data);
69 }
70 
71 }  // extern "C"
72 
73 // static
74 std::unique_ptr<Gav1DecodeCVPixelBufferPool>
Create(size_t num_buffers)75 Gav1DecodeCVPixelBufferPool::Create(size_t num_buffers) {
76   std::unique_ptr<Gav1DecodeCVPixelBufferPool> buffer_pool(
77       new (std::nothrow) Gav1DecodeCVPixelBufferPool(num_buffers));
78   return buffer_pool;
79 }
80 
Gav1DecodeCVPixelBufferPool(size_t num_buffers)81 Gav1DecodeCVPixelBufferPool::Gav1DecodeCVPixelBufferPool(size_t num_buffers)
82     : num_buffers_(static_cast<int>(num_buffers)) {}
83 
~Gav1DecodeCVPixelBufferPool()84 Gav1DecodeCVPixelBufferPool::~Gav1DecodeCVPixelBufferPool() {
85   CVPixelBufferPoolRelease(pool_);
86 }
87 
OnCVPixelBufferSizeChanged(int bitdepth,libgav1::ImageFormat image_format,int width,int height,int left_border,int right_border,int top_border,int bottom_border,int stride_alignment)88 libgav1::StatusCode Gav1DecodeCVPixelBufferPool::OnCVPixelBufferSizeChanged(
89     int bitdepth, libgav1::ImageFormat image_format, int width, int height,
90     int left_border, int right_border, int top_border, int bottom_border,
91     int stride_alignment) {
92   if (bitdepth != 8 || (image_format != libgav1::kImageFormatYuv420 &&
93                         image_format != libgav1::kImageFormatMonochrome400)) {
94     fprintf(stderr,
95             "Only bitdepth 8, 4:2:0 videos are supported: bitdepth %d, "
96             "image_format: %d.\n",
97             bitdepth, image_format);
98     return libgav1::kStatusUnimplemented;
99   }
100 
101   // stride_alignment must be a power of 2.
102   assert((stride_alignment & (stride_alignment - 1)) == 0);
103 
104   // The possible keys for CVPixelBufferPool are:
105   //   kCVPixelBufferPoolMinimumBufferCountKey
106   //   kCVPixelBufferPoolMaximumBufferAgeKey
107   //   kCVPixelBufferPoolAllocationThresholdKey
108   const void* pool_keys[] = {kCVPixelBufferPoolMinimumBufferCountKey};
109   const int min_buffer_count = 10;
110   UniqueCFNumberRef cf_min_buffer_count(
111       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &min_buffer_count));
112   if (cf_min_buffer_count == nullptr) {
113     fprintf(stderr, "CFNumberCreate failed.\n");
114     return libgav1::kStatusUnknownError;
115   }
116   const void* pool_values[] = {cf_min_buffer_count.get()};
117   UniqueCFDictionaryRef pool_attributes(CFDictionaryCreate(
118       nullptr, pool_keys, pool_values, 1, &kCFTypeDictionaryKeyCallBacks,
119       &kCFTypeDictionaryValueCallBacks));
120   if (pool_attributes == nullptr) {
121     fprintf(stderr, "CFDictionaryCreate failed.\n");
122     return libgav1::kStatusUnknownError;
123   }
124 
125   // The pixelBufferAttributes argument to CVPixelBufferPoolCreate() cannot be
126   // null and must contain the pixel format, width, and height, otherwise
127   // CVPixelBufferPoolCreate() fails with kCVReturnInvalidPixelBufferAttributes
128   // (-6682).
129 
130   // I420: kCVPixelFormatType_420YpCbCr8Planar (video range).
131   const int pixel_format = (image_format == libgav1::kImageFormatYuv420)
132                                ? kCVPixelFormatType_420YpCbCr8PlanarFullRange
133                                : kCVPixelFormatType_OneComponent8;
134   UniqueCFNumberRef cf_pixel_format(
135       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pixel_format));
136   UniqueCFNumberRef cf_width(
137       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &width));
138   UniqueCFNumberRef cf_height(
139       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &height));
140   UniqueCFNumberRef cf_left_border(
141       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &left_border));
142   UniqueCFNumberRef cf_right_border(
143       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &right_border));
144   UniqueCFNumberRef cf_top_border(
145       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &top_border));
146   UniqueCFNumberRef cf_bottom_border(
147       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &bottom_border));
148   UniqueCFNumberRef cf_stride_alignment(
149       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stride_alignment));
150 
151   const void* buffer_keys[] = {
152       kCVPixelBufferPixelFormatTypeKey,
153       kCVPixelBufferWidthKey,
154       kCVPixelBufferHeightKey,
155       kCVPixelBufferExtendedPixelsLeftKey,
156       kCVPixelBufferExtendedPixelsRightKey,
157       kCVPixelBufferExtendedPixelsTopKey,
158       kCVPixelBufferExtendedPixelsBottomKey,
159       kCVPixelBufferBytesPerRowAlignmentKey,
160   };
161   const void* buffer_values[] = {
162       cf_pixel_format.get(),  cf_width.get(),
163       cf_height.get(),        cf_left_border.get(),
164       cf_right_border.get(),  cf_top_border.get(),
165       cf_bottom_border.get(), cf_stride_alignment.get(),
166   };
167   UniqueCFDictionaryRef buffer_attributes(CFDictionaryCreate(
168       kCFAllocatorDefault, buffer_keys, buffer_values, 8,
169       &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
170   if (buffer_attributes == nullptr) {
171     fprintf(stderr, "CFDictionaryCreate of buffer_attributes failed.\n");
172     return libgav1::kStatusUnknownError;
173   }
174   CVPixelBufferPoolRef cv_pool;
175   CVReturn ret = CVPixelBufferPoolCreate(
176       /*allocator=*/nullptr, pool_attributes.get(), buffer_attributes.get(),
177       &cv_pool);
178   if (ret != kCVReturnSuccess) {
179     fprintf(stderr, "CVPixelBufferPoolCreate failed: %d.\n",
180             static_cast<int>(ret));
181     return libgav1::kStatusOutOfMemory;
182   }
183   CVPixelBufferPoolRelease(pool_);
184   pool_ = cv_pool;
185   return libgav1::kStatusOk;
186 }
187 
GetCVPixelBuffer(int bitdepth,libgav1::ImageFormat image_format,int,int,int,int,int,int,int,libgav1::FrameBuffer * frame_buffer)188 libgav1::StatusCode Gav1DecodeCVPixelBufferPool::GetCVPixelBuffer(
189     int bitdepth, libgav1::ImageFormat image_format, int /*width*/,
190     int /*height*/, int /*left_border*/, int /*right_border*/,
191     int /*top_border*/, int /*bottom_border*/, int /*stride_alignment*/,
192     libgav1::FrameBuffer* frame_buffer) {
193   static_cast<void>(bitdepth);
194   assert(bitdepth == 8 && (image_format == libgav1::kImageFormatYuv420 ||
195                            image_format == libgav1::kImageFormatMonochrome400));
196   const bool is_monochrome =
197       (image_format == libgav1::kImageFormatMonochrome400);
198 
199   // The dictionary must have kCVPixelBufferPoolAllocationThresholdKey,
200   // otherwise CVPixelBufferPoolCreatePixelBufferWithAuxAttributes() fails with
201   // kCVReturnWouldExceedAllocationThreshold (-6689).
202   UniqueCFNumberRef cf_num_buffers(
203       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num_buffers_));
204 
205   const void* buffer_keys[] = {
206       kCVPixelBufferPoolAllocationThresholdKey,
207   };
208   const void* buffer_values[] = {
209       cf_num_buffers.get(),
210   };
211   UniqueCFDictionaryRef aux_attributes(CFDictionaryCreate(
212       kCFAllocatorDefault, buffer_keys, buffer_values, 1,
213       &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
214   if (aux_attributes == nullptr) {
215     fprintf(stderr, "CFDictionaryCreate of aux_attributes failed.\n");
216     return libgav1::kStatusUnknownError;
217   }
218 
219   CVPixelBufferRef pixel_buffer;
220   CVReturn ret = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(
221       /*allocator=*/nullptr, pool_, aux_attributes.get(), &pixel_buffer);
222   if (ret != kCVReturnSuccess) {
223     fprintf(stderr,
224             "CVPixelBufferPoolCreatePixelBufferWithAuxAttributes failed: %d.\n",
225             static_cast<int>(ret));
226     return libgav1::kStatusOutOfMemory;
227   }
228 
229   ret = CVPixelBufferLockBaseAddress(pixel_buffer, /*lockFlags=*/0);
230   if (ret != kCVReturnSuccess) {
231     fprintf(stderr, "CVPixelBufferLockBaseAddress failed: %d.\n",
232             static_cast<int>(ret));
233     CFRelease(pixel_buffer);
234     return libgav1::kStatusUnknownError;
235   }
236 
237   // If the pixel format type is kCVPixelFormatType_OneComponent8, the pixel
238   // buffer is nonplanar (CVPixelBufferIsPlanar returns false and
239   // CVPixelBufferGetPlaneCount returns 0), but
240   // CVPixelBufferGetBytesPerRowOfPlane and CVPixelBufferGetBaseAddressOfPlane
241   // still work for plane index 0, even though the documentation says they
242   // return NULL for nonplanar pixel buffers.
243   frame_buffer->stride[0] =
244       static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 0));
245   frame_buffer->plane[0] = static_cast<uint8_t*>(
246       CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 0));
247   if (is_monochrome) {
248     frame_buffer->stride[1] = 0;
249     frame_buffer->stride[2] = 0;
250     frame_buffer->plane[1] = nullptr;
251     frame_buffer->plane[2] = nullptr;
252   } else {
253     frame_buffer->stride[1] =
254         static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 1));
255     frame_buffer->stride[2] =
256         static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 2));
257     frame_buffer->plane[1] = static_cast<uint8_t*>(
258         CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 1));
259     frame_buffer->plane[2] = static_cast<uint8_t*>(
260         CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 2));
261   }
262   frame_buffer->private_data = pixel_buffer;
263 
264   return libgav1::kStatusOk;
265 }
266 
ReleaseCVPixelBuffer(void * buffer_private_data)267 void Gav1DecodeCVPixelBufferPool::ReleaseCVPixelBuffer(
268     void* buffer_private_data) {
269   auto const pixel_buffer = static_cast<CVPixelBufferRef>(buffer_private_data);
270   CVReturn ret =
271       CVPixelBufferUnlockBaseAddress(pixel_buffer, /*unlockFlags=*/0);
272   if (ret != kCVReturnSuccess) {
273     fprintf(stderr, "%s:%d: CVPixelBufferUnlockBaseAddress failed: %d.\n",
274             __FILE__, __LINE__, static_cast<int>(ret));
275     abort();
276   }
277   CFRelease(pixel_buffer);
278 }
279