287 lines
9.9 KiB
C++
287 lines
9.9 KiB
C++
/* -*- 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_
|