| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef GARNET_DRIVERS_VIDEO_AMLOGIC_DECODER_VP9_DECODER_H_ |
| #define GARNET_DRIVERS_VIDEO_AMLOGIC_DECODER_VP9_DECODER_H_ |
| |
| #include <vector> |
| |
| #include "registers.h" |
| #include "video_decoder.h" |
| |
| // From libvpx |
| struct loop_filter_info_n; |
| struct loopfilter; |
| struct segmentation; |
| |
| class Vp9Decoder : public VideoDecoder { |
| public: |
| enum class InputType { |
| // A single stream is decoded at once |
| kSingleStream, |
| // Multiple streams are decoded at once |
| kMultiStream, |
| // Multiple streams, each with input buffers divided on frame boundaries, |
| // are decoded at once. |
| kMultiFrameBased |
| }; |
| class FrameDataProvider { |
| public: |
| // Called with the decoder locked. |
| virtual void ReadMoreInputData(Vp9Decoder* decoder) = 0; |
| virtual void ReadMoreInputDataFromReschedule(Vp9Decoder* decoder) = 0; |
| virtual void FrameWasOutput() = 0; |
| // Default behavior is for the benefit of test code; production |
| // implementation overrides all the methods. |
| virtual bool HasMoreInputData() { return true; } |
| }; |
| enum class DecoderState { |
| // In these two states the decoder is stopped because UpdateDecodeSize needs |
| // to be called. The difference between these two is how it needs to be |
| // restarted. |
| kInitialWaitingForInput, |
| kStoppedWaitingForInput, |
| |
| // A frame was produced and the hardware is waiting for permission to decode |
| // another frame. |
| kFrameJustProduced, |
| |
| // The hardware is currently processing data. |
| kRunning, |
| |
| // The hardware is waiting for reference frames and outputs to be |
| // initialized after decoding the uncompressed header and before decoding |
| // the compressed data. |
| kPausedAtHeader, |
| |
| // The hardware is waiting for references frames, but the special |
| // end-of-stream size was reached. It can safely be swapped out now, because |
| // its state doesn't matter. |
| kPausedAtEndOfStream, |
| |
| // The hardware's state doesn't reflect that of the Vp9Decoder. |
| kSwappedOut, |
| }; |
| |
| explicit Vp9Decoder(Owner* owner, InputType input_type); |
| Vp9Decoder(const Vp9Decoder&) = delete; |
| |
| ~Vp9Decoder() override; |
| |
| __WARN_UNUSED_RESULT zx_status_t Initialize() override; |
| __WARN_UNUSED_RESULT zx_status_t InitializeHardware() override; |
| void HandleInterrupt() override; |
| // In actual operation, the FrameReadyNotifier must not keep a reference on |
| // the frame shared_ptr<>, as that would interfere with muting calls to |
| // ReturnFrame(). See comment on Vp9Decoder::Frame::frame field. |
| void SetFrameReadyNotifier(FrameReadyNotifier notifier) override; |
| void ReturnFrame(std::shared_ptr<VideoFrame> frame) override; |
| void SetInitializeFramesHandler(InitializeFramesHandler handler) override; |
| void SetErrorHandler(fit::closure error_handler) override; |
| void SetCheckOutputReady(CheckOutputReady check_output_ready) override; |
| void InitializedFrames(std::vector<CodecFrame> frames, uint32_t width, |
| uint32_t height, uint32_t stride) override; |
| __WARN_UNUSED_RESULT bool CanBeSwappedIn() override; |
| __WARN_UNUSED_RESULT bool CanBeSwappedOut() const override { |
| return state_ == DecoderState::kFrameJustProduced || |
| state_ == DecoderState::kPausedAtEndOfStream; |
| } |
| void SetSwappedOut() override { state_ = DecoderState::kSwappedOut; } |
| void SwappedIn() override { |
| frame_data_provider_->ReadMoreInputDataFromReschedule(this); |
| } |
| |
| void SetFrameDataProvider(FrameDataProvider* provider) { |
| frame_data_provider_ = provider; |
| } |
| void UpdateDecodeSize(uint32_t size); |
| |
| __WARN_UNUSED_RESULT bool needs_more_input_data() const { |
| return state_ == DecoderState::kStoppedWaitingForInput || |
| state_ == DecoderState::kInitialWaitingForInput; |
| } |
| |
| __WARN_UNUSED_RESULT bool swapped_out() const { |
| return state_ == DecoderState::kSwappedOut; |
| } |
| |
| void SetPausedAtEndOfStream() { |
| ZX_DEBUG_ASSERT(state_ == DecoderState::kPausedAtHeader); |
| state_ = DecoderState::kPausedAtEndOfStream; |
| } |
| |
| private: |
| friend class Vp9UnitTest; |
| friend class TestVP9; |
| friend class TestFrameProvider; |
| friend class CodecAdapterVp9; |
| class WorkingBuffer; |
| |
| class BufferAllocator { |
| public: |
| void Register(WorkingBuffer* buffer); |
| zx_status_t AllocateBuffers(VideoDecoder::Owner* decoder); |
| void CheckBuffers(); |
| |
| private: |
| std::vector<WorkingBuffer*> buffers_; |
| }; |
| |
| class WorkingBuffer { |
| public: |
| WorkingBuffer(BufferAllocator* allocator, size_t size); |
| |
| ~WorkingBuffer(); |
| |
| uint32_t addr32(); |
| size_t size() const { return size_; } |
| io_buffer_t& buffer() { return buffer_; } |
| |
| private: |
| size_t size_; |
| io_buffer_t buffer_ = {}; |
| }; |
| |
| struct WorkingBuffers : public BufferAllocator { |
| WorkingBuffers() {} |
| |
| // Sizes are large enough for 4096x2304. |
| #define DEF_BUFFER(name, size) WorkingBuffer name = WorkingBuffer(this, size) |
| DEF_BUFFER(rpm, 0x400 * 2); |
| DEF_BUFFER(short_term_rps, 0x800); |
| DEF_BUFFER(picture_parameter_set, 0x2000); |
| DEF_BUFFER(swap, 0x800); |
| DEF_BUFFER(swap2, 0x800); |
| DEF_BUFFER(local_memory_dump, 0x400 * 2); |
| DEF_BUFFER(ipp_line_buffer, 0x4000); |
| DEF_BUFFER(sao_up, 0x2800); |
| DEF_BUFFER(scale_lut, 0x8000); |
| // HW/firmware requires first parameters + deblock data to be adjacent in |
| // that order. |
| static constexpr uint32_t kDeblockParametersSize = 0x80000; |
| static constexpr uint32_t kDeblockDataSize = 0x80000; |
| DEF_BUFFER(deblock_parameters, kDeblockParametersSize + kDeblockDataSize); |
| DEF_BUFFER(deblock_parameters2, 0x80000); // Only used on G12a. |
| DEF_BUFFER(segment_map, 0xd800); |
| DEF_BUFFER(probability_buffer, 0x1000 * 5); |
| DEF_BUFFER(count_buffer, 0x300 * 4 * 4); |
| DEF_BUFFER(motion_prediction_above, 0x10000); |
| DEF_BUFFER(mmu_vbh, 0x5000); |
| DEF_BUFFER(frame_map_mmu, 0x1200 * 4); |
| #undef DEF_BUFFER |
| }; |
| |
| struct Frame { |
| ~Frame(); |
| |
| // Index into frames_. |
| uint32_t index; |
| |
| // This is the count of references from reference_frame_map_, last_frame_, |
| // current_frame_, and any buffers the ultimate consumers have outstanding. |
| int32_t refcount = 0; |
| // Each VideoFrame is managed via shared_ptr<> here and via weak_ptr<> in |
| // CodecBuffer. There is a frame.reset() performed under |
| // video_decoder_lock_ that essentially signals to the weak_ptr<> in |
| // CodecBuffer not to call ReturnFrame() any more for this frame. For this |
| // reason, under normal operation (not self-test), it's important that |
| // FrameReadyNotifier and weak_ptr<>::lock() not result in keeping any |
| // shared_ptr<> reference on VideoFrame that lasts beyond the current |
| // video_decoder_lock_ interval, since that could allow calling |
| // ReturnFrame() on a frame that the Vp9Decoder doesn't want to hear about |
| // any more. |
| // |
| // TODO(dustingreen): Mute ReturnFrame() a different way; maybe just |
| // explicitly. Ideally, we'd use a way that's more similar between decoder |
| // self-test and "normal operation". |
| // |
| // This shared_ptr<> must not actually be shared outside of while |
| // video_decoder_lock_ is held. See previous paragraphs. |
| std::shared_ptr<VideoFrame> frame; |
| // With the MMU enabled the compressed frame header is stored separately |
| // from the data itself, allowing the data to be allocated in noncontiguous |
| // memory. |
| io_buffer_t compressed_header = {}; |
| |
| io_buffer_t compressed_data = {}; |
| |
| // This is decoded_frame_count_ when this frame was decoded into. |
| uint32_t decoded_index = 0xffffffff; |
| }; |
| |
| struct MpredBuffer { |
| ~MpredBuffer(); |
| // This stores the motion vectors used to decode a frame for use in |
| // calculating motion vectors for the next frame. |
| io_buffer_t mv_mpred_buffer = {}; |
| }; |
| |
| struct PictureData { |
| bool keyframe = false; |
| bool intra_only = false; |
| uint32_t refresh_frame_flags = 0; |
| bool show_frame; |
| bool error_resilient_mode; |
| bool has_pts = false; |
| uint64_t pts = 0; |
| }; |
| |
| union HardwareRenderParams; |
| |
| zx_status_t AllocateFrames(); |
| void InitializeHardwarePictureList(); |
| void InitializeParser(); |
| bool FindNewFrameBuffer(HardwareRenderParams* params); |
| void InitLoopFilter(); |
| void UpdateLoopFilter(HardwareRenderParams* params); |
| void ProcessCompletedFrames(); |
| void ShowExistingFrame(HardwareRenderParams* params); |
| void PrepareNewFrame(); |
| void ConfigureFrameOutput(uint32_t width, uint32_t height, bool bit_depth_8); |
| void ConfigureMcrcc(); |
| void UpdateLoopFilterThresholds(); |
| void ConfigureMotionPrediction(); |
| void ConfigureReferenceFrameHardware(); |
| void SetRefFrames(HardwareRenderParams* params); |
| __WARN_UNUSED_RESULT zx_status_t InitializeBuffers(); |
| void InitializeLoopFilterData(); |
| |
| Owner* owner_; |
| InputType input_type_; |
| |
| FrameDataProvider* frame_data_provider_ = nullptr; |
| |
| WorkingBuffers working_buffers_; |
| FrameReadyNotifier notifier_; |
| InitializeFramesHandler initialize_frames_handler_; |
| CheckOutputReady check_output_ready_; |
| fit::closure error_handler_; |
| DecoderState state_ = DecoderState::kSwappedOut; |
| |
| std::vector<std::unique_ptr<Frame>> frames_; |
| Frame* last_frame_ = nullptr; |
| Frame* current_frame_ = nullptr; |
| std::unique_ptr<loop_filter_info_n> loop_filter_info_; |
| std::unique_ptr<loopfilter> loop_filter_; |
| std::unique_ptr<segmentation> segmentation_ = {}; |
| // Waiting for an available frame buffer (with reference count 0). |
| bool waiting_for_empty_frames_ = false; |
| // Waiting for an available output packet, to avoid show_existing_frame |
| // potentially allowing too much queued output, as a show_existing_frame |
| // output frame doesn't use up a frame buffer - but it does use up an output |
| // packet. We don't directly track the output packets in the h264_decoder, |
| // but this bool corresponds to being out of output packets in |
| // codec_adapter_vp9. We re-try PrepareNewFrame() during ReturnFrame() even |
| // if no refcount on any Frame has reached 0 |
| bool waiting_for_output_ready_ = false; |
| |
| // This is the count of frames decoded since this object was created. |
| uint32_t decoded_frame_count_ = 0; |
| |
| uint32_t frame_done_count_ = 0; |
| |
| PictureData last_frame_data_; |
| PictureData current_frame_data_; |
| |
| std::unique_ptr<MpredBuffer> last_mpred_buffer_; |
| std::unique_ptr<MpredBuffer> current_mpred_buffer_; |
| |
| // One previously-used buffer is kept around so a new buffer doesn't have to |
| // be allocated each frame. |
| std::unique_ptr<MpredBuffer> cached_mpred_buffer_; |
| |
| // The VP9 specification requires that 8 reference frames can be stored - |
| // they're saved in this structure. |
| Frame* reference_frame_map_[8] = {}; |
| |
| // Each frame that's being decoded can reference 3 of the frames that are in |
| // reference_frame_map_. |
| Frame* current_reference_frames_[3] = {}; |
| }; |
| |
| #endif // GARNET_DRIVERS_VIDEO_AMLOGIC_DECODER_VP9_DECODER_H_ |