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