From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "Jan Alexander Steffens (heftig)" Date: Thu, 30 May 2024 00:54:43 +0200 Subject: [PATCH] libav: Fix compatibility with ffmpeg 7 Squash of the following commits: - avdemux: Remove typefinder implementation - libav: Fix signature of avprotocol write function for ffmpeg 7 - libav: Update AVCodecContext lifetime to work properly with ffmpeg 7 - avdemux: Fix leak of demuxer input context in error cases - avmux: Reset input context to NULL after closing in the muxer - avvidenc: Only use 2 ticks per frame if encoding interlaced video - avvidenc: Set the DTS to 0 if it is negative, not the PTS - avviddec: Only use 2 ticks per frame if decoding interlaced video - avvidenc: Make sure to pass always increasing PTS to the encoder - typefind: Add typefinders for formats that were previously available via ffmpeg The latest commit was 39d2beac641315b8f53816d5f1fdcdfb5d14411c from 2024-05-01 18:05:35 +0300. Source: 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/gstavcodecmap.c | 24 +++ .../gst-libav/ext/libav/gstavdeinterlace.c | 5 +- subprojects/gst-libav/ext/libav/gstavdemux.c | 124 ++--------- subprojects/gst-libav/ext/libav/gstavmux.c | 1 + .../gst-libav/ext/libav/gstavprotocol.c | 4 + subprojects/gst-libav/ext/libav/gstavviddec.c | 111 +++++----- subprojects/gst-libav/ext/libav/gstavviddec.h | 1 - subprojects/gst-libav/ext/libav/gstavvidenc.c | 127 ++++++----- subprojects/gst-libav/ext/libav/gstavvidenc.h | 2 +- .../gst/typefind/gsttypefindfunctions.c | 203 ++++++++++++++++++ .../gst/typefind/gsttypefindfunctionsplugin.c | 10 + .../gst/typefind/gsttypefindfunctionsplugin.h | 10 + .../gst/typefind/gsttypefindfunctionsriff.c | 4 + .../typefind/gsttypefindfunctionsstartwith.c | 2 + 21 files changed, 457 insertions(+), 349 deletions(-) diff --git a/subprojects/gst-libav/ext/libav/gstav.c b/subprojects/gst-libav/ext/libav/gstav.c index 00fcc6388137..0c9353f0c157 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 a7fbb019fd55..9cdb14503c3a 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 48c4be6b6a14..2279e690ee5e 100644 --- a/subprojects/gst-libav/ext/libav/gstavauddec.c +++ b/subprojects/gst-libav/ext/libav/gstavauddec.c @@ -145,136 +145,89 @@ 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); gst_audio_decoder_set_drainable (GST_AUDIO_DECODER (ffmpegdec), TRUE); gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (ffmpegdec), TRUE); } static void gst_ffmpegauddec_finalize (GObject * object) { GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) object; av_frame_free (&ffmpegdec->frame); avcodec_free_context (&ffmpegdec->context); G_OBJECT_CLASS (parent_class)->finalize (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; } static gboolean 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); gst_audio_info_init (&ffmpegdec->info); gst_caps_replace (&ffmpegdec->last_caps, NULL); return TRUE; } /* with LOCK */ static gboolean gst_ffmpegauddec_open (GstFFMpegAudDec * ffmpegdec) { GstFFMpegAudDecClass *oclass; oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (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); gst_audio_info_init (&ffmpegdec->info); return TRUE; /* 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,32 +640,32 @@ gst_ffmpegauddec_flush (GstAudioDecoder * decoder, gboolean hard) { GstFFMpegAudDec *ffmpegdec = (GstFFMpegAudDec *) decoder; - if (ffmpegdec->opened) { + if (ffmpegdec->context) { avcodec_flush_buffers (ffmpegdec->context); } } static GstFlowReturn gst_ffmpegauddec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf) { GstFFMpegAudDec *ffmpegdec; GstFFMpegAudDecClass *oclass; guint8 *data; GstMapInfo map; gint size; gboolean got_any_frames = FALSE; gboolean got_frame; GstFlowReturn ret = GST_FLOW_OK; gboolean is_header; AVPacket packet; GstAudioClippingMeta *clipping_meta = NULL; guint32 num_clipped_samples = 0; gboolean fully_clipped = FALSE; gboolean need_more_data = FALSE; 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 d91de0d2b29e..93466ad99fb4 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 57f41fe617d0..6ff966d32cee 100644 --- a/subprojects/gst-libav/ext/libav/gstavaudenc.c +++ b/subprojects/gst-libav/ext/libav/gstavaudenc.c @@ -161,92 +161,80 @@ 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); } static void gst_ffmpegaudenc_finalize (GObject * object) { GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) object; /* clean up remaining allocated data */ av_frame_free (&ffmpegaudenc->frame); avcodec_free_context (&ffmpegaudenc->context); avcodec_free_context (&ffmpegaudenc->refcontext); G_OBJECT_CLASS (parent_class)->finalize (object); } 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; } static gboolean 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; } static void gst_ffmpegaudenc_flush (GstAudioEncoder * encoder) { GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder; - if (ffmpegaudenc->opened) { + if (ffmpegaudenc->context) { avcodec_flush_buffers (ffmpegaudenc->context); } } static gboolean gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info) { GstFFMpegAudEnc *ffmpegaudenc = (GstFFMpegAudEnc *) encoder; GstCaps *other_caps; GstCaps *allowed_caps; GstCaps *icaps; gsize frame_size; GstFFMpegAudEncClass *oclass = (GstFFMpegAudEncClass *) G_OBJECT_GET_CLASS (ffmpegaudenc); 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,56 +286,48 @@ 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 != FF_COMPLIANCE_EXPERIMENTAL) { GST_ELEMENT_ERROR (ffmpegaudenc, LIBRARY, SETTINGS, ("Codec is experimental, but settings don't allow encoders to " "produce output of experimental quality"), ("This codec may not create output that is conformant to the specs " "or of good quality. If you must use it anyway, set the " "compliance property to experimental")); } + avcodec_free_context (&ffmpegaudenc->context); return FALSE; } /* try to set this caps on the other side */ other_caps = gst_ffmpeg_codecid_to_caps (oclass->in_plugin->id, ffmpegaudenc->context, TRUE); if (!other_caps) { 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; } icaps = gst_caps_intersect (allowed_caps, other_caps); gst_caps_unref (allowed_caps); 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); if (!gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (ffmpegaudenc), 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,17 +365,14 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info) } /* success! */ - ffmpegaudenc->opened = TRUE; - ffmpegaudenc->need_reopen = FALSE; return TRUE; } 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,20 +612,23 @@ 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; } return ret; } static GstFlowReturn 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 3c94aef4e061..e21de8337d6b 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 bcc501c39cc8..6092b086c474 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/gstavcodecmap.c b/subprojects/gst-libav/ext/libav/gstavcodecmap.c index c9c0a0fa7730..3d5c19d67d64 100644 --- a/subprojects/gst-libav/ext/libav/gstavcodecmap.c +++ b/subprojects/gst-libav/ext/libav/gstavcodecmap.c @@ -3833,6 +3833,18 @@ gst_ffmpeg_formatid_to_caps (const gchar * format_name) caps = gst_caps_from_string ("audio/x-brstm"); } else if (!strcmp (format_name, "bfstm")) { caps = gst_caps_from_string ("audio/x-bfstm"); + } else if (!strcmp (format_name, "avs")) { + caps = gst_caps_from_string ("video/x-avs"); + } else if (!strcmp (format_name, "dsf")) { + caps = gst_caps_from_string ("audio/x-dsf"); + } else if (!strcmp (format_name, "ea")) { + caps = gst_caps_from_string ("video/x-ea"); + } else if (!strcmp (format_name, "film_cpk")) { + caps = gst_caps_from_string ("video/x-film-cpk"); + } else if (!strcmp (format_name, "xwma")) { + caps = gst_caps_from_string ("audio/x-xwma"); + } else if (!strcmp (format_name, "iff")) { + caps = gst_caps_from_string ("application/x-iff"); } else { gchar *name; @@ -4029,6 +4041,18 @@ gst_ffmpeg_formatid_get_codecids (const gchar * format_name, *video_codec_list = ivf_video_list; *audio_codec_list = ivf_audio_list; + } else if ((!strcmp (format_name, "film_cpk"))) { + static enum AVCodecID cpk_video_list[] = { + AV_CODEC_ID_CINEPAK, + AV_CODEC_ID_NONE + }; + static enum AVCodecID cpk_audio_list[] = { + AV_CODEC_ID_PCM_S16BE, + AV_CODEC_ID_NONE + }; + + *video_codec_list = cpk_video_list; + *audio_codec_list = cpk_audio_list; } else if ((plugin->audio_codec != AV_CODEC_ID_NONE) || (plugin->video_codec != AV_CODEC_ID_NONE)) { tmp_vlist[0] = plugin->video_codec; diff --git a/subprojects/gst-libav/ext/libav/gstavdeinterlace.c b/subprojects/gst-libav/ext/libav/gstavdeinterlace.c index 2d46c5090185..49dcdffb492f 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/gstavdemux.c b/subprojects/gst-libav/ext/libav/gstavdemux.c index ab6ac1b5c944..2c68d622f1a8 100644 --- a/subprojects/gst-libav/ext/libav/gstavdemux.c +++ b/subprojects/gst-libav/ext/libav/gstavdemux.c @@ -67,7 +67,6 @@ struct _GstFFMpegDemux guint group_id; AVFormatContext *context; - gboolean opened; GstFFStream *streams[MAX_STREAMS]; @@ -273,7 +272,6 @@ gst_ffmpegdemux_init (GstFFMpegDemux * demux) demux->have_group_id = FALSE; demux->group_id = G_MAXUINT; - demux->opened = FALSE; demux->context = NULL; for (n = 0; n < MAX_STREAMS; n++) { @@ -324,7 +322,7 @@ gst_ffmpegdemux_close (GstFFMpegDemux * demux) gint n; GstEvent **event_p; - if (!demux->opened) + if (!demux->context) return; /* remove pads from ourselves */ @@ -353,12 +351,8 @@ gst_ffmpegdemux_close (GstFFMpegDemux * demux) gst_ffmpeg_pipe_close (demux->context->pb); demux->context->pb = NULL; avformat_close_input (&demux->context); - if (demux->context) - avformat_free_context (demux->context); - demux->context = NULL; GST_OBJECT_LOCK (demux); - demux->opened = FALSE; event_p = &demux->seek_event; gst_event_replace (event_p, NULL); GST_OBJECT_UNLOCK (demux); @@ -700,7 +694,7 @@ gst_ffmpegdemux_send_event (GstElement * element, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: GST_OBJECT_LOCK (demux); - if (!demux->opened) { + if (!demux->context) { GstEvent **event_p; GST_DEBUG_OBJECT (demux, "caching seek event"); @@ -1309,7 +1303,6 @@ gst_ffmpegdemux_open (GstFFMpegDemux * demux) demux->segment.duration = demux->duration; GST_OBJECT_LOCK (demux); - demux->opened = TRUE; event = demux->seek_event; demux->seek_event = NULL; cached_events = demux->cached_events; @@ -1366,88 +1359,40 @@ gst_ffmpegdemux_open (GstFFMpegDemux * demux) /* ERRORS */ beach: { + if (demux->context->pb) { + if (demux->seekable) + gst_ffmpegdata_close (demux->context->pb); + else + gst_ffmpeg_pipe_close (demux->context->pb); + demux->context->pb = NULL; + } + avformat_close_input (&demux->context); + GST_ELEMENT_ERROR (demux, LIBRARY, FAILED, (NULL), ("%s", gst_ffmpegdemux_averror (res))); return FALSE; } } -#define GST_FFMPEG_TYPE_FIND_SIZE 4096 -#define GST_FFMPEG_TYPE_FIND_MIN_SIZE 256 - -static void -gst_ffmpegdemux_type_find (GstTypeFind * tf, gpointer priv) -{ - const guint8 *data; - AVInputFormat *in_plugin = (AVInputFormat *) priv; - gint res = 0; - guint64 length; - GstCaps *sinkcaps; - - /* We want GST_FFMPEG_TYPE_FIND_SIZE bytes, but if the file is shorter than - * that we'll give it a try... */ - length = gst_type_find_get_length (tf); - if (length == 0 || length > GST_FFMPEG_TYPE_FIND_SIZE) - length = GST_FFMPEG_TYPE_FIND_SIZE; - - /* The ffmpeg typefinders assume there's a certain minimum amount of data - * and will happily do invalid memory access if there isn't, so let's just - * skip the ffmpeg typefinders if the data available is too short - * (in which case it's unlikely to be a media file anyway) */ - if (length < GST_FFMPEG_TYPE_FIND_MIN_SIZE) { - GST_LOG ("not typefinding %" G_GUINT64_FORMAT " bytes, too short", length); - return; - } - - GST_LOG ("typefinding %" G_GUINT64_FORMAT " bytes", length); - if (in_plugin->read_probe && - (data = gst_type_find_peek (tf, 0, length)) != NULL) { - AVProbeData probe_data; - - probe_data.filename = ""; - probe_data.buf = (guint8 *) data; - probe_data.buf_size = length; - - res = in_plugin->read_probe (&probe_data); - if (res > 0) { - res = MAX (1, res * GST_TYPE_FIND_MAXIMUM / AVPROBE_SCORE_MAX); - /* Restrict the probability for MPEG-TS streams, because there is - * probably a better version in plugins-base, if the user has a recent - * plugins-base (in fact we shouldn't even get here for ffmpeg mpegts or - * mpegtsraw typefinders, since we blacklist them) */ - if (g_str_has_prefix (in_plugin->name, "mpegts")) - res = MIN (res, GST_TYPE_FIND_POSSIBLE); - - sinkcaps = gst_ffmpeg_formatid_to_caps (in_plugin->name); - - GST_LOG ("libav typefinder '%s' suggests %" GST_PTR_FORMAT ", p=%u%%", - in_plugin->name, sinkcaps, res); - - gst_type_find_suggest (tf, res, sinkcaps); - gst_caps_unref (sinkcaps); - } - } -} - /* Task */ static void gst_ffmpegdemux_loop (GstFFMpegDemux * demux) { GstFlowReturn ret; gint res = -1; AVPacket pkt; GstPad *srcpad; GstFFStream *stream; AVStream *avstream; GstBuffer *outbuf = NULL; GstClockTime timestamp, duration; gint outsize; gboolean rawvideo; GstFlowReturn stream_last_flow; gint64 pts; /* open file if we didn't so already */ - if (!demux->opened) + if (!demux->context) if (!gst_ffmpegdemux_open (demux)) goto open_failed; @@ -1782,7 +1727,7 @@ gst_ffmpegdemux_sink_event (GstPad * sinkpad, GstObject * parent, * If the demuxer isn't opened, push straight away, since we'll * be waiting against a cond that will never be signalled. */ if (GST_EVENT_IS_SERIALIZED (event)) { - if (demux->opened) { + if (demux->context) { GST_FFMPEG_PIPE_MUTEX_LOCK (ffpipe); while (!ffpipe->needed) GST_FFMPEG_PIPE_WAIT (ffpipe); @@ -2055,7 +2000,6 @@ gst_ffmpegdemux_register (GstPlugin * plugin) while ((in_plugin = av_demuxer_iterate (&i))) { gchar *type_name, *typefind_name; gint rank; - gboolean register_typefind_func = TRUE; GST_LOG ("Attempting to handle libav demuxer plugin %s [%s]", in_plugin->name, in_plugin->long_name); @@ -2102,42 +2046,6 @@ gst_ffmpegdemux_register (GstPlugin * plugin) !strcmp (in_plugin->name, "ffmetadata")) continue; - /* Don't use the typefind functions of formats for which we already have - * better typefind functions */ - if (!strcmp (in_plugin->name, "mov,mp4,m4a,3gp,3g2,mj2") || - !strcmp (in_plugin->name, "ass") || - !strcmp (in_plugin->name, "avi") || - !strcmp (in_plugin->name, "asf") || - !strcmp (in_plugin->name, "mpegvideo") || - !strcmp (in_plugin->name, "mp3") || - !strcmp (in_plugin->name, "matroska") || - !strcmp (in_plugin->name, "matroska_webm") || - !strcmp (in_plugin->name, "matroska,webm") || - !strcmp (in_plugin->name, "mpeg") || - !strcmp (in_plugin->name, "wav") || - !strcmp (in_plugin->name, "au") || - !strcmp (in_plugin->name, "tta") || - !strcmp (in_plugin->name, "rm") || - !strcmp (in_plugin->name, "amr") || - !strcmp (in_plugin->name, "ogg") || - !strcmp (in_plugin->name, "aiff") || - !strcmp (in_plugin->name, "ape") || - !strcmp (in_plugin->name, "dv") || - !strcmp (in_plugin->name, "flv") || - !strcmp (in_plugin->name, "mpc") || - !strcmp (in_plugin->name, "mpc8") || - !strcmp (in_plugin->name, "mpegts") || - !strcmp (in_plugin->name, "mpegtsraw") || - !strcmp (in_plugin->name, "mxf") || - !strcmp (in_plugin->name, "nuv") || - !strcmp (in_plugin->name, "swf") || - !strcmp (in_plugin->name, "voc") || - !strcmp (in_plugin->name, "pva") || - !strcmp (in_plugin->name, "gif") || - !strcmp (in_plugin->name, "vc1test") || - !strcmp (in_plugin->name, "ivf")) - register_typefind_func = FALSE; - /* Set the rank of demuxers known to work to MARGINAL. * Set demuxers for which we already have another implementation to NONE * Set All others to NONE*/ @@ -2214,11 +2122,7 @@ gst_ffmpegdemux_register (GstPlugin * plugin) else extensions = NULL; - if (!gst_element_register (plugin, type_name, rank, type) || - (register_typefind_func == TRUE && - !gst_type_find_register (plugin, typefind_name, rank, - gst_ffmpegdemux_type_find, extensions, NULL, - (gpointer) in_plugin, NULL))) { + if (!gst_element_register (plugin, type_name, rank, type)) { g_warning ("Registration of type %s failed", type_name); g_free (type_name); g_free (typefind_name); diff --git a/subprojects/gst-libav/ext/libav/gstavmux.c b/subprojects/gst-libav/ext/libav/gstavmux.c index 60da7f0c29e1..389a873cf717 100644 --- a/subprojects/gst-libav/ext/libav/gstavmux.c +++ b/subprojects/gst-libav/ext/libav/gstavmux.c @@ -796,6 +796,7 @@ gst_ffmpegmux_change_state (GstElement * element, GstStateChange transition) if (ffmpegmux->opened) { ffmpegmux->opened = FALSE; gst_ffmpegdata_close (ffmpegmux->context->pb); + ffmpegmux->context->pb = NULL; } break; case GST_STATE_CHANGE_READY_TO_NULL: diff --git a/subprojects/gst-libav/ext/libav/gstavprotocol.c b/subprojects/gst-libav/ext/libav/gstavprotocol.c index 249b24064275..cb607d301299 100644 --- a/subprojects/gst-libav/ext/libav/gstavprotocol.c +++ b/subprojects/gst-libav/ext/libav/gstavprotocol.c @@ -102,7 +102,11 @@ gst_ffmpegdata_read (void *priv_data, unsigned char *buf, int size) } static int +#if LIBAVUTIL_VERSION_MAJOR >= 59 +gst_ffmpegdata_write (void *priv_data, const uint8_t * buf, int size) +#else gst_ffmpegdata_write (void *priv_data, uint8_t * buf, int size) +#endif { GstProtocolInfo *info; GstBuffer *outbuf; diff --git a/subprojects/gst-libav/ext/libav/gstavviddec.c b/subprojects/gst-libav/ext/libav/gstavviddec.c index fe1ea51aa61b..f70da082b0fb 100644 --- a/subprojects/gst-libav/ext/libav/gstavviddec.c +++ b/subprojects/gst-libav/ext/libav/gstavviddec.c @@ -337,34 +337,29 @@ 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; ffmpegdec->output_corrupt = DEFAULT_OUTPUT_CORRUPT; ffmpegdec->thread_type = DEFAULT_THREAD_TYPE; ffmpegdec->std_compliance = DEFAULT_STD_COMPLIANCE; GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (ffmpegdec)); gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST (ffmpegdec), TRUE); gst_video_decoder_set_needs_format (GST_VIDEO_DECODER (ffmpegdec), TRUE); } static void 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,72 +390,56 @@ 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 */ static gboolean gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec) { GstFFMpegVidDecClass *oclass; guint i; oclass = GST_FFMPEGVIDDEC_GET_CLASS (ffmpegdec); if (gst_ffmpeg_avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0) goto could_not_open; 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); gst_ffmpegviddec_context_set_flags (ffmpegdec->context, AV_CODEC_FLAG_OUTPUT_CORRUPT, ffmpegdec->output_corrupt); #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT (60, 31, 100) gst_ffmpegviddec_context_set_flags (ffmpegdec->context, AV_CODEC_FLAG_COPY_OPAQUE, TRUE); #endif return TRUE; /* 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,29 +516,34 @@ 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; ffmpegdec->pic_par_n = 0; ffmpegdec->pic_par_d = 0; ffmpegdec->pic_interlaced = 0; ffmpegdec->pic_field_order = 0; ffmpegdec->pic_field_order_changed = FALSE; ffmpegdec->ctx_ticks = 0; ffmpegdec->ctx_time_n = 0; ffmpegdec->ctx_time_d = 0; ffmpegdec->cur_multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE; 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 */ @@ -686,7 +670,9 @@ update_state: const gint fps_n = ffmpegdec->context->time_base.den; #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 31, 100) const gint ticks_per_frame = - (ffmpegdec->context-> + (GST_VIDEO_INFO_IS_INTERLACED (&ffmpegdec->input_state->info) + && ffmpegdec->context->codec_descriptor + && ffmpegdec->context-> codec_descriptor->props & AV_CODEC_PROP_FIELDS) ? 2 : 1; #else const gint ticks_per_frame = ffmpegdec->context->ticks_per_frame; @@ -718,12 +704,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; } } @@ -1165,8 +1157,11 @@ static gboolean context_changed (GstFFMpegVidDec * ffmpegdec, AVCodecContext * context) { #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 31, 100) - const gint ticks_per_frame = (context->codec_descriptor - && context->codec_descriptor->props & AV_CODEC_PROP_FIELDS) ? 2 : 1; + const gint ticks_per_frame = + (GST_VIDEO_INFO_IS_INTERLACED (&ffmpegdec->input_state->info) + && ffmpegdec->context->codec_descriptor + && ffmpegdec->context-> + codec_descriptor->props & AV_CODEC_PROP_FIELDS) ? 2 : 1; #else const gint ticks_per_frame = context->ticks_per_frame; #endif @@ -1238,8 +1233,11 @@ update_video_context (GstFFMpegVidDec * ffmpegdec, AVCodecContext * context, ffmpegdec->pic_field_order_changed = FALSE; #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 31, 100) - const gint ticks_per_frame = (context->codec_descriptor - && context->codec_descriptor->props & AV_CODEC_PROP_FIELDS) ? 2 : 1; + const gint ticks_per_frame = + (GST_VIDEO_INFO_IS_INTERLACED (&ffmpegdec->input_state->info) + && ffmpegdec->context->codec_descriptor + && ffmpegdec->context-> + codec_descriptor->props & AV_CODEC_PROP_FIELDS) ? 2 : 1; #else const gint ticks_per_frame = context->ticks_per_frame; #endif @@ -2119,7 +2117,7 @@ gst_ffmpegviddec_frame (GstFFMpegVidDec * ffmpegdec, GstVideoCodecFrame * frame, { gboolean got_frame = FALSE; - if (G_UNLIKELY (ffmpegdec->context->codec == NULL)) + if (G_UNLIKELY (!ffmpegdec->context)) goto no_codec; *ret = GST_FLOW_OK; @@ -2149,7 +2147,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); @@ -2195,15 +2193,22 @@ 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, gst_buffer_get_size (frame->input_buffer), GST_TIME_ARGS (frame->dts), GST_TIME_ARGS (frame->pts), GST_TIME_ARGS (frame->duration)); 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; } @@ -2312,31 +2317,26 @@ 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; } static gboolean 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; @@ -2390,7 +2390,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); @@ -2579,11 +2579,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 0f713de5691c..14d5a9aff352 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 461263b4f645..90c35d216dcf 100644 --- a/subprojects/gst-libav/ext/libav/gstavvidenc.c +++ b/subprojects/gst-libav/ext/libav/gstavvidenc.c @@ -208,54 +208,47 @@ 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; } static void gst_ffmpegvidenc_finalize (GObject * object) { GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) 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); } static gboolean gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state) { GstCaps *other_caps; GstCaps *allowed_caps; GstCaps *icaps; GstVideoCodecState *output_format; enum AVPixelFormat pix_fmt; GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; GstFFMpegVidEncClass *oclass = (GstFFMpegVidEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); 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; } + ffmpegenc->last_pts_ff = G_MININT64; /* additional avcodec settings */ gst_ffmpeg_cfg_fill_context (G_OBJECT (ffmpegenc), ffmpegenc->context); @@ -402,89 +395,87 @@ gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder, /* success! */ ffmpegenc->pts_offset = GST_CLOCK_TIME_NONE; - ffmpegenc->opened = TRUE; return TRUE; /* ERRORS */ 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: { 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; } 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: { GST_DEBUG_OBJECT (ffmpegenc, "avenc_%s: Failed to open libav codec", oclass->in_plugin->name); goto close_codec; } pix_fmt_err: { GST_DEBUG_OBJECT (ffmpegenc, "avenc_%s: AV wants different colourspace (%d given, %d wanted)", oclass->in_plugin->name, pix_fmt, ffmpegenc->context->pix_fmt); goto close_codec; } bad_input_fmt: { GST_DEBUG_OBJECT (ffmpegenc, "avenc_%s: Failed to determine input format", oclass->in_plugin->name); goto close_codec; } 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; } } static gboolean gst_ffmpegvidenc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query) { gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder, query); } static void gst_ffmpegvidenc_free_avpacket (gpointer pkt) { - av_packet_unref ((AVPacket *) pkt); - g_free (pkt); + av_packet_free ((AVPacket **) & pkt); } typedef struct @@ -560,6 +551,7 @@ gst_ffmpegvidenc_send_frame (GstFFMpegVidEnc * ffmpegenc, gint res; GstFlowReturn ret = GST_FLOW_ERROR; AVFrame *picture = NULL; + GstClockTime pts, pts_running_time; if (!frame) goto send_frame; @@ -629,26 +621,46 @@ gst_ffmpegvidenc_send_frame (GstFFMpegVidEnc * ffmpegenc, picture->width = GST_VIDEO_FRAME_WIDTH (&buffer_info->vframe); picture->height = GST_VIDEO_FRAME_HEIGHT (&buffer_info->vframe); + // Use the running time to calculate a PTS that is passed to the encoder. + // This ensures that it is increasing even if there are segment changes and + // makes it unnecessary to drain the encoder on every segment change. + pts = frame->pts; + pts_running_time = + gst_segment_to_running_time (&GST_VIDEO_ENCODER + (ffmpegenc)->input_segment, GST_FORMAT_TIME, pts); + if (ffmpegenc->pts_offset == GST_CLOCK_TIME_NONE) { - ffmpegenc->pts_offset = frame->pts; + ffmpegenc->pts_offset = pts_running_time; } - if (frame->pts == GST_CLOCK_TIME_NONE) { + if (pts_running_time == GST_CLOCK_TIME_NONE) { picture->pts = AV_NOPTS_VALUE; - } else if (frame->pts < ffmpegenc->pts_offset) { + } else if (pts_running_time < ffmpegenc->pts_offset) { GST_ERROR_OBJECT (ffmpegenc, "PTS is going backwards"); picture->pts = AV_NOPTS_VALUE; } else { #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 31, 100) - const gint ticks_per_frame = (ffmpegenc->context->codec_descriptor + const gint ticks_per_frame = + (GST_VIDEO_INFO_IS_INTERLACED (&ffmpegenc->input_state->info) + && ffmpegenc->context->codec_descriptor && ffmpegenc->context-> codec_descriptor->props & AV_CODEC_PROP_FIELDS) ? 2 : 1; #else const gint ticks_per_frame = ffmpegenc->context->ticks_per_frame; #endif picture->pts = - gst_ffmpeg_time_gst_to_ff ((frame->pts - ffmpegenc->pts_offset) / + gst_ffmpeg_time_gst_to_ff ((pts_running_time - ffmpegenc->pts_offset) / ticks_per_frame, ffmpegenc->context->time_base); + + // Certain codecs require always increasing PTS to work correctly. This + // affects at least all MPEG1/2/4 based encoders. + if (ffmpegenc->last_pts_ff == G_MININT64 + || picture->pts > ffmpegenc->last_pts_ff) { + ffmpegenc->last_pts_ff = picture->pts; + } else { + ffmpegenc->last_pts_ff += 1; + picture->pts = ffmpegenc->last_pts_ff; + } } send_frame: @@ -692,18 +704,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; } @@ -743,50 +755,52 @@ gst_ffmpegvidenc_receive_packet (GstFFMpegVidEnc * ffmpegenc, ffmpegenc->context->time_base); if (gst_pts_dts_diff > frame->pts) - frame->pts = 0; + frame->dts = 0; else frame->dts = frame->pts - gst_pts_dts_diff; } else { frame->dts = frame->pts + gst_ffmpeg_time_ff_to_gst (pts_dts_diff, ffmpegenc->context->time_base); } } ret = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (ffmpegenc), frame); done: return ret; } static GstFlowReturn gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame) { GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) 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; GstVideoCodecState *input_state; GST_DEBUG_OBJECT (ffmpegenc, "Open encoder again"); 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; } input_state = gst_video_codec_state_ref (ffmpegenc->input_state); reopen_ret = gst_ffmpegvidenc_set_format (encoder, input_state); gst_video_codec_state_unref (input_state); 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 +845,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 +881,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,45 +935,50 @@ 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; } return TRUE; } 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; } static gboolean 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 340fb2520421..3b1f2e848825 100644 --- a/subprojects/gst-libav/ext/libav/gstavvidenc.h +++ b/subprojects/gst-libav/ext/libav/gstavvidenc.h @@ -41,7 +41,7 @@ struct _GstFFMpegVidEnc AVCodecContext *context; AVFrame *picture; GstClockTime pts_offset; - gboolean opened; + gint64 last_pts_ff; gboolean need_reopen; gboolean discont; guint pass;