From 73d0cac16cc8faf47064160857c24fae485d6876 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
Date: Wed, 1 May 2024 13:56:56 +0300
Subject: [PATCH] libav: Update AVCodecContext lifetime to work properly with
 ffmpeg 7

avcodec_close() is deprecated and it's not supported anymore to re-open
a codec, so we only ever allocate the codec in set_format() now and
always free it after usage.

As part of this, also fix various memory leaks in related code paths.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6505>
---
 subprojects/gst-libav/ext/libav/gstav.c       | 12 ---
 subprojects/gst-libav/ext/libav/gstav.h       |  1 -
 subprojects/gst-libav/ext/libav/gstavauddec.c | 92 ++++++------------
 subprojects/gst-libav/ext/libav/gstavauddec.h |  1 -
 subprojects/gst-libav/ext/libav/gstavaudenc.c | 65 +++++--------
 subprojects/gst-libav/ext/libav/gstavaudenc.h |  1 -
 subprojects/gst-libav/ext/libav/gstavcfg.c    |  6 +-
 .../gst-libav/ext/libav/gstavdeinterlace.c    |  5 +-
 subprojects/gst-libav/ext/libav/gstavviddec.c | 93 +++++++++----------
 subprojects/gst-libav/ext/libav/gstavviddec.h |  1 -
 subprojects/gst-libav/ext/libav/gstavvidenc.c | 93 +++++++++----------
 subprojects/gst-libav/ext/libav/gstavvidenc.h |  1 -
 12 files changed, 143 insertions(+), 228 deletions(-)

diff --git a/subprojects/gst-libav/ext/libav/gstav.c b/subprojects/gst-libav/ext/libav/gstav.c
index 00fcc63881..0c9353f0c1 100644
--- a/subprojects/gst-libav/ext/libav/gstav.c
+++ b/subprojects/gst-libav/ext/libav/gstav.c
@@ -72,18 +72,6 @@ gst_ffmpeg_avcodec_open (AVCodecContext * avctx, const AVCodec * codec)
   return ret;
 }
 
-int
-gst_ffmpeg_avcodec_close (AVCodecContext * avctx)
-{
-  int ret;
-
-  g_mutex_lock (&gst_avcodec_mutex);
-  ret = avcodec_close (avctx);
-  g_mutex_unlock (&gst_avcodec_mutex);
-
-  return ret;
-}
-
 int
 gst_ffmpeg_av_find_stream_info (AVFormatContext * ic)
 {
diff --git a/subprojects/gst-libav/ext/libav/gstav.h b/subprojects/gst-libav/ext/libav/gstav.h
index a7fbb019fd..9cdb14503c 100644
--- a/subprojects/gst-libav/ext/libav/gstav.h
+++ b/subprojects/gst-libav/ext/libav/gstav.h
@@ -46,7 +46,6 @@ extern gboolean gst_ffmpegdeinterlace_register (GstPlugin * plugin);
 extern gboolean gst_ffmpegvidcmp_register (GstPlugin * plugin);
 
 int gst_ffmpeg_avcodec_open (AVCodecContext *avctx, const AVCodec *codec);
-int gst_ffmpeg_avcodec_close (AVCodecContext *avctx);
 int gst_ffmpeg_av_find_stream_info(AVFormatContext *ic);
 
 G_END_DECLS
diff --git a/subprojects/gst-libav/ext/libav/gstavauddec.c b/subprojects/gst-libav/ext/libav/gstavauddec.c
index 48c4be6b6a..2279e690ee 100644
--- a/subprojects/gst-libav/ext/libav/gstavauddec.c
+++ b/subprojects/gst-libav/ext/libav/gstavauddec.c
@@ -145,16 +145,6 @@ gst_ffmpegauddec_class_init (GstFFMpegAudDecClass * klass)
 static void
 gst_ffmpegauddec_init (GstFFMpegAudDec * ffmpegdec)
 {
-  GstFFMpegAudDecClass *klass =
-      (GstFFMpegAudDecClass *) G_OBJECT_GET_CLASS (ffmpegdec);
-
-  /* some ffmpeg data */
-  ffmpegdec->context = avcodec_alloc_context3 (klass->in_plugin);
-  ffmpegdec->context->opaque = ffmpegdec;
-  ffmpegdec->opened = FALSE;
-
-  ffmpegdec->frame = av_frame_alloc ();
-
   GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (ffmpegdec));
   gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
       (ffmpegdec), TRUE);
@@ -175,60 +165,24 @@ gst_ffmpegauddec_finalize (GObject * object)
 }
 
 /* With LOCK */
-static gboolean
-gst_ffmpegauddec_close (GstFFMpegAudDec * ffmpegdec, gboolean reset)
+static void
+gst_ffmpegauddec_close (GstFFMpegAudDec * ffmpegdec)
 {
-  GstFFMpegAudDecClass *oclass;
-
-  oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
-
   GST_LOG_OBJECT (ffmpegdec, "closing libav codec");
 
   gst_caps_replace (&ffmpegdec->last_caps, NULL);
-
-  gst_ffmpeg_avcodec_close (ffmpegdec->context);
-  ffmpegdec->opened = FALSE;
-
   av_freep (&ffmpegdec->context->extradata);
-
-  if (reset) {
-    avcodec_free_context (&ffmpegdec->context);
-    ffmpegdec->context = avcodec_alloc_context3 (oclass->in_plugin);
-    if (ffmpegdec->context == NULL) {
-      GST_DEBUG_OBJECT (ffmpegdec, "Failed to set context defaults");
-      return FALSE;
-    }
-    ffmpegdec->context->opaque = ffmpegdec;
-  }
-
-  return TRUE;
+  avcodec_free_context (&ffmpegdec->context);
 }
 
 static gboolean
 gst_ffmpegauddec_start (GstAudioDecoder * decoder)
 {
   GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) decoder;
