| /* |
| * 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_ |