blob: f35a633f9283f15a750623a5380b636122147cb0 [file] [log] [blame]
/*
* 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.
*/
#ifndef LIBGAV1_SRC_BUFFER_POOL_H_
#define LIBGAV1_SRC_BUFFER_POOL_H_
#include <array>
#include <cassert>
#include <climits>
#include <condition_variable> // NOLINT (unapproved c++11 header)
#include <cstdint>
#include <cstring>
#include <mutex> // NOLINT (unapproved c++11 header)
#include "src/dsp/common.h"
#include "src/gav1/decoder_buffer.h"
#include "src/gav1/frame_buffer.h"
#include "src/internal_frame_buffer_list.h"
#include "src/symbol_decoder_context.h"
#include "src/utils/compiler_attributes.h"
#include "src/utils/constants.h"
#include "src/utils/reference_info.h"
#include "src/utils/segmentation.h"
#include "src/utils/segmentation_map.h"
#include "src/utils/types.h"
#include "src/utils/vector.h"
#include "src/yuv_buffer.h"
namespace libgav1 {
class BufferPool;
enum FrameState : uint8_t {
kFrameStateUnknown,
kFrameStateStarted,
kFrameStateParsed,
kFrameStateDecoded
};
// A reference-counted frame buffer. Clients should access it via
// RefCountedBufferPtr, which manages reference counting transparently.
class RefCountedBuffer {
public:
// Not copyable or movable.
RefCountedBuffer(const RefCountedBuffer&) = delete;
RefCountedBuffer& operator=(const RefCountedBuffer&) = delete;
// Allocates the YUV buffer. Returns true on success. Returns false on
// failure. This function ensures the thread safety of the |get_frame_buffer_|
// call (i.e.) only one |get_frame_buffer_| call will happen at a given time.
// TODO(b/142583029): In frame parallel mode, we can require the callbacks to
// be thread safe so that we can remove the thread safety of this function and
// applications can have fine grained locks.
//
// * |width| and |height| are the image dimensions in pixels.
// * |subsampling_x| and |subsampling_y| (either 0 or 1) specify the
// subsampling of the width and height of the chroma planes, respectively.
// * |left_border|, |right_border|, |top_border|, and |bottom_border| are
// the sizes (in pixels) of the borders on the left, right, top, and
// bottom sides, respectively.
//
// NOTE: The strides are a multiple of 16. Since the first row in each plane
// is 16-byte aligned, subsequent rows are also 16-byte aligned.
bool Realloc(int bitdepth, bool is_monochrome, int width, int height,
int subsampling_x, int subsampling_y, int left_border,
int right_border, int top_border, int bottom_border);
YuvBuffer* buffer() { return &yuv_buffer_; }
// Returns the buffer private data set by the get frame buffer callback when
// it allocated the YUV buffer.
void* buffer_private_data() const {
assert(buffer_private_data_valid_);
return buffer_private_data_;
}
// NOTE: In the current frame, this is the frame_type syntax element in the
// frame header. In a reference frame, this implements the RefFrameType array
// in the spec.
FrameType frame_type() const { return frame_type_; }
void set_frame_type(FrameType frame_type) { frame_type_ = frame_type; }
// The sample position for subsampled streams. This is the
// chroma_sample_position syntax element in the sequence header.
//
// NOTE: The decoder does not use chroma_sample_position, but it needs to be
// passed on to the client in DecoderBuffer.
ChromaSamplePosition chroma_sample_position() const {
return chroma_sample_position_;
}
void set_chroma_sample_position(ChromaSamplePosition chroma_sample_position) {
chroma_sample_position_ = chroma_sample_position;
}
// Whether the frame can be used as show existing frame in the future.
bool showable_frame() const { return showable_frame_; }
void set_showable_frame(bool value) { showable_frame_ = value; }
// Sets upscaled_width_, frame_width_, frame_height_, render_width_,
// render_height_, rows4x4_ and columns4x4_ from the corresponding fields
// in frame_header. Allocates reference_info_.motion_field_reference_frame,
// reference_info_.motion_field_mv_, and segmentation_map_. Returns true on
// success, false on failure.
bool SetFrameDimensions(const ObuFrameHeader& frame_header);
int32_t upscaled_width() const { return upscaled_width_; }
int32_t frame_width() const { return frame_width_; }
int32_t frame_height() const { return frame_height_; }
// RenderWidth() and RenderHeight() return the render size, which is a hint
// to the application about the desired display size.
int32_t render_width() const { return render_width_; }
int32_t render_height() const { return render_height_; }
int32_t rows4x4() const { return rows4x4_; }
int32_t columns4x4() const { return columns4x4_; }
int spatial_id() const { return spatial_id_; }
void set_spatial_id(int value) { spatial_id_ = value; }
int temporal_id() const { return temporal_id_; }
void set_temporal_id(int value) { temporal_id_ = value; }
SegmentationMap* segmentation_map() { return &segmentation_map_; }
const SegmentationMap* segmentation_map() const { return &segmentation_map_; }
// Only the |params| field of each GlobalMotion struct should be used.
const std::array<GlobalMotion, kNumReferenceFrameTypes>& GlobalMotions()
const {
return global_motion_;
}
// Saves the GlobalMotion array. Only the |params| field of each GlobalMotion
// struct is saved.
void SetGlobalMotions(
const std::array<GlobalMotion, kNumReferenceFrameTypes>& global_motions);
// Returns the saved CDF tables.
const SymbolDecoderContext& FrameContext() const { return frame_context_; }
// Saves the CDF tables. The intra_frame_y_mode_cdf table is reset to the
// default. The last entry in each table, representing the symbol count for
// that context, is set to 0.
void SetFrameContext(const SymbolDecoderContext& context);
const std::array<int8_t, kNumReferenceFrameTypes>& loop_filter_ref_deltas()
const {
return loop_filter_ref_deltas_;
}
const std::array<int8_t, kLoopFilterMaxModeDeltas>& loop_filter_mode_deltas()
const {
return loop_filter_mode_deltas_;
}
// Saves the ref_deltas and mode_deltas arrays in loop_filter.
void SetLoopFilterDeltas(const LoopFilter& loop_filter) {
loop_filter_ref_deltas_ = loop_filter.ref_deltas;
loop_filter_mode_deltas_ = loop_filter.mode_deltas;
}
// Copies the saved values of the following fields to the Segmentation
// struct: feature_enabled, feature_data, segment_id_pre_skip, and
// last_active_segment_id. The other fields are left unchanged.
void GetSegmentationParameters(Segmentation* segmentation) const;
// Saves the feature_enabled, feature_data, segment_id_pre_skip, and
// last_active_segment_id fields of the Segmentation struct.
void SetSegmentationParameters(const Segmentation& segmentation);
const FilmGrainParams& film_grain_params() const {
return film_grain_params_;
}
void set_film_grain_params(const FilmGrainParams& params) {
film_grain_params_ = params;
}
const ReferenceInfo* reference_info() const { return &reference_info_; }
ReferenceInfo* reference_info() { return &reference_info_; }
// This will wake up the WaitUntil*() functions and make them return false.
void Abort() {
{
std::lock_guard<std::mutex> lock(mutex_);
abort_ = true;
}
parsed_condvar_.notify_all();
decoded_condvar_.notify_all();
progress_row_condvar_.notify_all();
}
void SetFrameState(FrameState frame_state) {
{
std::lock_guard<std::mutex> lock(mutex_);
frame_state_ = frame_state;
}
if (frame_state == kFrameStateParsed) {
parsed_condvar_.notify_all();
} else if (frame_state == kFrameStateDecoded) {
decoded_condvar_.notify_all();
progress_row_condvar_.notify_all();
}
}
// Sets the progress of this frame to |progress_row| and notifies any threads
// that may be waiting on rows <= |progress_row|.
void SetProgress(int progress_row) {
{
std::lock_guard<std::mutex> lock(mutex_);
if (progress_row_ >= progress_row) return;
progress_row_ = progress_row;
}
progress_row_condvar_.notify_all();
}
void MarkFrameAsStarted() {
std::lock_guard<std::mutex> lock(mutex_);
if (frame_state_ != kFrameStateUnknown) return;
frame_state_ = kFrameStateStarted;
}
// All the WaitUntil* functions will return true if the desired wait state was
// reached successfully. If the return value is false, then the caller must
// assume that the wait was not successful and try to stop whatever they are
// doing as early as possible.
// Waits until the frame has been parsed.
bool WaitUntilParsed() {
std::unique_lock<std::mutex> lock(mutex_);
while (frame_state_ < kFrameStateParsed && !abort_) {
parsed_condvar_.wait(lock);
}
return !abort_;
}
// Waits until the |progress_row| has been decoded (as indicated either by
// |progress_row_| or |frame_state_|). |progress_row_cache| must not be
// nullptr and will be populated with the value of |progress_row_| after the
// wait.
//
// Typical usage of |progress_row_cache| is as follows:
// * Initialize |*progress_row_cache| to INT_MIN.
// * Call WaitUntil only if |*progress_row_cache| < |progress_row|.
bool WaitUntil(int progress_row, int* progress_row_cache) {
// If |progress_row| is negative, it means that the wait is on the top
// border to be available. The top border will be available when row 0 has
// been decoded. So we can simply wait on row 0 instead.
progress_row = std::max(progress_row, 0);
std::unique_lock<std::mutex> lock(mutex_);
while (progress_row_ < progress_row && frame_state_ != kFrameStateDecoded &&
!abort_) {
progress_row_condvar_.wait(lock);
}
// Once |frame_state_| reaches kFrameStateDecoded, |progress_row_| may no
// longer be updated. So we set |*progress_row_cache| to INT_MAX in that
// case.
*progress_row_cache =
(frame_state_ != kFrameStateDecoded) ? progress_row_ : INT_MAX;
return !abort_;
}
// Waits until the entire frame has been decoded.
bool WaitUntilDecoded() {
std::unique_lock<std::mutex> lock(mutex_);
while (frame_state_ != kFrameStateDecoded && !abort_) {
decoded_condvar_.wait(lock);
}
return !abort_;
}
private:
friend class BufferPool;
// Methods for BufferPool:
RefCountedBuffer();
~RefCountedBuffer();
void SetBufferPool(BufferPool* pool);
static void ReturnToBufferPool(RefCountedBuffer* ptr);
BufferPool* pool_ = nullptr;
bool buffer_private_data_valid_ = false;
void* buffer_private_data_ = nullptr;
YuvBuffer yuv_buffer_;
bool in_use_ = false; // Only used by BufferPool.
std::mutex mutex_;
FrameState frame_state_ = kFrameStateUnknown LIBGAV1_GUARDED_BY(mutex_);
int progress_row_ = -1 LIBGAV1_GUARDED_BY(mutex_);
// Signaled when progress_row_ is updated or when frame_state_ is set to
// kFrameStateDecoded.
std::condition_variable progress_row_condvar_;
// Signaled when the frame state is set to kFrameStateParsed.
std::condition_variable parsed_condvar_;
// Signaled when the frame state is set to kFrameStateDecoded.
std::condition_variable decoded_condvar_;
bool abort_ = false LIBGAV1_GUARDED_BY(mutex_);
FrameType frame_type_ = kFrameKey;
ChromaSamplePosition chroma_sample_position_ = kChromaSamplePositionUnknown;
bool showable_frame_ = false;
int32_t upscaled_width_ = 0;
int32_t frame_width_ = 0;
int32_t frame_height_ = 0;
int32_t render_width_ = 0;
int32_t render_height_ = 0;
int32_t columns4x4_ = 0;
int32_t rows4x4_ = 0;
int spatial_id_ = 0;
int temporal_id_ = 0;
// segmentation_map_ contains a rows4x4_ by columns4x4_ 2D array.
SegmentationMap segmentation_map_;
// Only the |params| field of each GlobalMotion struct is used.
// global_motion_[0] (for kReferenceFrameIntra) is not used.
std::array<GlobalMotion, kNumReferenceFrameTypes> global_motion_ = {};
SymbolDecoderContext frame_context_;
std::array<int8_t, kNumReferenceFrameTypes> loop_filter_ref_deltas_;
std::array<int8_t, kLoopFilterMaxModeDeltas> loop_filter_mode_deltas_;
// Only the feature_enabled, feature_data, segment_id_pre_skip, and
// last_active_segment_id fields of the Segmentation struct are used.
//
// Note: The spec only requires that we save feature_enabled and
// feature_data. Since segment_id_pre_skip and last_active_segment_id depend
// on feature_enabled only, we also save their values as an optimization.
Segmentation segmentation_ = {};
FilmGrainParams film_grain_params_ = {};
ReferenceInfo reference_info_;
};
// RefCountedBufferPtr contains a reference to a RefCountedBuffer.
//
// Note: For simplicity, RefCountedBufferPtr is implemented as a
// std::shared_ptr<RefCountedBuffer>. This requires a heap allocation of the
// control block for std::shared_ptr. To avoid that heap allocation, we can
// add a |ref_count_| field to RefCountedBuffer and implement a custom
// RefCountedBufferPtr class.
using RefCountedBufferPtr = std::shared_ptr<RefCountedBuffer>;
// BufferPool maintains a pool of RefCountedBuffers.
class BufferPool {
public:
BufferPool(FrameBufferSizeChangedCallback on_frame_buffer_size_changed,
GetFrameBufferCallback get_frame_buffer,
ReleaseFrameBufferCallback release_frame_buffer,
void* callback_private_data);
// Not copyable or movable.
BufferPool(const BufferPool&) = delete;
BufferPool& operator=(const BufferPool&) = delete;
~BufferPool();
LIBGAV1_MUST_USE_RESULT bool OnFrameBufferSizeChanged(
int bitdepth, Libgav1ImageFormat image_format, int width, int height,
int left_border, int right_border, int top_border, int bottom_border);
// Finds a free buffer in the buffer pool and returns a reference to the free
// buffer. If there is no free buffer, returns a null pointer. This function
// is thread safe.
RefCountedBufferPtr GetFreeBuffer();
// Aborts all the buffers that are in use.
void Abort();
private:
friend class RefCountedBuffer;
// Returns an unused buffer to the buffer pool. Called by RefCountedBuffer
// only. This function is thread safe.
void ReturnUnusedBuffer(RefCountedBuffer* buffer);
// Used to make the following functions thread safe: GetFreeBuffer(),
// ReturnUnusedBuffer(), RefCountedBuffer::Realloc().
std::mutex mutex_;
// Storing a RefCountedBuffer object in a Vector is complicated because of the
// copy/move semantics. So the simplest way around that is to store a list of
// pointers in the vector.
Vector<RefCountedBuffer*> buffers_ LIBGAV1_GUARDED_BY(mutex_);
InternalFrameBufferList internal_frame_buffers_;
// Frame buffer callbacks.
FrameBufferSizeChangedCallback on_frame_buffer_size_changed_;
GetFrameBufferCallback get_frame_buffer_;
ReleaseFrameBufferCallback release_frame_buffer_;
// Private data associated with the frame buffer callbacks.
void* callback_private_data_;
};
} // namespace libgav1
#endif // LIBGAV1_SRC_BUFFER_POOL_H_