diff --git a/config/external/moz.build b/config/external/moz.build index 60fdd7b70f..1d33bcf3c8 100644 --- a/config/external/moz.build +++ b/config/external/moz.build @@ -30,7 +30,6 @@ if CONFIG['MOZ_WEBM_ENCODER']: external_dirs += ['media/libvpx'] if CONFIG['MOZ_AV1']: - external_dirs += ['media/libaom'] external_dirs += ['media/libdav1d'] external_dirs += ['media/libpng'] diff --git a/dom/media/platforms/agnostic/AOMDecoder.cpp b/dom/media/platforms/agnostic/AOMDecoder.cpp deleted file mode 100644 index 1a6aca483f..0000000000 --- a/dom/media/platforms/agnostic/AOMDecoder.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* -*- 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/. */ - -#include "AOMDecoder.h" -#include "MediaResult.h" -#include "TimeUnits.h" -#include "aom/aomdx.h" -#include "aom/aom_image.h" -#include "gfx2DGlue.h" -#include "mozilla/PodOperations.h" -#include "mozilla/SyncRunnable.h" -#include "nsError.h" -#include "prsystem.h" - -#include - -#undef LOG -#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("AOMDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) - -namespace mozilla { - -using namespace gfx; -using namespace layers; - -AOMDecoder::AOMDecoder(const CreateDecoderParams& aParams) - : mImageContainer(aParams.mImageContainer) - , mTaskQueue(aParams.mTaskQueue) - , mCallback(aParams.mCallback) - , mIsFlushing(false) - , mInfo(aParams.VideoConfig()) -{ - PodZero(&mCodec); -} - -AOMDecoder::~AOMDecoder() -{ -} - -void -AOMDecoder::Shutdown() -{ - aom_codec_destroy(&mCodec); -} - -RefPtr -AOMDecoder::Init() -{ - int decode_threads = 2; - - aom_codec_iface_t* dx = aom_codec_av1_dx(); - if (mInfo.mDisplay.width >= 2048) { - decode_threads = 8; - } - else if (mInfo.mDisplay.width >= 1024) { - decode_threads = 4; - } - decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors()); - - aom_codec_dec_cfg_t config; - PodZero(&config); - config.threads = decode_threads; - config.w = config.h = 0; // set after decode - config.allow_lowbitdepth = true; - - aom_codec_flags_t flags = 0; - - if (!dx || aom_codec_dec_init(&mCodec, dx, &config, flags)) { - return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__); - } - return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__); -} - -void -AOMDecoder::Flush() -{ - MOZ_ASSERT(mCallback->OnReaderTaskQueue()); - mIsFlushing = true; - nsCOMPtr r = NS_NewRunnableFunction([this] () { - // nothing to do for now. - }); - SyncRunnable::DispatchToThread(mTaskQueue, r); - mIsFlushing = false; -} - -// Ported from third_party/aom/tools_common.c. -static aom_codec_err_t -highbd_img_downshift(aom_image_t *dst, aom_image_t *src, int down_shift) { - int plane; - if (dst->d_w != src->d_w || dst->d_h != src->d_h) - return AOM_CODEC_INVALID_PARAM; - if (dst->x_chroma_shift != src->x_chroma_shift) - return AOM_CODEC_INVALID_PARAM; - if (dst->y_chroma_shift != src->y_chroma_shift) - return AOM_CODEC_INVALID_PARAM; - if (dst->fmt != (src->fmt & ~AOM_IMG_FMT_HIGHBITDEPTH)) - return AOM_CODEC_INVALID_PARAM; - if (down_shift < 0) - return AOM_CODEC_INVALID_PARAM; - switch (dst->fmt) { - case AOM_IMG_FMT_I420: - case AOM_IMG_FMT_I422: - case AOM_IMG_FMT_I444: - break; - default: - return AOM_CODEC_INVALID_PARAM; - } - switch (src->fmt) { - case AOM_IMG_FMT_I42016: - case AOM_IMG_FMT_I42216: - case AOM_IMG_FMT_I44416: - break; - default: - // We don't support anything that's not 16 bit - return AOM_CODEC_UNSUP_BITSTREAM; - } - for (plane = 0; plane < 3; plane++) { - int w = src->d_w; - int h = src->d_h; - int x, y; - if (plane) { - w = (w + src->x_chroma_shift) >> src->x_chroma_shift; - h = (h + src->y_chroma_shift) >> src->y_chroma_shift; - } - for (y = 0; y < h; y++) { - uint16_t *p_src = - (uint16_t *)(src->planes[plane] + y * src->stride[plane]); - uint8_t *p_dst = - dst->planes[plane] + y * dst->stride[plane]; - for (x = 0; x < w; x++) *p_dst++ = (*p_src++ >> down_shift) & 0xFF; - } - } - return AOM_CODEC_OK; -} - -// UniquePtr dtor wrapper for aom_image_t. -struct AomImageFree { - void operator()(aom_image_t* img) { aom_img_free(img); } -}; - -MediaResult -AOMDecoder::DoDecode(MediaRawData* aSample) -{ - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); - -#if defined(DEBUG) - NS_ASSERTION(IsKeyframe(*aSample) == aSample->mKeyframe, - "AOM Decode Keyframe error sample->mKeyframe and si.si_kf out of sync"); -#endif - - if (aom_codec_err_t r = aom_codec_decode(&mCodec, aSample->Data(), aSample->Size(), nullptr)) { - LOG("AOM Decode error: %s", aom_codec_err_to_string(r)); - return MediaResult( - NS_ERROR_DOM_MEDIA_DECODE_ERR, - RESULT_DETAIL("AOM error decoding AV1 sample: %s", aom_codec_err_to_string(r))); - } - - aom_codec_iter_t iter = nullptr; - aom_image_t *img; - UniquePtr img8; - - while ((img = aom_codec_get_frame(&mCodec, &iter))) { - // Track whether the underlying buffer is 8 or 16 bits per channel. - bool highbd = bool(img->fmt & AOM_IMG_FMT_HIGHBITDEPTH); - if (highbd) { - // Downsample images with more than 8 bits per channel. - aom_img_fmt_t fmt8 = static_cast(img->fmt ^ AOM_IMG_FMT_HIGHBITDEPTH); - img8.reset(aom_img_alloc(NULL, fmt8, img->d_w, img->d_h, 16)); - if (img8 == nullptr) { - LOG("Couldn't allocate bitdepth reduction target!"); - return MediaResult( - NS_ERROR_OUT_OF_MEMORY, - RESULT_DETAIL("Couldn't allocate conversion buffer for AV1 frame")); - } - if (aom_codec_err_t r = highbd_img_downshift(img8.get(), img, img->bit_depth - 8)) { - return MediaResult( - NS_ERROR_DOM_MEDIA_DECODE_ERR, - RESULT_DETAIL("Error converting AV1 frame to 8 bits: %s", - aom_codec_err_to_string(r))); - } - // img normally points to storage owned by mCodec, so it is not freed. - // To copy out the contents of img8 we can overwrite img with an alias. - // Since img is assigned at the start of the while loop and img8 is held - // outside that loop, the alias won't outlive the storage it points to. - img = img8.get(); - highbd = false; - } - - NS_ASSERTION(img->fmt == AOM_IMG_FMT_I420 || - img->fmt == AOM_IMG_FMT_I42016 || - img->fmt == AOM_IMG_FMT_I444 || - img->fmt == AOM_IMG_FMT_I44416, - "AV1 image format not I420 or I444"); - - // Chroma shifts are rounded down as per the decoding examples in the SDK - VideoData::YCbCrBuffer b; - b.mPlanes[0].mData = img->planes[0]; - b.mPlanes[0].mStride = img->stride[0]; - b.mPlanes[0].mHeight = img->d_h; - b.mPlanes[0].mWidth = img->d_w; - b.mPlanes[0].mOffset = 0; - b.mPlanes[0].mSkip = highbd ? 1 : 0; - - b.mPlanes[1].mData = img->planes[1]; - b.mPlanes[1].mStride = img->stride[1]; - b.mPlanes[1].mOffset = 0; - b.mPlanes[1].mSkip = highbd ? 1 : 0; - - b.mPlanes[2].mData = img->planes[2]; - b.mPlanes[2].mStride = img->stride[2]; - b.mPlanes[2].mOffset = 0; - b.mPlanes[2].mSkip = highbd ? 1 : 0; - - if (img->fmt == AOM_IMG_FMT_I420 || - img->fmt == AOM_IMG_FMT_I42016) { - b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift; - b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift; - - b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift; - b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift; - } else if (img->fmt == AOM_IMG_FMT_I444) { - b.mPlanes[1].mHeight = img->d_h; - b.mPlanes[1].mWidth = img->d_w; - - b.mPlanes[2].mHeight = img->d_h; - b.mPlanes[2].mWidth = img->d_w; - } else { - LOG("AOM Unknown image format"); - return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, - RESULT_DETAIL("AOM Unknown image format")); - } - - switch (img->mc) { - case AOM_CICP_MC_BT_601: - b.mYUVColorSpace = YUVColorSpace::BT601; - break; - case AOM_CICP_MC_BT_709: - b.mYUVColorSpace = YUVColorSpace::BT709; - break; - case AOM_CICP_MC_IDENTITY: - b.mYUVColorSpace = YUVColorSpace::IDENTITY; - break; - default: - LOG("Unhandled colorspace %d", img->mc); - break; - } - b.mColorRange = img->range == AOM_CR_FULL_RANGE ? ColorRange::FULL - : ColorRange::LIMITED; - - RefPtr v = - VideoData::CreateAndCopyData(mInfo, - mImageContainer, - aSample->mOffset, - aSample->mTime, - aSample->mDuration, - b, - aSample->mKeyframe, - aSample->mTimecode, - mInfo.ScaledImageRect(img->d_w, - img->d_h)); - - if (!v) { - LOG("Image allocation error source %ux%u display %ux%u picture %ux%u", - img->d_w, img->d_h, mInfo.mDisplay.width, mInfo.mDisplay.height, - mInfo.mImage.width, mInfo.mImage.height); - return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__); - } - mCallback->Output(v); - } - return NS_OK; -} - -void -AOMDecoder::ProcessDecode(MediaRawData* aSample) -{ - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); - if (mIsFlushing) { - return; - } - MediaResult rv = DoDecode(aSample); - if (NS_FAILED(rv)) { - mCallback->Error(rv); - } else { - mCallback->InputExhausted(); - } -} - -void -AOMDecoder::Input(MediaRawData* aSample) -{ - MOZ_ASSERT(mCallback->OnReaderTaskQueue()); - mTaskQueue->Dispatch(NewRunnableMethod>( - this, &AOMDecoder::ProcessDecode, aSample)); -} - -void -AOMDecoder::ProcessDrain() -{ - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); - mCallback->DrainComplete(); -} - -void -AOMDecoder::Drain() -{ - MOZ_ASSERT(mCallback->OnReaderTaskQueue()); - mTaskQueue->Dispatch(NewRunnableMethod(this, &AOMDecoder::ProcessDrain)); -} - -/* static */ -bool -AOMDecoder::IsAV1(const nsACString& aMimeType) -{ - return aMimeType.EqualsLiteral("video/webm; codecs=av1") || - aMimeType.EqualsLiteral("video/av1"); -} - -/* static */ -bool -AOMDecoder::IsKeyframe(Span aBuffer) { - aom_codec_stream_info_t info; - PodZero(&info); - - aom_codec_peek_stream_info(aom_codec_av1_dx(), - aBuffer.Elements(), - aBuffer.Length(), - &info); - - return bool(info.is_kf); -} - -/* static */ -nsIntSize -AOMDecoder::GetFrameSize(Span aBuffer) { - aom_codec_stream_info_t info; - PodZero(&info); - - aom_codec_peek_stream_info(aom_codec_av1_dx(), - aBuffer.Elements(), - aBuffer.Length(), - &info); - - return nsIntSize(info.w, info.h); -} - -} // namespace mozilla -#undef LOG diff --git a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp index 10432ad493..a1ba164289 100644 --- a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp +++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp @@ -13,7 +13,7 @@ #include "TheoraDecoder.h" #ifdef MOZ_AV1 -#include "AOMDecoder.h" +#include "Dav1dDecoder.h" #endif namespace mozilla { @@ -30,7 +30,7 @@ AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType, TheoraDecoder::IsTheora(aMimeType); #ifdef MOZ_AV1 if (MediaPrefs::AV1Enabled()) { - supports |= AOMDecoder::IsAV1(aMimeType); + supports |= Dav1dDecoder::IsAV1(aMimeType); } #endif MOZ_LOG(sPDMLog, LogLevel::Debug, ("Agnostic decoder %s requested type", @@ -47,9 +47,9 @@ AgnosticDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) m = new VPXDecoder(aParams); } #ifdef MOZ_AV1 - else if (AOMDecoder::IsAV1(aParams.mConfig.mMimeType) && + else if (Dav1dDecoder::IsAV1(aParams.mConfig.mMimeType) && MediaPrefs::AV1Enabled()) { - m = new AOMDecoder(aParams); + m = new Dav1dDecoder(aParams); } #endif else if (TheoraDecoder::IsTheora(aParams.mConfig.mMimeType)) { diff --git a/dom/media/platforms/agnostic/Dav1dDecoder.cpp b/dom/media/platforms/agnostic/Dav1dDecoder.cpp new file mode 100644 index 0000000000..9a94374251 --- /dev/null +++ b/dom/media/platforms/agnostic/Dav1dDecoder.cpp @@ -0,0 +1,376 @@ +/* -*- 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/. */ + +#include "Dav1dDecoder.h" +#include "MediaResult.h" +#include "TimeUnits.h" +#include "dav1d/dav1d.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/PodOperations.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" +#include "nsError.h" +#include "prsystem.h" + +#include +#include +#include + +#undef LOG +#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("Dav1dDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) + +namespace mozilla { + +using namespace gfx; +using namespace layers; + +Dav1dDecoder::Dav1dDecoder(const CreateDecoderParams& aParams) + : mImageContainer(aParams.mImageContainer) + , mTaskQueue(aParams.mTaskQueue) + , mCallback(aParams.mCallback) + , mIsFlushing(false) + , mDecoder(nullptr) + , mInfo(aParams.VideoConfig()) +{ +} + +Dav1dDecoder::~Dav1dDecoder() +{ +} + +void +Dav1dDecoder::Shutdown() +{ + if (mDecoder) { + dav1d_close(&mDecoder); + } +} + +RefPtr +Dav1dDecoder::Init() +{ + int decodeThreads = 2; + if (mInfo.mDisplay.width >= 2048) { + decodeThreads = 8; + } else if (mInfo.mDisplay.width >= 1024) { + decodeThreads = 4; + } + decodeThreads = std::min(decodeThreads, PR_GetNumberOfProcessors()); + + Dav1dSettings settings; + dav1d_default_settings(&settings); + settings.n_threads = decodeThreads; + settings.logger.callback = nullptr; + + if (dav1d_open(&mDecoder, &settings) < 0) { + return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__); + } + return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__); +} + +void +Dav1dDecoder::Flush() +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + mIsFlushing = true; + nsCOMPtr r = NS_NewRunnableFunction([this] () { + if (mDecoder) { + dav1d_flush(mDecoder); + } + }); + SyncRunnable::DispatchToThread(mTaskQueue, r); + mIsFlushing = false; +} + +static bool +GetPlaneSize(int aWidth, int aHeight, size_t* aSize) +{ + CheckedInt size = aWidth; + size *= aHeight; + if (!size.isValid()) { + return false; + } + *aSize = size.value(); + return true; +} + +static void +DownshiftPlane(uint8_t* aDst, int aDstStride, const uint8_t* aSrc, + ptrdiff_t aSrcStride, int aWidth, int aHeight, int aShift) +{ + for (int y = 0; y < aHeight; y++) { + const uint16_t* src = + reinterpret_cast(aSrc + y * aSrcStride); + uint8_t* dst = aDst + y * aDstStride; + for (int x = 0; x < aWidth; x++) { + dst[x] = (src[x] >> aShift) & 0xff; + } + } +} + +MediaResult +Dav1dDecoder::OutputPicture(const Dav1dPicture& aPicture) +{ + const int width = aPicture.p.w; + const int height = aPicture.p.h; + if (width <= 0 || height <= 0) { + return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("dav1d returned invalid AV1 picture size")); + } + + int chromaWidth = width; + int chromaHeight = height; + switch (aPicture.p.layout) { + case DAV1D_PIXEL_LAYOUT_I420: + chromaWidth = (width + 1) >> 1; + chromaHeight = (height + 1) >> 1; + break; + case DAV1D_PIXEL_LAYOUT_I422: + chromaWidth = (width + 1) >> 1; + chromaHeight = height; + break; + case DAV1D_PIXEL_LAYOUT_I444: + break; + default: + return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("dav1d returned unsupported AV1 pixel layout: %d", + int(aPicture.p.layout))); + } + + const int planeWidths[3] = { width, chromaWidth, chromaWidth }; + const int planeHeights[3] = { height, chromaHeight, chromaHeight }; + const ptrdiff_t srcStrides[3] = { + aPicture.stride[0], + aPicture.stride[1], + aPicture.stride[1] + }; + uint8_t* planeData[3] = { + static_cast(aPicture.data[0]), + static_cast(aPicture.data[1]), + static_cast(aPicture.data[2]) + }; + uint32_t planeStrides[3] = { 0, 0, 0 }; + UniquePtr downshifted[3]; + + if (aPicture.p.bpc > 8) { + const int shift = aPicture.p.bpc - 8; + for (int plane = 0; plane < 3; plane++) { + size_t planeSize; + if (!GetPlaneSize(planeWidths[plane], planeHeights[plane], &planeSize)) { + return MediaResult(NS_ERROR_OUT_OF_MEMORY, + RESULT_DETAIL("AV1 downshift plane size overflow")); + } + downshifted[plane] = MakeUniqueFallible(planeSize); + if (!downshifted[plane]) { + return MediaResult(NS_ERROR_OUT_OF_MEMORY, + RESULT_DETAIL("Couldn't allocate AV1 conversion buffer")); + } + DownshiftPlane(downshifted[plane].get(), planeWidths[plane], + planeData[plane], srcStrides[plane], + planeWidths[plane], planeHeights[plane], shift); + planeData[plane] = downshifted[plane].get(); + planeStrides[plane] = planeWidths[plane]; + } + } else { + for (int plane = 0; plane < 3; plane++) { + CheckedInt stride(srcStrides[plane]); + if (srcStrides[plane] < 0 || !stride.isValid()) { + return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("dav1d returned invalid AV1 stride")); + } + planeStrides[plane] = stride.value(); + } + } + + VideoData::YCbCrBuffer b; + for (int plane = 0; plane < 3; plane++) { + b.mPlanes[plane].mData = planeData[plane]; + b.mPlanes[plane].mStride = planeStrides[plane]; + b.mPlanes[plane].mHeight = planeHeights[plane]; + b.mPlanes[plane].mWidth = planeWidths[plane]; + b.mPlanes[plane].mOffset = 0; + b.mPlanes[plane].mSkip = 0; + } + + if (aPicture.seq_hdr) { + switch (aPicture.seq_hdr->mtrx) { + case DAV1D_MC_BT601: + case DAV1D_MC_BT470BG: + b.mYUVColorSpace = YUVColorSpace::BT601; + break; + case DAV1D_MC_BT709: + b.mYUVColorSpace = YUVColorSpace::BT709; + break; + case DAV1D_MC_IDENTITY: + b.mYUVColorSpace = YUVColorSpace::IDENTITY; + break; + default: + LOG("Unhandled colorspace %d", aPicture.seq_hdr->mtrx); + break; + } + b.mColorRange = aPicture.seq_hdr->color_range ? ColorRange::FULL + : ColorRange::LIMITED; + } + + const int64_t time = + aPicture.m.timestamp == INT64_MIN ? 0 : aPicture.m.timestamp; + const int64_t duration = aPicture.m.duration; + const int64_t offset = aPicture.m.offset; + const bool keyframe = + aPicture.frame_hdr && + aPicture.frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY; + + RefPtr v = + VideoData::CreateAndCopyData(mInfo, + mImageContainer, + offset, + time, + duration, + b, + keyframe, + time, + mInfo.ScaledImageRect(width, height)); + + if (!v) { + LOG("Image allocation error source %dx%d display %ux%u picture %ux%u", + width, height, mInfo.mDisplay.width, mInfo.mDisplay.height, + mInfo.mImage.width, mInfo.mImage.height); + return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__); + } + mCallback->Output(v); + return NS_OK; +} + +MediaResult +Dav1dDecoder::DrainOutput() +{ + while (true) { + Dav1dPicture picture; + PodZero(&picture); + const int res = dav1d_get_picture(mDecoder, &picture); + if (res == DAV1D_ERR(EAGAIN)) { + return NS_OK; + } + if (res < 0) { + return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("dav1d error getting AV1 picture: %d", + res)); + } + + MediaResult rv = OutputPicture(picture); + dav1d_picture_unref(&picture); + if (NS_FAILED(rv)) { + return rv; + } + } +} + +MediaResult +Dav1dDecoder::DoDecode(MediaRawData* aSample) +{ + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + + Dav1dData data; + PodZero(&data); + uint8_t* dst = dav1d_data_create(&data, aSample->Size()); + if (!dst) { + return MediaResult(NS_ERROR_OUT_OF_MEMORY, + RESULT_DETAIL("Couldn't allocate dav1d AV1 input buffer")); + } + memcpy(dst, aSample->Data(), aSample->Size()); + data.m.timestamp = aSample->mTime; + data.m.duration = aSample->mDuration; + data.m.offset = aSample->mOffset; + data.m.size = aSample->Size(); + + while (data.sz) { + const int res = dav1d_send_data(mDecoder, &data); + if (res == DAV1D_ERR(EAGAIN)) { + MediaResult rv = DrainOutput(); + if (NS_FAILED(rv)) { + dav1d_data_unref(&data); + return rv; + } + continue; + } + if (res < 0) { + dav1d_data_unref(&data); + return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("dav1d error decoding AV1 sample: %d", + res)); + } + } + + dav1d_data_unref(&data); + return DrainOutput(); +} + +void +Dav1dDecoder::ProcessDecode(MediaRawData* aSample) +{ + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + if (mIsFlushing) { + return; + } + MediaResult rv = DoDecode(aSample); + if (NS_FAILED(rv)) { + mCallback->Error(rv); + } else { + mCallback->InputExhausted(); + } +} + +void +Dav1dDecoder::Input(MediaRawData* aSample) +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + mTaskQueue->Dispatch(NewRunnableMethod>( + this, &Dav1dDecoder::ProcessDecode, aSample)); +} + +void +Dav1dDecoder::ProcessDrain() +{ + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + MediaResult rv = DrainOutput(); + if (NS_FAILED(rv)) { + mCallback->Error(rv); + } else { + mCallback->DrainComplete(); + } +} + +void +Dav1dDecoder::Drain() +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &Dav1dDecoder::ProcessDrain)); +} + +/* static */ +bool +Dav1dDecoder::IsAV1(const nsACString& aMimeType) +{ + return aMimeType.EqualsLiteral("video/webm; codecs=av1") || + aMimeType.EqualsLiteral("video/av1"); +} + +/* static */ +nsIntSize +Dav1dDecoder::GetFrameSize(Span aBuffer) +{ + Dav1dSequenceHeader seqHdr; + PodZero(&seqHdr); + if (dav1d_parse_sequence_header(&seqHdr, + aBuffer.Elements(), + aBuffer.Length()) < 0) { + return nsIntSize(0, 0); + } + return nsIntSize(seqHdr.max_width, seqHdr.max_height); +} + +} // namespace mozilla +#undef LOG diff --git a/dom/media/platforms/agnostic/AOMDecoder.h b/dom/media/platforms/agnostic/Dav1dDecoder.h similarity index 69% rename from dom/media/platforms/agnostic/AOMDecoder.h rename to dom/media/platforms/agnostic/Dav1dDecoder.h index 73b3be67c7..e8b1c6aee3 100644 --- a/dom/media/platforms/agnostic/AOMDecoder.h +++ b/dom/media/platforms/agnostic/Dav1dDecoder.h @@ -3,21 +3,23 @@ * 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_ +#if !defined(Dav1dDecoder_h_) +#define Dav1dDecoder_h_ #include "PlatformDecoderModule.h" #include "mozilla/Span.h" #include -#include "aom/aom_decoder.h" + +typedef struct Dav1dContext Dav1dContext; +typedef struct Dav1dPicture Dav1dPicture; namespace mozilla { -class AOMDecoder : public MediaDataDecoder +class Dav1dDecoder : public MediaDataDecoder { public: - explicit AOMDecoder(const CreateDecoderParams& aParams); + explicit Dav1dDecoder(const CreateDecoderParams& aParams); RefPtr Init() override; void Input(MediaRawData* aSample) override; @@ -26,23 +28,22 @@ public: void Shutdown() override; const char* GetDescriptionName() const override { - return "libaom (AV1) video decoder"; + return "dav1d (AV1) video decoder"; } // 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 aBuffer); - - // Return the frame dimensions for a sample. + // Return the frame dimensions from a sequence header, when one is present. static nsIntSize GetFrameSize(Span aBuffer); private: - ~AOMDecoder(); + ~Dav1dDecoder(); void ProcessDecode(MediaRawData* aSample); MediaResult DoDecode(MediaRawData* aSample); + MediaResult DrainOutput(); + MediaResult OutputPicture(const Dav1dPicture& aPicture); void ProcessDrain(); const RefPtr mImageContainer; @@ -50,12 +51,11 @@ private: MediaDataDecoderCallback* mCallback; Atomic mIsFlushing; - // AOM decoder state - aom_codec_ctx_t mCodec; + Dav1dContext* mDecoder; const VideoInfo& mInfo; }; } // namespace mozilla -#endif // AOMDecoder_h_ +#endif // Dav1dDecoder_h_ diff --git a/dom/media/platforms/moz.build b/dom/media/platforms/moz.build index 09d9afa4d2..d8fba5d158 100644 --- a/dom/media/platforms/moz.build +++ b/dom/media/platforms/moz.build @@ -62,10 +62,10 @@ if CONFIG['MOZ_FFMPEG']: if CONFIG['MOZ_AV1']: EXPORTS += [ - 'agnostic/AOMDecoder.h', + 'agnostic/Dav1dDecoder.h', ] UNIFIED_SOURCES += [ - 'agnostic/AOMDecoder.cpp', + 'agnostic/Dav1dDecoder.cpp', ] if CONFIG['MOZ_APPLEMEDIA']: diff --git a/dom/media/webm/WebMDecoder.cpp b/dom/media/webm/WebMDecoder.cpp index da534b1cf5..24f02a43a2 100644 --- a/dom/media/webm/WebMDecoder.cpp +++ b/dom/media/webm/WebMDecoder.cpp @@ -4,9 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/Preferences.h" -#ifdef MOZ_AV1 -#include "AOMDecoder.h" -#endif #include "MediaPrefs.h" #include "MediaDecoderStateMachine.h" #include "WebMDemuxer.h" @@ -136,4 +133,3 @@ WebMDecoder::GetMozDebugReaderData(nsAString& aString) } } // namespace mozilla - diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index 67e0c71cd5..a5dbdc52c7 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -8,7 +8,7 @@ #include "AbstractMediaDecoder.h" #include "MediaResource.h" #ifdef MOZ_AV1 -#include "AOMDecoder.h" +#include "Dav1dDecoder.h" #endif #include "OpusDecoder.h" #include "VPXDecoder.h" @@ -711,7 +711,7 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl break; #ifdef MOZ_AV1 case NESTEGG_CODEC_AV1: - isKeyframe = AOMDecoder::IsKeyframe(sample); + isKeyframe = nestegg_packet_has_keyframe(holder->Packet()) == NESTEGG_PACKET_HAS_KEYFRAME_TRUE; break; #endif case NESTEGG_CODEC_AVC1: @@ -734,16 +734,18 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl break; #ifdef MOZ_AV1 case NESTEGG_CODEC_AV1: - dimensions = AOMDecoder::GetFrameSize(sample); + dimensions = Dav1dDecoder::GetFrameSize(sample); break; #endif } - if (mLastSeenFrameSize.isSome() - && (dimensions != mLastSeenFrameSize.value())) { - mInfo.mVideo.mDisplay = dimensions; - mSharedVideoTrackInfo = new SharedTrackInfo(mInfo.mVideo, ++sStreamSourceID); + if (dimensions.width > 0 && dimensions.height > 0) { + if (mLastSeenFrameSize.isSome() + && (dimensions != mLastSeenFrameSize.value())) { + mInfo.mVideo.mDisplay = dimensions; + mSharedVideoTrackInfo = new SharedTrackInfo(mInfo.mVideo, ++sStreamSourceID); + } + mLastSeenFrameSize = Some(dimensions); } - mLastSeenFrameSize = Some(dimensions); } } } diff --git a/dom/media/webm/moz.build b/dom/media/webm/moz.build index e9387e8c98..b1926d9a04 100644 --- a/dom/media/webm/moz.build +++ b/dom/media/webm/moz.build @@ -24,6 +24,11 @@ if CONFIG['MOZ_WEBM_ENCODER']: 'WebMWriter.cpp', ] +if CONFIG['MOZ_AV1']: + LOCAL_INCLUDES += [ + '/dom/media/platforms/agnostic', + ] + CXXFLAGS += CONFIG['MOZ_LIBVPX_CFLAGS'] FINAL_LIBRARY = 'xul'