You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
442 lines
17 KiB
442 lines
17 KiB
/*
|
|
* 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 <algorithm>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <climits>
|
|
#include <condition_variable> // NOLINT (unapproved c++11 header)
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#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/dynamic_buffer.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.
|
|
// The alignment requirement is due to the SymbolDecoderContext member
|
|
// frame_context_.
|
|
class RefCountedBuffer : public MaxAlignedAllocable {
|
|
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; }
|
|
|
|
ObuMetadataHdrCll hdr_cll() const { return hdr_cll_; }
|
|
void set_hdr_cll(const ObuMetadataHdrCll& hdr_cll) {
|
|
hdr_cll_set_ = true;
|
|
hdr_cll_ = hdr_cll;
|
|
}
|
|
bool hdr_cll_set() const { return hdr_cll_set_; }
|
|
|
|
ObuMetadataHdrMdcv hdr_mdcv() const { return hdr_mdcv_; }
|
|
void set_hdr_mdcv(const ObuMetadataHdrMdcv& hdr_mdcv) {
|
|
hdr_mdcv_set_ = true;
|
|
hdr_mdcv_ = hdr_mdcv;
|
|
}
|
|
bool hdr_mdcv_set() const { return hdr_mdcv_set_; }
|
|
|
|
ObuMetadataItutT35 itut_t35() const { return itut_t35_; }
|
|
bool set_itut_t35(const ObuMetadataItutT35& itut_t35,
|
|
const uint8_t* const payload) {
|
|
itut_t35_ = itut_t35;
|
|
if (itut_t35.payload_size > 0) {
|
|
if (!itut_t35_payload_.Resize(itut_t35.payload_size)) return false;
|
|
memcpy(itut_t35_payload_.get(), payload, itut_t35.payload_size);
|
|
itut_t35_.payload_bytes = itut_t35_payload_.get();
|
|
} else {
|
|
itut_t35_.payload_bytes = nullptr;
|
|
}
|
|
itut_t35_set_ = true;
|
|
return true;
|
|
}
|
|
bool itut_t35_set() const { return itut_t35_set_; }
|
|
|
|
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;
|
|
|
|
ObuMetadataHdrCll hdr_cll_ = {};
|
|
bool hdr_cll_set_ = false; // Set to true when set_hdr_cll() is called.
|
|
ObuMetadataHdrMdcv hdr_mdcv_ = {};
|
|
bool hdr_mdcv_set_ = false; // Set to true when set_hdr_mdcv() is called.
|
|
ObuMetadataItutT35 itut_t35_ = {};
|
|
DynamicBuffer<uint8_t> itut_t35_payload_;
|
|
bool itut_t35_set_ = false; // Set to true when set_itut_t35() is called.
|
|
|
|
// 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_
|