From 52080fb44349b7ec8edff83a92afa455450c18a6 Mon Sep 17 00:00:00 2001 From: "Ronald S. Bultje" Date: Sun, 8 Jun 2003 13:31:53 +0000 Subject: [PATCH] Some modifications to make the demuxers work, plus moving typefinding over from separate plugin to the demuxers thems... Original commit message from CVS: Some modifications to make the demuxers work, plus moving typefinding over from separate plugin to the demuxers themselves --- ext/ffmpeg/gstffmpegdemux.c | 487 +++++++++++++++++++++++++++----------------- 1 file changed, 305 insertions(+), 182 deletions(-) diff --git a/ext/ffmpeg/gstffmpegdemux.c b/ext/ffmpeg/gstffmpegdemux.c index 986285b..d3fd7c8 100644 --- a/ext/ffmpeg/gstffmpegdemux.c +++ b/ext/ffmpeg/gstffmpegdemux.c @@ -17,8 +17,11 @@ * Boston, MA 02111-1307, USA. */ -#include +#ifdef HAVE_CONFIG_H #include "config.h" +#endif + +#include #ifdef HAVE_FFMPEG_UNINSTALLED #include #include @@ -29,12 +32,7 @@ #include -extern GstCaps* gst_ffmpegcodec_codec_context_to_caps (AVCodecContext *context, int codec_id); - -typedef enum { - STATE_OPEN, - STATE_DEMUX, -} DemuxState; +#include "gstffmpegcodecmap.h" typedef struct _GstFFMpegDemux GstFFMpegDemux; @@ -45,17 +43,30 @@ struct _GstFFMpegDemux { GstPad *sinkpad; AVFormatContext *context; - DemuxState state; + gboolean opened; GstPad *srcpads[MAX_STREAMS]; + gint videopads, audiopads; }; +typedef struct _GstFFMpegDemuxClassParams { + AVInputFormat *in_plugin; + GstPadTemplate *sinktempl; + GstPadTemplate *videosrctempl; + GstPadTemplate *audiosrctempl; + GstPluginFeature *typefind_feature; +} GstFFMpegDemuxClassParams; + typedef struct _GstFFMpegDemuxClass GstFFMpegDemuxClass; struct _GstFFMpegDemuxClass { GstElementClass parent_class; AVInputFormat *in_plugin; + GstPadTemplate *sinktempl; + GstPadTemplate *videosrctempl; + GstPadTemplate *audiosrctempl; + GstPluginFeature *typefind_feature; }; #define GST_TYPE_FFMPEGDEC \ @@ -79,42 +90,17 @@ enum { /* FILL ME */ }; -/* This factory is much simpler, and defines the source pad. */ -GST_PAD_TEMPLATE_FACTORY (gst_ffmpegdemux_sink_factory, - "sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - NULL -) - -/* This factory is much simpler, and defines the source pad. */ -GST_PAD_TEMPLATE_FACTORY (gst_ffmpegdemux_audio_src_factory, - "audio_%02d", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - NULL -) - -/* This factory is much simpler, and defines the source pad. */ -GST_PAD_TEMPLATE_FACTORY (gst_ffmpegdemux_video_src_factory, - "video_%02d", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - NULL -) - -static GHashTable *global_plugins; +static GHashTable *global_plugins, *typefind; /* A number of functon prototypes are given so we can refer to them later. */ static void gst_ffmpegdemux_class_init (GstFFMpegDemuxClass *klass); static void gst_ffmpegdemux_init (GstFFMpegDemux *ffmpegdemux); +static void gst_ffmpegdemux_dispose (GObject *object); static void gst_ffmpegdemux_loop (GstElement *element); -static void gst_ffmpegdemux_set_property (GObject *object, guint prop_id, const GValue *value, - GParamSpec *pspec); -static void gst_ffmpegdemux_get_property (GObject *object, guint prop_id, GValue *value, - GParamSpec *pspec); +static GstElementStateReturn + gst_ffmpegdemux_change_state (GstElement *element); static GstElementClass *parent_class = NULL; @@ -125,179 +111,251 @@ gst_ffmpegdemux_class_init (GstFFMpegDemuxClass *klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; + GstFFMpegDemuxClassParams *params; gobject_class = (GObjectClass*)klass; gstelement_class = (GstElementClass*)klass; parent_class = g_type_class_ref(GST_TYPE_ELEMENT); - klass->in_plugin = g_hash_table_lookup (global_plugins, - GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class))); + params = g_hash_table_lookup (global_plugins, + GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class))); - gobject_class->set_property = gst_ffmpegdemux_set_property; - gobject_class->get_property = gst_ffmpegdemux_get_property; + klass->in_plugin = params->in_plugin; + klass->typefind_feature = params->typefind_feature; + klass->videosrctempl = params->videosrctempl; + klass->audiosrctempl = params->audiosrctempl; + klass->sinktempl = params->sinktempl; + + gstelement_class->change_state = gst_ffmpegdemux_change_state; + gobject_class->dispose = gst_ffmpegdemux_dispose; } static void gst_ffmpegdemux_init(GstFFMpegDemux *ffmpegdemux) { - //GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass*)(G_OBJECT_GET_CLASS (ffmpegdemux)); + GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass*)(G_OBJECT_GET_CLASS (ffmpegdemux)); - ffmpegdemux->sinkpad = gst_pad_new_from_template ( - GST_PAD_TEMPLATE_GET (gst_ffmpegdemux_sink_factory), "sink"); + ffmpegdemux->sinkpad = gst_pad_new_from_template (oclass->sinktempl, + "sink"); + gst_element_add_pad (GST_ELEMENT (ffmpegdemux), + ffmpegdemux->sinkpad); + gst_element_set_loop_function (GST_ELEMENT (ffmpegdemux), + gst_ffmpegdemux_loop); - gst_element_add_pad (GST_ELEMENT (ffmpegdemux), ffmpegdemux->sinkpad); - gst_element_set_loop_function (GST_ELEMENT (ffmpegdemux), gst_ffmpegdemux_loop); + ffmpegdemux->opened = FALSE; - ffmpegdemux->state = STATE_OPEN; + ffmpegdemux->videopads = 0; + ffmpegdemux->audiopads = 0; } static void -gst_ffmpegdemux_loop (GstElement *element) +gst_ffmpegdemux_dispose (GObject *object) { - GstFFMpegDemux *ffmpegdemux = (GstFFMpegDemux *)(element); - GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass*)(G_OBJECT_GET_CLASS (ffmpegdemux)); + GstFFMpegDemux *ffmpegdemux = (GstFFMpegDemux *) object; + + if (ffmpegdemux->opened) { + av_close_input_file (ffmpegdemux->context); + ffmpegdemux->opened = FALSE; + } +} + +static GstCaps* +gst_ffmpegdemux_typefind (GstBuffer *buffer, + gpointer priv) +{ + GstFFMpegDemuxClassParams *params; + AVInputFormat *in_plugin; gint res = 0; + gint required = AVPROBE_SCORE_MAX * 0.8; /* 80% certainty enough? */ + + params = g_hash_table_lookup (typefind, priv); - switch (ffmpegdemux->state) { - case STATE_OPEN: - { - res = av_open_input_file (&ffmpegdemux->context, - g_strdup_printf ("gstreamer://%p", ffmpegdemux->sinkpad), - oclass->in_plugin, - 0, - NULL); + in_plugin = params->in_plugin; - ffmpegdemux->state = STATE_DEMUX; - break; - } - case STATE_DEMUX: - { - gint res; - AVPacket pkt; - AVFormatContext *ct = ffmpegdemux->context; - AVStream *st; - GstPad *pad; - - res = av_read_packet(ct, &pkt); - if (res < 0) { - if (url_feof (&ct->pb)) { - gint i; - - for (i = 0; i < ct->nb_streams; i++) { - GstPad *pad; - - pad = ffmpegdemux->srcpads[i]; - - if (GST_PAD_IS_USABLE (pad)) { - gst_pad_push (pad, GST_BUFFER (gst_event_new (GST_EVENT_EOS))); - } - } - gst_element_set_eos (element); - } - return; - } + if (in_plugin->read_probe) { + AVProbeData probe_data; - st = ct->streams[pkt.stream_index]; + probe_data.filename = ""; + probe_data.buf = GST_BUFFER_DATA (buffer); + probe_data.buf_size = GST_BUFFER_SIZE (buffer); - if (st->codec_info_state == 0) { - gchar *templname = NULL; + res = in_plugin->read_probe (&probe_data); + if (res >= required) { + GstCaps *caps; + caps = GST_PAD_TEMPLATE_CAPS (params->sinktempl); + /* make sure we still hold a refcount to this caps */ + gst_caps_ref (caps); + return caps; + } + } - st->codec_info_state = 1; - - if (st->codec.codec_type == CODEC_TYPE_VIDEO) { - templname = "video_%02d"; - } - else if (st->codec.codec_type == CODEC_TYPE_AUDIO) { - templname = "audio_%02d"; - } - - if (templname != NULL) { - gchar *padname; - GstCaps *caps; - GstPadTemplate *templ; - - caps = gst_ffmpegcodec_codec_context_to_caps (&st->codec, st->codec.codec_id); - templ = gst_pad_template_new (templname, - GST_PAD_SRC, - GST_PAD_SOMETIMES, - caps, NULL); - - padname = g_strdup_printf (templname, pkt.stream_index); - pad = gst_pad_new_from_template (templ, padname); - - ffmpegdemux->srcpads[pkt.stream_index] = pad; - gst_element_add_pad (GST_ELEMENT (ffmpegdemux), pad); - } - else { - g_warning ("unknown pad type %d", st->codec.codec_type); - return; - } - } - else { - pad = ffmpegdemux->srcpads[pkt.stream_index]; - } + return NULL; +} - if (GST_PAD_IS_USABLE (pad)) { - GstBuffer *outbuf; +static void +gst_ffmpegdemux_loop (GstElement *element) +{ + GstFFMpegDemux *ffmpegdemux = (GstFFMpegDemux *)(element); + GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass*)(G_OBJECT_GET_CLASS (ffmpegdemux)); - outbuf = gst_buffer_new (); - GST_BUFFER_DATA (outbuf) = pkt.data; - GST_BUFFER_SIZE (outbuf) = pkt.size; + gint res; + AVPacket pkt; + AVFormatContext *ct; + AVStream *st; + GstPad *pad; + + /* open file if we didn't so already */ + if (!ffmpegdemux->opened) { + res = av_open_input_file (&ffmpegdemux->context, + g_strdup_printf ("gstreamer://i/%p", + ffmpegdemux->sinkpad), + oclass->in_plugin, 0, NULL); + if (res < 0) { + gst_element_error (GST_ELEMENT (ffmpegdemux), + "Failed to open demuxer/file context"); + return; + } - if (pkt.pts != AV_NOPTS_VALUE && ct->pts_den) { - GST_BUFFER_TIMESTAMP (outbuf) = pkt.pts * GST_SECOND * ct->pts_num / ct->pts_den; - } - else { - GST_BUFFER_TIMESTAMP (outbuf) = -1; - } + ffmpegdemux->opened = TRUE; + } - gst_pad_push (pad, outbuf); + /* shortcut to context */ + ct = ffmpegdemux->context; + + /* read a package */ + res = av_read_packet (ct, &pkt); + if (res < 0) { + if (url_feof (&ct->pb)) { + int i; + + /* we're at the end of file - send an EOS to + * each stream that we opened so far */ + for (i = 0; i < ct->nb_streams; i++) { + GstPad *pad; + GstEvent *event = gst_event_new (GST_EVENT_EOS); + + pad = ffmpegdemux->srcpads[i]; + if (GST_PAD_IS_USABLE (pad)) { + gst_data_ref (GST_DATA (event)); + gst_pad_push (pad, GST_BUFFER (event)); + } + gst_data_unref (GST_DATA (event)); } - break; - } - default: gst_element_set_eos (element); - break; + + /* FIXME: should we go into + * should we close the context here? + * either way, a new media stream needs an + * event too */ + } + return; } -} -static void -gst_ffmpegdemux_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - GstFFMpegDemux *ffmpegdemux; + /* shortcut to stream */ + st = ct->streams[pkt.stream_index]; + + /* create the pad/stream if we didn't do so already */ + if (st->codec_info_state == 0) { + GstPadTemplate *templ = NULL; + GstCaps *caps; + gchar *padname; + gint num; + + /* mark as handled */ + st->codec_info_state = 1; + + /* find template */ + switch (st->codec.codec_type) { + case CODEC_TYPE_VIDEO: + templ = oclass->videosrctempl; + num = ffmpegdemux->videopads++; + break; + case CODEC_TYPE_AUDIO: + templ = oclass->audiosrctempl; + num = ffmpegdemux->audiopads++; + break; + default: + g_warning ("Unknown pad type %d", + st->codec.codec_type); + return; + } + + /* create new pad for this stream */ + padname = g_strdup_printf (GST_PAD_TEMPLATE_NAME_TEMPLATE(templ), + num); + pad = gst_pad_new_from_template (templ, padname); + g_free (padname); + + /* FIXME: convert() and query() functions for pad */ + + /* store pad internally */ + ffmpegdemux->srcpads[pkt.stream_index] = pad; + gst_element_add_pad (GST_ELEMENT (ffmpegdemux), pad); + + /* get caps that belongs to this stream */ + caps = gst_ffmpeg_codecid_to_caps (st->codec.codec_id, + &st->codec); + if (gst_pad_try_set_caps (pad, caps) <= 0) { + GST_DEBUG (GST_CAT_PLUGIN_ERRORS, + "Failed to set caps from ffdemuxer on next element"); + /* we continue here, in the next pad-is-usable check, + * we'll return nonetheless */ + } + } - /* Get a pointer of the right type. */ - ffmpegdemux = (GstFFMpegDemux *)(object); + /* shortcut to pad belonging to this stream */ + pad = ffmpegdemux->srcpads[pkt.stream_index]; - /* Check the argument id to see which argument we're setting. */ - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + /* and handle the data by pushing it forward... */ + if (GST_PAD_IS_USABLE (pad)) { + GstBuffer *outbuf; + + outbuf = gst_buffer_new_and_alloc (pkt.size); + memcpy (GST_BUFFER_DATA (outbuf), pkt.data, pkt.size); + GST_BUFFER_SIZE (outbuf) = pkt.size; + + if (pkt.pts != AV_NOPTS_VALUE && ct->pts_den) { + GST_BUFFER_TIMESTAMP (outbuf) = pkt.pts * GST_SECOND * + ct->pts_num / ct->pts_den; + } + + if (pkt.flags & PKT_FLAG_KEY) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_KEY_UNIT); + } + + gst_pad_push (pad, outbuf); + pkt.destruct (&pkt); } } -/* The set function is simply the inverse of the get fuction. */ -static void -gst_ffmpegdemux_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +static GstElementStateReturn +gst_ffmpegdemux_change_state (GstElement *element) { - GstFFMpegDemux *ffmpegdemux; - - /* It's not null if we got it, but it might not be ours */ - ffmpegdemux = (GstFFMpegDemux *)(object); + GstFFMpegDemux *ffmpegdemux = (GstFFMpegDemux *)(element); + gint transition = GST_STATE_TRANSITION (element); - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + switch (transition) { + case GST_STATE_PAUSED_TO_READY: + if (ffmpegdemux->opened) { + av_close_input_file (ffmpegdemux->context); + ffmpegdemux->opened = FALSE; + } break; } + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; } gboolean gst_ffmpegdemux_register (GstPlugin *plugin) { GstElementFactory *factory; + GstTypeFactory *type_factory; + GstTypeDefinition *type_definition; GTypeInfo typeinfo = { sizeof(GstFFMpegDemuxClass), NULL, @@ -312,14 +370,47 @@ gst_ffmpegdemux_register (GstPlugin *plugin) GType type; GstElementDetails *details; AVInputFormat *in_plugin; + GstFFMpegDemuxClassParams *params; + AVCodec *in_codec; in_plugin = first_iformat; global_plugins = g_hash_table_new (NULL, NULL); + typefind = g_hash_table_new (NULL, NULL); while (in_plugin) { gchar *type_name; gchar *p; + GstCaps *sinkcaps, *audiosrccaps, *videosrccaps; + + /* Try to find the caps that belongs here */ + sinkcaps = gst_ffmpeg_formatid_to_caps (in_plugin->name); + if (!sinkcaps) { + goto next; + } + /* This is a bit ugly, but we just take all formats + * for the pad template. We'll get an exact match + * when we open the stream */ + audiosrccaps = NULL; + videosrccaps = NULL; + for (in_codec = first_avcodec; in_codec != NULL; + in_codec = in_codec->next) { + GstCaps *temp = gst_ffmpeg_codecid_to_caps (in_codec->id, NULL); + if (!temp) { + continue; + } + switch (in_codec->type) { + case CODEC_TYPE_VIDEO: + videosrccaps = gst_caps_append (videosrccaps, temp); + break; + case CODEC_TYPE_AUDIO: + audiosrccaps = gst_caps_append (audiosrccaps, temp); + break; + default: + gst_caps_unref (temp); + break; + } + } /* construct the type */ type_name = g_strdup_printf("ffdemux_%s", in_plugin->name); @@ -337,39 +428,71 @@ gst_ffmpegdemux_register (GstPlugin *plugin) goto next; } - /* create the gtk type now */ + /* create the type now */ type = g_type_register_static(GST_TYPE_ELEMENT, type_name , &typeinfo, 0); /* construct the element details struct */ - details = g_new0 (GstElementDetails,1); - details->longname = g_strdup (in_plugin->name); - details->klass = "Codec/Demuxer/FFMpeg"; - details->license = "LGPL"; - details->description = g_strdup (in_plugin->name); - details->version = g_strdup("1.0.0"); - details->author = g_strdup("The FFMPEG crew, GStreamer plugin by Wim Taymans "); - details->copyright = g_strdup("(c) 2002"); - - g_hash_table_insert (global_plugins, - GINT_TO_POINTER (type), - (gpointer) in_plugin); + details = g_new0 (GstElementDetails, 1); + details->longname = g_strdup (in_plugin->long_name); + details->klass = g_strdup ("Codec/Demuxer"); + details->license = g_strdup ("LGPL"); + details->description = g_strdup_printf ("FFMPEG %s demuxer", + in_plugin->name); + details->version = g_strdup (VERSION); + details->author = g_strdup ("The FFMPEG crew\n" + "Wim Taymans \n" + "Ronald Bultje "); + details->copyright = g_strdup ("(c) 2002-2003"); /* register the plugin with gstreamer */ factory = gst_element_factory_new(type_name,type,details); g_return_val_if_fail(factory != NULL, FALSE); - gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_NONE); + /* typefind info */ + type_definition = g_new0 (GstTypeDefinition, 1); + type_definition->name = g_strdup_printf ("fftype_%s", + in_plugin->name); + type_definition->mime = g_strdup (gst_caps_get_mime (sinkcaps)); + type_definition->exts = g_strdup (in_plugin->extensions); + type_definition->typefindfunc = gst_ffmpegdemux_typefind; + + type_factory = gst_type_factory_new (type_definition); + + /* create a cache for these properties */ + params = g_new0 (GstFFMpegDemuxClassParams, 1); + params->in_plugin = in_plugin; + params->typefind_feature = GST_PLUGIN_FEATURE (type_factory); + params->sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK, + GST_PAD_ALWAYS, + sinkcaps, NULL); + gst_element_factory_add_pad_template (factory, + params->sinktempl); + params->audiosrctempl = gst_pad_template_new ("audio_%02d", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + audiosrccaps, NULL); + gst_element_factory_add_pad_template (factory, + params->audiosrctempl); + params->videosrctempl = gst_pad_template_new ("video_%02d", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + videosrccaps, NULL); + gst_element_factory_add_pad_template (factory, + params->videosrctempl); + + g_hash_table_insert (global_plugins, + GINT_TO_POINTER (type), + (gpointer) params); - gst_element_factory_add_pad_template (factory, - GST_PAD_TEMPLATE_GET (gst_ffmpegdemux_sink_factory)); + g_hash_table_insert (typefind, + (gpointer) type_factory, + (gpointer) params); - gst_element_factory_add_pad_template (factory, - GST_PAD_TEMPLATE_GET (gst_ffmpegdemux_video_src_factory)); - gst_element_factory_add_pad_template (factory, - GST_PAD_TEMPLATE_GET (gst_ffmpegdemux_audio_src_factory)); + gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_MARGINAL); /* The very last thing is to register the elementfactory with the plugin. */ gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type_factory)); next: in_plugin = in_plugin->next; -- 2.7.4