PR #23018 opened by AdityaTeltia URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23018 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23018.patch
This commit introduces support for AV2 decoding via the external libdav2d library. The implementation is heavily modeled after the existing libdav1d AV1 wrapper, mapping FFmpeg's AVCodecContext and AVFrame structures to Dav2dContext and Dav2dPicture. Notable differences from the AV1 implementation include extracting color metadata (transfer characteristics, colorspace) and timing information directly from the newly introduced Content Interpretation (ci) headers attached to the picture rather than the sequence header, in accordance with the updated dav2d API spec. closes #22998 >From b99b9f375a492235bfa211a42b554a028b671515 Mon Sep 17 00:00:00 2001 From: AdityaTeltia <[email protected]> Date: Sun, 3 May 2026 12:12:22 +0530 Subject: [PATCH] avcodec: libdav2d AV2 decoder wrapper Modeled after the libdav1d AV1 decoder implementation. --- Changelog | 1 + configure | 5 + doc/general_contents.texi | 7 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/codec_desc.c | 7 + libavcodec/codec_id.h | 1 + libavcodec/libdav2d.c | 706 ++++++++++++++++++++++++++++++++++++++ libavcodec/version.h | 2 +- 9 files changed, 730 insertions(+), 1 deletion(-) create mode 100644 libavcodec/libdav2d.c diff --git a/Changelog b/Changelog index 3f5e38e0c7..8074290c7d 100644 --- a/Changelog +++ b/Changelog @@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release, releases are sorted from youngest to oldest. version <next>: +- AV2 decoding support through libdav2d - Extend AMF Color Converter (vf_vpp_amf) HDR capabilities - LCEVC track muxing support in MP4 muxer - Playdate video encoder and muxer diff --git a/configure b/configure index 9bce658932..6d71a6f3df 100755 --- a/configure +++ b/configure @@ -226,6 +226,7 @@ External library support: --enable-libcdio enable audio CD grabbing with libcdio [no] --enable-libcodec2 enable codec2 en/decoding using libcodec2 [no] --enable-libdav1d enable AV1 decoding via libdav1d [no] + --enable-libdav2d enable AV2 decoding via libdav2d [no] --enable-libdavs2 enable AVS2 decoding via libdavs2 [no] --enable-libdc1394 enable IIDC-1394 grabbing using libdc1394 and libraw1394 [no] @@ -2088,6 +2089,7 @@ EXTERNAL_LIBRARY_LIST=" libcelt libcodec2 libdav1d + libdav2d libdc1394 libflite libfontconfig @@ -3822,6 +3824,8 @@ libcodec2_decoder_deps="libcodec2" libcodec2_encoder_deps="libcodec2" libdav1d_decoder_deps="libdav1d" libdav1d_decoder_select="atsc_a53 dovi_rpudec" +libdav2d_decoder_deps="libdav2d" +libdav2d_decoder_select="atsc_a53 dovi_rpudec" libdavs2_decoder_deps="libdavs2" libdavs2_decoder_select="avs2_parser" libfdk_aac_decoder_deps="libfdk_aac" @@ -7319,6 +7323,7 @@ enabled libcelt && require libcelt celt/celt.h celt_decode -lcelt0 && enabled libcaca && require_pkg_config libcaca caca caca.h caca_create_canvas enabled libcodec2 && require libcodec2 codec2/codec2.h codec2_create -lcodec2 enabled libdav1d && require_pkg_config libdav1d "dav1d >= 1.0.0" "dav1d/dav1d.h" dav1d_version +enabled libdav2d && require_pkg_config libdav2d "dav2d >= 1.0.0" "dav2d/dav2d.h" dav2d_version enabled libdavs2 && require_pkg_config libdavs2 "davs2 >= 1.6.0" davs2.h davs2_decoder_open enabled libdc1394 && require_pkg_config libdc1394 libdc1394-2 dc1394/dc1394.h dc1394_new enabled libdrm && check_pkg_config libdrm libdrm xf86drm.h drmGetVersion diff --git a/doc/general_contents.texi b/doc/general_contents.texi index 47ac1989f2..651825fbd8 100644 --- a/doc/general_contents.texi +++ b/doc/general_contents.texi @@ -105,6 +105,13 @@ FFmpeg can make use of the dav1d library for AV1 video decoding. Go to @url{https://code.videolan.org/videolan/dav1d} and follow the instructions for installing the library. Then pass @code{--enable-libdav1d} to configure to enable it. +@section dav2d + +FFmpeg can make use of the dav2d library for AV2 video decoding. + +Go to @url{https://code.videolan.org/videolan/dav2d} and follow the instructions for +installing the library. Then pass @code{--enable-libdav2d} to configure to enable it. + @section davs2 FFmpeg can make use of the davs2 library for AVS2-P2/IEEE1857.4 video decoding. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 85d35913f3..fa8b01458a 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1176,6 +1176,7 @@ OBJS-$(CONFIG_LIBCELT_DECODER) += libcelt_dec.o OBJS-$(CONFIG_LIBCODEC2_DECODER) += libcodec2.o OBJS-$(CONFIG_LIBCODEC2_ENCODER) += libcodec2.o OBJS-$(CONFIG_LIBDAV1D_DECODER) += libdav1d.o av1_parse.o +OBJS-$(CONFIG_LIBDAV2D_DECODER) += libdav2d.o OBJS-$(CONFIG_LIBDAVS2_DECODER) += libdavs2.o OBJS-$(CONFIG_LIBFDK_AAC_DECODER) += libfdk-aacdec.o OBJS-$(CONFIG_LIBFDK_AAC_ENCODER) += libfdk-aacenc.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 5e798c1430..e9ca5fc4e4 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -789,6 +789,7 @@ extern const FFCodec ff_libcelt_decoder; extern const FFCodec ff_libcodec2_encoder; extern const FFCodec ff_libcodec2_decoder; extern const FFCodec ff_libdav1d_decoder; +extern const FFCodec ff_libdav2d_decoder; extern const FFCodec ff_libdavs2_decoder; extern const FFCodec ff_libfdk_aac_encoder; extern const FFCodec ff_libfdk_aac_decoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index a9f21f8152..a7d72a8a14 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1651,6 +1651,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, .profiles = NULL_IF_CONFIG_SMALL(ff_av1_profiles), }, + { + .id = AV_CODEC_ID_AV2, + .type = AVMEDIA_TYPE_VIDEO, + .name = "av2", + .long_name = NULL_IF_CONFIG_SMALL("Alliance for Open Media AV2"), + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, + }, { .id = AV_CODEC_ID_BITPACKED, .type = AVMEDIA_TYPE_VIDEO, diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 6529f0a6bc..2037bec17c 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -282,6 +282,7 @@ enum AVCodecID { AV_CODEC_ID_CLEARVIDEO, AV_CODEC_ID_XPM, AV_CODEC_ID_AV1, + AV_CODEC_ID_AV2, AV_CODEC_ID_BITPACKED, AV_CODEC_ID_MSCC, AV_CODEC_ID_SRGC, diff --git a/libavcodec/libdav2d.c b/libavcodec/libdav2d.c new file mode 100644 index 0000000000..0e63f34efe --- /dev/null +++ b/libavcodec/libdav2d.c @@ -0,0 +1,706 @@ +/* + * Copyright (c) 2018 Ronald S. Bultje <rsbultje gmail com> + * Copyright (c) 2018 James Almer <jamrial gmail com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <dav2d/dav2d.h> + +#include "libavutil/avassert.h" +#include "libavutil/cpu.h" +#include "libavutil/film_grain_params.h" +#include "libavutil/hdr_dynamic_metadata.h" +#include "libavutil/mastering_display_metadata.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" + +#include "atsc_a53.h" +static AVRational ff_av2_framerate(int64_t ticks_per_frame, int64_t units_per_tick, + int64_t time_scale) +{ + AVRational fr; + + if (ticks_per_frame && units_per_tick && time_scale && + ticks_per_frame < INT64_MAX / units_per_tick && + av_reduce(&fr.den, &fr.num, units_per_tick * ticks_per_frame, + time_scale, INT_MAX)) + return fr; + + return (AVRational){ 0, 1 }; +} +#include "avcodec.h" +#include "bytestream.h" +#include "codec_internal.h" +#include "decode.h" +#include "dovi_rpu.h" +#include "internal.h" +#include "itut35.h" + +#define FF_DAV2D_VERSION_AT_LEAST(x,y) \ + (DAV2D_API_VERSION_MAJOR > (x) || DAV2D_API_VERSION_MAJOR == (x) && DAV2D_API_VERSION_MINOR >= (y)) + +typedef struct Libdav2dContext { + AVClass *class; + Dav2dContext *c; + AVBufferPool *pool; + DOVIContext dovi; + int pool_size; + + Dav2dData data; + int max_frame_delay; + int apply_grain; + int operating_point; + int all_layers; +} Libdav2dContext; + +static const enum AVPixelFormat pix_fmt[][3] = { + [DAV2D_PIXEL_LAYOUT_I400] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12 }, + [DAV2D_PIXEL_LAYOUT_I420] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P12 }, + [DAV2D_PIXEL_LAYOUT_I422] = { AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12 }, + [DAV2D_PIXEL_LAYOUT_I444] = { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12 }, +}; + +static const enum AVPixelFormat pix_fmt_rgb[3] = { + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, +}; + +static void libdav2d_log_callback(void *opaque, const char *fmt, va_list vl) +{ + AVCodecContext *c = opaque; + + av_vlog(c, AV_LOG_ERROR, fmt, vl); +} + +static int libdav2d_picture_allocator(Dav2dPicture *p, void *cookie) +{ + Libdav2dContext *dav2d = cookie; + enum AVPixelFormat format = pix_fmt[p->p.layout][p->seq_hdr->hbd]; + int ret, linesize[4], h = FFALIGN(p->p.h, 128), w = FFALIGN(p->p.w, 128); + uint8_t *aligned_ptr, *data[4]; + AVBufferRef *buf; + + ret = av_image_get_buffer_size(format, w, h, DAV2D_PICTURE_ALIGNMENT); + if (ret < 0) + return ret; + + if (ret != dav2d->pool_size) { + av_buffer_pool_uninit(&dav2d->pool); + // Use twice the amount of required padding bytes for aligned_ptr below. + dav2d->pool = av_buffer_pool_init(ret + DAV2D_PICTURE_ALIGNMENT * 2, NULL); + if (!dav2d->pool) { + dav2d->pool_size = 0; + return AVERROR(ENOMEM); + } + dav2d->pool_size = ret; + } + buf = av_buffer_pool_get(dav2d->pool); + if (!buf) + return AVERROR(ENOMEM); + + // libdav2d requires DAV2D_PICTURE_ALIGNMENT aligned buffers, which av_malloc() + // doesn't guarantee for example when AVX is disabled at configure time. + // Use the extra DAV2D_PICTURE_ALIGNMENT padding bytes in the buffer to align it + // if required. + aligned_ptr = (uint8_t *)FFALIGN((uintptr_t)buf->data, DAV2D_PICTURE_ALIGNMENT); + ret = av_image_fill_arrays(data, linesize, aligned_ptr, format, w, h, + DAV2D_PICTURE_ALIGNMENT); + if (ret < 0) { + av_buffer_unref(&buf); + return ret; + } + + p->data[0] = data[0]; + p->data[1] = data[1]; + p->data[2] = data[2]; + p->stride[0] = linesize[0]; + p->stride[1] = linesize[1]; + p->allocator_data = buf; + + return 0; +} + +static void libdav2d_picture_release(Dav2dPicture *p, void *cookie) +{ + AVBufferRef *buf = p->allocator_data; + + av_buffer_unref(&buf); +} + +static void libdav2d_init_params(AVCodecContext *c, const Dav2dSequenceHeader *seq) +{ + c->profile = seq->profile; + c->level = seq->level; + + if (seq->layout == DAV2D_PIXEL_LAYOUT_I444 && c->colorspace == AVCOL_SPC_RGB) + c->pix_fmt = pix_fmt_rgb[seq->hbd]; + else + c->pix_fmt = pix_fmt[seq->layout][seq->hbd]; +} + +static av_cold int libdav2d_parse_extradata(AVCodecContext *c) +{ + Dav2dSequenceHeader seq; + size_t offset = 0; + int res; + + if (!c->extradata || c->extradata_size <= 0) + return 0; + + if (c->extradata[0] & 0x80) { + int version = c->extradata[0] & 0x7F; + + if (version != 1 || c->extradata_size < 4) { + int explode = !!(c->err_recognition & AV_EF_EXPLODE); + av_log(c, explode ? AV_LOG_ERROR : AV_LOG_WARNING, + "Error decoding extradata\n"); + return explode ? AVERROR_INVALIDDATA : 0; + } + + // Do nothing if there are no configOBUs to parse + if (c->extradata_size == 4) + return 0; + + offset = 4; + } + + res = dav2d_parse_sequence_header(&seq, c->extradata + offset, + c->extradata_size - offset); + if (res < 0) + return 0; // Assume no seqhdr OBUs are present + + libdav2d_init_params(c, &seq); + res = ff_set_dimensions(c, seq.max_width, seq.max_height); + if (res < 0) + return res; + + return 0; +} + +static av_cold int libdav2d_init(AVCodecContext *c) +{ + Libdav2dContext *dav2d = c->priv_data; + Dav2dSettings s; + int threads = c->thread_count; + const AVPacketSideData *sd; + int res; + + av_log(c, AV_LOG_VERBOSE, "libdav2d %s\n", dav2d_version()); + + dav2d_default_settings(&s); + s.logger.cookie = c; + s.logger.callback = libdav2d_log_callback; + s.allocator.cookie = dav2d; + s.allocator.alloc_picture_callback = libdav2d_picture_allocator; + s.allocator.release_picture_callback = libdav2d_picture_release; + s.frame_size_limit = c->max_pixels; + if (dav2d->apply_grain >= 0) + s.apply_grain = dav2d->apply_grain; + else + s.apply_grain = !(c->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN); + + s.all_layers = dav2d->all_layers; + if (dav2d->operating_point >= 0) + s.operating_point = dav2d->operating_point; + s.strict_std_compliance = c->strict_std_compliance > 0; + + s.n_threads = FFMIN(threads, DAV2D_MAX_THREADS); + if (dav2d->max_frame_delay > 0 && (c->flags & AV_CODEC_FLAG_LOW_DELAY)) + av_log(c, AV_LOG_WARNING, "Low delay mode requested, forcing max_frame_delay 1\n"); + s.max_frame_delay = (c->flags & AV_CODEC_FLAG_LOW_DELAY) ? 1 : dav2d->max_frame_delay; + av_log(c, AV_LOG_DEBUG, "Using %d threads, %d max_frame_delay\n", + s.n_threads, s.max_frame_delay); + +#if FF_DAV2D_VERSION_AT_LEAST(6,8) + if (c->skip_frame >= AVDISCARD_NONKEY) + s.decode_frame_type = DAV2D_DECODEFRAMETYPE_KEY; + else if (c->skip_frame >= AVDISCARD_NONINTRA) + s.decode_frame_type = DAV2D_DECODEFRAMETYPE_INTRA; + else if (c->skip_frame >= AVDISCARD_NONREF) + s.decode_frame_type = DAV2D_DECODEFRAMETYPE_REFERENCE; +#endif + + res = libdav2d_parse_extradata(c); + if (res < 0) + return res; + + res = dav2d_open(&dav2d->c, &s); + if (res < 0) + return AVERROR(ENOMEM); + +#if FF_DAV2D_VERSION_AT_LEAST(6,7) + res = dav2d_get_frame_delay(&s); + if (res < 0) // Should not happen + return AVERROR_EXTERNAL; + + // When dav2d_get_frame_delay() returns 1, there's no delay whatsoever + c->delay = res > 1 ? res : 0; +#endif + + dav2d->dovi.logctx = c; + dav2d->dovi.cfg.dv_profile = 10; // default for AV2 + sd = ff_get_coded_side_data(c, AV_PKT_DATA_DOVI_CONF); + if (sd && sd->size >= sizeof(dav2d->dovi.cfg)) + dav2d->dovi.cfg = *(AVDOVIDecoderConfigurationRecord *) sd->data; + return 0; +} + +static void libdav2d_flush(AVCodecContext *c) +{ + Libdav2dContext *dav2d = c->priv_data; + + dav2d_data_unref(&dav2d->data); + dav2d_flush(dav2d->c); +} + +static void libdav2d_data_free(const uint8_t *data, void *opaque) { + AVBufferRef *buf = opaque; + + av_buffer_unref(&buf); +} + +static void libdav2d_user_data_free(const uint8_t *data, void *opaque) { + AVPacket *pkt = opaque; + av_assert0(data == opaque); + av_packet_free(&pkt); +} + +static int libdav2d_receive_frame_internal(AVCodecContext *c, Dav2dPicture *p) +{ + Libdav2dContext *dav2d = c->priv_data; + Dav2dData *data = &dav2d->data; + int res; + + if (!data->sz) { + AVPacket *pkt = av_packet_alloc(); + + if (!pkt) + return AVERROR(ENOMEM); + + res = ff_decode_get_packet(c, pkt); + if (res < 0 && res != AVERROR_EOF) { + av_packet_free(&pkt); + return res; + } + + if (pkt->size) { + res = dav2d_data_wrap(data, pkt->data, pkt->size, + libdav2d_data_free, pkt->buf); + if (res < 0) { + av_packet_free(&pkt); + return res; + } + + pkt->buf = NULL; + + res = dav2d_data_wrap_user_data(data, (const uint8_t *)pkt, + libdav2d_user_data_free, pkt); + if (res < 0) { + av_packet_free(&pkt); + dav2d_data_unref(data); + return res; + } + pkt = NULL; + } else { + av_packet_free(&pkt); + if (res >= 0) + return AVERROR(EAGAIN); + } + } + + res = dav2d_send_data(dav2d->c, data); + if (res < 0) { + if (res == AVERROR(EINVAL)) + res = AVERROR_INVALIDDATA; + if (res != AVERROR(EAGAIN)) { + dav2d_data_unref(data); + return res; + } + } + + res = dav2d_get_picture(dav2d->c, p); + if (res < 0) { + if (res == AVERROR(EINVAL)) { + dav2d_data_unref(data); + res = AVERROR_INVALIDDATA; + } else if (res == AVERROR(EAGAIN)) + res = c->internal->draining ? AVERROR_EOF : 1; + } + + return res; +} + +static int parse_itut_t35_metadata(Libdav2dContext *dav2d, Dav2dPicture *p, + const Dav2dITUTT35 *itut_t35, AVCodecContext *c, + AVFrame *frame) { + GetByteContext gb; + int provider_code, country_code; + int res; + + bytestream2_init(&gb, itut_t35->payload, itut_t35->payload_size); + + country_code = itut_t35->country_code; + switch (country_code) { + case ITU_T_T35_COUNTRY_CODE_US: + if (bytestream2_get_bytes_left(&gb) < 2) + return AVERROR_INVALIDDATA; + provider_code = bytestream2_get_be16u(&gb); + + switch (provider_code) { + case ITU_T_T35_PROVIDER_CODE_ATSC: { + uint32_t user_identifier = bytestream2_get_be32(&gb); + switch (user_identifier) { + case MKBETAG('G', 'A', '9', '4'): { // closed captions + AVBufferRef *buf = NULL; + + res = ff_parse_a53_cc(&buf, gb.buffer, bytestream2_get_bytes_left(&gb)); + if (res < 0) + return res; + if (!res) + return 0; // no cc found, ignore + + res = ff_frame_new_side_data_from_buf(c, frame, AV_FRAME_DATA_A53_CC, &buf); + if (res < 0) + return res; + + + break; + } + default: // ignore unsupported identifiers + break; + } + break; + } + case ITU_T_T35_PROVIDER_CODE_SAMSUNG: { + AVDynamicHDRPlus *hdrplus; + int provider_oriented_code = bytestream2_get_be16(&gb); + int application_identifier = bytestream2_get_byte(&gb); + + if (provider_oriented_code != 1 || application_identifier != 4) + return 0; // ignore + + hdrplus = av_dynamic_hdr_plus_create_side_data(frame); + if (!hdrplus) + return AVERROR(ENOMEM); + + res = av_dynamic_hdr_plus_from_t35(hdrplus, gb.buffer, + bytestream2_get_bytes_left(&gb)); + if (res < 0) + return res; + break; + } + case ITU_T_T35_PROVIDER_CODE_DOLBY: { + int provider_oriented_code = bytestream2_get_be32(&gb); + if (provider_oriented_code != 0x800) + return 0; // ignore + + res = ff_dovi_rpu_parse(&dav2d->dovi, gb.buffer, bytestream2_get_bytes_left(&gb), + c->err_recognition); + if (res < 0) { + av_log(c, AV_LOG_WARNING, "Error parsing DOVI OBU.\n"); + return 0; // ignore + } + + res = ff_dovi_attach_side_data(&dav2d->dovi, frame); + if (res < 0) + return res; + break; + } + case ITU_T_T35_PROVIDER_CODE_SMPTE: { + AVDynamicHDRSmpte2094App5 *hdr_smpte2094_app5; + int provider_oriented_code = bytestream2_get_be16(&gb); + if (provider_oriented_code != 1) + return 0; // ignore + + hdr_smpte2094_app5 = av_dynamic_hdr_smpte2094_app5_create_side_data(frame); + if (!hdr_smpte2094_app5) + return AVERROR(ENOMEM); + + res = av_dynamic_hdr_smpte2094_app5_from_t35(hdr_smpte2094_app5, gb.buffer, + bytestream2_get_bytes_left(&gb)); + if (res < 0) + return res; + break; + } + default: + break; + } + break; + case ITU_T_T35_COUNTRY_CODE_UK: + bytestream2_skipu(&gb, 1); // t35_uk_country_code_second_octet + if (bytestream2_get_bytes_left(&gb) < 2) + return AVERROR_INVALIDDATA; + + provider_code = bytestream2_get_be16u(&gb); + switch (provider_code) { + case ITU_T_T35_PROVIDER_CODE_VNOVA: { + AVFrameSideData *sd; + if (bytestream2_get_bytes_left(&gb) < 2) + return AVERROR_INVALIDDATA; + + res = ff_frame_new_side_data(c, frame, AV_FRAME_DATA_LCEVC, + bytestream2_get_bytes_left(&gb), &sd); + if (res < 0) + return res; + if (!sd) + break; + + bytestream2_get_bufferu(&gb, sd->data, sd->size); + break; + } + default: + break; + } + break; + + default: + // ignore unsupported provider codes + break; + } + return 0; +} + +static int libdav2d_receive_frame(AVCodecContext *c, AVFrame *frame) +{ + Libdav2dContext *dav2d = c->priv_data; + Dav2dPicture pic = { 0 }, *p = &pic; + const AVPacket *pkt; + enum Dav2dEventFlags event_flags = 0; + int res; + + do { + res = libdav2d_receive_frame_internal(c, p); + } while (res > 0); + + if (res < 0) + return res; + + av_assert0(p->data[0] && p->allocator_data); + + // This requires the custom allocator above + frame->buf[0] = av_buffer_ref(p->allocator_data); + if (!frame->buf[0]) { + dav2d_picture_unref(p); + return AVERROR(ENOMEM); + } + + frame->data[0] = p->data[0]; + frame->data[1] = p->data[1]; + frame->data[2] = p->data[2]; + frame->linesize[0] = p->stride[0]; + frame->linesize[1] = p->stride[1]; + frame->linesize[2] = p->stride[1]; + + dav2d_get_event_flags(dav2d->c, &event_flags); + if (c->pix_fmt == AV_PIX_FMT_NONE || event_flags & DAV2D_EVENT_FLAG_NEW_SEQUENCE) + libdav2d_init_params(c, p->seq_hdr); + + + if (p->ci) { + switch (p->ci->chr[0]) { + case DAV2D_CHR_LEFT: + c->chroma_sample_location = AVCHROMA_LOC_LEFT; + break; + case DAV2D_CHR_TOPLEFT: + c->chroma_sample_location = AVCHROMA_LOC_TOPLEFT; + break; + default: + break; + } + if (p->ci->color_description_present) { + c->colorspace = (enum AVColorSpace) p->ci->color.mtrx; + c->color_primaries = (enum AVColorPrimaries) p->ci->color.pri; + c->color_trc = (enum AVColorTransferCharacteristic) p->ci->color.trc; + } + c->color_range = p->ci->color.range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + + if (p->ci->timing_info_present) { + c->framerate = ff_av2_framerate(p->ci->timing.num_ticks_per_elemental_duration, + p->ci->timing.num_units_in_display_tick, + p->ci->timing.time_scale); + } + } + + res = ff_decode_frame_props(c, frame); + if (res < 0) + goto fail; + + frame->width = p->p.w; + frame->height = p->p.h; + if (c->width != p->p.w || c->height != p->p.h) { + res = ff_set_dimensions(c, p->p.w, p->p.h); + if (res < 0) + goto fail; + } + + av_reduce(&frame->sample_aspect_ratio.num, + &frame->sample_aspect_ratio.den, + frame->height * (int64_t)p->frame_hdr->width, + frame->width * (int64_t)p->frame_hdr->height, + INT_MAX); + ff_set_sar(c, frame->sample_aspect_ratio); + + pkt = (const AVPacket *)p->m.user_data.data; + + // match timestamps and packet size + res = ff_decode_frame_props_from_pkt(c, frame, pkt); + if (res < 0) + goto fail; + + frame->pkt_dts = pkt->pts; + if (p->frame_hdr->frame_type == DAV2D_FRAME_TYPE_KEY) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; + + switch (p->frame_hdr->frame_type) { + case DAV2D_FRAME_TYPE_KEY: + case DAV2D_FRAME_TYPE_INTRA: + frame->pict_type = AV_PICTURE_TYPE_I; + break; + case DAV2D_FRAME_TYPE_INTER: + frame->pict_type = AV_PICTURE_TYPE_P; + break; + case DAV2D_FRAME_TYPE_SWITCH: + frame->pict_type = AV_PICTURE_TYPE_SP; + break; + default: + res = AVERROR_INVALIDDATA; + goto fail; + } + + if (p->mastering_display) { + AVMasteringDisplayMetadata *mastering; + + res = ff_decode_mastering_display_new(c, frame, &mastering); + if (res < 0) + goto fail; + + if (mastering) { + for (int i = 0; i < 3; i++) { + mastering->display_primaries[i][0] = av_make_q(p->mastering_display->primaries[i][0], 1 << 16); + mastering->display_primaries[i][1] = av_make_q(p->mastering_display->primaries[i][1], 1 << 16); + } + mastering->white_point[0] = av_make_q(p->mastering_display->white_point[0], 1 << 16); + mastering->white_point[1] = av_make_q(p->mastering_display->white_point[1], 1 << 16); + + mastering->max_luminance = av_make_q(p->mastering_display->max_luminance, 1 << 8); + mastering->min_luminance = av_make_q(p->mastering_display->min_luminance, 1 << 14); + + mastering->has_primaries = 1; + mastering->has_luminance = 1; + } + } + if (p->content_light) { + AVContentLightMetadata *light; + + res = ff_decode_content_light_new(c, frame, &light); + if (res < 0) + goto fail; + + if (light) { + light->MaxCLL = p->content_light->max_content_light_level; + light->MaxFALL = p->content_light->max_frame_average_light_level; + } + } + if (p->itut_t35) { +#if FF_DAV2D_VERSION_AT_LEAST(6,9) + for (size_t i = 0; i < p->n_itut_t35; i++) { + const Dav2dITUTT35 *itut_t35 = &p->itut_t35[i]; +#else + const Dav2dITUTT35 *itut_t35 = p->itut_t35; +#endif + res = parse_itut_t35_metadata(dav2d, p, itut_t35, c, frame); + if (res < 0) + goto fail; +#if FF_DAV2D_VERSION_AT_LEAST(6,9) + } +#endif + } + // Film grain export logic is disabled for AV2 until AV_FILM_GRAIN_PARAMS_AV2 is introduced + if (p->frame_hdr->film_grain.present && (!dav2d->apply_grain || + (c->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN))) { + av_log(c, AV_LOG_WARNING, "Film grain export not supported for AV2 yet.\n"); + } + + res = ff_attach_decode_data(c, frame); + if (res < 0) + return res; + + res = 0; +fail: + dav2d_picture_unref(p); + if (res < 0) + av_frame_unref(frame); + return res; +} + +static av_cold int libdav2d_close(AVCodecContext *c) +{ + Libdav2dContext *dav2d = c->priv_data; + + av_buffer_pool_uninit(&dav2d->pool); + ff_dovi_ctx_unref(&dav2d->dovi); + dav2d_data_unref(&dav2d->data); + dav2d_close(&dav2d->c); + + return 0; +} + +#ifndef DAV2D_MAX_FRAME_THREADS +#define DAV2D_MAX_FRAME_THREADS DAV2D_MAX_THREADS +#endif +#ifndef DAV2D_MAX_TILE_THREADS +#define DAV2D_MAX_TILE_THREADS DAV2D_MAX_THREADS +#endif +#ifndef DAV2D_MAX_FRAME_DELAY +#define DAV2D_MAX_FRAME_DELAY DAV2D_MAX_FRAME_THREADS +#endif + +#define OFFSET(x) offsetof(Libdav2dContext, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption libdav2d_options[] = { + { "max_frame_delay", "Max frame delay", OFFSET(max_frame_delay), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, DAV2D_MAX_FRAME_DELAY, VD }, + { "filmgrain", "Apply Film Grain", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD | AV_OPT_FLAG_DEPRECATED }, + { "oppoint", "Select an operating point of the scalable bitstream", OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 31, VD }, + { "alllayers", "Output all spatial layers", OFFSET(all_layers), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD }, + { NULL } +}; + +static const AVClass libdav2d_class = { + .class_name = "libdav2d decoder", + .item_name = av_default_item_name, + .option = libdav2d_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_libdav2d_decoder = { + .p.name = "libdav2d", + CODEC_LONG_NAME("dav2d AV2 decoder by VideoLAN"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV2, + .priv_data_size = sizeof(Libdav2dContext), + .init = libdav2d_init, + .close = libdav2d_close, + .flush = libdav2d_flush, + FF_CODEC_RECEIVE_FRAME_CB(libdav2d_receive_frame), + .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS, + .caps_internal = FF_CODEC_CAP_SETS_FRAME_PROPS | + FF_CODEC_CAP_AUTO_THREADS, + .p.priv_class = &libdav2d_class, + .p.wrapper_name = "libdav2d", +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index aeb58e3fed..497389d3f3 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,7 +29,7 @@ #include "version_major.h" -#define LIBAVCODEC_VERSION_MINOR 30 +#define LIBAVCODEC_VERSION_MINOR 31 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
