[FFmpeg-devel] [PATCH] avcodec/libvpx: fix assembling vp9 packets with alpha channel

Vignesh Venkatasubramanian vigneshv at google.com
Wed Aug 24 01:27:47 EEST 2022


On Sun, Aug 21, 2022 at 10:21 AM James Almer <jamrial at gmail.com> wrote:
>
> There's no warranty that vpx_codec_encode() will generate a list with the same
> amount of packets for both the yuv planes encoder and the alpha plane encoder,
> so queueing packets based on what the main encoder returns will fail when the
> amount of packets in both lists differ.
> Queue all data packets for every vpx_codec_encode() call from both encoders
> before attempting to assemble output AVPackets out of them.
>
> Fixes ticket #9884
>

lgtm. thanks for fixing this!

> Signed-off-by: James Almer <jamrial at gmail.com>
> ---
>  libavcodec/libvpxenc.c | 83 ++++++++++++++++++++----------------------
>  1 file changed, 40 insertions(+), 43 deletions(-)
>
> diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c
> index 5b7c7735a1..e08df5fb96 100644
> --- a/libavcodec/libvpxenc.c
> +++ b/libavcodec/libvpxenc.c
> @@ -56,8 +56,6 @@
>  struct FrameListData {
>      void *buf;                       /**< compressed data buffer */
>      size_t sz;                       /**< length of compressed data */
> -    void *buf_alpha;
> -    size_t sz_alpha;
>      int64_t pts;                     /**< time stamp to show frame
>                                            (in timebase units) */
>      unsigned long duration;          /**< duration to show frame
> @@ -87,6 +85,7 @@ typedef struct VPxEncoderContext {
>      int have_sse; /**< true if we have pending sse[] */
>      uint64_t frame_number;
>      struct FrameListData *coded_frame_list;
> +    struct FrameListData *alpha_coded_frame_list;
>
>      int cpu_used;
>      int sharpness;
> @@ -311,8 +310,6 @@ static void coded_frame_add(void *list, struct FrameListData *cx_frame)
>  static av_cold void free_coded_frame(struct FrameListData *cx_frame)
>  {
>      av_freep(&cx_frame->buf);
> -    if (cx_frame->buf_alpha)
> -        av_freep(&cx_frame->buf_alpha);
>      av_freep(&cx_frame);
>  }
>
> @@ -446,6 +443,7 @@ static av_cold int vpx_free(AVCodecContext *avctx)
>      av_freep(&ctx->twopass_stats.buf);
>      av_freep(&avctx->stats_out);
>      free_frame_list(ctx->coded_frame_list);
> +    free_frame_list(ctx->alpha_coded_frame_list);
>      if (ctx->hdr10_plus_fifo)
>          free_hdr10_plus_fifo(&ctx->hdr10_plus_fifo);
>      return 0;
> @@ -1205,7 +1203,6 @@ static av_cold int vpx_init(AVCodecContext *avctx,
>
>  static inline void cx_pktcpy(struct FrameListData *dst,
>                               const struct vpx_codec_cx_pkt *src,
> -                             const struct vpx_codec_cx_pkt *src_alpha,
>                               VPxContext *ctx)
>  {
>      dst->pts      = src->data.frame.pts;
> @@ -1229,13 +1226,6 @@ static inline void cx_pktcpy(struct FrameListData *dst,
>      } else {
>          dst->frame_number = -1;   /* sanity marker */
>      }
> -    if (src_alpha) {
> -        dst->buf_alpha = src_alpha->data.frame.buf;
> -        dst->sz_alpha = src_alpha->data.frame.sz;
> -    } else {
> -        dst->buf_alpha = NULL;
> -        dst->sz_alpha = 0;
> -    }
>  }
>
>  /**
> @@ -1246,7 +1236,7 @@ static inline void cx_pktcpy(struct FrameListData *dst,
>   * @return a negative AVERROR on error
>   */
>  static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame,
> -                      AVPacket *pkt)
> +                      struct FrameListData *alpha_cx_frame, AVPacket *pkt)
>  {
>      VPxContext *ctx = avctx->priv_data;
>      int ret = ff_get_encode_buffer(avctx, pkt, cx_frame->sz, 0);
> @@ -1279,16 +1269,16 @@ static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame,
>              avctx->error[i] += cx_frame->sse[i + 1];
>          cx_frame->have_sse = 0;
>      }
> -    if (cx_frame->sz_alpha > 0) {
> +    if (alpha_cx_frame) {
>          side_data = av_packet_new_side_data(pkt,
>                                              AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
> -                                            cx_frame->sz_alpha + 8);
> +                                            alpha_cx_frame->sz + 8);
>          if (!side_data) {
>              av_packet_unref(pkt);
>              return AVERROR(ENOMEM);
>          }
>          AV_WB64(side_data, 1);
> -        memcpy(side_data + 8, cx_frame->buf_alpha, cx_frame->sz_alpha);
> +        memcpy(side_data + 8, alpha_cx_frame->buf, alpha_cx_frame->sz);
>      }
>      if (cx_frame->frame_number != -1) {
>          if (ctx->hdr10_plus_fifo) {
> @@ -1309,40 +1299,37 @@ static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame,
>   * @return AVERROR(EINVAL) on output size error
>   * @return AVERROR(ENOMEM) on coded frame queue data allocation error
>   */
> -static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out)
> +static int queue_frames(AVCodecContext *avctx, struct vpx_codec_ctx *encoder,
> +                        struct FrameListData **frame_list, AVPacket *pkt_out)
>  {
>      VPxContext *ctx = avctx->priv_data;
>      const struct vpx_codec_cx_pkt *pkt;
> -    const struct vpx_codec_cx_pkt *pkt_alpha = NULL;
>      const void *iter = NULL;
> -    const void *iter_alpha = NULL;
>      int size = 0;
>
> -    if (ctx->coded_frame_list) {
> -        struct FrameListData *cx_frame = ctx->coded_frame_list;
> +    if (!ctx->is_alpha && *frame_list) {
> +        struct FrameListData *cx_frame = *frame_list;
>          /* return the leading frame if we've already begun queueing */
> -        size = storeframe(avctx, cx_frame, pkt_out);
> +        size = storeframe(avctx, cx_frame, NULL, pkt_out);
>          if (size < 0)
>              return size;
> -        ctx->coded_frame_list = cx_frame->next;
> +        *frame_list = cx_frame->next;
>          free_coded_frame(cx_frame);
>      }
>
>      /* consume all available output from the encoder before returning. buffers
>         are only good through the next vpx_codec call */
> -    while ((pkt = vpx_codec_get_cx_data(&ctx->encoder, &iter)) &&
> -           (!ctx->is_alpha ||
> -            (pkt_alpha = vpx_codec_get_cx_data(&ctx->encoder_alpha, &iter_alpha)))) {
> +    while (pkt = vpx_codec_get_cx_data(encoder, &iter)) {
>          switch (pkt->kind) {
>          case VPX_CODEC_CX_FRAME_PKT:
> -            if (!size) {
> +            if (!ctx->is_alpha && !size) {
>                  struct FrameListData cx_frame;
>
>                  /* avoid storing the frame when the list is empty and we haven't yet
>                     provided a frame for output */
>                  av_assert0(!ctx->coded_frame_list);
> -                cx_pktcpy(&cx_frame, pkt, pkt_alpha, ctx);
> -                size = storeframe(avctx, &cx_frame, pkt_out);
> +                cx_pktcpy(&cx_frame, pkt, ctx);
> +                size = storeframe(avctx, &cx_frame, NULL, pkt_out);
>                  if (size < 0)
>                      return size;
>              } else {
> @@ -1353,7 +1340,7 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out)
>                             "Frame queue element alloc failed\n");
>                      return AVERROR(ENOMEM);
>                  }
> -                cx_pktcpy(cx_frame, pkt, pkt_alpha, ctx);
> +                cx_pktcpy(cx_frame, pkt, ctx);
>                  cx_frame->buf = av_malloc(cx_frame->sz);
>
>                  if (!cx_frame->buf) {
> @@ -1364,23 +1351,14 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out)
>                      return AVERROR(ENOMEM);
>                  }
>                  memcpy(cx_frame->buf, pkt->data.frame.buf, pkt->data.frame.sz);
> -                if (ctx->is_alpha) {
> -                    cx_frame->buf_alpha = av_malloc(cx_frame->sz_alpha);
> -                    if (!cx_frame->buf_alpha) {
> -                        av_log(avctx, AV_LOG_ERROR,
> -                               "Data buffer alloc (%"SIZE_SPECIFIER" bytes) failed\n",
> -                               cx_frame->sz_alpha);
> -                        av_free(cx_frame);
> -                        return AVERROR(ENOMEM);
> -                    }
> -                    memcpy(cx_frame->buf_alpha, pkt_alpha->data.frame.buf, pkt_alpha->data.frame.sz);
> -                }
> -                coded_frame_add(&ctx->coded_frame_list, cx_frame);
> +                coded_frame_add(frame_list, cx_frame);
>              }
>              break;
>          case VPX_CODEC_STATS_PKT: {
>              struct vpx_fixed_buf *stats = &ctx->twopass_stats;
>              int err;
> +            if (!pkt_out)
> +                break;
>              if ((err = av_reallocp(&stats->buf,
>                                     stats->sz +
>                                     pkt->data.twopass_stats.sz)) < 0) {
> @@ -1394,6 +1372,8 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out)
>              break;
>          }
>          case VPX_CODEC_PSNR_PKT:
> +            if (!pkt_out)
> +                break;
>              av_assert0(!ctx->have_sse);
>              ctx->sse[0] = pkt->data.psnr.sse[0];
>              ctx->sse[1] = pkt->data.psnr.sse[1];
> @@ -1788,7 +1768,24 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt,
>          }
>      }
>
> -    coded_size = queue_frames(avctx, pkt);
> +    coded_size = queue_frames(avctx, &ctx->encoder, &ctx->coded_frame_list, pkt);
> +    if (ctx->is_alpha) {
> +        queue_frames(avctx, &ctx->encoder_alpha, &ctx->alpha_coded_frame_list, NULL);
> +
> +        if (ctx->coded_frame_list && ctx->alpha_coded_frame_list) {
> +            struct FrameListData *cx_frame = ctx->coded_frame_list;
> +            struct FrameListData *alpha_cx_frame = ctx->alpha_coded_frame_list;
> +            av_assert0(!coded_size);
> +            /* return the leading frame if we've already begun queueing */
> +            coded_size = storeframe(avctx, cx_frame, alpha_cx_frame, pkt);
> +            if (coded_size < 0)
> +                return coded_size;
> +            ctx->coded_frame_list = cx_frame->next;
> +            ctx->alpha_coded_frame_list = alpha_cx_frame->next;
> +            free_coded_frame(cx_frame);
> +            free_coded_frame(alpha_cx_frame);
> +        }
> +    }
>
>      if (!frame && avctx->flags & AV_CODEC_FLAG_PASS1) {
>          unsigned int b64_size = AV_BASE64_SIZE(ctx->twopass_stats.sz);
> --
> 2.37.2
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".



-- 
Vignesh


More information about the ffmpeg-devel mailing list