plugin_LTLIBRARIES = libgstffmpeg.la
libgstffmpeg_la_SOURCES = gstffmpeg.c \
- gstffmpegenc.c \
+ gstffmpegcodecmap.c \
gstffmpegdec.c \
gstffmpegdemux.c \
+ gstffmpegenc.c \
gstffmpegmux.c \
gstffmpegprotocol.c \
- gstffmpegcodecmap.c
+ gstffmpegtypes.c
libgstffmpeg_la_CFLAGS = $(GST_CFLAGS) -I/opt/src/sourceforge/ffmpeg/ -I/opt/src/sourceforge/ffmpeg/libavcodec/
libgstffmpeg_la_LIBADD =
extern gboolean gst_ffmpegdemux_register (GstPlugin *plugin);
extern gboolean gst_ffmpegdec_register (GstPlugin *plugin);
extern gboolean gst_ffmpegenc_register (GstPlugin *plugin);
+extern gboolean gst_ffmpegtypes_register (GstPlugin *plugin);
+
+extern URLProtocol gstreamer_protocol;
static gboolean
plugin_init (GModule *module, GstPlugin *plugin)
gst_ffmpegenc_register (plugin);
gst_ffmpegdec_register (plugin);
gst_ffmpegdemux_register (plugin);
+ gst_ffmpegtypes_register (plugin);
+
+ register_protocol (&gstreamer_protocol);
/* Now we can return the pointer to the newly created Plugin object. */
return TRUE;
#include <gst/gst.h>
GstCaps *
-gst_ffmpegcodec_codec_context_to_caps (AVCodecContext *context)
+gst_ffmpegcodec_codec_context_to_caps (AVCodecContext *context, int codec_id)
{
- switch (context->codec_id) {
+ switch (codec_id) {
case CODEC_ID_NONE:
return GST_CAPS_NEW ("ffmpeg_none",
"unkown/unkown",
NULL);
break;
case CODEC_ID_MPEG4:
- return GST_CAPS_NEW ("ffmpeg_mpeg4",
- "video/avi",
- "format", GST_PROPS_STRING ("strf_vids"),
- "fourcc", GST_PROPS_FOURCC (context->fourcc),
- "width", GST_PROPS_INT (context->width),
- "height", GST_PROPS_INT (context->height)
- );
+ if (context) {
+ return GST_CAPS_NEW ("ffmpeg_mpeg4",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (context->fourcc),
+ "width", GST_PROPS_INT (context->width),
+ "height", GST_PROPS_INT (context->height)
+ );
+ }
+ else {
+ return GST_CAPS_NEW ("ffmpeg_mpeg4",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (GST_STR_FOURCC ("DIV3")),
+ "width", GST_PROPS_INT_RANGE (0, 4096),
+ "height", GST_PROPS_INT_RANGE (0, 4096)
+ );
+ }
break;
case CODEC_ID_RAWVIDEO:
return GST_CAPS_NEW ("ffmpeg_rawvideo",
NULL);
break;
case CODEC_ID_MSMPEG4V1:
- return GST_CAPS_NEW ("ffmpeg_msmpeg4v1",
- "video/avi",
- "format", GST_PROPS_STRING ("strf_vids"),
- "fourcc", GST_PROPS_FOURCC (context->fourcc),
- "width", GST_PROPS_INT (context->width),
- "height", GST_PROPS_INT (context->height)
- );
+ if (context) {
+ return GST_CAPS_NEW ("ffmpeg_msmpeg4v1",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (GST_STR_FOURCC ("MPG4")),
+ "width", GST_PROPS_INT (context->width),
+ "height", GST_PROPS_INT (context->height)
+ );
+ }
+ else {
+ return GST_CAPS_NEW ("ffmpeg_msmpeg4v1",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (GST_STR_FOURCC ("MPG4")),
+ "width", GST_PROPS_INT_RANGE (0, 4096),
+ "height", GST_PROPS_INT_RANGE (0, 4096)
+ );
+ }
break;
case CODEC_ID_MSMPEG4V2:
- return GST_CAPS_NEW ("ffmpeg_msmpeg4v2",
- "video/avi",
- "format", GST_PROPS_STRING ("strf_vids"),
- "fourcc", GST_PROPS_FOURCC (context->fourcc),
- "width", GST_PROPS_INT (context->width),
- "height", GST_PROPS_INT (context->height)
- );
+ if (context) {
+ return GST_CAPS_NEW ("ffmpeg_msmpeg4v2",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (GST_STR_FOURCC ("MP42")),
+ "width", GST_PROPS_INT (context->width),
+ "height", GST_PROPS_INT (context->height)
+ );
+ }
+ else {
+ return GST_CAPS_NEW ("ffmpeg_msmpeg4v2",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (GST_STR_FOURCC ("MP42")),
+ "width", GST_PROPS_INT_RANGE (0, 4096),
+ "height", GST_PROPS_INT_RANGE (0, 4096)
+ );
+ }
break;
case CODEC_ID_MSMPEG4V3:
- return GST_CAPS_NEW ("ffmpeg_msmpeg4v3",
- "video/avi",
- "format", GST_PROPS_STRING ("strf_vids"),
- "fourcc", GST_PROPS_FOURCC (context->fourcc),
- "width", GST_PROPS_INT (context->width),
- "height", GST_PROPS_INT (context->height)
- );
+ if (context) {
+ return GST_CAPS_NEW ("ffmpeg_msmpeg4v3",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (GST_STR_FOURCC ("DIV3")),
+ "width", GST_PROPS_INT (context->width),
+ "height", GST_PROPS_INT (context->height)
+ );
+ }
+ else {
+ return GST_CAPS_NEW ("ffmpeg_msmpeg4v3",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (GST_STR_FOURCC ("DIV3")),
+ "width", GST_PROPS_INT_RANGE (0, 4096),
+ "height", GST_PROPS_INT_RANGE (0, 4096)
+ );
+ }
break;
case CODEC_ID_WMV1:
- return GST_CAPS_NEW ("ffmpeg_wmv1",
- "video/avi",
- "format", GST_PROPS_STRING ("strf_vids"),
- "fourcc", GST_PROPS_FOURCC (GST_STR_FOURCC ("WMV1")),
- "width", GST_PROPS_INT (context->width),
- "height", GST_PROPS_INT (context->height)
- );
+ if (context) {
+ return GST_CAPS_NEW ("ffmpeg_wmv1",
+ "video/avi",
+ "format", GST_PROPS_STRING ("strf_vids"),
+ "compression", GST_PROPS_FOURCC (GST_STR_FOURCC ("WMV1")),
+ "width", GST_PROPS_INT (context->width),
+ "height", GST_PROPS_INT (context->height)
+ );
+ }
+ else {
+ return GST_CAPS_NEW ("ffmpeg_wmv1",
+ "video/x-wmv1",
+ NULL
+ );
+ }
break;
case CODEC_ID_WMV2:
return GST_CAPS_NEW ("ffmpeg_wmv2",
NULL);
break;
default:
- g_warning ("no caps found for codec id %d\n", context->codec_id);
+ g_warning ("no caps found for codec id %d\n", codec_id);
break;
}
#include <gst/gst.h>
+extern GstCaps* gst_ffmpegcodec_codec_context_to_caps (AVCodecContext *ctx, int id);
+
+
typedef struct _GstFFMpegDec GstFFMpegDec;
struct _GstFFMpegDec {
GstElementClass parent_class;
AVCodec *in_plugin;
+ GstPadTemplate *templ;
};
+typedef struct {
+ AVCodec *in_plugin;
+ GstPadTemplate *templ;
+} GstFFMpegClassParams;
+
#define GST_TYPE_FFMPEGDEC \
(gst_ffmpegdec_get_type())
#define GST_FFMPEGDEC(obj) \
};
/* This factory is much simpler, and defines the source pad. */
-GST_PAD_TEMPLATE_FACTORY (gst_ffmpegdec_sink_factory,
- "sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_CAPS_NEW (
- "ffmpegdec_sink",
- "video/avi",
- "format", GST_PROPS_STRING ("strf_vids")
- ),
- GST_CAPS_NEW (
- "ffmpegdec_sink",
- "video/mpeg",
- NULL
- )
-)
-
-/* This factory is much simpler, and defines the source pad. */
GST_PAD_TEMPLATE_FACTORY (gst_ffmpegdec_audio_src_factory,
"src",
GST_PAD_SRC,
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
+ GstFFMpegClassParams *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,
+ params = g_hash_table_lookup (global_plugins,
GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
+ klass->in_plugin = params->in_plugin;
+ klass->templ = params->templ;
+
gobject_class->set_property = gst_ffmpegdec_set_property;
gobject_class->get_property = gst_ffmpegdec_get_property;
}
gst_caps_get_int (caps, "height", &ffmpegdec->context->height);
ffmpegdec->context->pix_fmt = PIX_FMT_YUV420P;
- ffmpegdec->context->frame_rate = 23 * FRAME_RATE_BASE;
ffmpegdec->context->bit_rate = 0;
/* FIXME bug in ffmpeg */
ffmpegdec->context = g_malloc0 (sizeof (AVCodecContext));
- ffmpegdec->sinkpad = gst_pad_new_from_template (
- GST_PAD_TEMPLATE_GET (gst_ffmpegdec_sink_factory), "sink");
+ ffmpegdec->sinkpad = gst_pad_new_from_template (oclass->templ, "sink");
gst_pad_set_connect_function (ffmpegdec->sinkpad, gst_ffmpegdec_sinkconnect);
if (oclass->in_plugin->type == CODEC_TYPE_VIDEO) {
while (in_plugin) {
gchar *type_name;
gchar *codec_type;
+ GstPadTemplate *sinktempl;
+ GstCaps *sinkcaps;
+ GstFFMpegClassParams *params;
if (in_plugin->decode) {
codec_type = "dec";
details->author = g_strdup("The FFMPEG crew, GStreamer plugin by Wim Taymans <wim.taymans@chello.be>");
details->copyright = g_strdup("(c) 2001");
- g_hash_table_insert (global_plugins,
- GINT_TO_POINTER (type),
- (gpointer) in_plugin);
-
/* 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);
- gst_element_factory_add_pad_template (factory,
- GST_PAD_TEMPLATE_GET (gst_ffmpegdec_sink_factory));
+ sinkcaps = gst_ffmpegcodec_codec_context_to_caps (NULL, in_plugin->id);
+ sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sinkcaps, NULL);
+ gst_element_factory_add_pad_template (factory, sinktempl);
+
+ params = g_new0 (GstFFMpegClassParams, 1);
+ params->in_plugin = in_plugin;
+ params->templ = sinktempl;
+
+ g_hash_table_insert (global_plugins,
+ GINT_TO_POINTER (type),
+ (gpointer) params);
if (in_plugin->type == CODEC_TYPE_VIDEO) {
gst_element_factory_add_pad_template (factory,
#include <gst/gst.h>
-extern URLProtocol gstreamer_protocol;
+extern GstCaps* gst_ffmpegcodec_codec_context_to_caps (AVCodecContext *context, int codec_id);
typedef enum {
STATE_OPEN,
- STATE_STREAM_INFO,
STATE_DEMUX,
- STATE_END,
} DemuxState;
typedef struct _GstFFMpegDemux GstFFMpegDemux;
0,
NULL);
- /* this doesn't work */
- av_set_pts_info (ffmpegdemux->context, 33, 1, 100000);
-
ffmpegdemux->state = STATE_DEMUX;
break;
}
res = av_read_packet(ct, &pkt);
if (res < 0) {
- gint i;
+ if (url_feof (&ct->pb)) {
+ gint i;
- for (i = 0; i < ct->nb_streams; i++) {
- GstPad *pad;
+ for (i = 0; i < ct->nb_streams; i++) {
+ GstPad *pad;
- pad = ffmpegdemux->srcpads[i];
+ pad = ffmpegdemux->srcpads[i];
- if (GST_PAD_IS_USABLE (pad)) {
- gst_pad_push (pad, GST_BUFFER (gst_event_new (GST_EVENT_EOS)));
+ if (GST_PAD_IS_USABLE (pad)) {
+ gst_pad_push (pad, GST_BUFFER (gst_event_new (GST_EVENT_EOS)));
+ }
}
+ gst_element_set_eos (element);
}
- gst_element_set_eos (element);
return;
}
st = ct->streams[pkt.stream_index];
if (st->codec_info_state == 0) {
- gchar *padname = NULL;
- GstPadTemplate *templ = NULL;
+ gchar *templname = NULL;
st->codec_info_state = 1;
if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
- padname = g_strdup_printf ("video_%02d", pkt.stream_index);
- templ = GST_PAD_TEMPLATE_GET (gst_ffmpegdemux_video_src_factory);
+ templname = "video_%02d";
}
else if (st->codec.codec_type == CODEC_TYPE_AUDIO) {
- padname = g_strdup_printf ("audio_%02d", pkt.stream_index);
- templ = GST_PAD_TEMPLATE_GET (gst_ffmpegdemux_audio_src_factory);
+ templname = "audio_%02d";
}
- if (padname != NULL) {
+ 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);
- g_print ("new pad\n");
}
else {
g_warning ("unkown pad type %d", st->codec.codec_type);
outbuf = gst_buffer_new ();
GST_BUFFER_DATA (outbuf) = pkt.data;
GST_BUFFER_SIZE (outbuf) = pkt.size;
- if (pkt.pts != 0) {
- GST_BUFFER_TIMESTAMP (outbuf) = pkt.pts * GST_SECOND / 90000LL;
+
+ 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;
in_plugin = in_plugin->next;
}
- register_protocol (&gstreamer_protocol);
-
return TRUE;
}
struct _GstProtocolInfo
{
- GstPad *pad;
+ GstPad *pad;
- int flags;
+ int flags;
GstByteStream *bs;
+ gboolean eos;
};
static int
}
info->bs = gst_bytestream_new (pad);
+ info->eos = FALSE;
h->priv_data = (void *) info;
info = (GstProtocolInfo *) h->priv_data;
bs = info->bs;
+ if (info->eos)
+ return 0;
+
total = gst_bytestream_peek_bytes (bs, &data, size);
- memcpy (buf, data, total);
- gst_bytestream_flush_fast (bs, total);
+ if (total < size) {
+ GstEvent *event;
+ guint32 remaining;
+
+ gst_bytestream_get_status (bs, &remaining, &event);
+
+ if (!event) {
+ g_warning ("gstffmpegprotocol: no bytestream event");
+ return total;
+ }
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_DISCONTINUOUS:
+ gst_bytestream_flush_fast (bs, remaining);
+ case GST_EVENT_EOS:
+ info->eos = TRUE;
+ break;
+ default:
+ break;
+ }
+ gst_event_unref (event);
+ }
+
+ memcpy (buf, data, total);
+ gst_bytestream_flush (bs, total);
return total;
}
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <libav/avformat.h>
+
+#include <gst/gst.h>
+
+static GHashTable *global_types = NULL;
+
+extern GstCaps* gst_ffmpegcodec_codec_context_to_caps (AVCodecContext *ctx, int id);
+
+static GstCaps*
+gst_ffmpegtypes_typefind (GstBuffer *buffer, gpointer priv)
+{
+ AVInputFormat *in_plugin;
+ AVInputFormat *highest = NULL;
+ gint max = 0;
+ gint res = 0;
+
+ in_plugin = first_iformat;
+
+ while (in_plugin) {
+ if (in_plugin->read_probe) {
+ AVProbeData probe_data;
+
+ probe_data.filename = "";
+ probe_data.buf = GST_BUFFER_DATA (buffer);
+ probe_data.buf_size = GST_BUFFER_SIZE (buffer);
+
+ res = in_plugin->read_probe (&probe_data);
+ if (res > max) {
+ max = res;
+ highest = in_plugin;
+ }
+ }
+ in_plugin = in_plugin->next;
+ }
+ if (highest) {
+ GstCaps *caps;
+ caps = g_hash_table_lookup (global_types, highest->name);
+ return caps;
+ }
+
+ return NULL;
+}
+
+#define ADD_TYPE(key,caps) g_hash_table_insert (global_types, (key), (caps))
+
+static void
+register_standard_formats (void)
+{
+ global_types = g_hash_table_new (g_str_hash, g_str_equal);
+
+ ADD_TYPE ("avi", GST_CAPS_NEW ("ffmpeg_type_avi", "video/avi", NULL));
+ ADD_TYPE ("mpeg", GST_CAPS_NEW ("ffmpeg_type_mpeg", "video/mpeg",
+ "systemstream", GST_PROPS_BOOLEAN (TRUE)));
+ ADD_TYPE ("mpegts", GST_CAPS_NEW ("ffmpeg_type_mpegts", "video/x-mpegts",
+ "systemstream", GST_PROPS_BOOLEAN (TRUE)));
+ ADD_TYPE ("rm", GST_CAPS_NEW ("ffmpeg_type_rm", "audio/x-pn-realaudio", NULL));
+ ADD_TYPE ("asf", GST_CAPS_NEW ("ffmpeg_type_asf", "video/x-ms-asf", NULL));
+ ADD_TYPE ("avi", GST_CAPS_NEW ("ffmpeg_type_avi", "video/avi",
+ "format", GST_PROPS_STRING ("AVI")));
+ ADD_TYPE ("mov", GST_CAPS_NEW ("ffmpeg_type_mov", "video/quicktime", NULL));
+ ADD_TYPE ("swf", GST_CAPS_NEW ("ffmpeg_type_swf", "application/x-shockwave-flash", NULL));
+ ADD_TYPE ("au", GST_CAPS_NEW ("ffmpeg_type_au", "audio/basic", NULL));
+ ADD_TYPE ("mov", GST_CAPS_NEW ("ffmpeg_type_mov", "video/quicktime", NULL));
+}
+
+gboolean
+gst_ffmpegtypes_register (GstPlugin *plugin)
+{
+ AVInputFormat *in_plugin;
+ GstTypeFactory *factory;
+ GstTypeDefinition *definition;
+
+ in_plugin = first_iformat;
+
+ while (in_plugin) {
+ gchar *type_name;
+ gchar *p;
+
+ if (!in_plugin->read_probe)
+ goto next;
+
+ /* construct the type */
+ type_name = g_strdup_printf("fftype_%s", in_plugin->name);
+
+ p = type_name;
+
+ while (*p) {
+ if (*p == '.') *p = '_';
+ p++;
+ }
+
+ definition = g_new0 (GstTypeDefinition, 1);
+ definition->name = type_name;
+ definition->mime = type_name;
+ definition->exts = g_strdup (in_plugin->extensions);
+ definition->typefindfunc = gst_ffmpegtypes_typefind;
+
+ factory = gst_type_factory_new (definition);
+
+ /* The very last thing is to register the elementfactory with the plugin. */
+ gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
+next:
+ in_plugin = in_plugin->next;
+ }
+ register_standard_formats ();
+
+ return TRUE;
+}