Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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:
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:
enum class ChromaSamplePosition : uint8_t {
Unknown = 0,
Vertical = 1,
Colocated = 2,
Reserved = 3
};
struct OperatingPoint {
// 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;
// See A.3: Levels for a definition of the available levels.
uint8_t mLevel = 0;
// 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:
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.
nsTArray<OperatingPoint> mOperatingPoints = nsTArray<OperatingPoint>(1);
gfx::IntSize mImage = {0, 0};
// Color configs explained at:
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_