/* * 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 #include #include #include #include // NOLINT (unapproved c++11 header) #include #include #include // 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& GlobalMotions() const { return global_motion_; } // Saves the GlobalMotion array. Only the |params| field of each GlobalMotion // struct is saved. void SetGlobalMotions( const std::array& 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& loop_filter_ref_deltas() const { return loop_filter_ref_deltas_; } const std::array& 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 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 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 lock(mutex_); if (progress_row_ >= progress_row) return; progress_row_ = progress_row; } progress_row_condvar_.notify_all(); } void MarkFrameAsStarted() { std::lock_guard 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 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 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 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 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 global_motion_ = {}; SymbolDecoderContext frame_context_; std::array loop_filter_ref_deltas_; std::array 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. 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; // 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 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_