-  GstFFMpegAudDecClass *oclass;
-
-  oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
 
   GST_OBJECT_LOCK (ffmpegdec);
+  ffmpegdec->frame = av_frame_alloc ();
   avcodec_free_context (&ffmpegdec->context);
-  ffmpegdec->context = avcodec_alloc_context3 (oclass->in_plugin);
-  if (ffmpegdec->context == NULL) {
-    GST_DEBUG_OBJECT (ffmpegdec, "Failed to set context defaults");
-    GST_OBJECT_UNLOCK (ffmpegdec);
-    return FALSE;
-  }
-  ffmpegdec->context->opaque = ffmpegdec;
-
-  /* FIXME: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1474 */
-  if ((oclass->in_plugin->capabilities & AV_CODEC_CAP_DELAY) != 0
-      && (oclass->in_plugin->id == AV_CODEC_ID_WMAV1
-          || oclass->in_plugin->id == AV_CODEC_ID_WMAV2)) {
-    ffmpegdec->context->flags2 |= AV_CODEC_FLAG2_SKIP_MANUAL;
-  }
-
   GST_OBJECT_UNLOCK (ffmpegdec);
 
   return TRUE;
@@ -240,8 +194,9 @@ gst_ffmpegauddec_stop (GstAudioDecoder * decoder)
   GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) decoder;
 
   GST_OBJECT_LOCK (ffmpegdec);
-  gst_ffmpegauddec_close (ffmpegdec, FALSE);
+  av_frame_free (&ffmpegdec->frame);
   g_free (ffmpegdec->padded);
+  gst_ffmpegauddec_close (ffmpegdec);
   ffmpegdec->padded = NULL;
   ffmpegdec->padded_size = 0;
   GST_OBJECT_UNLOCK (ffmpegdec);
@@ -262,8 +217,6 @@ gst_ffmpegauddec_open (GstFFMpegAudDec * ffmpegdec)
   if (gst_ffmpeg_avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0)
     goto could_not_open;
 
-  ffmpegdec->opened = TRUE;
-
   GST_LOG_OBJECT (ffmpegdec, "Opened libav codec %s, id %d",
       oclass->in_plugin->name, oclass->in_plugin->id);
 
