[FFmpeg-devel] [PATCH v9 6/6] lavc/hevcdec: Parse DOVI RPU NALs

Andreas Rheinhardt andreas.rheinhardt at outlook.com
Sun Jan 2 06:31:30 EET 2022


Niklas Haas:
> From: Niklas Haas <git at haasn.dev>
> 
> And expose the parsed values as frame side data. Update FATE results to
> match.
> 
> It's worth documenting that this relies on the dovi configuration record
> being present on the first AVPacket fed to the decoder, which in
> practice is the case if if the API user has called something like
> av_format_inject_global_side_data, which is unfortunately not the
> default.
> 
> This commit is not the time and place to change that behavior, though.
> 
> Signed-off-by: Niklas Haas <git at haasn.dev>
> ---
>  configure                  |   2 +-
>  libavcodec/hevcdec.c       |  63 +++++++++--
>  libavcodec/hevcdec.h       |   3 +
>  tests/ref/fate/hevc-dv-rpu | 224 +++++++++++++++++++++++++++++++++++++
>  4 files changed, 283 insertions(+), 9 deletions(-)
> 
> diff --git a/configure b/configure
> index 68658a847f..7803aa47af 100755
> --- a/configure
> +++ b/configure
> @@ -2826,7 +2826,7 @@ h264_decoder_suggest="error_resilience"
>  hap_decoder_select="snappy texturedsp"
>  hap_encoder_deps="libsnappy"
>  hap_encoder_select="texturedspenc"
> -hevc_decoder_select="atsc_a53 bswapdsp cabac golomb hevcparse videodsp"
> +hevc_decoder_select="atsc_a53 bswapdsp cabac dovi_rpu golomb hevcparse videodsp"
>  huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp"
>  huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llvidencdsp"
>  hymt_decoder_select="huffyuv_decoder"
> diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c
> index 46d9edf8eb..298d89fea6 100644
> --- a/libavcodec/hevcdec.c
> +++ b/libavcodec/hevcdec.c
> @@ -2723,6 +2723,7 @@ error:
>  static int set_side_data(HEVCContext *s)
>  {
>      AVFrame *out = s->ref->frame;
> +    int ret;
>  
>      if (s->sei.frame_packing.present &&
>          s->sei.frame_packing.arrangement_type >= 3 &&
> @@ -2967,6 +2968,9 @@ static int set_side_data(HEVCContext *s)
>          s->rpu_buf = NULL;
>      }
>  
> +    if ((ret = ff_dovi_attach_side_data(&s->dovi_ctx, out)) < 0)
> +        return ret;
> +
>      return 0;
>  }
>  
> @@ -3298,16 +3302,24 @@ static int decode_nal_units(HEVCContext *s, const uint8_t *buf, int length)
>      if (s->pkt.nb_nals > 1 && s->pkt.nals[s->pkt.nb_nals - 1].type == HEVC_NAL_UNSPEC62 &&
>          s->pkt.nals[s->pkt.nb_nals - 1].size > 2 && !s->pkt.nals[s->pkt.nb_nals - 1].nuh_layer_id
>          && !s->pkt.nals[s->pkt.nb_nals - 1].temporal_id) {
> +        H2645NAL *nal = &s->pkt.nals[s->pkt.nb_nals - 1];
>          if (s->rpu_buf) {
>              av_buffer_unref(&s->rpu_buf);
>              av_log(s->avctx, AV_LOG_WARNING, "Multiple Dolby Vision RPUs found in one AU. Skipping previous.\n");
>          }
>  
> -        s->rpu_buf = av_buffer_alloc(s->pkt.nals[s->pkt.nb_nals - 1].raw_size - 2);
> +        s->rpu_buf = av_buffer_alloc(nal->raw_size - 2);
>          if (!s->rpu_buf)
>              return AVERROR(ENOMEM);
> +        memcpy(s->rpu_buf->data, nal->raw_data + 2, nal->raw_size - 2);
>  
> -        memcpy(s->rpu_buf->data, s->pkt.nals[s->pkt.nb_nals - 1].raw_data + 2, s->pkt.nals[s->pkt.nb_nals - 1].raw_size - 2);
> +        s->dovi_ctx.config = s->dovi_cfg ? (void *) s->dovi_cfg->data : NULL;
> +        ret = ff_dovi_rpu_parse(&s->dovi_ctx, nal->data + 2, nal->size - 2);
> +        if (ret < 0) {
> +            av_buffer_unref(&s->rpu_buf);
> +            av_log(s->avctx, AV_LOG_WARNING, "Error parsing DOVI NAL unit.\n");
> +            /* ignore */
> +        }
>      }
>  
>      /* decode the NAL units */
> @@ -3440,8 +3452,8 @@ static int hevc_decode_frame(AVCodecContext *avctx, void *data, int *got_output,
>                               AVPacket *avpkt)
>  {
>      int ret;
> -    size_t new_extradata_size;
> -    uint8_t *new_extradata;
> +    uint8_t *sd;
> +    size_t sd_size;
>      HEVCContext *s = avctx->priv_data;
>  
>      if (!avpkt->size) {
> @@ -3453,14 +3465,37 @@ static int hevc_decode_frame(AVCodecContext *avctx, void *data, int *got_output,
>          return 0;
>      }
>  
> -    new_extradata = av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA,
> -                                            &new_extradata_size);
> -    if (new_extradata && new_extradata_size > 0) {
> -        ret = hevc_decode_extradata(s, new_extradata, new_extradata_size, 0);
> +    sd = av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA, &sd_size);
> +    if (sd && sd_size > 0) {
> +        ret = hevc_decode_extradata(s, sd, sd_size, 0);
>          if (ret < 0)
>              return ret;
>      }
>  
> +    sd = av_packet_get_side_data(avpkt, AV_PKT_DATA_DOVI_CONF, &sd_size);
> +    if (sd && sd_size > 0) {
> +        if (s->dovi_cfg) {
> +            /* Reuse existing buffer */
> +            if ((ret = av_buffer_make_writable(&s->dovi_cfg)) < 0)
> +                return ret;
> +        } else {
> +            /* Allocate new buffer */
> +            AVDOVIDecoderConfigurationRecord *cfg;
> +            size_t cfg_size;
> +            cfg = av_dovi_alloc(&cfg_size);
> +            if (!cfg)
> +                return AVERROR(ENOMEM);
> +            s->dovi_cfg = av_buffer_create((uint8_t *) cfg, cfg_size, NULL, NULL, 0);
> +            if (!s->dovi_cfg) {
> +                av_free(cfg);
> +                return AVERROR(ENOMEM);
> +            }
> +        }
> +
> +        av_assert0(sd_size >= s->dovi_cfg->size);
> +        memcpy(s->dovi_cfg->data, sd, s->dovi_cfg->size);

dovi_cfg is only used for exactly one thing: To read its dv_profile in
ff_dovi_rpu_parse() later. This is quite a lot of effort to get this bit
of data. Why not add a ff_dovi_parse_config() that just copies the
needed field(s) from the user-supplied AVDOVIDecoderConfigurationRecord
to the DOVIContext instead?
(Furthermore, said config is explicitly intended to survive an unref as
happens in a flush, leading to a dangling pointer in case the user
unrefs dovi_cfg in flush. (Yes, the hevc decoder ensures that config is
always set correctly before every ff_dovi_rpu_parse() call, but it is
nevertheless nowhere stated that the profile is intended to be persistent.))

> +    }
> +
>      s->ref = NULL;
>      ret    = decode_nal_units(s, avpkt->data, avpkt->size);
>      if (ret < 0)
> @@ -3553,6 +3588,8 @@ static av_cold int hevc_decode_free(AVCodecContext *avctx)
>  
>      pic_arrays_free(s);
>  
> +    ff_dovi_ctx_unref(&s->dovi_ctx);
> +    av_buffer_unref(&s->dovi_cfg);
>      av_buffer_unref(&s->rpu_buf);
>  
>      av_freep(&s->md5_ctx);
> @@ -3637,6 +3674,7 @@ static av_cold int hevc_init_context(AVCodecContext *avctx)
>  
>      ff_bswapdsp_init(&s->bdsp);
>  
> +    s->dovi_ctx.logctx = avctx;
>      s->context_initialized = 1;
>      s->eos = 0;
>  
> @@ -3745,6 +3783,14 @@ static int hevc_update_thread_context(AVCodecContext *dst,
>      if (ret < 0)
>          return ret;
>  
> +    ret = av_buffer_replace(&s->dovi_cfg, s0->dovi_cfg);
> +    if (ret < 0)
> +        return ret;
> +
> +    ret = ff_dovi_ctx_replace(&s->dovi_ctx, &s0->dovi_ctx);
> +    if (ret < 0)
> +        return ret;
> +
>      s->sei.frame_packing        = s0->sei.frame_packing;
>      s->sei.display_orientation  = s0->sei.display_orientation;
>      s->sei.mastering_display    = s0->sei.mastering_display;
> @@ -3801,6 +3847,7 @@ static void hevc_decode_flush(AVCodecContext *avctx)
>      HEVCContext *s = avctx->priv_data;
>      ff_hevc_flush_dpb(s);
>      ff_hevc_reset_sei(&s->sei);
> +    ff_dovi_ctx_unref(&s->dovi_ctx);
>      av_buffer_unref(&s->rpu_buf);
>      s->max_ra = INT_MAX;
>      s->eos = 1;
> diff --git a/libavcodec/hevcdec.h b/libavcodec/hevcdec.h
> index 870ff178d4..c8dde6fd17 100644
> --- a/libavcodec/hevcdec.h
> +++ b/libavcodec/hevcdec.h
> @@ -32,6 +32,7 @@
>  #include "avcodec.h"
>  #include "bswapdsp.h"
>  #include "cabac.h"
> +#include "dovi_rpu.h"
>  #include "get_bits.h"
>  #include "hevcpred.h"
>  #include "h2645_parse.h"
> @@ -574,6 +575,8 @@ typedef struct HEVCContext {
>      int nuh_layer_id;
>  
>      AVBufferRef *rpu_buf;       ///< 0 or 1 Dolby Vision RPUs.
> +    AVBufferRef *dovi_cfg;      ///< contains AVDOVIDecoderConfigurationRecord
> +    DOVIContext dovi_ctx;       ///< Dolby Vision decoding context
>  } HEVCContext;
>  
>  /**


More information about the ffmpeg-devel mailing list