AOMDecoder.h (10273B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #if !defined(AOMDecoder_h_) 7 # define AOMDecoder_h_ 8 9 # include <aom/aom_decoder.h> 10 # include <stdint.h> 11 12 # include "PerformanceRecorder.h" 13 # include "PlatformDecoderModule.h" 14 # include "VideoUtils.h" 15 # include "mozilla/Span.h" 16 17 namespace mozilla { 18 19 DDLoggedTypeDeclNameAndBase(AOMDecoder, MediaDataDecoder); 20 21 class AOMDecoder final : public MediaDataDecoder, 22 public DecoderDoctorLifeLogger<AOMDecoder> { 23 public: 24 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AOMDecoder, final); 25 26 explicit AOMDecoder(const CreateDecoderParams& aParams); 27 28 RefPtr<InitPromise> Init() override; 29 RefPtr<DecodePromise> Decode(MediaRawData* aSample) override; 30 RefPtr<DecodePromise> Drain() override; 31 RefPtr<FlushPromise> Flush() override; 32 RefPtr<ShutdownPromise> Shutdown() override; 33 nsCString GetDescriptionName() const override { 34 return "av1 libaom video decoder"_ns; 35 } 36 nsCString GetCodecName() const override { return "av1"_ns; } 37 38 // Return true if aMimeType is a one of the strings used 39 // by our demuxers to identify AV1 streams. 40 static bool IsAV1(const nsACString& aMimeType); 41 42 // Return true if uses AV1 main profile. 43 static bool IsMainProfile(const MediaByteBuffer* aBox); 44 45 // Return true if a sample is a keyframe. 46 static bool IsKeyframe(Span<const uint8_t> aBuffer); 47 48 // Return the frame dimensions for a sample. 49 static gfx::IntSize GetFrameSize(Span<const uint8_t> aBuffer); 50 51 // obu_type defined at: 52 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=123 53 enum class OBUType : uint8_t { 54 Reserved = 0, 55 SequenceHeader = 1, 56 TemporalDelimiter = 2, 57 FrameHeader = 3, 58 TileGroup = 4, 59 Metadata = 5, 60 Frame = 6, 61 RedundantFrameHeader = 7, 62 TileList = 8, 63 Padding = 15 64 }; 65 66 struct OBUInfo { 67 OBUType mType = OBUType::Reserved; 68 bool mExtensionFlag = false; 69 Span<const uint8_t> mContents; 70 71 bool IsValid() const { 72 switch (mType) { 73 case OBUType::SequenceHeader: 74 case OBUType::TemporalDelimiter: 75 case OBUType::FrameHeader: 76 case OBUType::TileGroup: 77 case OBUType::Metadata: 78 case OBUType::Frame: 79 case OBUType::RedundantFrameHeader: 80 case OBUType::TileList: 81 case OBUType::Padding: 82 return true; 83 default: 84 return false; 85 } 86 } 87 }; 88 89 struct OBUIterator { 90 public: 91 explicit OBUIterator(const Span<const uint8_t>& aData) 92 : mData(aData), mPosition(0), mGoNext(true), mResult(NS_OK) {} 93 bool HasNext() { 94 UpdateNext(); 95 return !mGoNext; 96 } 97 OBUInfo Next() { 98 UpdateNext(); 99 mGoNext = true; 100 return mCurrent; 101 } 102 MediaResult GetResult() const { return mResult; } 103 104 private: 105 const Span<const uint8_t>& mData; 106 size_t mPosition; 107 OBUInfo mCurrent; 108 bool mGoNext; 109 MediaResult mResult; 110 111 // Used to fill mCurrent with the next OBU in the iterator. 112 // mGoNext must be set to false if the next OBU is retrieved, 113 // otherwise it will be true so that HasNext() returns false. 114 // When an invalid OBU is read, the iterator will finish and 115 // mCurrent will be reset to default OBUInfo(). 116 void UpdateNext(); 117 }; 118 119 // Create an iterator to parse Open Bitstream Units from a buffer. 120 static OBUIterator ReadOBUs(const Span<const uint8_t>& aData); 121 // Writes an Open Bitstream Unit header type and the contained subheader. 122 // Extension flag is set to 0 and size field is always present. 123 static already_AddRefed<MediaByteBuffer> CreateOBU( 124 const OBUType aType, const Span<const uint8_t>& aContents); 125 126 // chroma_sample_position defined at: 127 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=131 128 enum class ChromaSamplePosition : uint8_t { 129 Unknown = 0, 130 Vertical = 1, 131 Colocated = 2, 132 Reserved = 3 133 }; 134 135 struct OperatingPoint { 136 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=125 137 // operating_point_idc[ i ]: A set of bitwise flags determining 138 // the temporal and spatial layers to decode. 139 // A value of 0 indicates that scalability is not being used. 140 uint16_t mLayers = 0; 141 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=650 142 // See A.3: Levels for a definition of the available levels. 143 uint8_t mLevel = 0; 144 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=126 145 // seq_tier[ i ]: The tier for the selected operating point. 146 uint8_t mTier = 0; 147 148 bool operator==(const OperatingPoint& aOther) const { 149 return mLayers == aOther.mLayers && mLevel == aOther.mLevel && 150 mTier == aOther.mTier; 151 } 152 bool operator!=(const OperatingPoint& aOther) const { 153 return !(*this == aOther); 154 } 155 }; 156 157 struct AV1SequenceInfo { 158 AV1SequenceInfo() = default; 159 160 AV1SequenceInfo(const AV1SequenceInfo& aOther) { *this = aOther; } 161 162 // Profiles, levels and tiers defined at: 163 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=650 164 uint8_t mProfile = 0; 165 166 // choose_operating_point( ) defines that the operating points are 167 // specified in order of preference by the encoder. Higher operating 168 // points indices in the header will allow a tradeoff of quality for 169 // performance, dropping some data from the decoding process. 170 // Normally we are only interested in the first operating point. 171 // See: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=126 172 nsTArray<OperatingPoint> mOperatingPoints = nsTArray<OperatingPoint>(1); 173 174 gfx::IntSize mImage = {0, 0}; 175 176 // Color configs explained at: 177 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=129 178 uint8_t mBitDepth = 8; 179 bool mMonochrome = false; 180 bool mSubsamplingX = true; 181 bool mSubsamplingY = true; 182 ChromaSamplePosition mChromaSamplePosition = ChromaSamplePosition::Unknown; 183 184 VideoColorSpace mColorSpace; 185 186 gfx::ColorDepth ColorDepth() const { 187 return gfx::ColorDepthForBitDepth(mBitDepth); 188 } 189 190 bool operator==(const AV1SequenceInfo& aOther) const { 191 if (mProfile != aOther.mProfile || mImage != aOther.mImage || 192 mBitDepth != aOther.mBitDepth || mMonochrome != aOther.mMonochrome || 193 mSubsamplingX != aOther.mSubsamplingX || 194 mSubsamplingY != aOther.mSubsamplingY || 195 mChromaSamplePosition != aOther.mChromaSamplePosition || 196 mColorSpace != aOther.mColorSpace) { 197 return false; 198 } 199 200 size_t opCount = mOperatingPoints.Length(); 201 if (opCount != aOther.mOperatingPoints.Length()) { 202 return false; 203 } 204 for (size_t i = 0; i < opCount; i++) { 205 if (mOperatingPoints[i] != aOther.mOperatingPoints[i]) { 206 return false; 207 } 208 } 209 210 return true; 211 } 212 bool operator!=(const AV1SequenceInfo& aOther) const { 213 return !(*this == aOther); 214 } 215 AV1SequenceInfo& operator=(const AV1SequenceInfo& aOther) { 216 mProfile = aOther.mProfile; 217 218 size_t opCount = aOther.mOperatingPoints.Length(); 219 mOperatingPoints.ClearAndRetainStorage(); 220 mOperatingPoints.SetCapacity(opCount); 221 for (size_t i = 0; i < opCount; i++) { 222 mOperatingPoints.AppendElement(aOther.mOperatingPoints[i]); 223 } 224 225 mImage = aOther.mImage; 226 mBitDepth = aOther.mBitDepth; 227 mMonochrome = aOther.mMonochrome; 228 mSubsamplingX = aOther.mSubsamplingX; 229 mSubsamplingY = aOther.mSubsamplingY; 230 mChromaSamplePosition = aOther.mChromaSamplePosition; 231 mColorSpace = aOther.mColorSpace; 232 return *this; 233 } 234 }; 235 236 // Get a sequence header's info from a sample. 237 // Returns a MediaResult with codes: 238 // NS_OK: Sequence header was successfully found and read. 239 // NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: Sequence header was not present. 240 // Other errors will indicate that the data was corrupt. 241 static MediaResult ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample, 242 AV1SequenceInfo& aDestInfo); 243 // Writes a sequence header OBU to the buffer. 244 static already_AddRefed<MediaByteBuffer> CreateSequenceHeader( 245 const AV1SequenceInfo& aInfo, nsresult& aResult); 246 247 // Reads the raw data of an ISOBMFF-compatible av1 configuration box (av1C), 248 // including any included sequence header. 249 static void TryReadAV1CBox(const MediaByteBuffer* aBox, 250 AV1SequenceInfo& aDestInfo, 251 MediaResult& aSeqHdrResult); 252 // Reads the raw data of an ISOBMFF-compatible av1 configuration box (av1C), 253 // including any included sequence header. 254 // This function should only be called for av1C boxes made by WriteAV1CBox, as 255 // it will assert that the box and its contained OBUs are not corrupted. 256 static void ReadAV1CBox(const MediaByteBuffer* aBox, 257 AV1SequenceInfo& aDestInfo, bool& aHadSeqHdr) { 258 MediaResult seqHdrResult; 259 TryReadAV1CBox(aBox, aDestInfo, seqHdrResult); 260 nsresult code = seqHdrResult.Code(); 261 MOZ_ASSERT(code == NS_OK || code == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA); 262 aHadSeqHdr = code == NS_OK; 263 } 264 // Writes an ISOBMFF-compatible av1 configuration box (av1C) to the buffer. 265 static void WriteAV1CBox(const AV1SequenceInfo& aInfo, 266 MediaByteBuffer* aDestBox, bool& aHasSeqHdr); 267 268 // Create sequence info from a MIME codecs string. 269 static Maybe<AV1SequenceInfo> CreateSequenceInfoFromCodecs( 270 const nsAString& aCodec); 271 static bool SetVideoInfo(VideoInfo* aDestInfo, const nsAString& aCodec); 272 273 private: 274 ~AOMDecoder(); 275 RefPtr<DecodePromise> ProcessDecode(MediaRawData* aSample); 276 277 const RefPtr<layers::ImageContainer> mImageContainer; 278 const RefPtr<TaskQueue> mTaskQueue; 279 280 // AOM decoder state 281 aom_codec_ctx_t mCodec; 282 283 const VideoInfo mInfo; 284 const Maybe<TrackingId> mTrackingId; 285 PerformanceRecorderMulti<DecodeStage> mPerformanceRecorder; 286 }; 287 288 } // namespace mozilla 289 290 #endif // AOMDecoder_h_