From 4574dd5dc6ff75b1fc693afceec59fbcd51ccd4c Mon Sep 17 00:00:00 2001 From: Philip Langdale Date: Tue, 29 Nov 2022 11:15:16 -0800 Subject: ffmpeg: update to handle deprecation of `av_init_packet` This has been a long standing annoyance - ffmpeg is removing sizeof(AVPacket) from the API which means you cannot stack-allocate AVPacket anymore. However, that is something we take advantage of because we use short-lived AVPackets to bridge from native mpv packets in our main decoding paths. We don't think that switching these to `av_packet_alloc` is desirable, given the cost of heap allocation, so this change takes a different approach - allocating a single packet in the relevant context and reusing it over and over. That's fairly straight-forward, with the main caveat being that re-initialising the packet is unintuitive. There is no function that does exactly what we need (what `av_init_packet` did). The closest is `av_packet_unref`, which additionally frees buffers and side-data. However, we don't copy those things - we just assign them in from our own packet, so we have to explicitly clear the pointers before calling `av_packet_unref`. But at least we can make a wrapper function for that. The weirdest part of the change is the handling of the vtt subtitle conversion. This requires two packets, so I had to pre-allocate two in the context struct. That sounds excessive, but if allocating the primary packet is too expensive, then allocating the secondary one for vtt subtitles must also be too expensive. This change is not conditional as heap allocated AVPackets were available for years and years before the deprecation. --- audio/decode/ad_lavc.c | 8 +++++--- audio/decode/ad_spdif.c | 15 ++++++++++----- common/av_common.c | 24 +++++++++++++++++++++++- common/av_common.h | 1 + common/recorder.c | 16 +++++++++------- sub/lavc_conv.c | 28 +++++++++++++++++++--------- sub/sd_lavc.c | 11 ++++++++--- video/decode/vd_lavc.c | 16 ++++++++++------ 8 files changed, 85 insertions(+), 34 deletions(-) diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c index 6ae7d80bf5..cdb86c3507 100644 --- a/audio/decode/ad_lavc.c +++ b/audio/decode/ad_lavc.c @@ -46,6 +46,7 @@ struct priv { AVCodecContext *avctx; AVFrame *avframe; + AVPacket *avpkt; struct mp_chmap force_channel_map; uint32_t skip_samples, trim_samples; bool preroll_done; @@ -104,6 +105,7 @@ static bool init(struct mp_filter *da, struct mp_codec_params *codec, lavc_context = avcodec_alloc_context3(lavc_codec); ctx->avctx = lavc_context; ctx->avframe = av_frame_alloc(); + ctx->avpkt = av_packet_alloc(); lavc_context->codec_type = AVMEDIA_TYPE_AUDIO; lavc_context->codec_id = lavc_codec->id; lavc_context->pkt_timebase = ctx->codec_timebase; @@ -160,6 +162,7 @@ static void destroy(struct mp_filter *da) avcodec_free_context(&ctx->avctx); av_frame_free(&ctx->avframe); + mp_free_av_packet(&ctx->avpkt); } static void reset(struct mp_filter *da) @@ -185,10 +188,9 @@ static int send_packet(struct mp_filter *da, struct demux_packet *mpkt) if (mpkt && priv->next_pts == MP_NOPTS_VALUE) priv->next_pts = mpkt->pts; - AVPacket pkt; - mp_set_av_packet(&pkt, mpkt, &priv->codec_timebase); + mp_set_av_packet(priv->avpkt, mpkt, &priv->codec_timebase); - int ret = avcodec_send_packet(avctx, mpkt ? &pkt : NULL); + int ret = avcodec_send_packet(avctx, mpkt ? priv->avpkt : NULL); if (ret < 0) MP_ERR(da, "Error decoding audio.\n"); return ret; diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c index 520803c4b0..3b799660c1 100644 --- a/audio/decode/ad_spdif.c +++ b/audio/decode/ad_spdif.c @@ -41,6 +41,7 @@ struct spdifContext { struct mp_log *log; enum AVCodecID codec_id; AVFormatContext *lavf_ctx; + AVPacket *avpkt; int out_buffer_len; uint8_t out_buffer[OUTBUF_SIZE]; bool need_close; @@ -82,6 +83,7 @@ static void destroy(struct mp_filter *da) avformat_free_context(lavf_ctx); spdif_ctx->lavf_ctx = NULL; } + mp_free_av_packet(&spdif_ctx->avpkt); } static void determine_codec_params(struct mp_filter *da, AVPacket *pkt, @@ -295,15 +297,14 @@ static void process(struct mp_filter *da) struct mp_aframe *out = NULL; double pts = mpkt->pts; - AVPacket pkt; - mp_set_av_packet(&pkt, mpkt, NULL); - pkt.pts = pkt.dts = 0; + mp_set_av_packet(spdif_ctx->avpkt, mpkt, NULL); + spdif_ctx->avpkt->pts = spdif_ctx->avpkt->dts = 0; if (!spdif_ctx->lavf_ctx) { - if (init_filter(da, &pkt) < 0) + if (init_filter(da, spdif_ctx->avpkt) < 0) goto done; } spdif_ctx->out_buffer_len = 0; - int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt); + int ret = av_write_frame(spdif_ctx->lavf_ctx, spdif_ctx->avpkt); avio_flush(spdif_ctx->lavf_ctx->pb); if (ret < 0) { MP_ERR(da, "spdif mux error: '%s'\n", mp_strerror(AVUNERROR(ret))); @@ -424,6 +425,10 @@ static struct mp_decoder *create(struct mp_filter *parent, talloc_free(da); return NULL; } + + spdif_ctx->avpkt = av_packet_alloc(); + MP_HANDLE_OOM(spdif_ctx->avpkt); + return &spdif_ctx->public; } diff --git a/common/av_common.c b/common/av_common.c index 8b5a3970d0..db31988c1b 100644 --- a/common/av_common.c +++ b/common/av_common.c @@ -196,7 +196,11 @@ double mp_pts_from_av(int64_t av_pts, AVRational *tb) // Set duration field only if tb is set. void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb) { - av_init_packet(dst); + dst->side_data = NULL; + dst->side_data_elems = 0; + dst->buf = NULL; + av_packet_unref(dst); + dst->data = mpkt ? mpkt->buffer : NULL; dst->size = mpkt ? mpkt->len : 0; /* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info @@ -394,3 +398,21 @@ int mp_set_avopts_pos(struct mp_log *log, void *avobj, void *posargs, char **kv) } return success; } + +/** + * Must be used to free an AVPacket that was used with mp_set_av_packet(). + * + * We have a particular pattern where we "borrow" buffers and set them + * into an AVPacket to pass data to ffmpeg without extra copies. + * This applies to buf and side_data, so this function clears them before + * freeing. + */ +void mp_free_av_packet(AVPacket **pkt) +{ + if (*pkt) { + (*pkt)->side_data = NULL; + (*pkt)->side_data_elems = 0; + (*pkt)->buf = NULL; + } + av_packet_free(pkt); +} diff --git a/common/av_common.h b/common/av_common.h index 1b3e468884..dd5e88e003 100644 --- a/common/av_common.h +++ b/common/av_common.h @@ -50,5 +50,6 @@ void mp_set_avdict(struct AVDictionary **dict, char **kv); void mp_avdict_print_unset(struct mp_log *log, int msgl, struct AVDictionary *d); int mp_set_avopts(struct mp_log *log, void *avobj, char **kv); int mp_set_avopts_pos(struct mp_log *log, void *avobj, void *posargs, char **kv); +void mp_free_av_packet(AVPacket **pkt); #endif diff --git a/common/recorder.c b/common/recorder.c index 0cbc81d51c..cee837ba24 100644 --- a/common/recorder.c +++ b/common/recorder.c @@ -64,6 +64,7 @@ struct mp_recorder_sink { struct mp_recorder *owner; struct sh_stream *sh; AVStream *av_stream; + AVPacket *avpkt; double max_out_pts; bool discont; bool proper_eof; @@ -82,10 +83,11 @@ static int add_stream(struct mp_recorder *priv, struct sh_stream *sh) .owner = priv, .sh = sh, .av_stream = avformat_new_stream(priv->mux, NULL), + .avpkt = av_packet_alloc(), .max_out_pts = MP_NOPTS_VALUE, }; - if (!rst->av_stream) + if (!rst->av_stream || !rst->avpkt) return -1; AVCodecParameters *avp = mp_codec_params_to_av(sh->codec); @@ -237,15 +239,14 @@ static void mux_packet(struct mp_recorder_sink *rst, rst->max_out_pts = MP_PTS_MAX(rst->max_out_pts, pkt->pts); - AVPacket avpkt; - mp_set_av_packet(&avpkt, &mpkt, &rst->av_stream->time_base); + mp_set_av_packet(rst->avpkt, &mpkt, &rst->av_stream->time_base); - avpkt.stream_index = rst->av_stream->index; + rst->avpkt->stream_index = rst->av_stream->index; - if (avpkt.duration < 0 && rst->sh->type != STREAM_SUB) - avpkt.duration = 0; + if (rst->avpkt->duration < 0 && rst->sh->type != STREAM_SUB) + rst->avpkt->duration = 0; - AVPacket *new_packet = av_packet_clone(&avpkt); + AVPacket *new_packet = av_packet_clone(rst->avpkt); if (!new_packet) { MP_ERR(priv, "Failed to allocate packet.\n"); return; @@ -319,6 +320,7 @@ void mp_recorder_destroy(struct mp_recorder *priv) for (int n = 0; n < priv->num_streams; n++) { struct mp_recorder_sink *rst = priv->streams[n]; mux_packets(rst); + mp_free_av_packet(&rst->avpkt); } if (av_write_trailer(priv->mux) < 0) diff --git a/sub/lavc_conv.c b/sub/lavc_conv.c index d9f628492c..8e26c41237 100644 --- a/sub/lavc_conv.c +++ b/sub/lavc_conv.c @@ -34,6 +34,8 @@ struct lavc_conv { struct mp_log *log; AVCodecContext *avctx; + AVPacket *avpkt; + AVPacket *avpkt_vtt; char *codec; char *extradata; AVSubtitle cur; @@ -84,6 +86,11 @@ struct lavc_conv *lavc_conv_create(struct mp_log *log, const char *codec_name, if (mp_lavc_set_extradata(avctx, extradata, extradata_len) < 0) goto error; + priv->avpkt = av_packet_alloc(); + priv->avpkt_vtt = av_packet_alloc(); + if (!priv->avpkt || !priv->avpkt_vtt) + goto error; + #if LIBAVCODEC_VERSION_MAJOR < 59 av_dict_set(&opts, "sub_text_format", "ass", 0); #endif @@ -107,6 +114,8 @@ struct lavc_conv *lavc_conv_create(struct mp_log *log, const char *codec_name, MP_FATAL(priv, "Could not open libavcodec subtitle converter\n"); av_dict_free(&opts); av_free(avctx); + mp_free_av_packet(&priv->avpkt); + mp_free_av_packet(&priv->avpkt_vtt); talloc_free(priv); return NULL; } @@ -224,26 +233,25 @@ char **lavc_conv_decode(struct lavc_conv *priv, struct demux_packet *packet, double *sub_pts, double *sub_duration) { AVCodecContext *avctx = priv->avctx; - AVPacket pkt; - AVPacket parsed_pkt = {0}; + AVPacket *curr_pkt = priv->avpkt; int ret, got_sub; int num_cur = 0; avsubtitle_free(&priv->cur); - mp_set_av_packet(&pkt, packet, &avctx->time_base); - if (pkt.pts < 0) - pkt.pts = 0; + mp_set_av_packet(priv->avpkt, packet, &avctx->time_base); + if (priv->avpkt->pts < 0) + priv->avpkt->pts = 0; if (strcmp(priv->codec, "webvtt-webm") == 0) { - if (parse_webvtt(&pkt, &parsed_pkt) < 0) { + if (parse_webvtt(priv->avpkt, priv->avpkt_vtt) < 0) { MP_ERR(priv, "Error parsing subtitle\n"); goto done; } - pkt = parsed_pkt; + curr_pkt = priv->avpkt_vtt; } - ret = avcodec_decode_subtitle2(avctx, &priv->cur, &got_sub, &pkt); + ret = avcodec_decode_subtitle2(avctx, &priv->cur, &got_sub, curr_pkt); if (ret < 0) { MP_ERR(priv, "Error decoding subtitle\n"); } else if (got_sub) { @@ -266,7 +274,7 @@ char **lavc_conv_decode(struct lavc_conv *priv, struct demux_packet *packet, } done: - av_packet_unref(&parsed_pkt); + av_packet_unref(priv->avpkt_vtt); MP_TARRAY_APPEND(priv, priv->cur_list, num_cur, NULL); return priv->cur_list; } @@ -280,5 +288,7 @@ void lavc_conv_uninit(struct lavc_conv *priv) { avsubtitle_free(&priv->cur); avcodec_free_context(&priv->avctx); + mp_free_av_packet(&priv->avpkt); + mp_free_av_packet(&priv->avpkt_vtt); talloc_free(priv); } diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index ce0ef11a0a..7b0d0dfe38 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -59,6 +59,7 @@ struct seekpoint { struct sd_lavc_priv { AVCodecContext *avctx; + AVPacket *avpkt; AVRational pkt_timebase; struct sub subs[MAX_QUEUE]; // most recent event first struct sub_bitmap *outbitmaps; @@ -97,6 +98,9 @@ static int init(struct sd *sd) ctx = avcodec_alloc_context3(sub_codec); if (!ctx) goto error; + priv->avpkt = av_packet_alloc(); + if (!priv->avpkt) + goto error; mp_lavc_set_extradata(ctx, sd->codec->extradata, sd->codec->extradata_size); priv->pkt_timebase = mp_get_codec_timebase(sd->codec); ctx->pkt_timebase = priv->pkt_timebase; @@ -112,6 +116,7 @@ static int init(struct sd *sd) error: MP_FATAL(sd, "Could not open libavcodec subtitle decoder\n"); avcodec_free_context(&ctx); + mp_free_av_packet(&priv->avpkt); talloc_free(priv); return -1; } @@ -298,7 +303,6 @@ static void decode(struct sd *sd, struct demux_packet *packet) double endpts = MP_NOPTS_VALUE; double duration = packet->duration; AVSubtitle sub; - AVPacket pkt; // libavformat sets duration==0, even if the duration is unknown. Some files // also have actually subtitle packets with duration explicitly set to 0 @@ -311,7 +315,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) if (pts == MP_NOPTS_VALUE) MP_WARN(sd, "Subtitle with unknown start time.\n"); - mp_set_av_packet(&pkt, packet, &priv->pkt_timebase); + mp_set_av_packet(priv->avpkt, packet, &priv->pkt_timebase); if (ctx->codec_id == AV_CODEC_ID_DVB_TELETEXT) { char page[4]; @@ -320,7 +324,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) } int got_sub; - int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt); + int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, priv->avpkt); if (res < 0 || !got_sub) return; @@ -588,6 +592,7 @@ static void uninit(struct sd *sd) for (int n = 0; n < MAX_QUEUE; n++) clear_sub(&priv->subs[n]); avcodec_free_context(&priv->avctx); + mp_free_av_packet(&priv->avpkt); talloc_free(priv); } diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c index c65d9d708a..5e8bf40a66 100644 --- a/video/decode/vd_lavc.c +++ b/video/decode/vd_lavc.c @@ -173,6 +173,7 @@ typedef struct lavc_ctx { struct mp_codec_params *codec; AVCodecContext *avctx; AVFrame *pic; + AVPacket *avpkt; bool use_hwdec; struct hwdec_info hwdec; // valid only if use_hwdec==true AVRational codec_timebase; @@ -651,6 +652,10 @@ static void init_avctx(struct mp_filter *vd) if (!ctx->pic) goto error; + ctx->avpkt = av_packet_alloc(); + if (!ctx->avpkt) + goto error; + if (ctx->use_hwdec) { avctx->opaque = vd; avctx->thread_count = 1; @@ -752,9 +757,8 @@ static void init_avctx(struct mp_filter *vd) // x264 build number (encoded in a SEI element), needed to enable a // workaround for broken 4:4:4 streams produced by older x264 versions. if (lavc_codec->id == AV_CODEC_ID_H264 && c->first_packet) { - AVPacket avpkt; - mp_set_av_packet(&avpkt, c->first_packet, &ctx->codec_timebase); - avcodec_send_packet(avctx, &avpkt); + mp_set_av_packet(ctx->avpkt, c->first_packet, &ctx->codec_timebase); + avcodec_send_packet(avctx, ctx->avpkt); avcodec_receive_frame(avctx, ctx->pic); av_frame_unref(ctx->pic); avcodec_flush_buffers(ctx->avctx); @@ -802,6 +806,7 @@ static void uninit_avctx(struct mp_filter *vd) flush_all(vd); av_frame_free(&ctx->pic); + mp_free_av_packet(&ctx->avpkt); av_buffer_unref(&ctx->cached_hw_frames_ctx); avcodec_free_context(&ctx->avctx); @@ -1067,10 +1072,9 @@ static int send_packet(struct mp_filter *vd, struct demux_packet *pkt) if (avctx->skip_frame == AVDISCARD_ALL) return 0; - AVPacket avpkt; - mp_set_av_packet(&avpkt, pkt, &ctx->codec_timebase); + mp_set_av_packet(ctx->avpkt, pkt, &ctx->codec_timebase); - int ret = avcodec_send_packet(avctx, pkt ? &avpkt : NULL); + int ret = avcodec_send_packet(avctx, pkt ? ctx->avpkt : NULL); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return ret; -- cgit v1.2.3