diff options
Diffstat (limited to 'dom/media/platforms/agnostic/AOMDecoder.h')
-rw-r--r-- | dom/media/platforms/agnostic/AOMDecoder.h | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/dom/media/platforms/agnostic/AOMDecoder.h b/dom/media/platforms/agnostic/AOMDecoder.h new file mode 100644 index 0000000000..2d3d7761fa --- /dev/null +++ b/dom/media/platforms/agnostic/AOMDecoder.h @@ -0,0 +1,287 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#if !defined(AOMDecoder_h_) +# define AOMDecoder_h_ + +# include <stdint.h> + +# include "PerformanceRecorder.h" +# include "PlatformDecoderModule.h" +# include "aom/aom_decoder.h" +# include "mozilla/Span.h" +# include "VideoUtils.h" + +namespace mozilla { + +DDLoggedTypeDeclNameAndBase(AOMDecoder, MediaDataDecoder); + +class AOMDecoder final : public MediaDataDecoder, + public DecoderDoctorLifeLogger<AOMDecoder> { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AOMDecoder, final); + + explicit AOMDecoder(const CreateDecoderParams& aParams); + + RefPtr<InitPromise> Init() override; + RefPtr<DecodePromise> Decode(MediaRawData* aSample) override; + RefPtr<DecodePromise> Drain() override; + RefPtr<FlushPromise> Flush() override; + RefPtr<ShutdownPromise> Shutdown() override; + nsCString GetDescriptionName() const override { + return "av1 libaom video decoder"_ns; + } + nsCString GetCodecName() const override { return "av1"_ns; } + + // Return true if aMimeType is a one of the strings used + // by our demuxers to identify AV1 streams. + static bool IsAV1(const nsACString& aMimeType); + + // Return true if a sample is a keyframe. + static bool IsKeyframe(Span<const uint8_t> aBuffer); + + // Return the frame dimensions for a sample. + static gfx::IntSize GetFrameSize(Span<const uint8_t> aBuffer); + + // obu_type defined at: + // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=123 + enum class OBUType : uint8_t { + Reserved = 0, + SequenceHeader = 1, + TemporalDelimiter = 2, + FrameHeader = 3, + TileGroup = 4, + Metadata = 5, + Frame = 6, + RedundantFrameHeader = 7, + TileList = 8, + Padding = 15 + }; + + struct OBUInfo { + OBUType mType = OBUType::Reserved; + bool mExtensionFlag = false; + Span<const uint8_t> mContents; + + bool IsValid() const { + switch (mType) { + case OBUType::SequenceHeader: + case OBUType::TemporalDelimiter: + case OBUType::FrameHeader: + case OBUType::TileGroup: + case OBUType::Metadata: + case OBUType::Frame: + case OBUType::RedundantFrameHeader: + case OBUType::TileList: + case OBUType::Padding: + return true; + default: + return false; + } + } + }; + + struct OBUIterator { + public: + explicit OBUIterator(const Span<const uint8_t>& aData) + : mData(aData), mPosition(0), mGoNext(true), mResult(NS_OK) {} + bool HasNext() { + UpdateNext(); + return !mGoNext; + } + OBUInfo Next() { + UpdateNext(); + mGoNext = true; + return mCurrent; + } + MediaResult GetResult() const { return mResult; } + + private: + const Span<const uint8_t>& mData; + size_t mPosition; + OBUInfo mCurrent; + bool mGoNext; + MediaResult mResult; + + // Used to fill mCurrent with the next OBU in the iterator. + // mGoNext must be set to false if the next OBU is retrieved, + // otherwise it will be true so that HasNext() returns false. + // When an invalid OBU is read, the iterator will finish and + // mCurrent will be reset to default OBUInfo(). + void UpdateNext(); + }; + + // Create an iterator to parse Open Bitstream Units from a buffer. + static OBUIterator ReadOBUs(const Span<const uint8_t>& aData); + // Writes an Open Bitstream Unit header type and the contained subheader. + // Extension flag is set to 0 and size field is always present. + static already_AddRefed<MediaByteBuffer> CreateOBU( + const OBUType aType, const Span<const uint8_t>& aContents); + + // chroma_sample_position defined at: + // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=131 + enum class ChromaSamplePosition : uint8_t { + Unknown = 0, + Vertical = 1, + Colocated = 2, + Reserved = 3 + }; + + struct OperatingPoint { + // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=125 + // operating_point_idc[ i ]: A set of bitwise flags determining + // the temporal and spatial layers to decode. + // A value of 0 indicates that scalability is not being used. + uint16_t mLayers = 0; + // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=650 + // See A.3: Levels for a definition of the available levels. + uint8_t mLevel = 0; + // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=126 + // seq_tier[ i ]: The tier for the selected operating point. + uint8_t mTier = 0; + + bool operator==(const OperatingPoint& aOther) const { + return mLayers == aOther.mLayers && mLevel == aOther.mLevel && + mTier == aOther.mTier; + } + bool operator!=(const OperatingPoint& aOther) const { + return !(*this == aOther); + } + }; + + struct AV1SequenceInfo { + AV1SequenceInfo() = default; + + AV1SequenceInfo(const AV1SequenceInfo& aOther) { *this = aOther; } + + // Profiles, levels and tiers defined at: + // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=650 + uint8_t mProfile = 0; + + // choose_operating_point( ) defines that the operating points are + // specified in order of preference by the encoder. Higher operating + // points indices in the header will allow a tradeoff of quality for + // performance, dropping some data from the decoding process. + // Normally we are only interested in the first operating point. + // See: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=126 + nsTArray<OperatingPoint> mOperatingPoints = nsTArray<OperatingPoint>(1); + + gfx::IntSize mImage = {0, 0}; + + // Color configs explained at: + // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=129 + uint8_t mBitDepth = 8; + bool mMonochrome = false; + bool mSubsamplingX = true; + bool mSubsamplingY = true; + ChromaSamplePosition mChromaSamplePosition = ChromaSamplePosition::Unknown; + + VideoColorSpace mColorSpace; + + gfx::ColorDepth ColorDepth() const { + return gfx::ColorDepthForBitDepth(mBitDepth); + } + + bool operator==(const AV1SequenceInfo& aOther) const { + if (mProfile != aOther.mProfile || mImage != aOther.mImage || + mBitDepth != aOther.mBitDepth || mMonochrome != aOther.mMonochrome || + mSubsamplingX != aOther.mSubsamplingX || + mSubsamplingY != aOther.mSubsamplingY || + mChromaSamplePosition != aOther.mChromaSamplePosition || + mColorSpace != aOther.mColorSpace) { + return false; + } + + size_t opCount = mOperatingPoints.Length(); + if (opCount != aOther.mOperatingPoints.Length()) { + return false; + } + for (size_t i = 0; i < opCount; i++) { + if (mOperatingPoints[i] != aOther.mOperatingPoints[i]) { + return false; + } + } + + return true; + } + bool operator!=(const AV1SequenceInfo& aOther) const { + return !(*this == aOther); + } + AV1SequenceInfo& operator=(const AV1SequenceInfo& aOther) { + mProfile = aOther.mProfile; + + size_t opCount = aOther.mOperatingPoints.Length(); + mOperatingPoints.ClearAndRetainStorage(); + mOperatingPoints.SetCapacity(opCount); + for (size_t i = 0; i < opCount; i++) { + mOperatingPoints.AppendElement(aOther.mOperatingPoints[i]); + } + + mImage = aOther.mImage; + mBitDepth = aOther.mBitDepth; + mMonochrome = aOther.mMonochrome; + mSubsamplingX = aOther.mSubsamplingX; + mSubsamplingY = aOther.mSubsamplingY; + mChromaSamplePosition = aOther.mChromaSamplePosition; + mColorSpace = aOther.mColorSpace; + return *this; + } + }; + + // Get a sequence header's info from a sample. + // Returns a MediaResult with codes: + // NS_OK: Sequence header was successfully found and read. + // NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: Sequence header was not present. + // Other errors will indicate that the data was corrupt. + static MediaResult ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample, + AV1SequenceInfo& aDestInfo); + // Writes a sequence header OBU to the buffer. + static already_AddRefed<MediaByteBuffer> CreateSequenceHeader( + const AV1SequenceInfo& aInfo, nsresult& aResult); + + // Reads the raw data of an ISOBMFF-compatible av1 configuration box (av1C), + // including any included sequence header. + static void TryReadAV1CBox(const MediaByteBuffer* aBox, + AV1SequenceInfo& aDestInfo, + MediaResult& aSeqHdrResult); + // Reads the raw data of an ISOBMFF-compatible av1 configuration box (av1C), + // including any included sequence header. + // This function should only be called for av1C boxes made by WriteAV1CBox, as + // it will assert that the box and its contained OBUs are not corrupted. + static void ReadAV1CBox(const MediaByteBuffer* aBox, + AV1SequenceInfo& aDestInfo, bool& aHadSeqHdr) { + MediaResult seqHdrResult; + TryReadAV1CBox(aBox, aDestInfo, seqHdrResult); + nsresult code = seqHdrResult.Code(); + MOZ_ASSERT(code == NS_OK || code == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA); + aHadSeqHdr = code == NS_OK; + } + // Writes an ISOBMFF-compatible av1 configuration box (av1C) to the buffer. + static void WriteAV1CBox(const AV1SequenceInfo& aInfo, + MediaByteBuffer* aDestBox, bool& aHasSeqHdr); + + // Create sequence info from a MIME codecs string. + static Maybe<AV1SequenceInfo> CreateSequenceInfoFromCodecs( + const nsAString& aCodec); + static bool SetVideoInfo(VideoInfo* aDestInfo, const nsAString& aCodec); + + private: + ~AOMDecoder(); + RefPtr<DecodePromise> ProcessDecode(MediaRawData* aSample); + + const RefPtr<layers::ImageContainer> mImageContainer; + const RefPtr<TaskQueue> mTaskQueue; + + // AOM decoder state + aom_codec_ctx_t mCodec; + + const VideoInfo mInfo; + const Maybe<TrackingId> mTrackingId; + PerformanceRecorderMulti<DecodeStage> mPerformanceRecorder; +}; + +} // namespace mozilla + +#endif // AOMDecoder_h_ |