diff options
Diffstat (limited to 'examples/gav1_decode_cv_pixel_buffer_pool.cc')
-rw-r--r-- | examples/gav1_decode_cv_pixel_buffer_pool.cc | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/examples/gav1_decode_cv_pixel_buffer_pool.cc b/examples/gav1_decode_cv_pixel_buffer_pool.cc new file mode 100644 index 0000000..6aa4e61 --- /dev/null +++ b/examples/gav1_decode_cv_pixel_buffer_pool.cc @@ -0,0 +1,278 @@ +// Copyright 2020 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "examples/gav1_decode_cv_pixel_buffer_pool.h" + +#include <cassert> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <new> +#include <type_traits> + +namespace { + +struct CFTypeDeleter { + void operator()(CFTypeRef cf) const { CFRelease(cf); } +}; + +using UniqueCFNumberRef = + std::unique_ptr<std::remove_pointer<CFNumberRef>::type, CFTypeDeleter>; + +using UniqueCFDictionaryRef = + std::unique_ptr<std::remove_pointer<CFDictionaryRef>::type, CFTypeDeleter>; + +} // namespace + +extern "C" { + +libgav1::StatusCode 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) { + auto* buffer_pool = + static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data); + return buffer_pool->OnCVPixelBufferSizeChanged( + bitdepth, image_format, width, height, left_border, right_border, + top_border, bottom_border, stride_alignment); +} + +libgav1::StatusCode 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) { + auto* buffer_pool = + static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data); + return buffer_pool->GetCVPixelBuffer( + bitdepth, image_format, width, height, left_border, right_border, + top_border, bottom_border, stride_alignment, frame_buffer); +} + +void Gav1DecodeReleaseCVPixelBuffer(void* callback_private_data, + void* buffer_private_data) { + auto* buffer_pool = + static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data); + buffer_pool->ReleaseCVPixelBuffer(buffer_private_data); +} + +} // extern "C" + +// static +std::unique_ptr<Gav1DecodeCVPixelBufferPool> +Gav1DecodeCVPixelBufferPool::Create(size_t num_buffers) { + std::unique_ptr<Gav1DecodeCVPixelBufferPool> buffer_pool( + new (std::nothrow) Gav1DecodeCVPixelBufferPool(num_buffers)); + return buffer_pool; +} + +Gav1DecodeCVPixelBufferPool::Gav1DecodeCVPixelBufferPool(size_t num_buffers) + : num_buffers_(static_cast<int>(num_buffers)) {} + +Gav1DecodeCVPixelBufferPool::~Gav1DecodeCVPixelBufferPool() { + CVPixelBufferPoolRelease(pool_); +} + +libgav1::StatusCode Gav1DecodeCVPixelBufferPool::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) { + if (bitdepth != 8 || (image_format != libgav1::kImageFormatYuv420 && + image_format != libgav1::kImageFormatMonochrome400)) { + fprintf(stderr, + "Only bitdepth 8, 4:2:0 videos are supported: bitdepth %d, " + "image_format: %d.\n", + bitdepth, image_format); + return libgav1::kStatusUnimplemented; + } + + // stride_alignment must be a power of 2. + assert((stride_alignment & (stride_alignment - 1)) == 0); + + // The possible keys for CVPixelBufferPool are: + // kCVPixelBufferPoolMinimumBufferCountKey + // kCVPixelBufferPoolMaximumBufferAgeKey + // kCVPixelBufferPoolAllocationThresholdKey + const void* pool_keys[] = {kCVPixelBufferPoolMinimumBufferCountKey}; + const int min_buffer_count = 10; + UniqueCFNumberRef cf_min_buffer_count( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &min_buffer_count)); + if (cf_min_buffer_count == nullptr) { + fprintf(stderr, "CFNumberCreate failed.\n"); + return libgav1::kStatusUnknownError; + } + const void* pool_values[] = {cf_min_buffer_count.get()}; + UniqueCFDictionaryRef pool_attributes(CFDictionaryCreate( + nullptr, pool_keys, pool_values, 1, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + if (pool_attributes == nullptr) { + fprintf(stderr, "CFDictionaryCreate failed.\n"); + return libgav1::kStatusUnknownError; + } + + // The pixelBufferAttributes argument to CVPixelBufferPoolCreate() cannot be + // null and must contain the pixel format, width, and height, otherwise + // CVPixelBufferPoolCreate() fails with kCVReturnInvalidPixelBufferAttributes + // (-6682). + + // I420: kCVPixelFormatType_420YpCbCr8Planar (video range). + const int pixel_format = (image_format == libgav1::kImageFormatYuv420) + ? kCVPixelFormatType_420YpCbCr8PlanarFullRange + : kCVPixelFormatType_OneComponent8; + UniqueCFNumberRef cf_pixel_format( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pixel_format)); + UniqueCFNumberRef cf_width( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &width)); + UniqueCFNumberRef cf_height( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &height)); + UniqueCFNumberRef cf_left_border( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &left_border)); + UniqueCFNumberRef cf_right_border( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &right_border)); + UniqueCFNumberRef cf_top_border( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &top_border)); + UniqueCFNumberRef cf_bottom_border( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &bottom_border)); + UniqueCFNumberRef cf_stride_alignment( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stride_alignment)); + + const void* buffer_keys[] = { + kCVPixelBufferPixelFormatTypeKey, + kCVPixelBufferWidthKey, + kCVPixelBufferHeightKey, + kCVPixelBufferExtendedPixelsLeftKey, + kCVPixelBufferExtendedPixelsRightKey, + kCVPixelBufferExtendedPixelsTopKey, + kCVPixelBufferExtendedPixelsBottomKey, + kCVPixelBufferBytesPerRowAlignmentKey, + }; + const void* buffer_values[] = { + cf_pixel_format.get(), cf_width.get(), + cf_height.get(), cf_left_border.get(), + cf_right_border.get(), cf_top_border.get(), + cf_bottom_border.get(), cf_stride_alignment.get(), + }; + UniqueCFDictionaryRef buffer_attributes(CFDictionaryCreate( + kCFAllocatorDefault, buffer_keys, buffer_values, 8, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + if (buffer_attributes == nullptr) { + fprintf(stderr, "CFDictionaryCreate of buffer_attributes failed.\n"); + return libgav1::kStatusUnknownError; + } + CVPixelBufferPoolRef cv_pool; + CVReturn ret = CVPixelBufferPoolCreate( + /*allocator=*/nullptr, pool_attributes.get(), buffer_attributes.get(), + &cv_pool); + if (ret != kCVReturnSuccess) { + fprintf(stderr, "CVPixelBufferPoolCreate failed: %d.\n", + static_cast<int>(ret)); + return libgav1::kStatusOutOfMemory; + } + CVPixelBufferPoolRelease(pool_); + pool_ = cv_pool; + return libgav1::kStatusOk; +} + +libgav1::StatusCode Gav1DecodeCVPixelBufferPool::GetCVPixelBuffer( + 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) { + static_cast<void>(bitdepth); + assert(bitdepth == 8 && (image_format == libgav1::kImageFormatYuv420 || + image_format == libgav1::kImageFormatMonochrome400)); + const bool is_monochrome = + (image_format == libgav1::kImageFormatMonochrome400); + + // The dictionary must have kCVPixelBufferPoolAllocationThresholdKey, + // otherwise CVPixelBufferPoolCreatePixelBufferWithAuxAttributes() fails with + // kCVReturnWouldExceedAllocationThreshold (-6689). + UniqueCFNumberRef cf_num_buffers( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num_buffers_)); + + const void* buffer_keys[] = { + kCVPixelBufferPoolAllocationThresholdKey, + }; + const void* buffer_values[] = { + cf_num_buffers.get(), + }; + UniqueCFDictionaryRef aux_attributes(CFDictionaryCreate( + kCFAllocatorDefault, buffer_keys, buffer_values, 1, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + if (aux_attributes == nullptr) { + fprintf(stderr, "CFDictionaryCreate of aux_attributes failed.\n"); + return libgav1::kStatusUnknownError; + } + + CVPixelBufferRef pixel_buffer; + CVReturn ret = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes( + /*allocator=*/nullptr, pool_, aux_attributes.get(), &pixel_buffer); + if (ret != kCVReturnSuccess) { + fprintf(stderr, + "CVPixelBufferPoolCreatePixelBufferWithAuxAttributes failed: %d.\n", + static_cast<int>(ret)); + return libgav1::kStatusOutOfMemory; + } + + ret = CVPixelBufferLockBaseAddress(pixel_buffer, /*lockFlags=*/0); + if (ret != kCVReturnSuccess) { + fprintf(stderr, "CVPixelBufferLockBaseAddress failed: %d.\n", + static_cast<int>(ret)); + CFRelease(pixel_buffer); + return libgav1::kStatusUnknownError; + } + + // If the pixel format type is kCVPixelFormatType_OneComponent8, the pixel + // buffer is nonplanar (CVPixelBufferIsPlanar returns false and + // CVPixelBufferGetPlaneCount returns 0), but + // CVPixelBufferGetBytesPerRowOfPlane and CVPixelBufferGetBaseAddressOfPlane + // still work for plane index 0, even though the documentation says they + // return NULL for nonplanar pixel buffers. + frame_buffer->stride[0] = + static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 0)); + frame_buffer->plane[0] = static_cast<uint8_t*>( + CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 0)); + if (is_monochrome) { + frame_buffer->stride[1] = 0; + frame_buffer->stride[2] = 0; + frame_buffer->plane[1] = nullptr; + frame_buffer->plane[2] = nullptr; + } else { + frame_buffer->stride[1] = + static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 1)); + frame_buffer->stride[2] = + static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 2)); + frame_buffer->plane[1] = static_cast<uint8_t*>( + CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 1)); + frame_buffer->plane[2] = static_cast<uint8_t*>( + CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 2)); + } + frame_buffer->private_data = pixel_buffer; + + return libgav1::kStatusOk; +} + +void Gav1DecodeCVPixelBufferPool::ReleaseCVPixelBuffer( + void* buffer_private_data) { + auto const pixel_buffer = static_cast<CVPixelBufferRef>(buffer_private_data); + CVReturn ret = + CVPixelBufferUnlockBaseAddress(pixel_buffer, /*unlockFlags=*/0); + if (ret != kCVReturnSuccess) { + fprintf(stderr, "%s:%d: CVPixelBufferUnlockBaseAddress failed: %d.\n", + __FILE__, __LINE__, static_cast<int>(ret)); + abort(); + } + CFRelease(pixel_buffer); +} |