diff options
Diffstat (limited to 'ext/xvid/gstxvidenc.c')
-rw-r--r-- | ext/xvid/gstxvidenc.c | 494 |
1 files changed, 494 insertions, 0 deletions
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 <rbultje@ronald.bitfreak.net> + * + * 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 <string.h> +#include "gstxvidenc.h" +#include <gst/video/video.h> +#include <xvid.h> + +/* elementfactory information */ +GstElementDetails gst_xvidenc_details = { + "Xvid encoder", + "Codec/Video/Encoder", + "GPL", + "Xvid encoder based on xvidencore", + VERSION, + "Ronald Bultje <rbultje@ronald.bitfreak.net>", + "(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 +}; |