@@ -274,7 +227,7 @@ gst_ffmpegauddec_open (GstFFMpegAudDec * ffmpegdec)
   /* ERRORS */
 could_not_open:
   {
-    gst_ffmpegauddec_close (ffmpegdec, TRUE);
+    gst_ffmpegauddec_close (ffmpegdec);
     GST_DEBUG_OBJECT (ffmpegdec, "avdec_%s: Failed to open libav codec",
         oclass->in_plugin->name);
     return FALSE;
@@ -321,14 +274,26 @@ gst_ffmpegauddec_set_format (GstAudioDecoder * decoder, GstCaps * caps)
   gst_caps_replace (&ffmpegdec->last_caps, caps);
 
   /* close old session */
-  if (ffmpegdec->opened) {
+  if (ffmpegdec->context) {
     GST_OBJECT_UNLOCK (ffmpegdec);
     gst_ffmpegauddec_drain (ffmpegdec, FALSE);
     GST_OBJECT_LOCK (ffmpegdec);
-    if (!gst_ffmpegauddec_close (ffmpegdec, TRUE)) {
-      GST_OBJECT_UNLOCK (ffmpegdec);
-      return FALSE;
-    }
+    gst_ffmpegauddec_close (ffmpegdec);
+  }
+
+  ffmpegdec->context = avcodec_alloc_context3 (oclass->in_plugin);
+  if (ffmpegdec->context == NULL) {
+    GST_DEBUG_OBJECT (ffmpegdec, "Failed to allocate context");
+    GST_OBJECT_UNLOCK (ffmpegdec);
+    return FALSE;
+  }
+  ffmpegdec->context->opaque = ffmpegdec;
+
+  /* FIXME: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1474 */
+  if ((oclass->in_plugin->capabilities & AV_CODEC_CAP_DELAY) != 0
+      && (oclass->in_plugin->id == AV_CODEC_ID_WMAV1
+          || oclass->in_plugin->id == AV_CODEC_ID_WMAV2)) {
+    ffmpegdec->context->flags2 |= AV_CODEC_FLAG2_SKIP_MANUAL;
   }
 
   /* get size and so */
@@ -586,7 +551,7 @@ gst_ffmpegauddec_frame (GstFFMpegAudDec * ffmpegdec, GstFlowReturn * ret,
   GstBuffer *outbuf = NULL;
   gboolean got_frame = FALSE;
 
-  if (G_UNLIKELY (ffmpegdec->context->codec == NULL))
+  if (G_UNLIKELY (!ffmpegdec->context))
     goto no_codec;
 
   *ret = GST_FLOW_OK;
@@ -630,6 +595,9 @@ gst_ffmpegauddec_drain (GstFFMpegAudDec * ffmpegdec, gboolean force)
   gboolean need_more_data = FALSE;
   gboolean got_frame;
 
+  if (!ffmpegdec->context)
+    return GST_FLOW_OK;
+
   if (avcodec_send_packet (ffmpegdec->context, NULL))
     goto send_packet_failed;
 
@@ -672,7 +640,7 @@ gst_ffmpegauddec_flush (GstAudioDecoder * decoder, gboolean hard)
 {
   GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) decoder;
 
-  if (ffmpegdec->opened) {
+  if (ffmpegdec->context) {
     avcodec_flush_buffers (ffmpegdec->context);
   }
 }
@@ -697,7 +665,7 @@ gst_ffmpegauddec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf)
 
   ffmpegdec = (GstFFMpegAudDec *) decoder;
 
-  if (G_UNLIKELY (!ffmpegdec->opened))
+  if (G_UNLIKELY (!ffmpegdec->context))
     goto not_negotiated;
 
   if (inbuf == NULL) {
diff --git a/subprojects/gst-libav/ext/libav/gstavauddec.h b/subprojects/gst-libav/ext/libav/gstavauddec.h
index d91de0d2b2..93466ad99f 100644
--- a/subprojects/gst-libav/ext/libav/gstavauddec.h
+++ b/subprojects/gst-libav/ext/libav/gstavauddec.h
@@ -34,7 +34,6 @@ struct _GstFFMpegAudDec
 
   /* decoding */
   AVCodecContext *context;
-  gboolean opened;
 
   AVFrame *frame;
 
diff --git a/subprojects/gst-libav/ext/libav/gstavaudenc.c b/subprojects/gst-libav/ext/libav/gstavaudenc.c
index 57f41fe617..6ff966d32c 100644
--- a/subprojects/gst-libav/ext/libav/gstavaudenc.c
+++ b/subprojects/gst-libav/ext/libav/gstavaudenc.c
@@ -161,10 +161,7 @@ gst_ffmpegaudenc_init (GstFFMpegAudEnc * ffmpegaudenc)
   GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (ffmpegaudenc));
 
   /* ffmpeg objects */
-  ffmpegaudenc->context = avcodec_alloc_context3 (klass->in_plugin);
   ffmpegaudenc->refcontext = avcodec_alloc_context3 (klass->in_plugin);
-  ffmpegaudenc->opened = FALSE;
-  ffmpegaudenc->frame = av_frame_alloc ();
 
   gst_audio_encoder_set_drainable (GST_AUDIO_ENCODER (ffmpegaudenc), TRUE);
 }
@@ -186,18 +183,12 @@ static gboolean
 gst_ffmpegaudenc_start (GstAudioEncoder * encoder)
 {
   GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
-  GstFFMpegAudEncClass *oclass =
-      (GstFFMpegAudEncClass *) G_OBJECT_GET_CLASS (ffmpegaudenc);
-
-  ffmpegaudenc->opened = FALSE;
-  ffmpegaudenc->need_reopen = FALSE;
 
   avcodec_free_context (&ffmpegaudenc->context);
-  ffmpegaudenc->context = avcodec_alloc_context3 (oclass->in_plugin);
-  if (ffmpegaudenc->context == NULL) {
-    GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
-    return FALSE;
-  }
+  av_frame_free (&ffmpegaudenc->frame);
+  ffmpegaudenc->need_reopen = FALSE;
+
+  ffmpegaudenc->frame = av_frame_alloc ();
 
   return TRUE;
 }
@@ -208,8 +199,8 @@ gst_ffmpegaudenc_stop (GstAudioEncoder * encoder)
   GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
 
   /* close old session */
-  gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
-  ffmpegaudenc->opened = FALSE;
+  avcodec_free_context (&ffmpegaudenc->context);
+  av_frame_free (&ffmpegaudenc->frame);
   ffmpegaudenc->need_reopen = FALSE;
 
   return TRUE;
@@ -220,7 +211,7 @@ gst_ffmpegaudenc_flush (GstAudioEncoder * encoder)
 {
   GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
 
-  if (ffmpegaudenc->opened) {
+  if (ffmpegaudenc->context) {
     avcodec_flush_buffers (ffmpegaudenc->context);
   }
 }
@@ -239,14 +230,11 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
   ffmpegaudenc->need_reopen = FALSE;
 
   /* close old session */
-  if (ffmpegaudenc->opened) {
-    avcodec_free_context (&ffmpegaudenc->context);
-    ffmpegaudenc->opened = FALSE;
-    ffmpegaudenc->context = avcodec_alloc_context3 (oclass->in_plugin);
-    if (ffmpegaudenc->context == NULL) {
-      GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
-      return FALSE;
-    }
+  avcodec_free_context (&ffmpegaudenc->context);
+  ffmpegaudenc->context = avcodec_alloc_context3 (oclass->in_plugin);
+  if (ffmpegaudenc->context == NULL) {
+    GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
+    return FALSE;
   }
 
   gst_ffmpeg_cfg_fill_context (G_OBJECT (ffmpegaudenc), ffmpegaudenc->context);
@@ -298,12 +286,8 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
   /* open codec */
   if (gst_ffmpeg_avcodec_open (ffmpegaudenc->context, oclass->in_plugin) < 0) {
     gst_caps_unref (allowed_caps);
-    avcodec_free_context (&ffmpegaudenc->context);
     GST_DEBUG_OBJECT (ffmpegaudenc, "avenc_%s: Failed to open FFMPEG codec",
         oclass->in_plugin->name);
-    ffmpegaudenc->context = avcodec_alloc_context3 (oclass->in_plugin);
-    if (ffmpegaudenc->context == NULL)
-      GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
 
     if ((oclass->in_plugin->capabilities & AV_CODEC_CAP_EXPERIMENTAL) &&
         ffmpegaudenc->context->strict_std_compliance !=
@@ -315,6 +299,7 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
               "or of good quality. If you must use it anyway, set the "
               "compliance property to experimental"));
     }
+    avcodec_free_context (&ffmpegaudenc->context);
     return FALSE;
   }
 
@@ -326,9 +311,6 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
     gst_caps_unref (allowed_caps);
     avcodec_free_context (&ffmpegaudenc->context);
     GST_DEBUG ("Unsupported codec - no caps found");
-    ffmpegaudenc->context = avcodec_alloc_context3 (oclass->in_plugin);
-    if (ffmpegaudenc->context == NULL)
-      GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
     return FALSE;
   }
 
