summaryrefslogtreecommitdiffstats
path: root/image/decoders/nsAVIFDecoder.h
diff options
context:
space:
mode:
Diffstat (limited to 'image/decoders/nsAVIFDecoder.h')
-rw-r--r--image/decoders/nsAVIFDecoder.h289
1 files changed, 289 insertions, 0 deletions
diff --git a/image/decoders/nsAVIFDecoder.h b/image/decoders/nsAVIFDecoder.h
new file mode 100644
index 0000000000..1bd5e3b86e
--- /dev/null
+++ b/image/decoders/nsAVIFDecoder.h
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+#ifndef mozilla_image_decoders_nsAVIFDecoder_h
+#define mozilla_image_decoders_nsAVIFDecoder_h
+
+#include "Decoder.h"
+#include "mozilla/gfx/Types.h"
+#include "MP4Metadata.h"
+#include "mp4parse.h"
+#include "SampleIterator.h"
+#include "SurfacePipe.h"
+
+#include <aom/aom_decoder.h>
+#include "dav1d/dav1d.h"
+
+#include "mozilla/Telemetry.h"
+
+namespace mozilla {
+namespace image {
+class RasterImage;
+class AVIFDecoderStream;
+class AVIFParser;
+class AVIFDecoderInterface;
+
+class nsAVIFDecoder final : public Decoder {
+ public:
+ virtual ~nsAVIFDecoder();
+
+ DecoderType GetType() const override { return DecoderType::AVIF; }
+
+ protected:
+ LexerResult DoDecode(SourceBufferIterator& aIterator,
+ IResumable* aOnResume) override;
+ Maybe<Telemetry::HistogramID> SpeedHistogram() const override;
+
+ private:
+ friend class DecoderFactory;
+ friend class AVIFDecoderInterface;
+ friend class AVIFParser;
+
+ // Decoders should only be instantiated via DecoderFactory.
+ explicit nsAVIFDecoder(RasterImage* aImage);
+
+ static intptr_t ReadSource(uint8_t* aDestBuf, uintptr_t aDestBufSize,
+ void* aUserData);
+
+ typedef int Dav1dResult;
+ enum class NonAOMCodecError { NoFrame, SizeOverflow };
+ typedef Variant<aom_codec_err_t, NonAOMCodecError> AOMResult;
+ enum class NonDecoderResult {
+ NeedMoreData,
+ OutputAvailable,
+ Complete,
+ SizeOverflow,
+ OutOfMemory,
+ PipeInitError,
+ WriteBufferError,
+ AlphaYSizeMismatch,
+ AlphaYColorDepthMismatch,
+ MetadataImageSizeMismatch,
+ RenderSizeMismatch,
+ FrameSizeChanged,
+ InvalidCICP,
+ NoSamples,
+ };
+ using DecodeResult =
+ Variant<Mp4parseStatus, NonDecoderResult, Dav1dResult, AOMResult>;
+ Mp4parseStatus CreateParser();
+ DecodeResult CreateDecoder();
+ DecodeResult DoDecodeInternal(SourceBufferIterator& aIterator,
+ IResumable* aOnResume);
+
+ static bool IsDecodeSuccess(const DecodeResult& aResult);
+
+ void RecordDecodeResultTelemetry(const DecodeResult& aResult);
+
+ Vector<uint8_t> mBufferedData;
+ RefPtr<AVIFDecoderStream> mBufferStream;
+
+ /// Pointer to the next place to read from mBufferedData
+ const uint8_t* mReadCursor = nullptr;
+
+ UniquePtr<AVIFParser> mParser = nullptr;
+ UniquePtr<AVIFDecoderInterface> mDecoder = nullptr;
+
+ bool mIsAnimated = false;
+ bool mHasAlpha = false;
+};
+
+class AVIFDecoderStream : public ByteStream {
+ public:
+ explicit AVIFDecoderStream(Vector<uint8_t>* aBuffer) { mBuffer = aBuffer; }
+
+ virtual bool ReadAt(int64_t offset, void* data, size_t size,
+ size_t* bytes_read) override;
+ virtual bool CachedReadAt(int64_t offset, void* data, size_t size,
+ size_t* bytes_read) override {
+ return ReadAt(offset, data, size, bytes_read);
+ };
+ virtual bool Length(int64_t* size) override;
+ virtual const uint8_t* GetContiguousAccess(int64_t aOffset,
+ size_t aSize) override;
+
+ private:
+ Vector<uint8_t>* mBuffer;
+};
+
+struct AVIFImage {
+ uint32_t mFrameNum = 0;
+ FrameTimeout mDuration = FrameTimeout::Zero();
+ RefPtr<MediaRawData> mColorImage = nullptr;
+ RefPtr<MediaRawData> mAlphaImage = nullptr;
+};
+
+class AVIFParser {
+ public:
+ static Mp4parseStatus Create(const Mp4parseIo* aIo, ByteStream* aBuffer,
+ UniquePtr<AVIFParser>& aParserOut,
+ bool aAllowSequences, bool aAnimateAVIFMajor);
+
+ ~AVIFParser();
+
+ const Mp4parseAvifInfo& GetInfo() const { return mInfo; }
+
+ nsAVIFDecoder::DecodeResult GetImage(AVIFImage& aImage);
+
+ bool IsAnimated() const;
+
+ private:
+ explicit AVIFParser(const Mp4parseIo* aIo);
+
+ Mp4parseStatus Init(ByteStream* aBuffer, bool aAllowSequences,
+ bool aAnimateAVIFMajor);
+
+ struct FreeAvifParser {
+ void operator()(Mp4parseAvifParser* aPtr) { mp4parse_avif_free(aPtr); }
+ };
+
+ const Mp4parseIo* mIo;
+ UniquePtr<Mp4parseAvifParser, FreeAvifParser> mParser = nullptr;
+ Mp4parseAvifInfo mInfo = {};
+
+ UniquePtr<SampleIterator> mColorSampleIter = nullptr;
+ UniquePtr<SampleIterator> mAlphaSampleIter = nullptr;
+ uint32_t mFrameNum = 0;
+};
+
+struct Dav1dPictureUnref {
+ void operator()(Dav1dPicture* aPtr) {
+ dav1d_picture_unref(aPtr);
+ delete aPtr;
+ }
+};
+
+using OwnedDav1dPicture = UniquePtr<Dav1dPicture, Dav1dPictureUnref>;
+
+class OwnedAOMImage {
+ public:
+ ~OwnedAOMImage();
+
+ static OwnedAOMImage* CopyFrom(aom_image_t* aImage, bool aIsAlpha);
+
+ aom_image_t* GetImage() { return mImage.isSome() ? mImage.ptr() : nullptr; }
+
+ private:
+ OwnedAOMImage();
+
+ bool CloneFrom(aom_image_t* aImage, bool aIsAlpha);
+
+ // The mImage's planes are referenced to mBuffer
+ Maybe<aom_image_t> mImage;
+ UniquePtr<uint8_t[]> mBuffer;
+};
+
+struct AVIFDecodedData : layers::PlanarYCbCrData {
+ public:
+ Maybe<OrientedIntSize> mRenderSize = Nothing();
+ gfx::CICP::ColourPrimaries mColourPrimaries = gfx::CICP::CP_UNSPECIFIED;
+ gfx::CICP::TransferCharacteristics mTransferCharacteristics =
+ gfx::CICP::TC_UNSPECIFIED;
+ gfx::CICP::MatrixCoefficients mMatrixCoefficients = gfx::CICP::MC_UNSPECIFIED;
+
+ OwnedDav1dPicture mColorDav1d;
+ OwnedDav1dPicture mAlphaDav1d;
+ UniquePtr<OwnedAOMImage> mColorAOM;
+ UniquePtr<OwnedAOMImage> mAlphaAOM;
+
+ // CICP values (either from the BMFF container or the AV1 sequence header) are
+ // used to create the colorspace transform. CICP::MatrixCoefficients is only
+ // stored for the sake of telemetry, since the relevant information for YUV ->
+ // RGB conversion is stored in mYUVColorSpace.
+ //
+ // There are three potential sources of color information for an AVIF:
+ // 1. ICC profile via a ColourInformationBox (colr) defined in [ISOBMFF]
+ // § 12.1.5 "Colour information" and [MIAF] § 7.3.6.4 "Colour information
+ // property"
+ // 2. NCLX (AKA CICP see [ITU-T H.273]) values in the same
+ // ColourInformationBox
+ // which can have an ICC profile or NCLX values, not both).
+ // 3. NCLX values in the AV1 bitstream
+ //
+ // The 'colr' box is optional, but there are always CICP values in the AV1
+ // bitstream, so it is possible to have both. Per ISOBMFF § 12.1.5.1
+ // > If colour information is supplied in both this box, and also in the
+ // > video bitstream, this box takes precedence, and over-rides the
+ // > information in the bitstream.
+ //
+ // If present, the ICC profile takes precedence over CICP values, but only
+ // specifies the color space, not the matrix coefficients necessary to convert
+ // YCbCr data (as most AVIF are encoded) to RGB. The matrix coefficients are
+ // always derived from the CICP values for matrix_coefficients (and
+ // potentially colour_primaries, but in that case only the CICP values for
+ // colour_primaries will be used, not anything harvested from the ICC
+ // profile).
+ //
+ // If there is no ICC profile, the color space transform will be based on the
+ // CICP values either from the 'colr' box, or if absent/unspecified, the
+ // decoded AV1 sequence header.
+ //
+ // For values that are 2 (meaning unspecified) after trying both, the
+ // fallback values are:
+ // - CP: 1 (BT.709/sRGB)
+ // - TC: 13 (sRGB)
+ // - MC: 6 (BT.601)
+ // - Range: Full
+ //
+ // Additional details here:
+ // <https://github.com/AOMediaCodec/libavif/wiki/CICP#unspecified>. Note
+ // that this contradicts the current version of [MIAF] § 7.3.6.4 which
+ // specifies MC=1 (BT.709). This is revised in [MIAF DAMD2] and confirmed by
+ // <https://github.com/AOMediaCodec/av1-avif/issues/77#issuecomment-676526097>
+ //
+ // The precedence for applying the various values and defaults in the event
+ // no valid values are found are managed by the following functions.
+ //
+ // References:
+ // [ISOBMFF]: ISO/IEC 14496-12:2020 <https://www.iso.org/standard/74428.html>
+ // [MIAF]: ISO/IEC 23000-22:2019 <https://www.iso.org/standard/74417.html>
+ // [MIAF DAMD2]: ISO/IEC 23000-22:2019/FDAmd 2
+ // <https://www.iso.org/standard/81634.html>
+ // [ITU-T H.273]: Rec. ITU-T H.273 (12/2016)
+ // <https://www.itu.int/rec/T-REC-H.273-201612-I/en>
+ void SetCicpValues(
+ const Mp4parseNclxColourInformation* aNclx,
+ const gfx::CICP::ColourPrimaries aAv1ColourPrimaries,
+ const gfx::CICP::TransferCharacteristics aAv1TransferCharacteristics,
+ const gfx::CICP::MatrixCoefficients aAv1MatrixCoefficients);
+};
+
+// An interface to do decode and get the decoded data
+class AVIFDecoderInterface {
+ public:
+ using Dav1dResult = nsAVIFDecoder::Dav1dResult;
+ using NonAOMCodecError = nsAVIFDecoder::NonAOMCodecError;
+ using AOMResult = nsAVIFDecoder::AOMResult;
+ using NonDecoderResult = nsAVIFDecoder::NonDecoderResult;
+ using DecodeResult = nsAVIFDecoder::DecodeResult;
+
+ virtual ~AVIFDecoderInterface() = default;
+
+ // Set the mDecodedData if Decode() succeeds
+ virtual DecodeResult Decode(bool aShouldSendTelemetry,
+ const Mp4parseAvifInfo& aAVIFInfo,
+ const AVIFImage& aSamples) = 0;
+ // Must be called only once after Decode() succeeds
+ UniquePtr<AVIFDecodedData> GetDecodedData() {
+ MOZ_ASSERT(mDecodedData);
+ return std::move(mDecodedData);
+ }
+
+ protected:
+ explicit AVIFDecoderInterface() = default;
+
+ inline static bool IsDecodeSuccess(const DecodeResult& aResult) {
+ return nsAVIFDecoder::IsDecodeSuccess(aResult);
+ }
+
+ // The mDecodedData is valid after Decode() succeeds
+ UniquePtr<AVIFDecodedData> mDecodedData;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif // mozilla_image_decoders_nsAVIFDecoder_h