| // 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 SRC_MEDIA_DRIVERS_AMLOGIC_DECODER_PTS_MANAGER_H_ |
| #define SRC_MEDIA_DRIVERS_AMLOGIC_DECODER_PTS_MANAGER_H_ |
| |
| #include <zircon/assert.h> |
| #include <zircon/compiler.h> |
| |
| #include <cstdint> |
| #include <map> |
| #include <mutex> |
| |
| namespace amlogic_decoder { |
| |
| class PtsManager { |
| public: |
| // 8 is the max number of frames in a VP9 superframe. For H264, num_reorder_frames is max 16. So |
| // 32 should be enough for both VP9 and H264. |
| static constexpr uint32_t kMaxEntriesDueToFrameReordering = 32; |
| |
| // This "extra" value should take care of any buffering in the video decoder itself, and any delay |
| // outputting a decompressed frame after it has been removed from the stream buffer. |
| static constexpr uint32_t kMaxEntriesDueToExtraDecoderDelay = 32; |
| |
| // Large enough to store an entry per every 4 bytes of the 4k h264 stream buffer. This assumes |
| // every frame is a 3 byte start code + 1 byte NALU header and that's all. Real frames are |
| // larger, so this will be enough entries for our current worst case. |
| static constexpr uint32_t kH264SingleMaxEntriesDueToStreamBuffering = 4 * 1024 / 4; |
| |
| static constexpr uint32_t kH264SingleStreamMaxEntriesToKeep = |
| kMaxEntriesDueToFrameReordering + kMaxEntriesDueToExtraDecoderDelay + |
| kH264SingleMaxEntriesDueToStreamBuffering; |
| |
| // Large enough to account for the <= 1024 bytes of data required by the FW when using |
| // h264_multi_decoder before the FW is willing to start decoding the first available data. |
| // |
| // TODO(fxbug.dev/13483): Pad the data provided to FW with AUD + padding when we know we have at |
| // least one frame end available so far that hasn't seen a corresponding pic data done. Preferably |
| // without relying on PtsManager though. |
| static constexpr uint32_t kH264MultiMaxEntriesDueToFifo = 1024 / 4; |
| // Threshold used by h264_multi_decoder to avoid over-queueing data if we've already got more than |
| // enough PTS values, which should imply that frame boundaries exist, which should imply that some |
| // progress can be made decoding without adding more input data. |
| static constexpr uint32_t kH264MultiQueuedEntryCountThreshold = |
| kH264MultiMaxEntriesDueToFifo + kMaxEntriesDueToExtraDecoderDelay; |
| // Because we use kH264MultiMaxEntriesDueToFifo as a threshold for decoding more without adding |
| // any new data, we need to be sure the PtsManager can definitely hold at least |
| // kH264MultiMaxEntriesDueToFifo comfortably without eating into the margin provided by any of the |
| // other constants, so we keep 2x as many as we really need for this reason. |
| static constexpr uint32_t kH264MultiMaxEntriesDueToFifoWithMargin = |
| 2 * kH264MultiMaxEntriesDueToFifo; |
| |
| static constexpr uint32_t kH264MultiStreamMaxEntriesToKeep = |
| kMaxEntriesDueToFrameReordering + kMaxEntriesDueToExtraDecoderDelay + |
| kH264MultiMaxEntriesDueToFifoWithMargin; |
| |
| // TODO(fxbug.dev/13483): This should have its own constants, not just be the max of these other |
| // two. |
| static constexpr uint32_t kVp9MaxEntriesToKeep = |
| std::max(kH264SingleStreamMaxEntriesToKeep, kH264MultiStreamMaxEntriesToKeep); |
| |
| static constexpr uint32_t kMaxEntriesToKeep = std::max( |
| {kH264SingleStreamMaxEntriesToKeep, kH264MultiStreamMaxEntriesToKeep, kVp9MaxEntriesToKeep}); |
| |
| class LookupResult { |
| public: |
| // Outside of PtsManager, can only be copied, not created from scratch and |
| // not assigned. |
| LookupResult(const LookupResult& from) = default; |
| |
| bool is_end_of_stream() const { return is_end_of_stream_; } |
| bool has_pts() const { return has_pts_; } |
| uint64_t pts() const { return pts_; } |
| |
| private: |
| friend class PtsManager; |
| LookupResult() = delete; |
| |
| LookupResult(bool is_end_of_stream, bool has_pts, uint64_t pts) |
| : is_end_of_stream_(is_end_of_stream), has_pts_(has_pts), pts_(pts) { |
| // PTS == 0 is valid, but if we don't have a PTS, the field must be set to |
| // 0. In other words, we still need the sparate has_pts_ to tell whether |
| // we have a PTS when the pts field is 0 - this way all pts values are |
| // usable. |
| ZX_DEBUG_ASSERT(has_pts_ || !pts_); |
| ZX_DEBUG_ASSERT(!(is_end_of_stream_ && has_pts_)); |
| } |
| |
| // If is_end_of_stream_, there is no PTS. Instead, the stream is over. |
| const bool is_end_of_stream_ = false; |
| |
| // If !has_pts_, the pts_ field is not meaningful (but is set to 0). |
| const bool has_pts_ = false; |
| |
| // If has_pts(), the pts field is meaningful. |
| // |
| // When has_pts(), the PTS of the frame. |
| // When !has_pts(), 0. |
| const uint64_t pts_ = 0; |
| }; |
| |
| void SetLookupBitWidth(uint32_t lookup_bit_width); |
| |
| // Offset is the byte offset into the stream of the beginning of the frame. |
| void InsertPts(uint64_t offset, bool has_pts, uint64_t pts); |
| |
| // |end_of_stream_offset| is the first byte offset which is not part of the |
| // input stream data (stream offset of last input stream byte + 1). |
| void SetEndOfStreamOffset(uint64_t end_of_stream_offset); |
| |
| // Offset must be within the frame that's being looked up. |
| const LookupResult Lookup(uint64_t offset); |
| |
| // Counts how many InsertPts() entries exist with offset >= threshold_offset. |
| // This helps avoid queueing so much into h264_multi_decoder's PtsManager that |
| // kMaxEntriesToKeep is exhausted. |
| uint32_t CountEntriesBeyond(uint64_t threshold_offset) const; |
| |
| private: |
| // The last inserted offset is offset_to_result_.rbegin()->first, unless empty() in which case |
| // logically 0. |
| uint64_t GetLastInsertedOffset() __TA_REQUIRES(lock_); |
| uint32_t CountEntriesBeyondLocked(uint64_t threshold_offset) const __TA_REQUIRES(lock_); |
| |
| mutable std::mutex lock_; |
| __TA_GUARDED(lock_) |
| uint32_t lookup_bit_width_ = 64; |
| |
| // TODO(dustingreen): Consider switching to a SortedCircularBuffer (to be implemented) of size |
| // kMaxEntries instead, to avoid so many pointers and separate heap allocations. Despite the |
| // memory inefficiency vs. a circular buffer, this likely consumes ~128KiB, so switching isn't |
| // urgent. |
| __TA_GUARDED(lock_) |
| std::map<uint64_t, LookupResult> offset_to_result_; |
| }; |
| |
| } // namespace amlogic_decoder |
| |
| #endif // SRC_MEDIA_DRIVERS_AMLOGIC_DECODER_PTS_MANAGER_H_ |