nsAVIFDecoder.h (9926B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * 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 7 #ifndef mozilla_image_decoders_nsAVIFDecoder_h 8 #define mozilla_image_decoders_nsAVIFDecoder_h 9 10 #include "Decoder.h" 11 #include "mozilla/gfx/Types.h" 12 #include "MP4Metadata.h" 13 #include "mp4parse.h" 14 #include "SampleIterator.h" 15 #include "SurfacePipe.h" 16 17 #include <aom/aom_decoder.h> 18 #include "dav1d/dav1d.h" 19 20 namespace mozilla { 21 namespace image { 22 class RasterImage; 23 class AVIFDecoderStream; 24 class AVIFParser; 25 class AVIFDecoderInterface; 26 27 class nsAVIFDecoder final : public Decoder { 28 public: 29 virtual ~nsAVIFDecoder(); 30 31 DecoderType GetType() const override { return DecoderType::AVIF; } 32 33 protected: 34 LexerResult DoDecode(SourceBufferIterator& aIterator, 35 IResumable* aOnResume) override; 36 Maybe<glean::impl::MemoryDistributionMetric> SpeedMetric() const override; 37 38 private: 39 friend class DecoderFactory; 40 friend class AVIFDecoderInterface; 41 friend class AVIFParser; 42 43 // Decoders should only be instantiated via DecoderFactory. 44 explicit nsAVIFDecoder(RasterImage* aImage); 45 46 static intptr_t ReadSource(uint8_t* aDestBuf, uintptr_t aDestBufSize, 47 void* aUserData); 48 49 typedef int Dav1dResult; 50 enum class NonAOMCodecError { NoFrame, SizeOverflow }; 51 typedef Variant<aom_codec_err_t, NonAOMCodecError> AOMResult; 52 enum class NonDecoderResult { 53 NeedMoreData, 54 OutputAvailable, 55 Complete, 56 SizeOverflow, 57 OutOfMemory, 58 PipeInitError, 59 WriteBufferError, 60 AlphaYSizeMismatch, 61 AlphaYColorDepthMismatch, 62 MetadataImageSizeMismatch, 63 RenderSizeMismatch, 64 FrameSizeChanged, 65 InvalidCICP, 66 NoSamples, 67 ConvertYCbCrFailure, 68 }; 69 using DecodeResult = 70 Variant<Mp4parseStatus, NonDecoderResult, Dav1dResult, AOMResult>; 71 Mp4parseStatus CreateParser(); 72 DecodeResult CreateDecoder(); 73 DecodeResult DoDecodeInternal(SourceBufferIterator& aIterator, 74 IResumable* aOnResume); 75 76 static bool IsDecodeSuccess(const DecodeResult& aResult); 77 78 void RecordDecodeResultTelemetry(const DecodeResult& aResult); 79 80 Vector<uint8_t> mBufferedData; 81 RefPtr<AVIFDecoderStream> mBufferStream; 82 83 /// Pointer to the next place to read from mBufferedData 84 const uint8_t* mReadCursor = nullptr; 85 86 UniquePtr<AVIFParser> mParser = nullptr; 87 UniquePtr<AVIFDecoderInterface> mDecoder = nullptr; 88 89 bool mIsAnimated = false; 90 bool mHasAlpha = false; 91 bool mUsePipeTransform = true; 92 }; 93 94 class AVIFDecoderStream : public ByteStream { 95 public: 96 explicit AVIFDecoderStream(Vector<uint8_t>* aBuffer) { mBuffer = aBuffer; } 97 98 virtual nsresult ReadAt(int64_t offset, void* data, size_t size, 99 size_t* bytes_read) override; 100 virtual nsresult CachedReadAt(int64_t offset, void* data, size_t size, 101 size_t* bytes_read) override { 102 return ReadAt(offset, data, size, bytes_read); 103 }; 104 virtual bool Length(int64_t* size) override; 105 virtual const uint8_t* GetContiguousAccess(int64_t aOffset, 106 size_t aSize) override; 107 108 private: 109 Vector<uint8_t>* mBuffer; 110 }; 111 112 struct AVIFImage { 113 uint32_t mFrameNum = 0; 114 FrameTimeout mDuration = FrameTimeout::Zero(); 115 RefPtr<MediaRawData> mColorImage = nullptr; 116 RefPtr<MediaRawData> mAlphaImage = nullptr; 117 }; 118 119 class AVIFParser { 120 public: 121 static Mp4parseStatus Create(const Mp4parseIo* aIo, ByteStream* aBuffer, 122 UniquePtr<AVIFParser>& aParserOut, 123 bool aAllowSequences, bool aAnimateAVIFMajor); 124 125 ~AVIFParser(); 126 127 const Mp4parseAvifInfo& GetInfo() const { return mInfo; } 128 129 uint32_t GetFrameCount(); 130 131 nsAVIFDecoder::DecodeResult GetImage(AVIFImage& aImage); 132 133 bool IsAnimated() const; 134 135 private: 136 explicit AVIFParser(const Mp4parseIo* aIo); 137 138 Mp4parseStatus Init(ByteStream* aBuffer, bool aAllowSequences, 139 bool aAnimateAVIFMajor); 140 141 struct FreeAvifParser { 142 void operator()(Mp4parseAvifParser* aPtr) { mp4parse_avif_free(aPtr); } 143 }; 144 145 const Mp4parseIo* mIo; 146 UniquePtr<Mp4parseAvifParser, FreeAvifParser> mParser = nullptr; 147 Mp4parseAvifInfo mInfo = {}; 148 149 UniquePtr<SampleIterator> mColorSampleIter = nullptr; 150 UniquePtr<SampleIterator> mAlphaSampleIter = nullptr; 151 uint32_t mFrameNum = 0; 152 }; 153 154 struct Dav1dPictureUnref { 155 void operator()(Dav1dPicture* aPtr) { 156 dav1d_picture_unref(aPtr); 157 delete aPtr; 158 } 159 }; 160 161 using OwnedDav1dPicture = UniquePtr<Dav1dPicture, Dav1dPictureUnref>; 162 163 class OwnedAOMImage { 164 public: 165 ~OwnedAOMImage(); 166 167 static OwnedAOMImage* CopyFrom(aom_image_t* aImage, bool aIsAlpha); 168 169 aom_image_t* GetImage() { return mImage.isSome() ? mImage.ptr() : nullptr; } 170 171 private: 172 OwnedAOMImage(); 173 174 bool CloneFrom(aom_image_t* aImage, bool aIsAlpha); 175 176 // The mImage's planes are referenced to mBuffer 177 Maybe<aom_image_t> mImage; 178 UniquePtr<uint8_t[]> mBuffer; 179 }; 180 181 struct AVIFDecodedData : layers::PlanarYCbCrData { 182 public: 183 Maybe<OrientedIntSize> mRenderSize = Nothing(); 184 gfx::CICP::ColourPrimaries mColourPrimaries = gfx::CICP::CP_UNSPECIFIED; 185 gfx::CICP::TransferCharacteristics mTransferCharacteristics = 186 gfx::CICP::TC_UNSPECIFIED; 187 gfx::CICP::MatrixCoefficients mMatrixCoefficients = gfx::CICP::MC_UNSPECIFIED; 188 189 OwnedDav1dPicture mColorDav1d; 190 OwnedDav1dPicture mAlphaDav1d; 191 UniquePtr<OwnedAOMImage> mColorAOM; 192 UniquePtr<OwnedAOMImage> mAlphaAOM; 193 194 // CICP values (either from the BMFF container or the AV1 sequence header) are 195 // used to create the colorspace transform. CICP::MatrixCoefficients is only 196 // stored for the sake of telemetry, since the relevant information for YUV -> 197 // RGB conversion is stored in mYUVColorSpace. 198 // 199 // There are three potential sources of color information for an AVIF: 200 // 1. ICC profile via a ColourInformationBox (colr) defined in [ISOBMFF] 201 // § 12.1.5 "Colour information" and [MIAF] § 7.3.6.4 "Colour information 202 // property" 203 // 2. NCLX (AKA CICP see [ITU-T H.273]) values in the same 204 // ColourInformationBox 205 // which can have an ICC profile or NCLX values, not both). 206 // 3. NCLX values in the AV1 bitstream 207 // 208 // The 'colr' box is optional, but there are always CICP values in the AV1 209 // bitstream, so it is possible to have both. Per ISOBMFF § 12.1.5.1 210 // > If colour information is supplied in both this box, and also in the 211 // > video bitstream, this box takes precedence, and over-rides the 212 // > information in the bitstream. 213 // 214 // If present, the ICC profile takes precedence over CICP values, but only 215 // specifies the color space, not the matrix coefficients necessary to convert 216 // YCbCr data (as most AVIF are encoded) to RGB. The matrix coefficients are 217 // always derived from the CICP values for matrix_coefficients (and 218 // potentially colour_primaries, but in that case only the CICP values for 219 // colour_primaries will be used, not anything harvested from the ICC 220 // profile). 221 // 222 // If there is no ICC profile, the color space transform will be based on the 223 // CICP values either from the 'colr' box, or if absent/unspecified, the 224 // decoded AV1 sequence header. 225 // 226 // For values that are 2 (meaning unspecified) after trying both, the 227 // fallback values are: 228 // - CP: 1 (BT.709/sRGB) 229 // - TC: 13 (sRGB) 230 // - MC: 6 (BT.601) 231 // - Range: Full 232 // 233 // Additional details here: 234 // <https://github.com/AOMediaCodec/libavif/wiki/CICP#unspecified>. Note 235 // that this contradicts the current version of [MIAF] § 7.3.6.4 which 236 // specifies MC=1 (BT.709). This is revised in [MIAF DAMD2] and confirmed by 237 // <https://github.com/AOMediaCodec/av1-avif/issues/77#issuecomment-676526097> 238 // 239 // The precedence for applying the various values and defaults in the event 240 // no valid values are found are managed by the following functions. 241 // 242 // References: 243 // [ISOBMFF]: ISO/IEC 14496-12:2020 <https://www.iso.org/standard/74428.html> 244 // [MIAF]: ISO/IEC 23000-22:2019 <https://www.iso.org/standard/74417.html> 245 // [MIAF DAMD2]: ISO/IEC 23000-22:2019/FDAmd 2 246 // <https://www.iso.org/standard/81634.html> 247 // [ITU-T H.273]: Rec. ITU-T H.273 (12/2016) 248 // <https://www.itu.int/rec/T-REC-H.273-201612-I/en> 249 void SetCicpValues( 250 const Mp4parseNclxColourInformation* aNclx, 251 const gfx::CICP::ColourPrimaries aAv1ColourPrimaries, 252 const gfx::CICP::TransferCharacteristics aAv1TransferCharacteristics, 253 const gfx::CICP::MatrixCoefficients aAv1MatrixCoefficients); 254 }; 255 256 // An interface to do decode and get the decoded data 257 class AVIFDecoderInterface { 258 public: 259 using Dav1dResult = nsAVIFDecoder::Dav1dResult; 260 using NonAOMCodecError = nsAVIFDecoder::NonAOMCodecError; 261 using AOMResult = nsAVIFDecoder::AOMResult; 262 using NonDecoderResult = nsAVIFDecoder::NonDecoderResult; 263 using DecodeResult = nsAVIFDecoder::DecodeResult; 264 265 virtual ~AVIFDecoderInterface() = default; 266 267 // Set the mDecodedData if Decode() succeeds 268 virtual DecodeResult Decode(bool aShouldSendTelemetry, 269 const Mp4parseAvifInfo& aAVIFInfo, 270 const AVIFImage& aSamples) = 0; 271 // Must be called only once after Decode() succeeds 272 UniquePtr<AVIFDecodedData> GetDecodedData() { 273 MOZ_ASSERT(mDecodedData); 274 return std::move(mDecodedData); 275 } 276 277 protected: 278 explicit AVIFDecoderInterface() = default; 279 280 inline static bool IsDecodeSuccess(const DecodeResult& aResult) { 281 return nsAVIFDecoder::IsDecodeSuccess(aResult); 282 } 283 284 // The mDecodedData is valid after Decode() succeeds 285 UniquePtr<AVIFDecodedData> mDecodedData; 286 }; 287 288 } // namespace image 289 } // namespace mozilla 290 291 #endif // mozilla_image_decoders_nsAVIFDecoder_h