@@ -337,6 +319,7 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
   gst_caps_unref (other_caps);
   if (gst_caps_is_empty (icaps)) {
     gst_caps_unref (icaps);
+    avcodec_free_context (&ffmpegaudenc->context);
     return FALSE;
   }
   icaps = gst_caps_fixate (icaps);
@@ -345,9 +328,6 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
           icaps)) {
     avcodec_free_context (&ffmpegaudenc->context);
     gst_caps_unref (icaps);
-    ffmpegaudenc->context = avcodec_alloc_context3 (oclass->in_plugin);
-    if (ffmpegaudenc->context == NULL)
-      GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
     return FALSE;
   }
   gst_caps_unref (icaps);
@@ -385,8 +365,6 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
   }
 
   /* success! */
-  ffmpegaudenc->opened = TRUE;
-  ffmpegaudenc->need_reopen = FALSE;
 
   return TRUE;
 }
@@ -394,8 +372,7 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
 static void
 gst_ffmpegaudenc_free_avpacket (gpointer pkt)
 {
-  av_packet_unref ((AVPacket *) pkt);
-  g_free (pkt);
+  av_packet_free ((AVPacket **) & pkt);
 }
 
 typedef struct
@@ -596,8 +573,7 @@ gst_ffmpegaudenc_receive_packet (GstFFMpegAudEnc * ffmpegaudenc,
 
   ctx = ffmpegaudenc->context;
 
-  pkt = g_new0 (AVPacket, 1);
-
+  pkt = av_packet_alloc ();
   res = avcodec_receive_packet (ctx, pkt);
 
   if (res == 0) {
@@ -636,7 +612,7 @@ gst_ffmpegaudenc_receive_packet (GstFFMpegAudEnc * ffmpegaudenc,
     *got_packet = TRUE;
   } else {
     GST_LOG_OBJECT (ffmpegaudenc, "no output produced");
-    g_free (pkt);
+    av_packet_free (&pkt);
     ret = GST_FLOW_OK;
     *got_packet = FALSE;
   }
@@ -650,6 +626,9 @@ gst_ffmpegaudenc_drain (GstFFMpegAudEnc * ffmpegaudenc)
   GstFlowReturn ret = GST_FLOW_OK;
   gboolean got_packet;
 
+  if (!ffmpegaudenc->context)
+    return GST_FLOW_OK;
+
   ret = gst_ffmpegaudenc_send_frame (ffmpegaudenc, NULL);
 
   if (ret == GST_FLOW_OK) {
@@ -683,7 +662,7 @@ gst_ffmpegaudenc_handle_frame (GstAudioEncoder * encoder, GstBuffer * inbuf)
 
   ffmpegaudenc = (GstFFMpegAudEnc *) encoder;
 
-  if (G_UNLIKELY (!ffmpegaudenc->opened))
+  if (G_UNLIKELY (!ffmpegaudenc->context))
     goto not_negotiated;
 
   if (!inbuf)
@@ -752,7 +731,7 @@ gst_ffmpegaudenc_set_property (GObject * object,
 
   ffmpegaudenc = (GstFFMpegAudEnc *) (object);
 
-  if (ffmpegaudenc->opened) {
+  if (ffmpegaudenc->context) {
     GST_WARNING_OBJECT (ffmpegaudenc,
         "Can't change properties once encoder is setup !");
     return;
diff --git a/subprojects/gst-libav/ext/libav/gstavaudenc.h b/subprojects/gst-libav/ext/libav/gstavaudenc.h
index 3c94aef4e0..e21de8337d 100644
--- a/subprojects/gst-libav/ext/libav/gstavaudenc.h
+++ b/subprojects/gst-libav/ext/libav/gstavaudenc.h
@@ -38,7 +38,6 @@ struct _GstFFMpegAudEnc
 
   AVCodecContext *context;
   AVCodecContext *refcontext;
-  gboolean opened;
   gboolean need_reopen;
 
   AVFrame *frame;
diff --git a/subprojects/gst-libav/ext/libav/gstavcfg.c b/subprojects/gst-libav/ext/libav/gstavcfg.c
index bcc501c39c..6092b086c4 100644
--- a/subprojects/gst-libav/ext/libav/gstavcfg.c
+++ b/subprojects/gst-libav/ext/libav/gstavcfg.c
@@ -488,10 +488,8 @@ gst_ffmpeg_cfg_install_properties (GObjectClass * klass, AVCodec * in_plugin,
       install_opts ((GObjectClass *) klass, &ctx->av_class, prop_id, flags,
       " (Generic codec option, might have no effect)", generic_overrides);
 
-  if (ctx) {
-    gst_ffmpeg_avcodec_close (ctx);
-    av_free (ctx);
-  }
+  if (ctx)
+    avcodec_free_context (&ctx);
 }
 
 static gint
diff --git a/subprojects/gst-libav/ext/libav/gstavdeinterlace.c b/subprojects/gst-libav/ext/libav/gstavdeinterlace.c
index 2d46c50901..49dcdffb49 100644
--- a/subprojects/gst-libav/ext/libav/gstavdeinterlace.c
+++ b/subprojects/gst-libav/ext/libav/gstavdeinterlace.c
@@ -225,14 +225,13 @@ gst_ffmpegdeinterlace_sink_setcaps (GstPad * pad, GstObject * parent,
   ctx->pix_fmt = AV_PIX_FMT_NB;
   gst_ffmpeg_caps_with_codectype (AVMEDIA_TYPE_VIDEO, caps, ctx);
   if (ctx->pix_fmt == AV_PIX_FMT_NB) {
-    gst_ffmpeg_avcodec_close (ctx);
-    av_free (ctx);
+    avcodec_free_context (&ctx);
     return FALSE;
   }
 
   deinterlace->pixfmt = ctx->pix_fmt;
 
-  av_free (ctx);
+  avcodec_free_context (&ctx);
 
   deinterlace->to_size =
       av_image_get_buffer_size (deinterlace->pixfmt, deinterlace->width,
diff --git a/subprojects/gst-libav/ext/libav/gstavviddec.c b/subprojects/gst-libav/ext/libav/gstavviddec.c
index 576e96ee2a..f131e99c94 100644
--- a/subprojects/gst-libav/ext/libav/gstavviddec.c
+++ b/subprojects/gst-libav/ext/libav/gstavviddec.c
@@ -337,14 +337,7 @@ gst_ffmpegviddec_init (GstFFMpegVidDec * ffmpegdec)
 static void
 gst_ffmpegviddec_subinit (GstFFMpegVidDec * ffmpegdec)
 {
-  GstFFMpegVidDecClass *klass =
-      (GstFFMpegVidDecClass *) G_OBJECT_GET_CLASS (ffmpegdec);
-
   /* some ffmpeg data */
-  ffmpegdec->context = avcodec_alloc_context3 (klass->in_plugin);
-  ffmpegdec->context->opaque = ffmpegdec;
-  ffmpegdec->picture = av_frame_alloc ();
-  ffmpegdec->opened = FALSE;
   ffmpegdec->skip_frame = ffmpegdec->lowres = 0;
   ffmpegdec->direct_rendering = DEFAULT_DIRECT_RENDERING;
   ffmpegdec->max_threads = DEFAULT_MAX_THREADS;
@@ -365,6 +358,8 @@ gst_ffmpegviddec_finalize (GObject * object)
   GstFFMpegVidDec *ffmpegdec = GST_FFMPEGVIDDEC (object);
 
   av_frame_free (&ffmpegdec->picture);
+  if (ffmpegdec->context)
+    av_freep (&ffmpegdec->context->extradata);
   avcodec_free_context (&ffmpegdec->context);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -395,37 +390,23 @@ gst_ffmpegviddec_context_set_flags2 (AVCodecContext * context, guint flags,
 }
 
 /* with LOCK */
-static gboolean
-gst_ffmpegviddec_close (GstFFMpegVidDec * ffmpegdec, gboolean reset)
+static void
+gst_ffmpegviddec_close (GstFFMpegVidDec * ffmpegdec)
 {
-  GstFFMpegVidDecClass *oclass;
   guint i;
 
-  oclass = GST_FFMPEGVIDDEC_GET_CLASS (ffmpegdec);
-
   GST_LOG_OBJECT (ffmpegdec, "closing ffmpeg codec");
 
   gst_caps_replace (&ffmpegdec->last_caps, NULL);
 
-  gst_ffmpeg_avcodec_close (ffmpegdec->context);
-  ffmpegdec->opened = FALSE;
+  if (ffmpegdec->context)
+    av_freep (&ffmpegdec->context->extradata);
+  avcodec_free_context (&ffmpegdec->context);
 
   for (i = 0; i < G_N_ELEMENTS (ffmpegdec->stride); i++)
     ffmpegdec->stride[i] = -1;
 
   gst_buffer_replace (&ffmpegdec->palette, NULL);
-
-  av_freep (&ffmpegdec->context->extradata);
-  if (reset) {
-    avcodec_free_context (&ffmpegdec->context);
-    ffmpegdec->context = avcodec_alloc_context3 (oclass->in_plugin);
-    if (ffmpegdec->context == NULL) {
-      GST_DEBUG_OBJECT (ffmpegdec, "Failed to set context defaults");
-      return FALSE;
-    }
-    ffmpegdec->context->opaque = ffmpegdec;
-  }
-  return TRUE;
 }
 
 /* with LOCK */
@@ -443,8 +424,6 @@ gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec)
   for (i = 0; i < G_N_ELEMENTS (ffmpegdec->stride); i++)
     ffmpegdec->stride[i] = -1;
 
-  ffmpegdec->opened = TRUE;
-
   GST_LOG_OBJECT (ffmpegdec, "Opened libav codec %s, id %d",
       oclass->in_plugin->name, oclass->in_plugin->id);
 
@@ -460,7 +439,7 @@ gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec)
   /* ERRORS */
 could_not_open:
   {
-    gst_ffmpegviddec_close (ffmpegdec, TRUE);
+    gst_ffmpegviddec_close (ffmpegdec);
     GST_DEBUG_OBJECT (ffmpegdec, "avdec_%s: Failed to open libav codec",
         oclass->in_plugin->name);
     return FALSE;
@@ -537,14 +516,11 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder,
   }
 
   /* close old session */
-  if (ffmpegdec->opened) {
+  if (ffmpegdec->context) {
     GST_OBJECT_UNLOCK (ffmpegdec);
     gst_ffmpegviddec_finish (decoder);
     GST_OBJECT_LOCK (ffmpegdec);
-    if (!gst_ffmpegviddec_close (ffmpegdec, TRUE)) {
-      GST_OBJECT_UNLOCK (ffmpegdec);
-      return FALSE;
-    }
+    gst_ffmpegviddec_close (ffmpegdec);
     ffmpegdec->pic_pix_fmt = 0;
     ffmpegdec->pic_width = 0;
     ffmpegdec->pic_height = 0;
@@ -560,6 +536,14 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder,
     ffmpegdec->cur_multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
   }
 
+  ffmpegdec->context = avcodec_alloc_context3 (oclass->in_plugin);
+  if (ffmpegdec->context == NULL) {
+    GST_DEBUG_OBJECT (ffmpegdec, "Failed to allocate context");
+    GST_OBJECT_UNLOCK (ffmpegdec);
+    return FALSE;
+  }
+  ffmpegdec->context->opaque = ffmpegdec;
+
   gst_caps_replace (&ffmpegdec->last_caps, state->caps);
 
   /* set buffer functions */
@@ -718,12 +702,18 @@ done:
 open_failed:
   {
     GST_DEBUG_OBJECT (ffmpegdec, "Failed to open");
+    if (ffmpegdec->context)
+      av_freep (&ffmpegdec->context->extradata);
+    avcodec_free_context (&ffmpegdec->context);
     goto done;
   }
 nal_only_slice:
   {
     GST_ERROR_OBJECT (ffmpegdec,
         "Can't do NAL aligned H.264 with frame threading.");
+    if (ffmpegdec->context)
+      av_freep (&ffmpegdec->context->extradata);
+    avcodec_free_context (&ffmpegdec->context);
     goto done;
   }
 }
@@ -1848,7 +1838,7 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
   GstFFMpegVidDecVideoFrame *out_dframe;
   GstBufferPool *pool;
 
-  if (G_UNLIKELY (ffmpegdec->context->codec == NULL))
+  if (G_UNLIKELY (!ffmpegdec->context))
     goto no_codec;
 
 #if LIBAVCODEC_VERSION_MAJOR >= 60
@@ -2141,7 +2131,7 @@ gst_ffmpegviddec_drain (GstVideoDecoder * decoder)
   GstFlowReturn ret = GST_FLOW_OK;
   gboolean got_frame = FALSE;
 
-  if (!ffmpegdec->opened)
+  if (!ffmpegdec->context)
     return GST_FLOW_OK;
 
   GST_VIDEO_DECODER_STREAM_UNLOCK (ffmpegdec);
@@ -2187,6 +2177,12 @@ gst_ffmpegviddec_handle_frame (GstVideoDecoder * decoder,
   GstFlowReturn ret = GST_FLOW_OK;
   AVPacket *packet;
 
+  if (G_UNLIKELY (!ffmpegdec->context)) {
+    gst_video_codec_frame_unref (frame);
+    GST_ERROR_OBJECT (ffmpegdec, "no codec context");
+    return GST_FLOW_NOT_NEGOTIATED;
+  }
+
   GST_LOG_OBJECT (ffmpegdec,
       "Received new data of size %" G_GSIZE_FORMAT ", dts %" GST_TIME_FORMAT
       ", pts:%" GST_TIME_FORMAT ", dur:%" GST_TIME_FORMAT,
@@ -2196,6 +2192,7 @@ gst_ffmpegviddec_handle_frame (GstVideoDecoder * decoder,
   if (!gst_buffer_map (frame->input_buffer, &minfo, GST_MAP_READ)) {
     GST_ELEMENT_ERROR (ffmpegdec, STREAM, DECODE, ("Decoding problem"),
         ("Failed to map buffer for reading"));
+    gst_video_codec_frame_unref (frame);
     return GST_FLOW_ERROR;
   }
 
@@ -2304,19 +2301,13 @@ static gboolean
 gst_ffmpegviddec_start (GstVideoDecoder * decoder)
 {
   GstFFMpegVidDec *ffmpegdec = GST_FFMPEGVIDDEC (decoder);
-  GstFFMpegVidDecClass *oclass;
-
-  oclass = GST_FFMPEGVIDDEC_GET_CLASS (ffmpegdec);
 
   GST_OBJECT_LOCK (ffmpegdec);
+  av_frame_free (&ffmpegdec->picture);
+  if (ffmpegdec->context)
+    av_freep (&ffmpegdec->context->extradata);
   avcodec_free_context (&ffmpegdec->context);
-  ffmpegdec->context = avcodec_alloc_context3 (oclass->in_plugin);
-  if (ffmpegdec->context == NULL) {
-    GST_DEBUG_OBJECT (ffmpegdec, "Failed to set context defaults");
-    GST_OBJECT_UNLOCK (ffmpegdec);
-    return FALSE;
-  }
-  ffmpegdec->context->opaque = ffmpegdec;
+  ffmpegdec->picture = av_frame_alloc ();
   GST_OBJECT_UNLOCK (ffmpegdec);
 
   return TRUE;
@@ -2328,7 +2319,8 @@ gst_ffmpegviddec_stop (GstVideoDecoder * decoder)
   GstFFMpegVidDec *ffmpegdec = GST_FFMPEGVIDDEC (decoder);
 
   GST_OBJECT_LOCK (ffmpegdec);
-  gst_ffmpegviddec_close (ffmpegdec, FALSE);
+  av_frame_free (&ffmpegdec->picture);
+  gst_ffmpegviddec_close (ffmpegdec);
   GST_OBJECT_UNLOCK (ffmpegdec);
   g_free (ffmpegdec->padded);
   ffmpegdec->padded = NULL;
@@ -2382,7 +2374,7 @@ gst_ffmpegviddec_flush (GstVideoDecoder * decoder)
 {
   GstFFMpegVidDec *ffmpegdec = GST_FFMPEGVIDDEC (decoder);
 
-  if (ffmpegdec->opened) {
+  if (ffmpegdec->context) {
     GST_LOG_OBJECT (decoder, "flushing buffers");
     GST_VIDEO_DECODER_STREAM_UNLOCK (ffmpegdec);
     avcodec_flush_buffers (ffmpegdec->context);
@@ -2571,11 +2563,10 @@ gst_ffmpegviddec_set_property (GObject * object,
 
   switch (prop_id) {
     case PROP_LOWRES:
-      ffmpegdec->lowres = ffmpegdec->context->lowres = g_value_get_enum (value);
+      ffmpegdec->lowres = g_value_get_enum (value);
       break;
     case PROP_SKIPFRAME:
-      ffmpegdec->skip_frame = ffmpegdec->context->skip_frame =
-          g_value_get_enum (value);
+      ffmpegdec->skip_frame = g_value_get_enum (value);
       break;
     case PROP_DIRECT_RENDERING:
       ffmpegdec->direct_rendering = g_value_get_boolean (value);
diff --git a/subprojects/gst-libav/ext/libav/gstavviddec.h b/subprojects/gst-libav/ext/libav/gstavviddec.h
index 0f713de569..14d5a9aff3 100644
--- a/subprojects/gst-libav/ext/libav/gstavviddec.h
+++ b/subprojects/gst-libav/ext/libav/gstavviddec.h
@@ -54,7 +54,6 @@ struct _GstFFMpegVidDec
   GstVideoMultiviewMode picture_multiview_mode;
   GstVideoMultiviewFlags picture_multiview_flags;
   gint stride[AV_NUM_DATA_POINTERS];
-  gboolean opened;
 
   /* current output pictures */
   enum AVPixelFormat pic_pix_fmt;
diff --git a/subprojects/gst-libav/ext/libav/gstavvidenc.c b/subprojects/gst-libav/ext/libav/gstavvidenc.c
index 461263b4f6..78694c303f 100644
--- a/subprojects/gst-libav/ext/libav/gstavvidenc.c
+++ b/subprojects/gst-libav/ext/libav/gstavvidenc.c
@@ -208,10 +208,7 @@ gst_ffmpegvidenc_init (GstFFMpegVidEnc * ffmpegenc)
 
   GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_ENCODER_SINK_PAD (ffmpegenc));
 
-  ffmpegenc->context = avcodec_alloc_context3 (klass->in_plugin);
   ffmpegenc->refcontext = avcodec_alloc_context3 (klass->in_plugin);
-  ffmpegenc->picture = av_frame_alloc ();
-  ffmpegenc->opened = FALSE;
   ffmpegenc->file = NULL;
 }
 
@@ -222,10 +219,8 @@ gst_ffmpegvidenc_finalize (GObject * object)
 
   /* clean up remaining allocated data */
   av_frame_free (&ffmpegenc->picture);
-  gst_ffmpeg_avcodec_close (ffmpegenc->context);
-  gst_ffmpeg_avcodec_close (ffmpegenc->refcontext);
-  av_freep (&ffmpegenc->context);
-  av_freep (&ffmpegenc->refcontext);
+  avcodec_free_context (&ffmpegenc->context);
+  avcodec_free_context (&ffmpegenc->refcontext);
   g_free (ffmpegenc->filename);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -247,14 +242,11 @@ gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder,
   ffmpegenc->need_reopen = FALSE;
 
   /* close old session */
-  if (ffmpegenc->opened) {
-    avcodec_free_context (&ffmpegenc->context);
-    ffmpegenc->opened = FALSE;
-    ffmpegenc->context = avcodec_alloc_context3 (oclass->in_plugin);
-    if (ffmpegenc->context == NULL) {
-      GST_DEBUG_OBJECT (ffmpegenc, "Failed to set context defaults");
-      return FALSE;
-    }
+  avcodec_free_context (&ffmpegenc->context);
+  ffmpegenc->context = avcodec_alloc_context3 (oclass->in_plugin);
+  if (ffmpegenc->context == NULL) {
+    GST_DEBUG_OBJECT (ffmpegenc, "Failed to allocate context");
+    return FALSE;
   }
 
   /* additional avcodec settings */
@@ -402,7 +394,6 @@ gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder,
 
   /* success! */
   ffmpegenc->pts_offset = GST_CLOCK_TIME_NONE;
-  ffmpegenc->opened = TRUE;
 
   return TRUE;
 
@@ -412,6 +403,7 @@ open_file_err:
     GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, OPEN_WRITE,
         (("Could not open file \"%s\" for writing."), ffmpegenc->filename),
         GST_ERROR_SYSTEM);
+    avcodec_free_context (&ffmpegenc->context);
     return FALSE;
   }
 file_read_err:
@@ -419,6 +411,7 @@ file_read_err:
     GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, READ,
         (("Could not get contents of file \"%s\"."), ffmpegenc->filename),
         GST_ERROR_SYSTEM);
+    avcodec_free_context (&ffmpegenc->context);
     return FALSE;
   }
 
@@ -426,12 +419,12 @@ insane_timebase:
   {
     GST_ERROR_OBJECT (ffmpegenc, "Rejecting time base %d/%d",
         ffmpegenc->context->time_base.den, ffmpegenc->context->time_base.num);
-    goto cleanup_stats_in;
+    goto close_codec;
   }
 unsupported_codec:
   {
     GST_DEBUG ("Unsupported codec - no caps found");
-    goto cleanup_stats_in;
+    goto close_codec;
   }
 open_codec_fail:
   {
@@ -456,15 +449,13 @@ bad_input_fmt:
   }
 close_codec:
   {
+    if (ffmpegenc->context)
+      g_free (ffmpegenc->context->stats_in);
+    if (ffmpegenc->file) {
+      fclose (ffmpegenc->file);
+      ffmpegenc->file = NULL;
+    }
     avcodec_free_context (&ffmpegenc->context);
-    ffmpegenc->context = avcodec_alloc_context3 (oclass->in_plugin);
-    if (ffmpegenc->context == NULL)
-      GST_DEBUG_OBJECT (ffmpegenc, "Failed to set context defaults");
-    goto cleanup_stats_in;
-  }
-cleanup_stats_in:
-  {
-    g_free (ffmpegenc->context->stats_in);
     return FALSE;
   }
 }
@@ -483,8 +474,7 @@ gst_ffmpegvidenc_propose_allocation (GstVideoEncoder * encoder,
 static void
 gst_ffmpegvidenc_free_avpacket (gpointer pkt)
 {
-  av_packet_unref ((AVPacket *) pkt);
-  g_free (pkt);
+  av_packet_free ((AVPacket **) & pkt);
 }
 
 typedef struct
@@ -692,18 +682,18 @@ gst_ffmpegvidenc_receive_packet (GstFFMpegVidEnc * ffmpegenc,
 
   *got_packet = FALSE;
 
-  pkt = g_new0 (AVPacket, 1);
-
+  pkt = av_packet_alloc ();
   res = avcodec_receive_packet (ffmpegenc->context, pkt);
 
   if (res == AVERROR (EAGAIN)) {
-    g_free (pkt);
+    av_packet_free (&pkt);
     goto done;
   } else if (res == AVERROR_EOF) {
-    g_free (pkt);
+    av_packet_free (&pkt);
     ret = GST_FLOW_EOS;
     goto done;
   } else if (res < 0) {
+    av_packet_free (&pkt);
     ret = GST_FLOW_ERROR;
     goto done;
   }
@@ -767,7 +757,7 @@ gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder,
   GstFlowReturn ret;
   gboolean got_packet;
 
-  /* endoder was drained or flushed, and ffmpeg encoder doesn't support
+  /* encoder was drained or flushed, and ffmpeg encoder doesn't support
    * flushing. We need to re-open encoder then */
   if (ffmpegenc->need_reopen) {
     gboolean reopen_ret;
@@ -778,6 +768,7 @@ gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder,
     if (!ffmpegenc->input_state) {
       GST_ERROR_OBJECT (ffmpegenc,
           "Cannot re-open encoder without input state");
+      gst_video_codec_frame_unref (frame);
       return GST_FLOW_NOT_NEGOTIATED;
     }
 
@@ -787,6 +778,7 @@ gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder,
 
     if (!reopen_ret) {
       GST_ERROR_OBJECT (ffmpegenc, "Couldn't re-open encoder");
+      gst_video_codec_frame_unref (frame);
       return GST_FLOW_NOT_NEGOTIATED;
     }
   }
@@ -831,7 +823,7 @@ gst_ffmpegvidenc_flush_buffers (GstFFMpegVidEnc * ffmpegenc, gboolean send)
   GST_DEBUG_OBJECT (ffmpegenc, "flushing buffers with sending %d", send);
 
   /* no need to empty codec if there is none */
-  if (!ffmpegenc->opened)
+  if (!ffmpegenc->context)
     goto done;
 
   ret = gst_ffmpegvidenc_send_frame (ffmpegenc, NULL);
@@ -867,7 +859,7 @@ gst_ffmpegvidenc_set_property (GObject * object,
 
   ffmpegenc = (GstFFMpegVidEnc *) (object);
 
-  if (ffmpegenc->opened) {
+  if (ffmpegenc->context) {
     GST_WARNING_OBJECT (ffmpegenc,
         "Can't change properties once decoder is setup !");
     return;
@@ -921,7 +913,7 @@ gst_ffmpegvidenc_flush (GstVideoEncoder * encoder)
 {
   GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
 
-  if (ffmpegenc->opened) {
+  if (ffmpegenc->context) {
     avcodec_flush_buffers (ffmpegenc->context);
     ffmpegenc->pts_offset = GST_CLOCK_TIME_NONE;
   }
@@ -933,20 +925,19 @@ static gboolean
 gst_ffmpegvidenc_start (GstVideoEncoder * encoder)
 {
   GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
-  GstFFMpegVidEncClass *oclass =
-      (GstFFMpegVidEncClass *) G_OBJECT_GET_CLASS (ffmpegenc);
-
-  ffmpegenc->opened = FALSE;
-  ffmpegenc->need_reopen = FALSE;
 
   /* close old session */
-  avcodec_free_context (&ffmpegenc->context);
-  ffmpegenc->context = avcodec_alloc_context3 (oclass->in_plugin);
-  if (ffmpegenc->context == NULL) {
-    GST_DEBUG_OBJECT (ffmpegenc, "Failed to set context defaults");
-    return FALSE;
+  if (ffmpegenc->file) {
+    fclose (ffmpegenc->file);
+    ffmpegenc->file = NULL;
   }
+  if (ffmpegenc->context)
+    g_free (ffmpegenc->context->stats_in);
+  avcodec_free_context (&ffmpegenc->context);
+  av_frame_free (&ffmpegenc->picture);
+  ffmpegenc->need_reopen = FALSE;
 
+  ffmpegenc->picture = av_frame_alloc ();
   gst_video_encoder_set_min_pts (encoder, GST_SECOND * 60 * 60 * 1000);
 
   return TRUE;
@@ -958,8 +949,14 @@ gst_ffmpegvidenc_stop (GstVideoEncoder * encoder)
   GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
 
   gst_ffmpegvidenc_flush_buffers (ffmpegenc, FALSE);
-  gst_ffmpeg_avcodec_close (ffmpegenc->context);
-  ffmpegenc->opened = FALSE;
+  if (ffmpegenc->context)
+    g_free (ffmpegenc->context->stats_in);
+  if (ffmpegenc->file) {
+    fclose (ffmpegenc->file);
+    ffmpegenc->file = NULL;
+  }
+  avcodec_free_context (&ffmpegenc->context);
+  av_frame_free (&ffmpegenc->picture);
   ffmpegenc->need_reopen = FALSE;
 
   if (ffmpegenc->input_state) {
diff --git a/subprojects/gst-libav/ext/libav/gstavvidenc.h b/subprojects/gst-libav/ext/libav/gstavvidenc.h
index 340fb25204..1e73c9ac57 100644
--- a/subprojects/gst-libav/ext/libav/gstavvidenc.h
+++ b/subprojects/gst-libav/ext/libav/gstavvidenc.h
@@ -41,7 +41,6 @@ struct _GstFFMpegVidEnc
   AVCodecContext *context;
   AVFrame *picture;
   GstClockTime pts_offset;
-  gboolean opened;
   gboolean need_reopen;
   gboolean discont;
   guint pass;