diff options
Diffstat (limited to 'src/yuv_buffer.cc')
-rw-r--r-- | src/yuv_buffer.cc | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/yuv_buffer.cc b/src/yuv_buffer.cc new file mode 100644 index 0000000..c74e140 --- /dev/null +++ b/src/yuv_buffer.cc @@ -0,0 +1,201 @@ +// Copyright 2019 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 "src/yuv_buffer.h" + +#include <cassert> +#include <cstddef> +#include <new> + +#include "src/frame_buffer_utils.h" +#include "src/utils/common.h" +#include "src/utils/logging.h" + +namespace libgav1 { + +// Size conventions: +// * Widths, heights, and border sizes are in pixels. +// * Strides and plane sizes are in bytes. +// +// YuvBuffer objects may be reused through the BufferPool. Realloc() must +// assume that data members (except buffer_alloc_ and buffer_alloc_size_) may +// contain stale values from the previous use, and must set all data members +// from scratch. In particular, Realloc() must not rely on the initial values +// of data members set by the YuvBuffer constructor. +bool YuvBuffer::Realloc(int bitdepth, bool is_monochrome, int width, int height, + int8_t subsampling_x, int8_t subsampling_y, + int left_border, int right_border, int top_border, + int bottom_border, + GetFrameBufferCallback get_frame_buffer, + void* callback_private_data, + void** buffer_private_data) { + // Only support allocating buffers that have borders that are a multiple of + // 2. The border restriction is required because we may subsample the + // borders in the chroma planes. + if (((left_border | right_border | top_border | bottom_border) & 1) != 0) { + LIBGAV1_DLOG(ERROR, + "Borders must be a multiple of 2: left_border = %d, " + "right_border = %d, top_border = %d, bottom_border = %d.", + left_border, right_border, top_border, bottom_border); + return false; + } + + // Every row in the plane buffers needs to be kFrameBufferRowAlignment-byte + // aligned. Since the strides are multiples of kFrameBufferRowAlignment bytes, + // it suffices to just make the plane buffers kFrameBufferRowAlignment-byte + // aligned. + const int plane_align = kFrameBufferRowAlignment; + const int uv_width = + is_monochrome ? 0 : SubsampledValue(width, subsampling_x); + const int uv_height = + is_monochrome ? 0 : SubsampledValue(height, subsampling_y); + const int uv_left_border = is_monochrome ? 0 : left_border >> subsampling_x; + const int uv_right_border = is_monochrome ? 0 : right_border >> subsampling_x; + const int uv_top_border = is_monochrome ? 0 : top_border >> subsampling_y; + const int uv_bottom_border = + is_monochrome ? 0 : bottom_border >> subsampling_y; + + if (get_frame_buffer != nullptr) { + assert(buffer_private_data != nullptr); + + const Libgav1ImageFormat image_format = + ComposeImageFormat(is_monochrome, subsampling_x, subsampling_y); + FrameBuffer frame_buffer; + if (get_frame_buffer(callback_private_data, bitdepth, image_format, width, + height, left_border, right_border, top_border, + bottom_border, kFrameBufferRowAlignment, + &frame_buffer) != kStatusOk) { + return false; + } + + if (frame_buffer.plane[0] == nullptr || + (!is_monochrome && frame_buffer.plane[1] == nullptr) || + (!is_monochrome && frame_buffer.plane[2] == nullptr)) { + assert(false && "The get_frame_buffer callback malfunctioned."); + LIBGAV1_DLOG(ERROR, "The get_frame_buffer callback malfunctioned."); + return false; + } + + stride_[kPlaneY] = frame_buffer.stride[0]; + stride_[kPlaneU] = frame_buffer.stride[1]; + stride_[kPlaneV] = frame_buffer.stride[2]; + buffer_[kPlaneY] = frame_buffer.plane[0]; + buffer_[kPlaneU] = frame_buffer.plane[1]; + buffer_[kPlaneV] = frame_buffer.plane[2]; + *buffer_private_data = frame_buffer.private_data; + } else { + assert(callback_private_data == nullptr); + assert(buffer_private_data == nullptr); + + // Calculate y_stride (in bytes). It is padded to a multiple of + // kFrameBufferRowAlignment bytes. + int y_stride = width + left_border + right_border; +#if LIBGAV1_MAX_BITDEPTH >= 10 + if (bitdepth > 8) y_stride *= sizeof(uint16_t); +#endif + y_stride = Align(y_stride, kFrameBufferRowAlignment); + // Size of the Y plane in bytes. + const uint64_t y_plane_size = (height + top_border + bottom_border) * + static_cast<uint64_t>(y_stride) + + (plane_align - 1); + + // Calculate uv_stride (in bytes). It is padded to a multiple of + // kFrameBufferRowAlignment bytes. + int uv_stride = uv_width + uv_left_border + uv_right_border; +#if LIBGAV1_MAX_BITDEPTH >= 10 + if (bitdepth > 8) uv_stride *= sizeof(uint16_t); +#endif + uv_stride = Align(uv_stride, kFrameBufferRowAlignment); + // Size of the U or V plane in bytes. + const uint64_t uv_plane_size = + is_monochrome ? 0 + : (uv_height + uv_top_border + uv_bottom_border) * + static_cast<uint64_t>(uv_stride) + + (plane_align - 1); + + // Allocate unaligned y_buffer, u_buffer, and v_buffer. + uint8_t* y_buffer = nullptr; + uint8_t* u_buffer = nullptr; + uint8_t* v_buffer = nullptr; + + const uint64_t frame_size = y_plane_size + 2 * uv_plane_size; + if (frame_size > buffer_alloc_size_) { + // Allocation to hold larger frame, or first allocation. + if (frame_size != static_cast<size_t>(frame_size)) return false; + + buffer_alloc_.reset(new (std::nothrow) + uint8_t[static_cast<size_t>(frame_size)]); + if (buffer_alloc_ == nullptr) { + buffer_alloc_size_ = 0; + return false; + } + + buffer_alloc_size_ = static_cast<size_t>(frame_size); + } + + y_buffer = buffer_alloc_.get(); + if (!is_monochrome) { + u_buffer = y_buffer + y_plane_size; + v_buffer = u_buffer + uv_plane_size; + } + + stride_[kPlaneY] = y_stride; + stride_[kPlaneU] = stride_[kPlaneV] = uv_stride; + + int left_border_bytes = left_border; + int uv_left_border_bytes = uv_left_border; +#if LIBGAV1_MAX_BITDEPTH >= 10 + if (bitdepth > 8) { + left_border_bytes *= sizeof(uint16_t); + uv_left_border_bytes *= sizeof(uint16_t); + } +#endif + buffer_[kPlaneY] = AlignAddr( + y_buffer + (top_border * y_stride) + left_border_bytes, plane_align); + buffer_[kPlaneU] = + AlignAddr(u_buffer + (uv_top_border * uv_stride) + uv_left_border_bytes, + plane_align); + buffer_[kPlaneV] = + AlignAddr(v_buffer + (uv_top_border * uv_stride) + uv_left_border_bytes, + plane_align); + } + + y_width_ = width; + y_height_ = height; + left_border_[kPlaneY] = left_border; + right_border_[kPlaneY] = right_border; + top_border_[kPlaneY] = top_border; + bottom_border_[kPlaneY] = bottom_border; + + uv_width_ = uv_width; + uv_height_ = uv_height; + left_border_[kPlaneU] = left_border_[kPlaneV] = uv_left_border; + right_border_[kPlaneU] = right_border_[kPlaneV] = uv_right_border; + top_border_[kPlaneU] = top_border_[kPlaneV] = uv_top_border; + bottom_border_[kPlaneU] = bottom_border_[kPlaneV] = uv_bottom_border; + + subsampling_x_ = subsampling_x; + subsampling_y_ = subsampling_y; + + bitdepth_ = bitdepth; + is_monochrome_ = is_monochrome; + assert(!is_monochrome || stride_[kPlaneU] == 0); + assert(!is_monochrome || stride_[kPlaneV] == 0); + assert(!is_monochrome || buffer_[kPlaneU] == nullptr); + assert(!is_monochrome || buffer_[kPlaneV] == nullptr); + + return true; +} + +} // namespace libgav1 |