diff -r e2baa6947dbf -r 16857017fc11 gmyth-stream/gmencoder/src/gmencoder.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gmyth-stream/gmencoder/src/gmencoder.c Mon Sep 24 16:07:47 2007 +0100 @@ -0,0 +1,1207 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmencoder.h" + +#define G_MENCODER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), G_TYPE_MENCODER, GMencoderPrivate)) + +#define USE_MANUAL_SINK +#define GMENCODER_TIMEOUT 5000 + +typedef struct _GMencoderPrivate GMencoderPrivate; +typedef struct _SetupInfo SetupInfo; + +struct _SetupInfo { + gchar *video_encode; + gchar *mux_name; + gchar **video_encode_prop; + gdouble video_fps; + gdouble video_rate; + guint video_width; + guint video_height; + gchar *audio_encode; + gchar **audio_encode_prop; + guint audio_rate; +}; + + +struct _GMencoderPrivate { + GstElement *pipe; + GstElement *abin; + GstElement *vbin; + GstElement *sink; + GstElement *src; + + GnomeVFSHandle *handle; + + gboolean ready; + SetupInfo *info; + GstClockTime videot; + GstClockTime audiot; + gint sources; + gint tick_id; + gint64 duration; + gboolean send_chunked; + gint timeout_id; + + //V4l info + GstElement *v4lsrc; + gchar *channel; + gchar *norm; + glong frequency; +}; + +enum { + PAUSED, + PLAYING, + STOPED, + EOS, + ERROR, + LAST_SIGNAL +}; + +static void g_mencoder_class_init(GMencoderClass * klass); +static void g_mencoder_init(GMencoder * object); +static void g_mencoder_dispose(GObject * object); +static void g_mencoder_finalize(GObject * object); +static GstElement *_create_audio_bin(const gchar * encode, + gchar ** encode_prop, gint rate); +static GstElement *_create_video_bin(const gchar * encode, + gchar ** encode_prop, + gdouble fps, + gint rate, guint width, guint height, + gboolean use_deinterlace); + +static gboolean +_pipeline_bus_cb(GstBus * bus, GstMessage * msg, gpointer user_data); + +static void _decodebin_new_pad_cb(GstElement * object, + GstPad * pad, + gboolean flag, gpointer user_data); + +static void _decodebin_unknown_type_cb(GstElement * object, + GstPad * pad, + GstCaps * caps, + gpointer user_data); + +static void _close_output(GMencoder * self); +static gboolean _open_output(GMencoder * self, const gchar * uri); + +static GstElement *_create_source(GMencoder *self, const gchar * uri); +static GstElement *_create_pipeline(GMencoder * self, + const gchar * video_encode, + const gchar * mux_name, + gchar ** video_encode_prop, + gdouble video_fps, + gdouble video_rate, + guint video_width, + guint video_height, + const gchar * audio_encode, + gchar ** audio_encode_prop, + guint audio_rate, + gboolean deinterlace); +static gboolean _process_timeout_cb (gpointer user_data); +#ifdef USE_MANUAL_SINK +static void _flush_queue (GMencoder *self); +static void _buffer_arrive_cb (GstElement* object, + GstBuffer* buff, + GstPad* pad, + gpointer user_data); +#endif + + +static gboolean _tick_cb(gpointer data); + +static guint g_mencoder_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE(GMencoder, g_mencoder, G_TYPE_OBJECT) + +static void g_mencoder_class_init(GMencoderClass * klass) +{ + GObjectClass *object_class; + object_class = (GObjectClass *) klass; + g_type_class_add_private(klass, sizeof(GMencoderPrivate)); + + object_class->dispose = g_mencoder_dispose; + object_class->finalize = g_mencoder_finalize; + + g_mencoder_signals[PAUSED] = + g_signal_new("paused", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + g_mencoder_signals[PLAYING] = + g_signal_new("playing", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + g_mencoder_signals[STOPED] = + g_signal_new("stoped", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + g_mencoder_signals[EOS] = + g_signal_new("eos", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + g_mencoder_signals[ERROR] = + g_signal_new("error", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + +static void +g_mencoder_init(GMencoder * self) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + priv->info = g_new0(SetupInfo, 1); +} + +static void +g_mencoder_dispose(GObject * object) +{ +} + +static void +g_mencoder_finalize(GObject * object) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(object); + + // TODO: clear vars + g_mencoder_close_stream(G_MENCODER(object)); + g_free (priv->info); +} + +GMencoder * +g_mencoder_new(void) +{ + return g_object_new(G_TYPE_MENCODER, NULL); +} + + +static void +_obj_set_prop(GObject * obj, const gchar * prop_name, + const gchar * prop_val) +{ + GValue p = { 0 }; + GValue v = { 0 }; + GParamSpec *s = NULL; + GObjectClass *k = G_OBJECT_GET_CLASS(obj); + + + g_value_init(&v, G_TYPE_STRING); + g_value_set_string(&v, prop_val); + + s = g_object_class_find_property(k, prop_name); + if (s == NULL) { + g_print("Invalid property name: %s\n", prop_name); + return; + } + + g_value_init(&p, s->value_type); + switch (s->value_type) { + case G_TYPE_INT: + g_value_set_int(&p, atoi(prop_val)); + break; + case G_TYPE_ULONG: + g_value_set_ulong (&p, atol(prop_val)); + break; + case G_TYPE_STRING: + g_value_set_string(&p, prop_val); + break; + case G_TYPE_BOOLEAN: + g_value_set_boolean(&p, (gboolean) atoi (prop_val)); + break; + case G_TYPE_DOUBLE: + g_value_set_double(&p, atof (prop_val)); + break; + case G_TYPE_FLOAT: + g_value_set_float(&p, (float) atof (prop_val)); + break; + default: + g_value_set_enum(&p, atoi(prop_val)); + g_warning ("Property %s of type %s. Not supported using default enum", + prop_name, g_type_name (s->value_type)); + return; + } + + g_object_set_property(obj, prop_name, &p); + g_value_unset(&v); + g_value_unset(&p); +} + +static GstElement * +_create_element_with_prop(const gchar * factory_name, + const gchar * element_name, gchar ** prop) +{ + GstElement *ret; + int i; + + ret = gst_element_factory_make(factory_name, element_name); + if (ret == NULL) + return NULL; + + if (prop != NULL) { + for (i = 0; i < g_strv_length(prop); i++) { + if (prop[i] != NULL) { + char **v = g_strsplit(prop[i], "=", 2); + if (g_strv_length(v) == 2) { + _obj_set_prop(G_OBJECT(ret), v[0], v[1]); + } + g_strfreev(v); + } + } + } + + return ret; + +} + +static GstElement * +_create_audio_bin(const gchar * encode, gchar ** encode_prop, gint rate) +{ + GstElement *abin = NULL; + GstElement *aqueue = NULL; + GstElement *aconvert = NULL; + GstElement *aencode = NULL; + GstElement *aqueue_src = NULL; + GstPad *apad = NULL; + + // audio/x-raw-int ! queue ! audioconvert ! faac ! rtpmp4gpay ! + // udpsink name=upd_audio host=224.0.0.1 port=5002 + abin = gst_bin_new("abin"); + aqueue = gst_element_factory_make("queue", "aqueue"); + aconvert = gst_element_factory_make("audioconvert", "aconvert"); + aencode = + _create_element_with_prop((encode ? encode : "lame"), "aencode", + encode_prop); + aqueue_src = gst_element_factory_make("queue", "aqueue_src"); + + if ((abin == NULL) || (aqueue == NULL) || (aconvert == NULL) + || (aencode == NULL) || (aqueue_src == NULL)) { + g_warning("Audio elements not found"); + goto error; + } + + g_object_set(G_OBJECT(aencode), "bitrate", 32, NULL); + /* + * if (rate > 0) { g_object_set (G_OBJECT (aencode), "bitrate", 32, + * NULL); } + */ + + gst_bin_add_many(GST_BIN(abin), aqueue, aconvert, aencode, aqueue_src, + NULL); + if (gst_element_link_many(aqueue, aconvert, aencode, aqueue_src, NULL) + == FALSE) { + g_warning("Not Link audio elements"); + } + // TODO: apply audio rate + + // ghost pad the audio bin + apad = gst_element_get_pad(aqueue, "sink"); + gst_element_add_pad(abin, gst_ghost_pad_new("sink", apad)); + gst_object_unref(apad); + + apad = gst_element_get_pad(aqueue_src, "src"); + gst_element_add_pad(abin, gst_ghost_pad_new("src", apad)); + gst_object_unref(apad); + + return abin; + error: + if (abin != NULL) + gst_object_unref(abin); + + if (aqueue != NULL) + gst_object_unref(aqueue); + + if (aconvert != NULL) + gst_object_unref(aconvert); + + if (aencode != NULL) + gst_object_unref(aencode); + + if (aqueue_src != NULL) + gst_object_unref(aqueue_src); + + if (apad != NULL) + gst_object_unref(apad); + + return NULL; +} + + + + +// queue ! videoscale ! video/x-raw-yuv,width=240,height=144 ! colorspace +// ! rate ! encode ! queue +static GstElement * +_create_video_bin(const gchar * encode, + gchar ** encode_prop, + gdouble fps, gint rate, guint width, guint height, + gboolean use_deinterlace) +{ + GstElement *vbin = NULL; + GstElement *vqueue = NULL; + GstElement *vqueue_src = NULL; + GstElement *vcolorspace = NULL; + GstElement *vencode = NULL; + GstElement *vrate = NULL; + GstElement *deinterlace = NULL; + GstElement *walk = NULL; + GstPad *vpad = NULL; + + vbin = gst_bin_new("vbin"); + vqueue = gst_element_factory_make("queue", "vqueue"); + vcolorspace = + gst_element_factory_make("ffmpegcolorspace", "colorspace"); + + if (use_deinterlace) { + deinterlace = gst_element_factory_make ("ffdeinterlace", "deinterlace"); + if (deinterlace == NULL) { + g_warning ("Fail to create deinterlace element: Continue without deinterlace."); + } + } + + + vencode = _create_element_with_prop((encode != + NULL ? encode : + "ffenc_mpeg1video"), "vencode", + encode_prop); + vqueue_src = gst_element_factory_make("queue", "queue_src"); + + if ((vbin == NULL) || (vqueue == NULL) || (vcolorspace == NULL) + || (vencode == NULL) || (vqueue_src == NULL)) { + g_warning("Video elements not found"); + goto error; + } + + gst_bin_add_many(GST_BIN(vbin), vqueue, vcolorspace, vencode, + vqueue_src, NULL); + + if (deinterlace != NULL) { + gst_bin_add(GST_BIN(vbin), deinterlace); + gst_element_link (vqueue, deinterlace); + walk = deinterlace; + } else { + walk = vqueue; + } + + if ((width > 0) && (height > 0)) { + // Scalling video + GstCaps *vcaps; + GstElement *vscale = + gst_element_factory_make("videoscale", "vscale"); + + g_object_set (G_OBJECT (vscale), "method", 1, NULL); + + gst_bin_add(GST_BIN(vbin), vscale); + + vcaps = gst_caps_new_simple("video/x-raw-yuv", + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, NULL); + + gst_element_link(walk, vscale); + + if (gst_element_link_filtered(vscale, vcolorspace, vcaps) == FALSE) { + g_warning("Fail to resize video"); + gst_object_unref(vcaps); + gst_object_unref(vscale); + goto error; + } + gst_caps_unref(vcaps); + } else { + gst_element_link(walk, vcolorspace); + } + + if (fps > 0) { + // Changing the video fps + GstCaps *vcaps; + vrate = gst_element_factory_make("videorate", "vrate"); + + gst_bin_add(GST_BIN(vbin), vrate); + + if (gst_element_link(vcolorspace, vrate) == FALSE) { + g_warning("Fail to link video elements"); + goto error; + } + + vcaps = gst_caps_new_simple("video/x-raw-yuv", + "framerate", GST_TYPE_FRACTION, + (int) (fps * 1000), 1000, NULL); + + if (gst_element_link_filtered(vrate, vencode, vcaps) == FALSE) { + g_warning("Fail to link vrate with vencode."); + goto error; + } + gst_caps_unref(vcaps); + } else { + if (gst_element_link(vcolorspace, vencode) == FALSE) { + g_warning("Fail to link colorspace and video encode element."); + goto error; + } + } + + gst_element_link(vencode, vqueue_src); + + // ghost pad the video bin + vpad = gst_element_get_pad(vqueue, "sink"); + gst_element_add_pad(vbin, gst_ghost_pad_new("sink", vpad)); + gst_object_unref(vpad); + + vpad = gst_element_get_pad(vqueue_src, "src"); + gst_element_add_pad(vbin, gst_ghost_pad_new("src", vpad)); + gst_object_unref(vpad); + + return vbin; + + error: + if (vpad != NULL) + gst_object_unref(vpad); + + if (vbin != NULL) + gst_object_unref(vbin); + + if (vqueue != NULL) + gst_object_unref(vqueue); + + if (vencode != NULL) + gst_object_unref(vencode); + + if (vqueue_src != NULL) + gst_object_unref(vqueue_src); + + if (vcolorspace != NULL) + gst_object_unref(vcolorspace); + + return NULL; +} + + + +gboolean +g_mencoder_setup_stream(GMencoder * self, + gboolean chunked, + gboolean deinterlace, + const gchar * mux_name, + const gchar * video_encode, + gchar ** video_encode_prop, + gdouble video_fps, + gdouble video_rate, + guint video_width, + guint video_height, + const gchar * audio_encode, + gchar ** audio_encode_prop, + guint audio_rate, const gchar * out_uri) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + if (priv->ready == TRUE) { + g_warning + ("Stream already configured. You need close stream first."); + return FALSE; + } + + _close_output(self); + if (_open_output(self, out_uri) == FALSE) { + return FALSE; + } + + priv->sources = 0; + priv->send_chunked = chunked; + priv->pipe = _create_pipeline(self, + video_encode, + mux_name, + video_encode_prop, + video_fps, + video_rate, + video_width, + video_height, + audio_encode, audio_encode_prop, + audio_rate, + deinterlace); + + return (priv->pipe != NULL); +} + + +gboolean +g_mencoder_append_uri(GMencoder * self, const gchar * uri) +{ + GstPad *pad_src; + GstPad *pad_sink; + GstElement *src; + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + gboolean ret = FALSE; + GstElement *ap = NULL; + GstElement *vp = NULL; + + + g_return_val_if_fail(priv->pipe != NULL, FALSE); + g_return_val_if_fail(priv->ready == FALSE, FALSE); + + src = _create_source(self, uri); + if (src == NULL) + return FALSE; + + priv->src = gst_bin_get_by_name(GST_BIN(src), "src"); + + gst_bin_add(GST_BIN(priv->pipe), src); + + ap = gst_bin_get_by_name(GST_BIN(priv->pipe), "abin"); + vp = gst_bin_get_by_name(GST_BIN(priv->pipe), "vbin"); + + if ((vp == NULL) || (ap == NULL)) { + g_warning("Fail to get output bin"); + goto error; + } + + pad_src = gst_element_get_pad(src, "src_audio"); + pad_sink = gst_element_get_compatible_pad(ap, + pad_src, + gst_pad_get_caps(pad_src)); + + if ((pad_sink == NULL) || (pad_src == NULL)) + goto error; + + GstPadLinkReturn lret = gst_pad_link(pad_src, pad_sink); + if (lret != GST_PAD_LINK_OK) + goto error; + + gst_object_unref(pad_src); + gst_object_unref(pad_sink); + + pad_src = gst_element_get_pad(src, "src_video"); + pad_sink = gst_element_get_compatible_pad(vp, + pad_src, + gst_pad_get_caps(pad_src)); + + if ((pad_src == NULL) || (pad_sink == NULL)) + goto error; + + if (gst_pad_link(pad_src, pad_sink) != GST_PAD_LINK_OK) { + g_warning("invalid source. video"); + goto error; + } + + priv->sources++; + ret = TRUE; + error: + + if ((src != NULL) && (ret == FALSE)) { + gst_bin_remove(GST_BIN(priv->pipe), src); + gst_object_unref(src); + } + + if (ap != NULL) + gst_object_unref(ap); + + if (vp != NULL) + gst_object_unref(vp); + + if (pad_src != NULL) + gst_object_unref(pad_src); + + if (pad_sink != NULL) + gst_object_unref(pad_sink); + + return ret; +} + + + +void +g_mencoder_remove_uri(GMencoder * self, const gchar * uri) +{ + // GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); + // TODO: remove src +} + +void +g_mencoder_play_stream(GMencoder * self) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + g_return_if_fail(priv->ready == FALSE); + priv->ready = TRUE; + gst_element_set_state(priv->pipe, GST_STATE_PLAYING); + if (priv->tick_id != 0) { + g_source_remove (priv->tick_id); + } + priv->tick_id = g_timeout_add(500, _tick_cb, self); + + if (priv->timeout_id != 0) { + g_source_remove (priv->timeout_id); + } + //priv->timeout_id = g_timeout_add(GMENCODER_TIMEOUT, _process_timeout_cb, self); +} + +void +g_mencoder_pause_stream(GMencoder * self) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + g_return_if_fail(priv->ready == TRUE); + gst_element_set_state(priv->pipe, GST_STATE_PAUSED); +} + +void +g_mencoder_close_stream(GMencoder * self) +{ + + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + if (priv->tick_id != 0) { + g_source_remove(priv->tick_id); + priv->tick_id = 0; + } + + if (priv->timeout_id != 0) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (priv->pipe != NULL) { + // TODO: fixe pipeline dispose + //gst_element_set_state (priv->pipe, GST_STATE_NULL); + // g_debug ("SETING STATE TO NULL: OK"); + // gst_element_set_state (priv->pipe, GST_STATE_NULL); + //gst_object_unref (priv->pipe); + //gst_object_unref(priv->src); + priv->src = NULL; + priv->pipe = NULL; + priv->abin = NULL; + priv->vbin = NULL; + priv->sink = NULL; + } + priv->ready = FALSE; +} + +static GstElement * +_create_pipeline(GMencoder * self, + const gchar * video_encode, + const gchar * mux_name, + gchar ** video_encode_prop, + gdouble video_fps, + gdouble video_rate, + guint video_width, + guint video_height, + const gchar * audio_encode, + gchar ** audio_encode_prop, guint audio_rate, + gboolean deinterlace) +{ + GstBus *bus = NULL; + GstElement *pipe = NULL; + GstElement *sink = NULL; + GstElement *mux = NULL; + GstElement *abin = NULL; + GstElement *vbin = NULL; + GstElement *queue = NULL; + GstPad *aux_pad = NULL; + GstPad *mux_pad = NULL; + + pipe = gst_pipeline_new("pipe"); + + mux = + gst_element_factory_make((mux_name ? mux_name : "ffmux_mpeg"), + "mux"); + if (mux == NULL) + goto error; + + queue = gst_element_factory_make("queue", "queueu_sink"); + + + sink = gst_element_factory_make("fakesink", "sink"); + g_object_set (G_OBJECT (sink), "signal-handoffs", TRUE, NULL); + g_signal_connect (G_OBJECT (sink), + "handoff", + G_CALLBACK (_buffer_arrive_cb), + self); + + abin = _create_audio_bin(audio_encode, audio_encode_prop, audio_rate); + if (abin == NULL) + goto error; + + vbin = + _create_video_bin(video_encode, video_encode_prop, video_fps, + video_rate, video_width, video_height, deinterlace); + if (vbin == NULL) + goto error; + + // Finish Pipe + gst_bin_add_many(GST_BIN(pipe), abin, vbin, mux, queue, sink, NULL); + + + // Link bins with mux + aux_pad = gst_element_get_pad(abin, "src"); + mux_pad = + gst_element_get_compatible_pad(mux, aux_pad, + GST_PAD_CAPS(aux_pad)); + if (mux_pad == NULL) { + g_warning("Mux element no have audio PAD"); + goto error; + } + GstPadLinkReturn ret = gst_pad_link(aux_pad, mux_pad); + if (ret != GST_PAD_LINK_OK) { + g_warning("Fail link audio and mux: %d", ret); + goto error; + + } + gst_object_unref(aux_pad); + gst_object_unref(mux_pad); + + aux_pad = gst_element_get_pad(vbin, "src"); + mux_pad = + gst_element_get_compatible_pad(mux, aux_pad, + GST_PAD_CAPS(aux_pad)); + if (mux_pad == NULL) { + g_warning("Mux element no have video PAD"); + goto error; + } + ret = gst_pad_link(aux_pad, mux_pad); + if (ret != GST_PAD_LINK_OK) { + g_warning("Fail link video and mux: %d", ret); + goto error; + } + gst_object_unref(aux_pad); + gst_object_unref(mux_pad); + aux_pad = NULL; + mux_pad = NULL; + + // Link mux with sink + gst_element_link_many(mux, queue, sink, NULL); + + bus = gst_pipeline_get_bus(GST_PIPELINE(pipe)); + gst_bus_add_watch(bus, _pipeline_bus_cb, self); + gst_object_unref(bus); + return pipe; + + error: + g_warning("Invalid uri"); + + if (pipe != NULL) { + gst_object_unref(pipe); + } + + + if (mux != NULL) { + gst_object_unref(mux); + } + + if (mux_pad != NULL) { + gst_object_unref(mux_pad); + } + + if (aux_pad != NULL) { + gst_object_unref(mux_pad); + } + + if (sink != NULL) { + gst_object_unref(sink); + } + + if (abin != NULL) { + gst_object_unref(abin); + } + + if (vbin != NULL) { + gst_object_unref(vbin); + } + + return FALSE; +} + + +static void +_close_output(GMencoder * self) +{ +} + +static GstElement * +_create_v4l_source (GMencoder *self, const gchar * uri) +{ + gchar **info; + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + + + info = g_strsplit (uri+6, ":", 3); + if (g_strv_length (info) != 3) { + return NULL; + } + + priv->v4lsrc = gst_element_factory_make ("v4l2src", "src"); + g_debug ("channel %s, norm %s, frequ %s", info[0], info[1], info[2]); + g_object_set (G_OBJECT (priv->v4lsrc), + "channel", info[0], + "norm", info[1], + "frequency", atoi (info[2]), + NULL); + + return priv->v4lsrc; +} + +static GstElement * +_create_source(GMencoder *self, const gchar * uri) +{ + + GstElement *bsrc = NULL; + GstElement *src = NULL; + GstElement *aqueue = NULL; + GstElement *vqueue = NULL; + GstElement *decode = NULL; + GstPad *src_pad = NULL; + + + bsrc = gst_bin_new(NULL); + + // src = gst_element_factory_make ("gnomevfssrc", "src"); + // g_object_set (G_OBJECT (src), "location", uri, NULL); + if (strncmp (uri, "v4l://", 6) == 0) { + g_debug ("V4L"); + src = _create_v4l_source (self, uri); + } + else { + src = gst_element_make_from_uri(GST_URI_SRC, uri, "src"); + } + + if (src == NULL) + goto error; + + decode = gst_element_factory_make("decodebin2", "decode"); + if (decode == NULL) + goto error; + + aqueue = gst_element_factory_make("queue", "aqueue"); + if (aqueue == NULL) + goto error; + + vqueue = gst_element_factory_make("queue", "vqueue"); + if (vqueue == NULL) + goto error; + + gst_bin_add_many(GST_BIN(bsrc), src, decode, aqueue, vqueue, + NULL); + gst_element_link (src, decode); + + g_signal_connect(G_OBJECT(decode), + "new-decoded-pad", + G_CALLBACK(_decodebin_new_pad_cb), bsrc); + + g_signal_connect(G_OBJECT(decode), + "unknown-type", + G_CALLBACK(_decodebin_unknown_type_cb), pipe); + + src_pad = gst_element_get_pad(aqueue, "src"); + gst_element_add_pad(bsrc, gst_ghost_pad_new("src_audio", src_pad)); + gst_object_unref(src_pad); + + src_pad = gst_element_get_pad(vqueue, "src"); + gst_element_add_pad(bsrc, gst_ghost_pad_new("src_video", src_pad)); + gst_object_unref(src_pad); + + return bsrc; + + error: + g_debug ("Fail to create source element"); + if (src != NULL) { + gst_object_unref(src); + } + + if (decode != NULL) { + gst_object_unref(decode); + } + + if (aqueue != NULL) { + gst_object_unref(aqueue); + } + + if (vqueue != NULL) { + gst_object_unref(vqueue); + } + + return NULL; +} + +static gboolean +_open_output(GMencoder * self, const gchar * uri) +{ + gchar **i; + GnomeVFSResult result; + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + + i = g_strsplit(uri, "://", 0); + if (strcmp(i[0], "fd") == 0) { + result = gnome_vfs_open_fd (&priv->handle, atoi(i[1])); + } else { + if (g_file_test (i[1], G_FILE_TEST_EXISTS) == FALSE) { + result = gnome_vfs_create (&priv->handle, uri, GNOME_VFS_OPEN_WRITE, FALSE, + GNOME_VFS_PERM_USER_WRITE | GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_GROUP_READ); + } else { + result = gnome_vfs_open (&priv->handle, uri, + GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_TRUNCATE); + } + } + + g_strfreev(i); + return (result == GNOME_VFS_OK); +} + +static gboolean +_pipeline_bus_cb(GstBus * bus, GstMessage * msg, gpointer user_data) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data); + + switch (GST_MESSAGE_TYPE(msg)) { + + case GST_MESSAGE_STATE_CHANGED: + { + GstState oldstate; + GstState newstate; + GstState pendingstate; + + + gst_message_parse_state_changed(msg, &oldstate, + &newstate, &pendingstate); + + if (pendingstate != GST_STATE_VOID_PENDING) + break; + + if ((oldstate == GST_STATE_READY) + && (newstate == GST_STATE_PAUSED)) { + if (priv->ready) + g_signal_emit(user_data, g_mencoder_signals[PAUSED], + 0); + } else if ((oldstate == GST_STATE_PAUSED) + && (newstate == GST_STATE_PLAYING)) { + g_signal_emit(user_data, g_mencoder_signals[PLAYING], 0); + } else if ((oldstate == GST_STATE_READY) && + (newstate == GST_STATE_NULL)) { + g_signal_emit(user_data, g_mencoder_signals[STOPED], 0); + } + break; + } + + case GST_MESSAGE_ERROR: + { + GError *error; + gchar *debug; + gchar *err_str; + + if (priv->tick_id != 0) { + g_source_remove(priv->tick_id); + priv->tick_id = 0; + } + + gst_message_parse_error(msg, &error, &debug); + err_str = g_strdup_printf("Error [%d] %s (%s)", error->code, + error->message, debug); + priv->ready = FALSE; + g_signal_emit(user_data, g_mencoder_signals[ERROR], 0, + err_str); + g_free(err_str); + g_clear_error(&error); + g_free(debug); + break; + } + + case GST_MESSAGE_EOS: + priv->ready = FALSE; +#ifdef USE_MANUAL_SINK + _flush_queue (G_MENCODER (user_data)); +#endif + g_signal_emit(user_data, g_mencoder_signals[EOS], 0); + break; + + case GST_MESSAGE_DURATION: + { + GstFormat format; + gint64 duration; + gst_message_parse_duration(msg, &format, &duration); + if (format == GST_FORMAT_BYTES) + priv->duration = duration; + break; + } + default: + { + break; + } + } + return TRUE; +} + + + +static void +_decodebin_new_pad_cb(GstElement * object, + GstPad * pad, gboolean flag, gpointer user_data) +{ + GstCaps *caps; + gchar *str_caps = NULL; + GstElement *sink_element; + GstPad *sink_pad; + + caps = gst_pad_get_caps(pad); + str_caps = gst_caps_to_string(caps); + if (strstr(str_caps, "audio") != NULL) { + sink_element = gst_bin_get_by_name(GST_BIN(user_data), "aqueue"); + } else if (strstr(str_caps, "video") != NULL) { + sink_element = gst_bin_get_by_name(GST_BIN(user_data), "vqueue"); + } else { + g_warning("invalid caps %s", str_caps); + } + + sink_pad = gst_element_get_pad(sink_element, "sink"); + gst_pad_link(pad, sink_pad); + + gst_object_unref(sink_element); + gst_object_unref(sink_pad); + g_free(str_caps); + gst_caps_unref(caps); +} + +static void +_decodebin_unknown_type_cb(GstElement * object, + GstPad * pad, GstCaps * caps, + gpointer user_data) +{ + g_warning("Unknown Type"); + // priv->ready = FALSE; +} + +static gboolean +_tick_cb(gpointer user_data) +{ + GstFormat format = GST_FORMAT_BYTES; + gint64 cur = 0; + + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data); + + if (priv->duration == 0) { + gint64 d = 0; + if (gst_element_query_duration(priv->src, &format, &d)) + priv->duration = d; + } + + if (priv->duration != 0) { + gst_element_query_position(priv->src, &format, &cur); + g_print("PROGRESS:%lli\n", (99 * cur) / priv->duration); + } + + return TRUE; +} + +static gboolean +_process_timeout_cb (gpointer user_data) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data); + + g_signal_emit(user_data, g_mencoder_signals[ERROR], 0, "timeout"); + priv->timeout_id = 0; + return FALSE; +} + + +#ifdef USE_MANUAL_SINK +static gboolean +_send_buffer (GnomeVFSHandle *handle, gpointer buff, gint size) +{ + gchar *msg; + GByteArray *b_send; + GnomeVFSResult result; + GnomeVFSFileSize bytes_written; + + b_send = g_byte_array_new (); + msg = g_strdup_printf ("%x\r\n", size); + b_send = g_byte_array_append (b_send, (const guint8*) msg, strlen (msg) * sizeof (gchar)); + g_free (msg); + + b_send = g_byte_array_append (b_send, buff, size); + + msg = g_strdup ("\r\n"); + b_send = g_byte_array_append (b_send, (const guint8*) msg, strlen (msg) * sizeof (gchar)); + g_free (msg); + + result = gnome_vfs_write (handle, b_send->data, b_send->len, &bytes_written); + g_byte_array_free (b_send, TRUE); + + return (result == GNOME_VFS_OK); +} + +static void +_flush_queue (GMencoder *self) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self); + + if (priv->send_chunked) { + GnomeVFSFileSize bytes_written; + gchar *end_msg; + end_msg = g_strdup ("0\r\n\r\n"); + gnome_vfs_write (priv->handle, + (const guint8*) end_msg, + strlen(end_msg) * sizeof(gchar), + &bytes_written); + g_free (end_msg); + } +} + +static void +_buffer_arrive_cb (GstElement* object, + GstBuffer* buff, + GstPad* pad, + gpointer user_data) +{ + GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data); + + if (priv->timeout_id != 0) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (priv->send_chunked) { + if (_send_buffer (priv->handle, GST_BUFFER_DATA (buff), GST_BUFFER_SIZE (buff)) == FALSE) + goto error; + } else { + GnomeVFSResult result; + GnomeVFSFileSize bytes_written; + + result = gnome_vfs_write (priv->handle, + GST_BUFFER_DATA (buff), + GST_BUFFER_SIZE (buff), + &bytes_written); + + if (result != GNOME_VFS_OK) + goto error; + } + + return; + +error: + if (priv->tick_id != 0) { + g_source_remove(priv->tick_id); + priv->tick_id = 0; + } + g_signal_emit(user_data, g_mencoder_signals[ERROR], 0, "Fail to write on socket"); + gst_element_set_state (priv->pipe, GST_STATE_PAUSED); +} + +#endif