commit 428baadc62fae1ae5077af1244584b3166fb4244 Author: Thomas Vander Stichele Date: Sun Dec 23 13:25:04 2001 +0000 adding ffmpeg, but it needs proper handling Original commit message from CVS: adding ffmpeg, but it needs proper handling diff --git a/ext/ffmpeg/Makefile.am b/ext/ffmpeg/Makefile.am new file mode 100644 index 0000000000..76c21e41d3 --- /dev/null +++ b/ext/ffmpeg/Makefile.am @@ -0,0 +1,11 @@ +plugindir = $(libdir)/gst + +plugin_LTLIBRARIES = libgstffmpeg.la + +libgstffmpeg_la_SOURCES = gstffmpeg.c gstffmpegenc.c gstffmpegdec.c + +## FIXME: these really need a kick in the pants ! +## CFLAGS += -I/opt/src/ffmpeg/ +## LDFLAGS += -L/opt/src/ffmpeg/libavcodec -lavcodec -L/opt/src/ffmpeg/libav -lav + +noinst_HEADERS = gstffmpegenc.h gstffmpegdec.h diff --git a/ext/ffmpeg/gstffmpeg.c b/ext/ffmpeg/gstffmpeg.c new file mode 100644 index 0000000000..d3e56e5d92 --- /dev/null +++ b/ext/ffmpeg/gstffmpeg.c @@ -0,0 +1,44 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ + +/* First, include the header file for the plugin, to bring in the + * object definition and other useful things. + */ +#include "gstffmpegdec.h" +#include "gstffmpegenc.h" + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + avcodec_init (); + avcodec_register_all (); + + gst_ffmpegenc_register (plugin); + gst_ffmpegdec_register (plugin); + + /* Now we can return the pointer to the newly created Plugin object. */ + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "ffmpeg", + plugin_init +}; diff --git a/ext/ffmpeg/gstffmpegdec.c b/ext/ffmpeg/gstffmpegdec.c new file mode 100644 index 0000000000..14033cdf47 --- /dev/null +++ b/ext/ffmpeg/gstffmpegdec.c @@ -0,0 +1,378 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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 "gstffmpegdec.h" + +#include + +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + /* FILL ME */ +}; + +/* This factory is much simpler, and defines the source pad. */ +GST_PADTEMPLATE_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") + ) +) + +/* This factory is much simpler, and defines the source pad. */ +GST_PADTEMPLATE_FACTORY (gst_ffmpegdec_audio_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "ffmpegdec_src", + "audio/raw", + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT (16), + "depth", GST_PROPS_INT (16), + "rate", GST_PROPS_INT_RANGE (8000, 96000), + "channels", GST_PROPS_INT_RANGE (1, 2) + ) +) + +/* This factory is much simpler, and defines the source pad. */ +GST_PADTEMPLATE_FACTORY (gst_ffmpegdec_video_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "ffmpegdec_src", + "video/raw", + "format", GST_PROPS_LIST ( + GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")) + ), + "width", GST_PROPS_INT_RANGE (16, 4096), + "height", GST_PROPS_INT_RANGE (16, 4096) + ) +) + +static GHashTable *global_plugins; + +/* A number of functon prototypes are given so we can refer to them later. */ +static void gst_ffmpegdec_class_init (GstFFMpegDecClass *klass); +static void gst_ffmpegdec_init (GstFFMpegDec *ffmpegdec); + +static void gst_ffmpegdec_chain_audio (GstPad *pad, GstBuffer *buffer); +static void gst_ffmpegdec_chain_video (GstPad *pad, GstBuffer *buffer); + +static void gst_ffmpegdec_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *pspec); +static void gst_ffmpegdec_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; + +//static guint gst_ffmpegdec_signals[LAST_SIGNAL] = { 0 }; + +static void +gst_ffmpegdec_class_init (GstFFMpegDecClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + 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))); + + gobject_class->set_property = gst_ffmpegdec_set_property; + gobject_class->get_property = gst_ffmpegdec_get_property; +} + +static void +gst_ffmpegdec_newcaps (GstPad *pad, GstCaps *caps) +{ + GstFFMpegDec *ffmpegdec = (GstFFMpegDec *)(gst_pad_get_parent (pad)); + GstFFMpegDecClass *oclass = (GstFFMpegDecClass*)(G_OBJECT_GET_CLASS (ffmpegdec)); + + ffmpegdec->context->width = gst_caps_get_int (caps, "width"); + ffmpegdec->context->height = gst_caps_get_int (caps, "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 */ + if (avcodec_open (ffmpegdec->context, avcodec_find_encoder(CODEC_ID_MPEG1VIDEO)) <0 ) { + g_warning ("ffmpegdec: could not open codec"); + } + + if (avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0) { + g_warning ("ffmpegdec: could not open codec"); + } +} + +static void +gst_ffmpegdec_init(GstFFMpegDec *ffmpegdec) +{ + GstFFMpegDecClass *oclass = (GstFFMpegDecClass*)(G_OBJECT_GET_CLASS (ffmpegdec)); + + ffmpegdec->context = g_malloc0 (sizeof (AVCodecContext)); + + ffmpegdec->sinkpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (gst_ffmpegdec_sink_factory), "sink"); + gst_pad_set_newcaps_function (ffmpegdec->sinkpad, gst_ffmpegdec_newcaps); + + if (oclass->in_plugin->type == CODEC_TYPE_VIDEO) { + ffmpegdec->srcpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (gst_ffmpegdec_video_src_factory), "src"); + gst_pad_set_chain_function (ffmpegdec->sinkpad, gst_ffmpegdec_chain_video); + } + else if (oclass->in_plugin->type == CODEC_TYPE_AUDIO) { + ffmpegdec->srcpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (gst_ffmpegdec_audio_src_factory), "src"); + gst_pad_set_chain_function (ffmpegdec->sinkpad, gst_ffmpegdec_chain_audio); + } + + gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->sinkpad); + gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->srcpad); + + ffmpegdec->picture = g_malloc0 (sizeof (AVPicture)); +} + +static void +gst_ffmpegdec_chain_audio (GstPad *pad, GstBuffer *inbuf) +{ + //GstFFMpegDec *ffmpegdec = (GstFFMpegDec *)(gst_pad_get_parent (pad)); + gpointer data; + gint size; + + data = GST_BUFFER_DATA (inbuf); + size = GST_BUFFER_SIZE (inbuf); + + GST_DEBUG (0, "got buffer %p %d\n", data, size); + + gst_buffer_unref (inbuf); +} + +static void +gst_ffmpegdec_chain_video (GstPad *pad, GstBuffer *inbuf) +{ + GstBuffer *outbuf; + GstFFMpegDec *ffmpegdec = (GstFFMpegDec *)(gst_pad_get_parent (pad)); + guchar *data; + gint size, frame_size, len; + gint have_picture; + + data = GST_BUFFER_DATA (inbuf); + size = GST_BUFFER_SIZE (inbuf); + + do { + ffmpegdec->context->frame_number++; + + len = avcodec_decode_video (ffmpegdec->context, ffmpegdec->picture, + &have_picture, data, size); + + if (len < 0) { + g_warning ("ffmpegdec: decoding error"); + break; + } + + if (have_picture) { + guchar *picdata, *picdata2, *outdata, *outdata2; + gint xsize, i, width, height; + + width = ffmpegdec->context->width; + height = ffmpegdec->context->height; + + if (!GST_PAD_CAPS (ffmpegdec->srcpad)) { + gst_pad_set_caps (ffmpegdec->srcpad, + GST_CAPS_NEW ( + "ffmpegdec_src", + "video/raw", + "format", GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")), + "width", GST_PROPS_INT (width), + "height", GST_PROPS_INT (height) + )); + } + + frame_size = width * height; + + outbuf = gst_buffer_new (); + GST_BUFFER_SIZE (outbuf) = (frame_size*3)>>1; + outdata = GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf)); + GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf); + + picdata = ffmpegdec->picture->data[0]; + xsize = ffmpegdec->picture->linesize[0]; + for (i=height; i; i--) { + memcpy (outdata, picdata, width); + outdata += width; + picdata += xsize; + } + + frame_size >>= 2; + width >>= 1; + height >>= 1; + outdata2 = outdata + frame_size; + + picdata = ffmpegdec->picture->data[1]; + picdata2 = ffmpegdec->picture->data[2]; + xsize = ffmpegdec->picture->linesize[1]; + for (i=height; i; i--) { + memcpy (outdata, picdata, width); + memcpy (outdata2, picdata2, width); + outdata += width; outdata2 += width; + picdata += xsize; picdata2 += xsize; + } + + gst_pad_push (ffmpegdec->srcpad, outbuf); + } + + size -= len; + data += len; + } + while (size > 0); + + gst_buffer_unref (inbuf); +} + +static void +gst_ffmpegdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstFFMpegDec *ffmpegdec; + + /* Get a pointer of the right type. */ + ffmpegdec = (GstFFMpegDec *)(object); + + /* 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; + } +} + +/* The set function is simply the inverse of the get fuction. */ +static void +gst_ffmpegdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstFFMpegDec *ffmpegdec; + + /* It's not null if we got it, but it might not be ours */ + ffmpegdec = (GstFFMpegDec *)(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_ffmpegdec_register (GstPlugin *plugin) +{ + GstElementFactory *factory; + GTypeInfo typeinfo = { + sizeof(GstFFMpegDecClass), + NULL, + NULL, + (GClassInitFunc)gst_ffmpegdec_class_init, + NULL, + NULL, + sizeof(GstFFMpegDec), + 0, + (GInstanceInitFunc)gst_ffmpegdec_init, + }; + GType type; + GstElementDetails *details; + AVCodec *in_plugin; + + in_plugin = first_avcodec; + + global_plugins = g_hash_table_new (NULL, NULL); + + while (in_plugin) { + gchar *type_name; + gchar *codec_type; + + if (in_plugin->decode) { + codec_type = "dec"; + } + else { + goto next; + } + // construct the type + type_name = g_strdup_printf("ffmpeg%s_%s", codec_type, in_plugin->name); + + // if it's already registered, drop it + if (g_type_from_name(type_name)) { + g_free(type_name); + goto next; + } + + // create the gtk 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/FFMpeg"; + 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) 2001"); + + g_hash_table_insert (global_plugins, + GINT_TO_POINTER (type), + (gpointer) in_plugin); + + // register the plugin with gstreamer + factory = gst_elementfactory_new(type_name,type,details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, + GST_PADTEMPLATE_GET (gst_ffmpegdec_sink_factory)); + + if (in_plugin->type == CODEC_TYPE_VIDEO) { + gst_elementfactory_add_padtemplate (factory, + GST_PADTEMPLATE_GET (gst_ffmpegdec_video_src_factory)); + } + else if (in_plugin->type == CODEC_TYPE_AUDIO) { + gst_elementfactory_add_padtemplate (factory, + GST_PADTEMPLATE_GET (gst_ffmpegdec_audio_src_factory)); + } + + /* 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; + } + + return TRUE; +} diff --git a/ext/ffmpeg/gstffmpegdec.h b/ext/ffmpeg/gstffmpegdec.h new file mode 100644 index 0000000000..4eefb9eb84 --- /dev/null +++ b/ext/ffmpeg/gstffmpegdec.h @@ -0,0 +1,95 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ + + +#ifndef __GST_FFMPEGDEC_H__ +#define __GST_FFMPEGDEC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef __GST_FFMPEG_AV_H__ +#define __GST_FFMPEG_AV_H__ +#include +#endif /* __GST_FFMPEG_AV_H__ */ + +/* This is the definition of the element's object structure. */ +typedef struct _GstFFMpegDec GstFFMpegDec; + +/* The structure itself is derived from GstElement, as can be seen by the + * fact that there's a complete instance of the GstElement structure at + * the beginning of the object. This allows the element to be cast to + * an Element or even an Object. + */ +struct _GstFFMpegDec { + GstElement element; + + /* We need to keep track of our pads, so we do so here. */ + GstPad *srcpad; + GstPad *sinkpad; + + AVCodecContext *context; + AVPicture *picture; +}; + +/* The other half of the object is its class. The class also derives from + * the same parent, though it must be the class structure this time. + * Function pointers for polymophic methods and signals are placed in this + * structure. */ +typedef struct _GstFFMpegDecClass GstFFMpegDecClass; + +struct _GstFFMpegDecClass { + GstElementClass parent_class; + + AVCodec *in_plugin; +}; + +/* Five standard preprocessing macros are used in the Gtk+ object system. + * The first uses the object's _get_type function to return the GType + * of the object. + */ +#define GST_TYPE_FFMPEGDEC \ + (gst_ffmpegdec_get_type()) +/* The second is a checking cast to the correct type. If the object passed + * is not the right type, a warning will be generated on stderr. + */ +#define GST_FFMPEGDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEC,GstFFMpegDec)) +/* The third is a checking cast of the class instead of the object. */ +#define GST_FFMPEGDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEC,GstFFMpegDec)) +/* The last two simply check to see if the passed pointer is an object or + * class of the correct type. */ +#define GST_IS_FFMPEGDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEC)) +#define GST_IS_FFMPEGDEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEC)) + +gboolean gst_ffmpegdec_register (GstPlugin *plugin); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_FFMPEGDEC_H__ */ diff --git a/ext/ffmpeg/gstffmpegenc.c b/ext/ffmpeg/gstffmpegenc.c new file mode 100644 index 0000000000..deab9cb78d --- /dev/null +++ b/ext/ffmpeg/gstffmpegenc.c @@ -0,0 +1,552 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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 "gstffmpegenc.h" + +#include + +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_WIDTH, + ARG_HEIGHT, + ARG_BIT_RATE, + ARG_FRAME_RATE, + ARG_SAMPLE_RATE, + ARG_GOP_SIZE, + ARG_HQ, + ARG_ME_METHOD, + /* FILL ME */ +}; + +/* This factory is much simpler, and defines the source pad. */ +GST_PADTEMPLATE_FACTORY (gst_ffmpegenc_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "ffmpegenc_src", + "unknown/unknown", + NULL + ) +) + +/* This factory is much simpler, and defines the source pad. */ +GST_PADTEMPLATE_FACTORY (gst_ffmpegenc_audio_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "ffmpegenc_sink", + "audio/raw", + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT (16), + "depth", GST_PROPS_INT (16), + "rate", GST_PROPS_INT_RANGE (8000, 96000), + "channels", GST_PROPS_INT_RANGE (1, 2) + ) +) + +/* This factory is much simpler, and defines the source pad. */ +GST_PADTEMPLATE_FACTORY (gst_ffmpegenc_video_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "ffmpegenc_sink", + "video/raw", + "format", GST_PROPS_LIST ( + GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")), + GST_PROPS_FOURCC (GST_STR_FOURCC ("YUY2")) + ), + "width", GST_PROPS_INT_RANGE (16, 4096), + "height", GST_PROPS_INT_RANGE (16, 4096) + ) +) + +#define GST_TYPE_ME_METHOD (gst_ffmpegenc_me_method_get_type()) +static GType +gst_ffmpegenc_me_method_get_type (void) +{ + static GType ffmpegenc_me_method_type = 0; + static GEnumValue ffmpegenc_me_methods[] = { + { ME_ZERO, "0", "zero" }, + { ME_FULL, "1", "full" }, + { ME_LOG, "2", "logarithmic" }, + { ME_PHODS, "3", "phods" }, + { 0, NULL, NULL }, + }; + if (!ffmpegenc_me_method_type) { + ffmpegenc_me_method_type = g_enum_register_static ("GstFFMpegEncMeMethod", ffmpegenc_me_methods); + } + return ffmpegenc_me_method_type; +} + +static GHashTable *enc_global_plugins; + +/* A number of functon prototypes are given so we can refer to them later. */ +static void gst_ffmpegenc_class_init (GstFFMpegEncClass *klass); +static void gst_ffmpegenc_init (GstFFMpegEnc *ffmpegenc); + +static void gst_ffmpegenc_chain_audio (GstPad *pad, GstBuffer *buffer); +static void gst_ffmpegenc_chain_video (GstPad *pad, GstBuffer *buffer); + +static void gst_ffmpegenc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_ffmpegenc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; + +//static guint gst_ffmpegenc_signals[LAST_SIGNAL] = { 0 }; + +static void +gst_ffmpegenc_class_init (GstFFMpegEncClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + klass->in_plugin = g_hash_table_lookup (enc_global_plugins, + GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class))); + + if (klass->in_plugin->type == CODEC_TYPE_VIDEO) { + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_WIDTH, + g_param_spec_int ("width","width","width", + 0, G_MAXINT, -1, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_HEIGHT, + g_param_spec_int ("height","height","height", + 0, G_MAXINT, -1, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_BIT_RATE, + g_param_spec_int ("bit_rate","bit_rate","bit_rate", + 0, G_MAXINT, 300000, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_FRAME_RATE, + g_param_spec_int ("frame_rate","frame_rate","frame_rate", + 0, G_MAXINT, 25, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_GOP_SIZE, + g_param_spec_int ("gop_size","gop_size","gop_size", + 0, G_MAXINT, 15, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_HQ, + g_param_spec_boolean ("hq","hq","hq", + FALSE, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_ME_METHOD, + g_param_spec_enum ("me_method","me_method","me_method", + GST_TYPE_ME_METHOD, ME_LOG, G_PARAM_READWRITE)); + } + else if (klass->in_plugin->type == CODEC_TYPE_AUDIO) { + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_BIT_RATE, + g_param_spec_int ("bit_rate","bit_rate","bit_rate", + 0, G_MAXINT, 128000, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_SAMPLE_RATE, + g_param_spec_int ("sample_rate","rate","rate", + 0, G_MAXINT, -1, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_HQ, + g_param_spec_boolean ("hq","hq","hq", + FALSE, G_PARAM_READWRITE)); + } + + gobject_class->set_property = gst_ffmpegenc_set_property; + gobject_class->get_property = gst_ffmpegenc_get_property; +} + +static void +gst_ffmpegenc_newcaps (GstPad *pad, GstCaps *caps) +{ + GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) gst_pad_get_parent (pad); + GstFFMpegEncClass *oclass = (GstFFMpegEncClass*)(G_OBJECT_GET_CLASS(ffmpegenc)); + + if (strstr (gst_caps_get_mime (caps), "audio/raw")) { + ffmpegenc->context->sample_rate = gst_caps_get_int (caps, "rate"); + ffmpegenc->context->channels = gst_caps_get_int (caps, "channels"); + } + else if (strstr (gst_caps_get_mime (caps), "video/raw")) { + ffmpegenc->in_width = gst_caps_get_int (caps, "width"); + ffmpegenc->in_height = gst_caps_get_int (caps, "height"); + if (ffmpegenc->need_resample) { + ffmpegenc->context->width = ffmpegenc->out_width; + ffmpegenc->context->height = ffmpegenc->out_height; + } + else { + ffmpegenc->context->width = ffmpegenc->in_width; + ffmpegenc->context->height = ffmpegenc->in_height; + } + if (gst_caps_get_fourcc_int (caps, "format") == GST_STR_FOURCC ("I420")) { + ffmpegenc->context->pix_fmt = PIX_FMT_YUV420P; + } + else { + ffmpegenc->context->pix_fmt = PIX_FMT_YUV422; + } + + ffmpegenc->resample = img_resample_init (ffmpegenc->context->width, ffmpegenc->context->height, + ffmpegenc->in_width, ffmpegenc->in_height); + } + else { + g_warning ("ffmpegenc: invalid caps %s\n", gst_caps_get_mime (caps)); + } + + if (avcodec_open (ffmpegenc->context, oclass->in_plugin) < 0) { + g_warning ("ffmpegenc: could not open codec\n"); + } + + if (oclass->in_plugin->type == CODEC_TYPE_AUDIO) { + ffmpegenc->buffer = g_malloc (ffmpegenc->context->frame_size * 2 * + ffmpegenc->context->channels); + ffmpegenc->buffer_pos = 0; + } +} + +static void +gst_ffmpegenc_init(GstFFMpegEnc *ffmpegenc) +{ + GstFFMpegEncClass *oclass = (GstFFMpegEncClass*)(G_OBJECT_GET_CLASS (ffmpegenc)); + + ffmpegenc->context = g_malloc0 (sizeof (AVCodecContext)); + + if (oclass->in_plugin->type == CODEC_TYPE_VIDEO) { + ffmpegenc->sinkpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (gst_ffmpegenc_video_sink_factory), "sink"); + gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_video); + ffmpegenc->context->bit_rate = 300000; + ffmpegenc->context->gop_size = 15; + ffmpegenc->context->frame_rate = 25; + ffmpegenc->out_width = -1; + ffmpegenc->out_height = -1; + } + else if (oclass->in_plugin->type == CODEC_TYPE_AUDIO) { + ffmpegenc->sinkpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (gst_ffmpegenc_audio_sink_factory), "sink"); + gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_audio); + ffmpegenc->context->bit_rate = 128000; + ffmpegenc->context->sample_rate = -1; + } + + gst_pad_set_newcaps_function (ffmpegenc->sinkpad, gst_ffmpegenc_newcaps); + gst_element_add_pad (GST_ELEMENT (ffmpegenc), ffmpegenc->sinkpad); + + ffmpegenc->srcpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (gst_ffmpegenc_src_factory), "src"); + gst_element_add_pad (GST_ELEMENT (ffmpegenc), ffmpegenc->srcpad); + + /* Initialization of element's private variables. */ + ffmpegenc->need_resample = FALSE; +} + +static void +gst_ffmpegenc_chain_audio (GstPad *pad, GstBuffer *inbuf) +{ + GstBuffer *outbuf; + GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *)(gst_pad_get_parent (pad)); + gpointer data; + gint size; + gint frame_size = ffmpegenc->context->frame_size * ffmpegenc->context->channels * 2; + + data = GST_BUFFER_DATA (inbuf); + size = GST_BUFFER_SIZE (inbuf); + + GST_DEBUG (0, "got buffer %p %d\n", data, size); + + if (ffmpegenc->buffer_pos && (ffmpegenc->buffer_pos + size >= frame_size)) { + + memcpy (ffmpegenc->buffer + ffmpegenc->buffer_pos, data, frame_size - ffmpegenc->buffer_pos); + + outbuf = gst_buffer_new (); + GST_BUFFER_SIZE (outbuf) = frame_size; + GST_BUFFER_DATA (outbuf) = g_malloc (frame_size); + + GST_BUFFER_SIZE (outbuf) = avcodec_encode_audio (ffmpegenc->context, GST_BUFFER_DATA (outbuf), + GST_BUFFER_SIZE (outbuf), (const short *)ffmpegenc->buffer); + + gst_pad_push (ffmpegenc->srcpad, outbuf); + + size -= (frame_size - ffmpegenc->buffer_pos); + data += (frame_size - ffmpegenc->buffer_pos); + + ffmpegenc->buffer_pos = 0; + } + while (size >= frame_size) { + + outbuf = gst_buffer_new (); + GST_BUFFER_SIZE (outbuf) = frame_size; + GST_BUFFER_DATA (outbuf) = g_malloc (frame_size); + + GST_BUFFER_SIZE (outbuf) = avcodec_encode_audio (ffmpegenc->context, GST_BUFFER_DATA (outbuf), + GST_BUFFER_SIZE (outbuf), (const short *)data); + + gst_pad_push (ffmpegenc->srcpad, outbuf); + + size -= frame_size; + data += frame_size; + } + + // save leftover + if (size) { + memcpy (ffmpegenc->buffer + ffmpegenc->buffer_pos, data, size); + ffmpegenc->buffer_pos += size; + } + + gst_buffer_unref (inbuf); +} + +static void +gst_ffmpegenc_chain_video (GstPad *pad, GstBuffer *inbuf) +{ + GstBuffer *outbuf; + GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *)(gst_pad_get_parent (pad)); + gpointer data; + gint size, frame_size; + AVPicture picture, rpicture, *toencode; + gboolean free_data = FALSE, free_res = FALSE; + + data = GST_BUFFER_DATA (inbuf); + size = GST_BUFFER_SIZE (inbuf); + + frame_size = ffmpegenc->in_width * ffmpegenc->in_height; + + switch (ffmpegenc->context->pix_fmt) { + case PIX_FMT_YUV422: + { + guchar *temp; + + temp = g_malloc ((frame_size * 3) /2 ); + size = (frame_size * 3)/2; + + img_convert_to_yuv420 (temp, data, PIX_FMT_YUV422, + ffmpegenc->in_width, ffmpegenc->in_height); + data = temp; + free_data = TRUE; + break; + } + default: + break; + } + + picture.data[0] = data; + picture.data[1] = picture.data[0] + frame_size; + picture.data[2] = picture.data[1] + (frame_size >> 2); + picture.linesize[0] = ffmpegenc->in_width; + picture.linesize[1] = ffmpegenc->in_width >> 1; + picture.linesize[2] = ffmpegenc->in_width >> 1; + toencode = &picture; + + if (ffmpegenc->need_resample) { + gint rframe_size = ffmpegenc->context->width * ffmpegenc->context->height; + + rpicture.data[0] = g_malloc ((rframe_size * 3)/2); + rpicture.data[1] = rpicture.data[0] + rframe_size; + rpicture.data[2] = rpicture.data[1] + (rframe_size >> 2); + rpicture.linesize[0] = ffmpegenc->context->width; + rpicture.linesize[1] = ffmpegenc->context->width >> 1; + rpicture.linesize[2] = ffmpegenc->context->width >> 1; + free_res = TRUE; + toencode = &rpicture; + + img_resample (ffmpegenc->resample, &rpicture, &picture); + } + + outbuf = gst_buffer_new (); + GST_BUFFER_SIZE (outbuf) = size; + GST_BUFFER_DATA (outbuf) = g_malloc (size); + + GST_BUFFER_SIZE (outbuf) = avcodec_encode_video (ffmpegenc->context, GST_BUFFER_DATA (outbuf), + GST_BUFFER_SIZE (outbuf), toencode); + + gst_pad_push (ffmpegenc->srcpad, outbuf); + + if (free_data) + g_free (data); + if (free_res) + g_free (rpicture.data[0]); + + gst_buffer_unref (inbuf); +} + +static void +gst_ffmpegenc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstFFMpegEnc *ffmpegenc; + + /* Get a pointer of the right type. */ + ffmpegenc = (GstFFMpegEnc *)(object); + + /* Check the argument id to see which argument we're setting. */ + switch (prop_id) { + case ARG_WIDTH: + ffmpegenc->out_width = g_value_get_int (value); + ffmpegenc->need_resample = (ffmpegenc->out_width != -1); + break; + case ARG_HEIGHT: + ffmpegenc->out_height = g_value_get_int (value); + ffmpegenc->need_resample = (ffmpegenc->out_height != -1); + break; + case ARG_BIT_RATE: + ffmpegenc->context->bit_rate = g_value_get_int (value); + break; + case ARG_FRAME_RATE: + ffmpegenc->context->frame_rate = g_value_get_int (value); + break; + case ARG_SAMPLE_RATE: + ffmpegenc->context->sample_rate = g_value_get_int (value); + break; + case ARG_GOP_SIZE: + ffmpegenc->context->gop_size = g_value_get_int (value); + break; + case ARG_HQ: + ffmpegenc->context->flags &= ~CODEC_FLAG_HQ; + ffmpegenc->context->flags |= (g_value_get_boolean (value)?CODEC_FLAG_HQ:0); + break; + case ARG_ME_METHOD: + motion_estimation_method = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* The set function is simply the inverse of the get fuction. */ +static void +gst_ffmpegenc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstFFMpegEnc *ffmpegenc; + + /* It's not null if we got it, but it might not be ours */ + ffmpegenc = (GstFFMpegEnc *)(object); + + switch (prop_id) { + case ARG_WIDTH: + g_value_set_int (value, ffmpegenc->out_width); + break; + case ARG_HEIGHT: + g_value_set_int (value, ffmpegenc->out_height); + break; + case ARG_BIT_RATE: + g_value_set_int (value, ffmpegenc->context->bit_rate); + break; + case ARG_SAMPLE_RATE: + g_value_set_int (value, ffmpegenc->context->sample_rate); + break; + case ARG_FRAME_RATE: + g_value_set_int (value, ffmpegenc->context->frame_rate); + break; + case ARG_GOP_SIZE: + g_value_set_int (value, ffmpegenc->context->gop_size); + break; + case ARG_HQ: + g_value_set_boolean (value, ffmpegenc->context->flags & CODEC_FLAG_HQ); + break; + case ARG_ME_METHOD: + g_value_set_enum (value, motion_estimation_method); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_ffmpegenc_register (GstPlugin *plugin) +{ + GstElementFactory *factory; + GTypeInfo typeinfo = { + sizeof(GstFFMpegEncClass), + NULL, + NULL, + (GClassInitFunc)gst_ffmpegenc_class_init, + NULL, + NULL, + sizeof(GstFFMpegEnc), + 0, + (GInstanceInitFunc)gst_ffmpegenc_init, + }; + GType type; + GstElementDetails *details; + AVCodec *in_plugin; + + in_plugin = first_avcodec; + + enc_global_plugins = g_hash_table_new (NULL, NULL); + + while (in_plugin) { + gchar *type_name; + gchar *codec_type; + + if (in_plugin->encode) { + codec_type = "enc"; + } + else { + goto next; + } + // construct the type + type_name = g_strdup_printf("ffmpeg%s_%s", codec_type, in_plugin->name); + + // if it's already registered, drop it + if (g_type_from_name(type_name)) { + g_free(type_name); + goto next; + } + + // create the gtk 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/FFMpeg"; + 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) 2001"); + + g_hash_table_insert (enc_global_plugins, + GINT_TO_POINTER (type), + (gpointer) in_plugin); + + // register the plugin with gstreamer + factory = gst_elementfactory_new(type_name,type,details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, + GST_PADTEMPLATE_GET (gst_ffmpegenc_src_factory)); + if (in_plugin->type == CODEC_TYPE_VIDEO) { + gst_elementfactory_add_padtemplate (factory, + GST_PADTEMPLATE_GET (gst_ffmpegenc_video_sink_factory)); + } + else if (in_plugin->type == CODEC_TYPE_AUDIO) { + gst_elementfactory_add_padtemplate (factory, + GST_PADTEMPLATE_GET (gst_ffmpegenc_audio_sink_factory)); + } + + /* 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; + } + + return TRUE; +} diff --git a/ext/ffmpeg/gstffmpegenc.h b/ext/ffmpeg/gstffmpegenc.h new file mode 100644 index 0000000000..54a20c465f --- /dev/null +++ b/ext/ffmpeg/gstffmpegenc.h @@ -0,0 +1,101 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ + + +#ifndef __GST_FFMPEGENC_H__ +#define __GST_FFMPEGENC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef __GST_FFMPEG_AV_H__ +#define __GST_FFMPEG_AV_H__ +#include +#endif /* __GST_FFMPEG_AV_H__ */ + +/* This is the definition of the element's object structure. */ +typedef struct _GstFFMpegEnc GstFFMpegEnc; + +/* The structure itself is derived from GstElement, as can be seen by the + * fact that there's a complete instance of the GstElement structure at + * the beginning of the object. This allows the element to be cast to + * an Element or even an Object. + */ +struct _GstFFMpegEnc { + GstElement element; + + /* We need to keep track of our pads, so we do so here. */ + GstPad *srcpad; + GstPad *sinkpad; + + gboolean need_resample; + gint in_width, in_height; + gint out_width, out_height; + + guchar *buffer; + gint buffer_pos; + + AVCodecContext *context; + ImgReSampleContext *resample; +}; + +/* The other half of the object is its class. The class also derives from + * the same parent, though it must be the class structure this time. + * Function pointers for polymophic methods and signals are placed in this + * structure. */ +typedef struct _GstFFMpegEncClass GstFFMpegEncClass; + +struct _GstFFMpegEncClass { + GstElementClass parent_class; + + AVCodec *in_plugin; +}; + +/* Five standard preprocessing macros are used in the Gtk+ object system. + * The first uses the object's _get_type function to return the GType + * of the object. + */ +#define GST_TYPE_FFMPEGENC \ + (gst_ffmpegenc_get_type()) +/* The second is a checking cast to the correct type. If the object passed + * is not the right type, a warning will be generated on stderr. + */ +#define GST_FFMPEGENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGENC,GstFFMpegEnc)) +/* The third is a checking cast of the class instead of the object. */ +#define GST_FFMPEGENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGENC,GstFFMpegEnc)) +/* The last two simply check to see if the passed pointer is an object or + * class of the correct type. */ +#define GST_IS_FFMPEGENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGENC)) +#define GST_IS_FFMPEGENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGENC)) + +gboolean gst_ffmpegenc_register (GstPlugin *plugin); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_FFMPEGENC_H__ */