From ab57fbcd544e5226cb57aef7d54370f58e8badf4 Mon Sep 17 00:00:00 2001 From: "Ronald S. Bultje" Date: Mon, 21 Apr 2003 21:54:27 +0000 Subject: Adds divx/xvid encoders. divx encoder is based on divx4linux (commercial, closed-source) Original commit message from CVS: Adds divx/xvid encoders. * divx encoder is based on divx4linux (commercial, closed-source) * xvid encoder is based on xvidcore (http://www.xvid.org/, GPL - Christian? ;) ) Both use a GstCaps that doesn't conform with what we currently use, I might fix that later on or so. For now, it doesn't matter, it's just a test. We're also missing corresponding decoders (ffmpeg can decoded this too, but that's not the point), these might come later too. --- configure.ac | 33 ++++ ext/Makefile.am | 21 ++- ext/divx/Makefile.am | 10 + ext/divx/gstdivxenc.c | 469 +++++++++++++++++++++++++++++++++++++++++++++++ ext/divx/gstdivxenc.h | 78 ++++++++ ext/xvid/Makefile.am | 10 + ext/xvid/gstxvidenc.c | 494 ++++++++++++++++++++++++++++++++++++++++++++++++++ ext/xvid/gstxvidenc.h | 78 ++++++++ 8 files changed, 1189 insertions(+), 4 deletions(-) create mode 100644 ext/divx/Makefile.am create mode 100644 ext/divx/gstdivxenc.c create mode 100644 ext/divx/gstdivxenc.h create mode 100644 ext/xvid/Makefile.am create mode 100644 ext/xvid/gstxvidenc.c create mode 100644 ext/xvid/gstxvidenc.h diff --git a/configure.ac b/configure.ac index c9401d1e..c88fb58f 100644 --- a/configure.ac +++ b/configure.ac @@ -506,6 +506,21 @@ dnl FIXME : add second check somehow if that is necessary dnl AC_CHECK_LIB(cdda_paranoia, paranoia_init, : , HAVE_CDPARANOIA=no, -lcdda_interface ) dnl AC_CHECK_HEADER(cdda_paranoia.h, :, HAVE_CDPARANOIA=no) +dnl *** DIVX *** +translit(dnm, m, l) AM_CONDITIONAL(USE_DIVX, true) +GST_CHECK_FEATURE(DIVX, [divx plugins], divx, [ + HAVE_DIVX=yes + AC_CHECK_HEADER(encore2.h, , + [ AC_MSG_WARN([Divx4linux headers not found]) && + HAVE_DIVX=no ] ) + LIBS="-lm" + AC_CHECK_LIB(divxencore, encore, , + [ AC_MSG_WARN([Divx4linux libs not found]) && + HAVE_DIVX=no ] ) + DIVX_LIBS="-lxvidcore -lm" + AC_SUBST(DIVX_LIBS) +]) + dnl *** dvdread *** translit(dnm, m, l) AM_CONDITIONAL(USE_DVDREAD, true) GST_CHECK_FEATURE(DVDREAD, [dvdread library], dvdreadsrc, [ @@ -875,6 +890,22 @@ GST_CHECK_FEATURE(XMMS, [xmms plug-in], xmms, [ AM_PATH_XMMS(0.1.0, HAVE_XMMS=yes, HAVE_XMMS=no) ]) +dnl *** XVID *** +translit(dnm, m, l) AM_CONDITIONAL(USE_XVID, true) +GST_CHECK_FEATURE(XVID, [xvid plugins], xvid, [ + HAVE_XVID=yes + AC_CHECK_HEADER(xvid.h, , + [ AC_MSG_WARN([Xvid headers not found]) && + HAVE_XVID=no ] ) + LIBS="-lm" + AC_CHECK_LIB(xvidcore, xvid_encore, , + [ AC_MSG_WARN([Xvid libs not found]) && + HAVE_XVID=no ] ) + XVID_LIBS="-lxvidcore -lm" + AC_SUBST(XVID_LIBS) +]) + + fi dnl of EXT plugins dnl Check for atomic.h @@ -1127,6 +1158,7 @@ ext/artsd/Makefile ext/audiofile/Makefile ext/avifile/Makefile ext/cdparanoia/Makefile +ext/divx/Makefile ext/dv/Makefile ext/dvdread/Makefile ext/dvdnav/Makefile @@ -1162,6 +1194,7 @@ ext/swfdec/Makefile ext/vorbis/Makefile ext/tarkin/Makefile ext/xmms/Makefile +ext/xvid/Makefile gst-libs/Makefile gst-libs/gst/Makefile gst-libs/gst/audio/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 752bfbc2..cc849f97 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -46,6 +46,12 @@ else CDPARANOIA_DIR= endif +if USE_DIVX +DIVX_DIR=divx +else +DIVX_DIR= +endif + if USE_DVDREAD DVDREAD_DIR=dvdread else @@ -244,6 +250,12 @@ else VORBIS_DIR= endif +if USE_XVID +XVID_DIR=xvid +else +XVID_DIR= +endif + if USE_XMMS XMMS_DIR=xmms else @@ -257,8 +269,8 @@ SNAPSHOT_DIR= endif SUBDIRS=$(A52DEC_DIR) $(AALIB_DIR) $(ALSA_DIR) \ - $(ARTS_DIR) $(ARTSC_DIR) $(AUDIOFILE_DIR) \ - $(AVIFILE_DIR) $(CDPARANOIA_DIR) \ + $(ARTS_DIR) $(ARTSC_DIR) $(AUDIOFILE_DIR) \ + $(AVIFILE_DIR) $(CDPARANOIA_DIR) $(DIVX_DIR) \ $(DVDREAD_DIR) $(DVDNAV_DIR) $(ESD_DIR) $(MAS_DIR) \ $(FFMPEG_DIR) $(FLAC_DIR) $(GNOMEVFS_DIR) $(GSM_DIR) \ $(HERMES_DIR) $(HTTP_DIR) $(JACK_DIR) $(JPEG_DIR) \ @@ -268,7 +280,7 @@ SUBDIRS=$(A52DEC_DIR) $(AALIB_DIR) $(ALSA_DIR) \ $(OPENQUICKTIME_DIR) $(RAW1394_DIR) \ $(SDL_DIR) $(SHOUT_DIR) $(SIDPLAY_DIR) \ $(SMOOTHWAVE_DIR) $(SWFDEC_DIR) $(TARKIN_DIR) \ - $(VORBIS_DIR) $(XMMS_DIR) $(SNAPSHOT_DIR) + $(VORBIS_DIR) $(XVID_DIR) $(XMMS_DIR) $(SNAPSHOT_DIR) DIST_SUBDIRS=\ a52dec aalib alsa \ @@ -281,4 +293,5 @@ DIST_SUBDIRS=\ mad mikmod mjpegtools mpeg2dec \ openquicktime raw1394 \ sdl snapshot shout shout2 sidplay \ - smoothwave swfdec tarkin vorbis xmms + smoothwave swfdec tarkin vorbis \ + xmms xvid diff --git a/ext/divx/Makefile.am b/ext/divx/Makefile.am new file mode 100644 index 00000000..e6a6eaa5 --- /dev/null +++ b/ext/divx/Makefile.am @@ -0,0 +1,10 @@ +plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +plugin_LTLIBRARIES = libgstdivx.la + +libgstdivx_la_SOURCES = gstdivxenc.c +libgstdivx_la_CFLAGS = $(GST_CFLAGS) $(DIVX_CFLAGS) +libgstdivx_la_LIBADD = $(DIVX_LIBS) +libgstdivx_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstdivxenc.h diff --git a/ext/divx/gstdivxenc.c b/ext/divx/gstdivxenc.c new file mode 100644 index 00000000..207d1e19 --- /dev/null +++ b/ext/divx/gstdivxenc.c @@ -0,0 +1,469 @@ +/* GStreamer divx encoder plugin + * Copyright (C) 2003 Ronald Bultje + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstdivxenc.h" +#include +#include + +/* elementfactory information */ +GstElementDetails gst_divxenc_details = { + "Divx encoder", + "Codec/Video/Encoder", + "Commercial", + "Divx encoder based on divxencore", + VERSION, + "Ronald Bultje ", + "(C) 2003", +}; + +GST_PAD_TEMPLATE_FACTORY(sink_template, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW("divxenc_sink", + "video/raw", + "format", GST_PROPS_LIST( + GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','4','2','0')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','Y','U','V')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','U','Y','2')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','V','1','2')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('U','Y','V','Y')) + ), + "width", GST_PROPS_INT_RANGE(0, G_MAXINT), + "height", GST_PROPS_INT_RANGE(0, G_MAXINT), + NULL) +) + +GST_PAD_TEMPLATE_FACTORY(src_template, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW("divxenc_sink", + "video/divx", + NULL) +) + + +/* DivxEnc signals and args */ +enum { + FRAME_ENCODED, + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_BITRATE, + ARG_MAXKEYINTERVAL, + ARG_BUFSIZE +}; + + +static void gst_divxenc_class_init (GstDivxEncClass *klass); +static void gst_divxenc_init (GstDivxEnc *divxenc); +static void gst_divxenc_chain (GstPad *pad, + GstBuffer *buf); +static GstPadLinkReturn gst_divxenc_connect (GstPad *pad, + GstCaps *vscapslist); + +/* properties */ +static void gst_divxenc_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gst_divxenc_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; +static guint gst_divxenc_signals[LAST_SIGNAL] = { 0 }; + + +GType +gst_divxenc_get_type(void) +{ + static GType divxenc_type = 0; + + if (!divxenc_type) + { + static const GTypeInfo divxenc_info = { + sizeof(GstDivxEncClass), + NULL, + NULL, + (GClassInitFunc) gst_divxenc_class_init, + NULL, + NULL, + sizeof(GstDivxEnc), + 0, + (GInstanceInitFunc) gst_divxenc_init, + }; + divxenc_type = g_type_register_static(GST_TYPE_ELEMENT, + "GstDivxEnc", + &divxenc_info, 0); + } + return divxenc_type; +} + + +static void +gst_divxenc_class_init (GstDivxEncClass *klass) +{ + GstElementClass *gstelement_class; + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BITRATE, + g_param_spec_ulong("bitrate","Bitrate", + "Target video bitrate", + 0,G_MAXULONG,0,G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MAXKEYINTERVAL, + g_param_spec_int("max_key_interval","Max. Key Interval", + "Maximum number of frames between two keyframes", + 0,G_MAXINT,0,G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFSIZE, + g_param_spec_ulong("buffer_size", "Buffer Size", + "Size of the video buffers", + 0,G_MAXULONG,0,G_PARAM_READWRITE)); + + gobject_class->set_property = gst_divxenc_set_property; + gobject_class->get_property = gst_divxenc_get_property; + + gst_divxenc_signals[FRAME_ENCODED] = + g_signal_new ("frame_encoded", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstDivxEncClass, frame_encoded), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + + +static void +gst_divxenc_init (GstDivxEnc *divxenc) +{ + /* create the sink pad */ + divxenc->sinkpad = gst_pad_new_from_template( + GST_PAD_TEMPLATE_GET(sink_template), + "sink"); + gst_element_add_pad(GST_ELEMENT(divxenc), divxenc->sinkpad); + + gst_pad_set_chain_function(divxenc->sinkpad, gst_divxenc_chain); + gst_pad_set_link_function(divxenc->sinkpad, gst_divxenc_connect); + + /* create the src pad */ + divxenc->sinkpad = gst_pad_new_from_template( + GST_PAD_TEMPLATE_GET(src_template), + "src"); + gst_element_add_pad(GST_ELEMENT(divxenc), divxenc->srcpad); + + /* bitrate, etc. */ + divxenc->width = divxenc->height = divxenc->csp = -1; + divxenc->bitrate = 512 * 1024; + divxenc->max_key_interval = -1; /* default - 2*fps */ + divxenc->buffer_size = 512 * 1024; + + /* set divx handle to NULL */ + divxenc->handle = NULL; +} + + +static gboolean +gst_divxenc_setup (GstDivxEnc *divxenc) +{ + ENC_PARAM xenc; + gdouble fps; + int ret; + + fps = gst_video_frame_rate(GST_PAD_PEER(divxenc->sinkpad)); + + /* set it up */ + memset(&xenc, 0, sizeof(ENC_PARAM)); + xenc.x_dim = divxenc->width; + xenc.y_dim = divxenc->height; + xenc.framerate = fps; + + xenc.rc_period = 2000; + xenc.rc_reaction_period = 10; + xenc.rc_reaction_ratio = 20; + + xenc.max_quantizer = 31; + xenc.min_quantizer = 1; + + xenc.quality = 3; + xenc.bitrate = divxenc->bitrate; + + xenc.deinterlace = 0; + + xenc.max_key_interval = (divxenc->max_key_interval == -1) ? + (2 * xenc.framerate) : + divxenc->max_key_interval; + xenc.handle = NULL; + + if ((ret = encore(NULL, ENC_OPT_INIT, &xenc, NULL))) { + gst_element_error(GST_ELEMENT(divxenc), + "Error setting up divx encoder: %d\n", + ret); + return FALSE; + } + + divxenc->handle = xenc.handle; + + return TRUE; +} + + +static void +gst_divxenc_chain (GstPad *pad, + GstBuffer *buf) +{ + GstDivxEnc *divxenc; + GstBuffer *outbuf; + ENC_FRAME xframe; + ENC_RESULT xres; + int ret; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + divxenc = GST_DIVXENC(GST_OBJECT_PARENT(pad)); + + if (!divxenc->handle) { + if (!gst_divxenc_setup(divxenc)) { + gst_buffer_unref(buf); + return; + } + } + + outbuf = gst_buffer_new_and_alloc(divxenc->buffer_size); + GST_BUFFER_TIMESTAMP(outbuf) = GST_BUFFER_TIMESTAMP(buf); + + /* encode and so ... */ + xframe.image = GST_BUFFER_DATA(buf); + xframe.bitstream = (void *) GST_BUFFER_DATA(outbuf); + xframe.length = GST_BUFFER_MAXSIZE(outbuf); + xframe.mvs = NULL; + xframe.colorspace = divxenc->csp; + + if ((ret = encore(divxenc->handle, ENC_OPT_ENCODE, + &xframe, &xres))) { + gst_element_error(GST_ELEMENT(divxenc), + "Error encoding divx frame: %d\n", ret); + gst_buffer_unref(buf); + return; + } + + GST_BUFFER_SIZE(outbuf) = xframe.length; + if (xres.is_key_frame) + GST_BUFFER_FLAG_SET(outbuf, GST_BUFFER_KEY_UNIT); + + /* go out, multiply! */ + gst_pad_push(divxenc->srcpad, outbuf); + + /* proclaim destiny */ + g_signal_emit(G_OBJECT(divxenc),gst_divxenc_signals[FRAME_ENCODED], 0); + + /* until the final judgement */ + gst_buffer_unref(buf); +} + + +static GstPadLinkReturn +gst_divxenc_connect (GstPad *pad, + GstCaps *vscaps) +{ + GstDivxEnc *divxenc; + GstCaps *caps; + + divxenc = GST_DIVXENC(gst_pad_get_parent (pad)); + + /* if there's something old around, remove it */ + if (divxenc->handle) { + encore(divxenc->handle, ENC_OPT_RELEASE, NULL, NULL); + divxenc->handle = NULL; + } + + /* we are not going to act on variable caps */ + if (!GST_CAPS_IS_FIXED(vscaps)) + return GST_PAD_LINK_DELAYED; + + for (caps = vscaps; caps != NULL; caps = caps->next) + { + int w,h,d; + guint32 fourcc; + gint divx_cs; + gst_caps_get_int(caps, "width", &w); + gst_caps_get_int(caps, "height", &h); + gst_caps_get_fourcc_int(caps, "format", &fourcc); + + switch (fourcc) + { + case GST_MAKE_FOURCC('I','4','2','0'): + case GST_MAKE_FOURCC('I','Y','U','V'): + divx_cs = ENC_CSP_I420; + break; + case GST_MAKE_FOURCC('Y','U','Y','2'): + divx_cs = ENC_CSP_YUY2; + break; + case GST_MAKE_FOURCC('Y','V','1','2'): + divx_cs = ENC_CSP_YV12; + break; + case GST_MAKE_FOURCC('U','Y','V','Y'): + divx_cs = ENC_CSP_UYVY; + break; + case GST_MAKE_FOURCC('R','G','B',' '): + gst_caps_get_int(caps, "depth", &d); + switch (d) { + case 24: + divx_cs = ENC_CSP_RGB24; + break; + case 32: + divx_cs = ENC_CSP_RGB32; + break; + default: + goto trynext; + } + break; + default: + goto trynext; + } + + /* grmbl, we only know the peer pad *after* + * linking, so we accept here, get the fps on + * the first cycle and set it all up then */ + divxenc->csp = divx_cs; + divxenc->width = w; + divxenc->height = h; + return gst_pad_try_set_caps(divxenc->srcpad, + GST_CAPS_NEW("divxenc_src_caps", + "video/divx", + "width", GST_PROPS_INT(w), + "height", GST_PROPS_INT(h))); + +trynext: + continue; + } + + /* if we got here - it's not good */ + return GST_PAD_LINK_REFUSED; +} + + +static void +gst_divxenc_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GstDivxEnc *divxenc; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_DIVXENC (object)); + divxenc = GST_DIVXENC(object); + + switch (prop_id) + { + case ARG_BITRATE: + divxenc->bitrate = g_value_get_ulong(value); + break; + case ARG_BUFSIZE: + divxenc->buffer_size = g_value_get_ulong(value); + break; + case ARG_MAXKEYINTERVAL: + divxenc->max_key_interval = g_value_get_int(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_divxenc_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GstDivxEnc *divxenc; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_DIVXENC (object)); + divxenc = GST_DIVXENC(object); + + switch (prop_id) { + case ARG_BITRATE: + g_value_set_ulong(value, divxenc->bitrate); + break; + case ARG_BUFSIZE: + g_value_set_ulong(value, divxenc->buffer_size); + break; + case ARG_MAXKEYINTERVAL: + g_value_set_int(value, divxenc->max_key_interval); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +plugin_init (GModule *module, + GstPlugin *plugin) +{ + GstElementFactory *factory; + + if (!gst_library_load("gstvideo")) + return FALSE; + + /* create an elementfactory for the v4lmjpegsrcparse element */ + factory = gst_element_factory_new("divxenc", GST_TYPE_DIVXENC, + &gst_divxenc_details); + g_return_val_if_fail(factory != NULL, FALSE); + + /* add pad templates */ + gst_element_factory_add_pad_template(factory, + GST_PAD_TEMPLATE_GET(sink_template)); + gst_element_factory_add_pad_template(factory, + GST_PAD_TEMPLATE_GET(src_template)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "divxenc", + plugin_init +}; diff --git a/ext/divx/gstdivxenc.h b/ext/divx/gstdivxenc.h new file mode 100644 index 00000000..fac3b421 --- /dev/null +++ b/ext/divx/gstdivxenc.h @@ -0,0 +1,78 @@ +/* GStreamer divx encoder plugin + * Copyright (C) 2003 Ronald Bultje + * + * 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_DIVXENC_H__ +#define __GST_DIVXENC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_DIVXENC \ + (gst_divxenc_get_type()) +#define GST_DIVXENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_DIVXENC, GstDivxEnc)) +#define GST_DIVXENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_DIVXENC, GstDivxEnc)) +#define GST_IS_DIVXENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_DIVXENC)) +#define GST_IS_DIVXENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_DIVXENC)) + +typedef struct _GstDivxEnc GstDivxEnc; +typedef struct _GstDivxEncClass GstDivxEncClass; + +struct _GstDivxEnc { + GstElement element; + + /* pads */ + GstPad *sinkpad, *srcpad; + + /* quality of encoded image */ + gulong bitrate; + + /* size of the buffers */ + gulong buffer_size; + + /* max key interval */ + gint max_key_interval; + + /* divx handle */ + void *handle; + int csp; + int width, height; +}; + +struct _GstDivxEncClass { + GstElementClass parent_class; + + /* signals */ + void (*frame_encoded) (GstElement *element); +}; + +GType gst_divxenc_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GST_DIVXENC_H__ */ diff --git a/ext/xvid/Makefile.am b/ext/xvid/Makefile.am new file mode 100644 index 00000000..267b1ccc --- /dev/null +++ b/ext/xvid/Makefile.am @@ -0,0 +1,10 @@ +plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +plugin_LTLIBRARIES = libgstxvid.la + +libgstxvid_la_SOURCES = gstxvidenc.c +libgstxvid_la_CFLAGS = $(GST_CFLAGS) $(XVID_CFLAGS) +libgstxvid_la_LIBADD = $(XVID_LIBS) +libgstxvid_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstxvidenc.h diff --git a/ext/xvid/gstxvidenc.c b/ext/xvid/gstxvidenc.c new file mode 100644 index 00000000..e916bda1 --- /dev/null +++ b/ext/xvid/gstxvidenc.c @@ -0,0 +1,494 @@ +/* GStreamer xvid encoder plugin + * Copyright (C) 2003 Ronald Bultje + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstxvidenc.h" +#include +#include + +/* elementfactory information */ +GstElementDetails gst_xvidenc_details = { + "Xvid encoder", + "Codec/Video/Encoder", + "GPL", + "Xvid encoder based on xvidencore", + VERSION, + "Ronald Bultje ", + "(C) 2003", +}; + +GST_PAD_TEMPLATE_FACTORY(sink_template, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW("xvidenc_sink", + "video/raw", + "format", GST_PROPS_LIST( + GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','4','2','0')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','Y','U','V')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','U','Y','2')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','V','1','2')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','V','Y','U')), + GST_PROPS_FOURCC(GST_MAKE_FOURCC('U','Y','V','Y')) + ), + "width", GST_PROPS_INT_RANGE(0, G_MAXINT), + "height", GST_PROPS_INT_RANGE(0, G_MAXINT), + NULL) +) + +GST_PAD_TEMPLATE_FACTORY(src_template, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW("xvidenc_sink", + "video/xvid", + NULL) +) + + +/* XvidEnc signals and args */ +enum { + FRAME_ENCODED, + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_BITRATE, + ARG_MAXKEYINTERVAL, + ARG_BUFSIZE +}; + + +static void gst_xvidenc_class_init (GstXvidEncClass *klass); +static void gst_xvidenc_init (GstXvidEnc *xvidenc); +static void gst_xvidenc_chain (GstPad *pad, + GstBuffer *buf); +static GstPadLinkReturn gst_xvidenc_connect (GstPad *pad, + GstCaps *vscapslist); + +/* properties */ +static void gst_xvidenc_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gst_xvidenc_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; +static guint gst_xvidenc_signals[LAST_SIGNAL] = { 0 }; + + +GType +gst_xvidenc_get_type(void) +{ + static GType xvidenc_type = 0; + + if (!xvidenc_type) + { + static const GTypeInfo xvidenc_info = { + sizeof(GstXvidEncClass), + NULL, + NULL, + (GClassInitFunc) gst_xvidenc_class_init, + NULL, + NULL, + sizeof(GstXvidEnc), + 0, + (GInstanceInitFunc) gst_xvidenc_init, + }; + xvidenc_type = g_type_register_static(GST_TYPE_ELEMENT, + "GstXvidEnc", + &xvidenc_info, 0); + } + return xvidenc_type; +} + + +static void +gst_xvidenc_class_init (GstXvidEncClass *klass) +{ + XVID_INIT_PARAM xinit; + GstElementClass *gstelement_class; + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + /* set up xvid initially (function pointers, CPU flags) */ + memset(&xinit, 0, sizeof(xinit)); + xinit.cpu_flags = 0; + xvid_init(NULL, 0, &xinit, NULL); + if (xinit.api_version != API_VERSION) { + g_error("Xvid API version mismatch! %d.%d (that's us) != %d.%d (lib)", + (API_VERSION >> 8) & 0xff, API_VERSION & 0xff, + (xinit.api_version >> 8) & 0xff, xinit.api_version & 0xff); + return; + } + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BITRATE, + g_param_spec_ulong("bitrate","Bitrate", + "Target video bitrate", + 0,G_MAXULONG,0,G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MAXKEYINTERVAL, + g_param_spec_int("max_key_interval","Max. Key Interval", + "Maximum number of frames between two keyframes", + 0,G_MAXINT,0,G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFSIZE, + g_param_spec_ulong("buffer_size", "Buffer Size", + "Size of the video buffers", + 0,G_MAXULONG,0,G_PARAM_READWRITE)); + + gobject_class->set_property = gst_xvidenc_set_property; + gobject_class->get_property = gst_xvidenc_get_property; + + gst_xvidenc_signals[FRAME_ENCODED] = + g_signal_new ("frame_encoded", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstXvidEncClass, frame_encoded), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + + +static void +gst_xvidenc_init (GstXvidEnc *xvidenc) +{ + /* create the sink pad */ + xvidenc->sinkpad = gst_pad_new_from_template( + GST_PAD_TEMPLATE_GET(sink_template), + "sink"); + gst_element_add_pad(GST_ELEMENT(xvidenc), xvidenc->sinkpad); + + gst_pad_set_chain_function(xvidenc->sinkpad, gst_xvidenc_chain); + gst_pad_set_link_function(xvidenc->sinkpad, gst_xvidenc_connect); + + /* create the src pad */ + xvidenc->sinkpad = gst_pad_new_from_template( + GST_PAD_TEMPLATE_GET(src_template), + "src"); + gst_element_add_pad(GST_ELEMENT(xvidenc), xvidenc->srcpad); + + /* bitrate, etc. */ + xvidenc->width = xvidenc->height = xvidenc->csp = -1; + xvidenc->bitrate = 512 * 1024; + xvidenc->max_key_interval = -1; /* default - 2*fps */ + xvidenc->buffer_size = 512 * 1024; + + /* set xvid handle to NULL */ + xvidenc->handle = NULL; +} + + +static gboolean +gst_xvidenc_setup (GstXvidEnc *xvidenc) +{ + XVID_ENC_PARAM xenc; + gdouble fps; + int ret; + + fps = gst_video_frame_rate(GST_PAD_PEER(xvidenc->sinkpad)); + + /* set up xvid codec parameters - grab docs from + * xvid.org for more info */ + memset(&xenc, 0, sizeof(XVID_ENC_PARAM)); + xenc.width = xvidenc->width; + xenc.height = xvidenc->height; + xenc.fincr = (int)(fps * 1000); + xenc.fbase = 1000; + xenc.rc_bitrate = xvidenc->bitrate; + xenc.rc_reaction_delay_factor = -1; + xenc.rc_averaging_period = -1; + xenc.rc_buffer = -1; + xenc.min_quantizer = 1; + xenc.max_quantizer = 31; + xenc.max_key_interval = (xvidenc->max_key_interval == -1) ? + (2 * xenc.fincr / xenc.fbase) : + xvidenc->max_key_interval; + xenc.handle = NULL; + + if ((ret = xvid_encore(NULL, XVID_ENC_CREATE, + &xenc, NULL)) != XVID_ERR_OK) { + gst_element_error(GST_ELEMENT(xvidenc), + "Error setting up xvid encoder: %d\n", ret); + return FALSE; + } + + xvidenc->handle = xenc.handle; + + return TRUE; +} + + +static void +gst_xvidenc_chain (GstPad *pad, + GstBuffer *buf) +{ + GstXvidEnc *xvidenc; + GstBuffer *outbuf; + XVID_ENC_FRAME xframe; + int ret; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + xvidenc = GST_XVIDENC(GST_OBJECT_PARENT(pad)); + + if (!xvidenc->handle) { + if (!gst_xvidenc_setup(xvidenc)) { + gst_buffer_unref(buf); + return; + } + } + + outbuf = gst_buffer_new_and_alloc(xvidenc->buffer_size); + GST_BUFFER_TIMESTAMP(outbuf) = GST_BUFFER_TIMESTAMP(buf); + + /* encode and so ... */ + xframe.image = GST_BUFFER_DATA(buf); + xframe.bitstream = (void *) GST_BUFFER_DATA(outbuf); + xframe.length = GST_BUFFER_MAXSIZE(outbuf); + xframe.intra = -1; + xframe.quant = 0; + xframe.colorspace = xvidenc->csp; + xframe.general = XVID_H263QUANT | + XVID_INTER4V | + XVID_HALFPEL; + xframe.motion = PMV_EARLYSTOP16 | + PMV_HALFPELREFINE16 | + PMV_EXTSEARCH16 | + PMV_EARLYSTOP8 | + PMV_HALFPELREFINE8; + + if ((ret = xvid_encore(xvidenc->handle, XVID_ENC_ENCODE, + &xframe, NULL)) != XVID_ERR_OK) { + gst_element_error(GST_ELEMENT(xvidenc), + "Error encoding xvid frame: %d\n", ret); + gst_buffer_unref(buf); + return; + } + + GST_BUFFER_SIZE(outbuf) = xframe.length; + if (xframe.intra) + GST_BUFFER_FLAG_SET(outbuf, GST_BUFFER_KEY_UNIT); + + /* go out, multiply! */ + gst_pad_push(xvidenc->srcpad, outbuf); + + /* proclaim destiny */ + g_signal_emit(G_OBJECT(xvidenc),gst_xvidenc_signals[FRAME_ENCODED], 0); + + /* until the final judgement */ + gst_buffer_unref(buf); +} + + +static GstPadLinkReturn +gst_xvidenc_connect (GstPad *pad, + GstCaps *vscaps) +{ + GstXvidEnc *xvidenc; + GstCaps *caps; + + xvidenc = GST_XVIDENC(gst_pad_get_parent (pad)); + + /* if there's something old around, remove it */ + if (xvidenc->handle) { + xvid_encore(xvidenc->handle, XVID_ENC_DESTROY, NULL, NULL); + xvidenc->handle = NULL; + } + + /* we are not going to act on variable caps */ + if (!GST_CAPS_IS_FIXED(vscaps)) + return GST_PAD_LINK_DELAYED; + + for (caps = vscaps; caps != NULL; caps = caps->next) + { + int w,h,d; + guint32 fourcc; + gint xvid_cs; + gst_caps_get_int(caps, "width", &w); + gst_caps_get_int(caps, "height", &h); + gst_caps_get_fourcc_int(caps, "format", &fourcc); + + switch (fourcc) + { + case GST_MAKE_FOURCC('I','4','2','0'): + case GST_MAKE_FOURCC('I','Y','U','V'): + xvid_cs = XVID_CSP_I420; + break; + case GST_MAKE_FOURCC('Y','U','Y','2'): + xvid_cs = XVID_CSP_YUY2; + break; + case GST_MAKE_FOURCC('Y','V','1','2'): + xvid_cs = XVID_CSP_YV12; + break; + case GST_MAKE_FOURCC('U','Y','V','Y'): + xvid_cs = XVID_CSP_UYVY; + break; + case GST_MAKE_FOURCC('Y','V','Y','U'): + xvid_cs = XVID_CSP_YVYU; + break; + case GST_MAKE_FOURCC('R','G','B',' '): + gst_caps_get_int(caps, "depth", &d); + switch (d) { + case 15: + xvid_cs = XVID_CSP_RGB555; + break; + case 16: + xvid_cs = XVID_CSP_RGB565; + break; + case 24: + xvid_cs = XVID_CSP_RGB24; + break; + case 32: + xvid_cs = XVID_CSP_RGB32; + break; + default: + goto trynext; + } + break; + default: + goto trynext; + } + + /* grmbl, we only know the peer pad *after* + * linking, so we accept here, get the fps on + * the first cycle and set it all up then */ + xvidenc->csp = xvid_cs; + xvidenc->width = w; + xvidenc->height = h; + return gst_pad_try_set_caps(xvidenc->srcpad, + GST_CAPS_NEW("xvidenc_src_caps", + "video/xvid", + "width", GST_PROPS_INT(w), + "height", GST_PROPS_INT(h))); + +trynext: + continue; + } + + /* if we got here - it's not good */ + return GST_PAD_LINK_REFUSED; +} + + +static void +gst_xvidenc_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GstXvidEnc *xvidenc; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_XVIDENC (object)); + xvidenc = GST_XVIDENC(object); + + switch (prop_id) + { + case ARG_BITRATE: + xvidenc->bitrate = g_value_get_ulong(value); + break; + case ARG_BUFSIZE: + xvidenc->buffer_size = g_value_get_ulong(value); + break; + case ARG_MAXKEYINTERVAL: + xvidenc->max_key_interval = g_value_get_int(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_xvidenc_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GstXvidEnc *xvidenc; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_XVIDENC (object)); + xvidenc = GST_XVIDENC(object); + + switch (prop_id) { + case ARG_BITRATE: + g_value_set_ulong(value, xvidenc->bitrate); + break; + case ARG_BUFSIZE: + g_value_set_ulong(value, xvidenc->buffer_size); + break; + case ARG_MAXKEYINTERVAL: + g_value_set_int(value, xvidenc->max_key_interval); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +plugin_init (GModule *module, + GstPlugin *plugin) +{ + GstElementFactory *factory; + + if (!gst_library_load("gstvideo")) + return FALSE; + + /* create an elementfactory for the v4lmjpegsrcparse element */ + factory = gst_element_factory_new("xvidenc", GST_TYPE_XVIDENC, + &gst_xvidenc_details); + g_return_val_if_fail(factory != NULL, FALSE); + + /* add pad templates */ + gst_element_factory_add_pad_template(factory, + GST_PAD_TEMPLATE_GET(sink_template)); + gst_element_factory_add_pad_template(factory, + GST_PAD_TEMPLATE_GET(src_template)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "xvidenc", + plugin_init +}; diff --git a/ext/xvid/gstxvidenc.h b/ext/xvid/gstxvidenc.h new file mode 100644 index 00000000..24cf6a2a --- /dev/null +++ b/ext/xvid/gstxvidenc.h @@ -0,0 +1,78 @@ +/* GStreamer xvid encoder plugin + * Copyright (C) 2003 Ronald Bultje + * + * 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_XVIDENC_H__ +#define __GST_XVIDENC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_XVIDENC \ + (gst_xvidenc_get_type()) +#define GST_XVIDENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_XVIDENC, GstXvidEnc)) +#define GST_XVIDENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_XVIDENC, GstXvidEnc)) +#define GST_IS_XVIDENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_XVIDENC)) +#define GST_IS_XVIDENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_XVIDENC)) + +typedef struct _GstXvidEnc GstXvidEnc; +typedef struct _GstXvidEncClass GstXvidEncClass; + +struct _GstXvidEnc { + GstElement element; + + /* pads */ + GstPad *sinkpad, *srcpad; + + /* quality of encoded JPEG image */ + gulong bitrate; + + /* size of the JPEG buffers */ + gulong buffer_size; + + /* max key interval */ + gint max_key_interval; + + /* xvid handle */ + void *handle; + int csp; + int width, height; +}; + +struct _GstXvidEncClass { + GstElementClass parent_class; + + /* signals */ + void (*frame_encoded) (GstElement *element); +}; + +GType gst_xvidenc_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GST_XVIDENC_H__ */ -- cgit v1.2.1