renatofilho@588: #ifdef HAVE_CONFIG_H renatofilho@588: #include "config.h" renatofilho@588: #endif renatofilho@588: renatofilho@600: #include renatofilho@600: #include renatofilho@600: #include renatofilho@588: #include renatofilho@588: #include renatofilho@588: #include renatofilho@588: renatofilho@588: #include "gmencoder.h" renatofilho@588: renatofilho@588: #define G_MENCODER_GET_PRIVATE(obj) \ renatofilho@588: (G_TYPE_INSTANCE_GET_PRIVATE ((obj), G_TYPE_MENCODER, GMencoderPrivate)) renatofilho@588: renatofilho@634: //#define SUPPORT_MULT_INPUT 0 renatofilho@588: renatofilho@588: typedef struct _GMencoderPrivate GMencoderPrivate; renatofilho@600: typedef struct _SetupInfo SetupInfo; renatofilho@600: renatofilho@600: struct _SetupInfo renatofilho@600: { renatofilho@600: gchar* video_encode; renatofilho@600: gchar* mux_name; renatofilho@600: gchar** video_encode_prop; renatofilho@600: gdouble video_fps; renatofilho@600: gdouble video_rate; renatofilho@600: guint video_width; renatofilho@600: guint video_height; renatofilho@600: gchar* audio_encode; renatofilho@600: gchar** audio_encode_prop; renatofilho@600: guint audio_rate; renatofilho@600: }; renatofilho@600: renatofilho@588: renatofilho@588: struct _GMencoderPrivate renatofilho@588: { renatofilho@588: GstElement *pipe; renatofilho@588: GstElement *abin; renatofilho@588: GstElement *vbin; renatofilho@588: GstElement *sink; renatofilho@588: gboolean ready; renatofilho@600: SetupInfo *info; renatofilho@600: GstClockTime videot; renatofilho@600: GstClockTime audiot; renatofilho@600: gint fd; renatofilho@634: gint sources; renatofilho@654: gint tick_id; renatofilho@588: }; renatofilho@588: renatofilho@588: enum { renatofilho@588: PAUSED, renatofilho@588: PLAYING, renatofilho@588: STOPED, renatofilho@588: EOS, renatofilho@588: ERROR, renatofilho@588: LAST_SIGNAL renatofilho@588: }; renatofilho@588: renatofilho@588: static void g_mencoder_class_init (GMencoderClass *klass); renatofilho@588: static void g_mencoder_init (GMencoder *object); renatofilho@588: static void g_mencoder_dispose (GObject *object); renatofilho@588: static void g_mencoder_finalize (GObject *object); renatofilho@588: static GstElement* renatofilho@600: _create_audio_bin (const gchar* encode, renatofilho@600: gchar** encode_prop, renatofilho@600: gint rate); renatofilho@588: static GstElement* renatofilho@600: _create_video_bin (const gchar* encode, renatofilho@588: gchar** encode_prop, renatofilho@588: gdouble fps, renatofilho@588: gint rate, renatofilho@588: guint width, renatofilho@588: guint height); renatofilho@588: renatofilho@588: static gboolean renatofilho@588: _pipeline_bus_cb (GstBus *bus, renatofilho@588: GstMessage *msg, renatofilho@588: gpointer user_data); renatofilho@588: static void _decodebin_new_pad_cb (GstElement* object, renatofilho@588: GstPad* pad, renatofilho@588: gboolean flag, renatofilho@588: gpointer user_data); renatofilho@588: static void _decodebin_unknown_type_cb (GstElement* object, renatofilho@588: GstPad* pad, renatofilho@588: GstCaps* caps, renatofilho@588: gpointer user_data); renatofilho@600: static void _close_output (GMencoder *self); renatofilho@600: static void _open_output (GMencoder *self, renatofilho@600: const gchar* uri); renatofilho@600: static GstElement* _create_source (const gchar* uri); renatofilho@600: static GstElement*_create_pipeline (GMencoder *self, renatofilho@600: const gchar* video_encode, renatofilho@600: const gchar* mux_name, renatofilho@600: gchar** video_encode_prop, renatofilho@600: gdouble video_fps, renatofilho@600: gdouble video_rate, renatofilho@600: guint video_width, renatofilho@600: guint video_height, renatofilho@600: const gchar* audio_encode, renatofilho@600: gchar** audio_encode_prop, renatofilho@600: guint audio_rate); renatofilho@654: static gboolean _tick_cb (gpointer data); renatofilho@654: renatofilho@600: renatofilho@600: renatofilho@600: renatofilho@588: renatofilho@588: renatofilho@588: static guint g_mencoder_signals[LAST_SIGNAL] = { 0 }; renatofilho@588: renatofilho@588: G_DEFINE_TYPE(GMencoder, g_mencoder, G_TYPE_OBJECT) renatofilho@588: renatofilho@588: static void renatofilho@588: g_mencoder_class_init (GMencoderClass *klass) renatofilho@588: { renatofilho@588: GObjectClass *object_class; renatofilho@588: renatofilho@588: object_class = (GObjectClass *) klass; renatofilho@588: renatofilho@588: g_type_class_add_private (klass, sizeof (GMencoderPrivate)); renatofilho@588: renatofilho@588: object_class->dispose = g_mencoder_dispose; renatofilho@588: object_class->finalize = g_mencoder_finalize; renatofilho@588: renatofilho@588: g_mencoder_signals[PAUSED] = renatofilho@588: g_signal_new ("paused", renatofilho@588: G_OBJECT_CLASS_TYPE (object_class), renatofilho@588: G_SIGNAL_RUN_FIRST, renatofilho@588: 0, NULL, NULL, renatofilho@588: g_cclosure_marshal_VOID__VOID, renatofilho@588: G_TYPE_NONE, 0); renatofilho@588: renatofilho@588: g_mencoder_signals[PLAYING] = renatofilho@588: g_signal_new ("playing", renatofilho@588: G_OBJECT_CLASS_TYPE (object_class), renatofilho@588: G_SIGNAL_RUN_FIRST, renatofilho@588: 0, NULL, NULL, renatofilho@588: g_cclosure_marshal_VOID__VOID, renatofilho@588: G_TYPE_NONE, 0); renatofilho@588: renatofilho@588: g_mencoder_signals[STOPED] = renatofilho@588: g_signal_new ("stoped", renatofilho@588: G_OBJECT_CLASS_TYPE (object_class), renatofilho@588: G_SIGNAL_RUN_FIRST, renatofilho@588: 0, NULL, NULL, renatofilho@588: g_cclosure_marshal_VOID__VOID, renatofilho@588: G_TYPE_NONE, 0); renatofilho@588: renatofilho@588: g_mencoder_signals[EOS] = renatofilho@588: g_signal_new ("eos", renatofilho@588: G_OBJECT_CLASS_TYPE (object_class), renatofilho@588: G_SIGNAL_RUN_FIRST, renatofilho@588: 0, NULL, NULL, renatofilho@588: g_cclosure_marshal_VOID__VOID, renatofilho@588: G_TYPE_NONE, 0); renatofilho@588: renatofilho@588: renatofilho@588: g_mencoder_signals[ERROR] = renatofilho@588: g_signal_new ("error", renatofilho@588: G_OBJECT_CLASS_TYPE (object_class), renatofilho@588: G_SIGNAL_RUN_FIRST, renatofilho@588: 0, NULL, NULL, renatofilho@588: g_cclosure_marshal_VOID__STRING, renatofilho@588: G_TYPE_NONE, 1, G_TYPE_STRING); renatofilho@588: } renatofilho@588: renatofilho@588: static void renatofilho@588: g_mencoder_init (GMencoder *self) renatofilho@588: { renatofilho@600: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@600: priv->info = g_new0 (SetupInfo, 1); renatofilho@588: } renatofilho@588: renatofilho@588: static void renatofilho@588: g_mencoder_dispose (GObject *object) renatofilho@588: { renatofilho@588: } renatofilho@588: renatofilho@588: static void renatofilho@588: g_mencoder_finalize (GObject *object) renatofilho@588: { renatofilho@600: //TODO: clear vars renatofilho@588: g_mencoder_close_stream (G_MENCODER (object)); renatofilho@588: } renatofilho@588: renatofilho@588: GMencoder* renatofilho@588: g_mencoder_new (void) renatofilho@588: { renatofilho@588: return g_object_new (G_TYPE_MENCODER, NULL); renatofilho@588: } renatofilho@588: renatofilho@600: renatofilho@588: static void renatofilho@588: _obj_set_prop (GObject *obj, renatofilho@588: const gchar *prop_name, renatofilho@588: const gchar *prop_val) renatofilho@588: { renatofilho@588: GValue p = {0}; renatofilho@588: GValue v = {0}; renatofilho@588: GParamSpec *s = NULL; renatofilho@588: GObjectClass *k = G_OBJECT_GET_CLASS (obj); renatofilho@588: renatofilho@588: renatofilho@588: g_value_init (&v, G_TYPE_STRING); renatofilho@588: g_value_set_string (&v, prop_val); renatofilho@588: renatofilho@588: s = g_object_class_find_property (k, prop_name); renatofilho@588: if (s == NULL) { renatofilho@588: g_print ("Invalid property name: %s\n", prop_name); renatofilho@588: return; renatofilho@588: } renatofilho@588: renatofilho@588: g_value_init (&p, s->value_type); renatofilho@588: switch (s->value_type) renatofilho@588: { renatofilho@588: case G_TYPE_INT: renatofilho@588: g_value_set_int (&p, atoi (prop_val)); renatofilho@588: break; renatofilho@588: case G_TYPE_STRING: renatofilho@588: g_value_set_string (&p, prop_val); renatofilho@588: break; renatofilho@588: default: renatofilho@588: return; renatofilho@588: } renatofilho@588: renatofilho@588: g_object_set_property (obj, prop_name, &p); renatofilho@588: g_value_unset (&v); renatofilho@588: g_value_unset (&p); renatofilho@588: } renatofilho@588: renatofilho@588: static GstElement* renatofilho@588: _create_element_with_prop (const gchar* factory_name, renatofilho@588: const gchar* element_name, renatofilho@588: gchar** prop) renatofilho@588: { renatofilho@588: GstElement *ret; renatofilho@588: int i; renatofilho@588: renatofilho@616: g_debug ("Creating element: %s", factory_name); renatofilho@616: renatofilho@588: ret = gst_element_factory_make (factory_name, element_name); renatofilho@588: if (ret == NULL) renatofilho@588: return NULL; renatofilho@588: renatofilho@588: if (prop != NULL) { renatofilho@588: for (i=0; i < g_strv_length (prop); i++) { renatofilho@588: char** v = g_strsplit(prop[i], "=", 2); renatofilho@588: if (g_strv_length (v) == 2) { renatofilho@588: _obj_set_prop (G_OBJECT (ret), v[0], v[1]); renatofilho@588: } renatofilho@588: g_strfreev (v); renatofilho@588: } renatofilho@588: } renatofilho@588: renatofilho@588: return ret; renatofilho@588: renatofilho@588: } renatofilho@588: renatofilho@588: static GstElement* renatofilho@600: _create_audio_bin (const gchar* encode, renatofilho@600: gchar** encode_prop, renatofilho@600: gint rate) renatofilho@588: { renatofilho@588: GstElement *abin = NULL; renatofilho@588: GstElement *aqueue = NULL; renatofilho@588: GstElement *aconvert = NULL; renatofilho@588: GstElement *aencode = NULL; renatofilho@588: GstElement *aqueue_src = NULL; renatofilho@588: GstPad *apad = NULL; renatofilho@588: renatofilho@588: //audio/x-raw-int ! queue ! audioconvert ! faac ! rtpmp4gpay ! udpsink name=upd_audio host=224.0.0.1 port=5002 renatofilho@588: abin = gst_bin_new ("abin"); renatofilho@588: aqueue = gst_element_factory_make ("queue", "aqueue"); renatofilho@588: aconvert= gst_element_factory_make ("audioconvert", "aconvert"); renatofilho@588: aencode = _create_element_with_prop ((encode ? encode : "lame"), "aencode", encode_prop); renatofilho@588: aqueue_src= gst_element_factory_make ("queue", "aqueue_src"); renatofilho@588: renatofilho@588: if ((abin == NULL) || (aqueue == NULL) || (aconvert == NULL) renatofilho@588: || (aencode == NULL) || (aqueue_src == NULL)) { renatofilho@588: g_warning ("Audio elements not found"); renatofilho@588: goto error; renatofilho@588: } renatofilho@588: renatofilho@600: g_object_set (G_OBJECT (aencode), "bitrate", 32, NULL); renatofilho@600: /* renatofilho@600: if (rate > 0) { renatofilho@600: g_object_set (G_OBJECT (aencode), "bitrate", 32, NULL); renatofilho@600: } renatofilho@600: */ renatofilho@600: renatofilho@588: gst_bin_add_many (GST_BIN (abin), aqueue, aconvert, aencode, aqueue_src, NULL); renatofilho@600: if (gst_element_link_many (aqueue, aconvert, aencode, aqueue_src, NULL) == FALSE) { renatofilho@600: g_warning ("Not Link audio elements"); renatofilho@600: } renatofilho@588: renatofilho@588: //TODO: apply audio rate renatofilho@588: renatofilho@588: // ghost pad the audio bin renatofilho@588: apad = gst_element_get_pad (aqueue, "sink"); renatofilho@588: gst_element_add_pad (abin, gst_ghost_pad_new("sink", apad)); renatofilho@588: gst_object_unref (apad); renatofilho@588: renatofilho@588: apad = gst_element_get_pad (aqueue_src, "src"); renatofilho@588: gst_element_add_pad (abin, gst_ghost_pad_new("src", apad)); renatofilho@588: gst_object_unref (apad); renatofilho@588: renatofilho@588: return abin; renatofilho@588: error: renatofilho@588: if (abin != NULL) renatofilho@588: gst_object_unref (abin); renatofilho@588: renatofilho@588: if (aqueue != NULL) renatofilho@588: gst_object_unref (aqueue); renatofilho@588: renatofilho@588: if (aconvert != NULL) renatofilho@588: gst_object_unref (aconvert); renatofilho@588: renatofilho@588: if (aencode != NULL) renatofilho@588: gst_object_unref (aencode); renatofilho@588: renatofilho@588: if (aqueue_src != NULL) renatofilho@588: gst_object_unref (aqueue_src); renatofilho@588: renatofilho@588: if (apad != NULL) renatofilho@588: gst_object_unref (apad); renatofilho@588: renatofilho@588: return NULL; renatofilho@588: } renatofilho@588: renatofilho@588: renatofilho@588: renatofilho@588: renatofilho@588: //queue ! videoscale ! video/x-raw-yuv,width=240,height=144 ! colorspace ! rate ! encode ! queue renatofilho@588: static GstElement* renatofilho@600: _create_video_bin (const gchar* encode, renatofilho@588: gchar** encode_prop, renatofilho@588: gdouble fps, renatofilho@588: gint rate, renatofilho@588: guint width, renatofilho@588: guint height) renatofilho@588: { renatofilho@588: GstElement *vbin = NULL; renatofilho@588: GstElement *vqueue = NULL; renatofilho@588: GstElement* vqueue_src = NULL; renatofilho@588: GstElement *vcolorspace = NULL; renatofilho@588: GstElement *vencode = NULL; renatofilho@588: GstElement *vrate = NULL; renatofilho@588: GstPad *vpad = NULL; renatofilho@588: renatofilho@588: vbin = gst_bin_new ("vbin"); renatofilho@588: vqueue = gst_element_factory_make ("queue", "vqueue"); renatofilho@588: vcolorspace = gst_element_factory_make ("ffmpegcolorspace", "colorspace"); renatofilho@616: renatofilho@616: vencode = _create_element_with_prop ( renatofilho@616: (encode != NULL ? encode : "ffenc_mpeg1video"), renatofilho@616: "vencode", encode_prop); renatofilho@588: vqueue_src = gst_element_factory_make ("queue", "queue_src"); renatofilho@588: renatofilho@588: if ((vbin == NULL) || (vqueue == NULL) || (vcolorspace == NULL) renatofilho@588: || (vencode == NULL) || (vqueue_src == NULL)) { renatofilho@588: g_warning ("Video elements not found"); renatofilho@588: goto error; renatofilho@588: } renatofilho@588: renatofilho@588: gst_bin_add_many (GST_BIN (vbin), vqueue, vcolorspace, vencode, vqueue_src, NULL); renatofilho@588: renatofilho@588: renatofilho@588: if ((width > 0) && (height > 0)) { renatofilho@588: //Scalling video renatofilho@588: GstCaps *vcaps; renatofilho@588: GstElement *vscale = gst_element_factory_make ("videoscale", "vscale"); renatofilho@588: renatofilho@588: gst_bin_add (GST_BIN (vbin), vscale); renatofilho@588: renatofilho@588: vcaps = gst_caps_new_simple ("video/x-raw-yuv", renatofilho@588: "width", G_TYPE_INT, width, renatofilho@588: "height", G_TYPE_INT, height, renatofilho@588: NULL); renatofilho@588: renatofilho@588: gst_element_link (vqueue, vscale); renatofilho@588: renatofilho@588: if (gst_element_link_filtered (vscale, vcolorspace, vcaps) == FALSE) { renatofilho@588: g_warning ("Fail to resize video"); renatofilho@588: gst_object_unref (vcaps); renatofilho@588: gst_object_unref (vscale); renatofilho@588: goto error; renatofilho@588: } renatofilho@588: gst_caps_unref (vcaps); renatofilho@588: } else { renatofilho@588: gst_element_link (vqueue, vcolorspace); renatofilho@588: } renatofilho@600: renatofilho@588: renatofilho@588: if (fps > 0) { renatofilho@588: //Changing the video fps renatofilho@588: GstCaps *vcaps; renatofilho@616: vrate = gst_element_factory_make ("videorate", "vrate"); renatofilho@588: renatofilho@616: g_debug ("Setting FPS: %.2f", fps); renatofilho@588: gst_bin_add (GST_BIN (vbin), vrate); renatofilho@588: renatofilho@588: if (gst_element_link (vcolorspace, vrate) == FALSE) { renatofilho@588: g_warning ("Fail to link video elements"); renatofilho@588: goto error; renatofilho@588: } renatofilho@588: renatofilho@588: vcaps = gst_caps_new_simple ("video/x-raw-yuv", renatofilho@616: "framerate", GST_TYPE_FRACTION, (int) (fps * 1000), 1000, NULL); renatofilho@588: renatofilho@588: if (gst_element_link_filtered (vrate, vencode, vcaps) == FALSE) { renatofilho@588: g_warning ("Fail to link vrate with vencode."); renatofilho@588: goto error; renatofilho@588: } renatofilho@588: gst_caps_unref (vcaps); renatofilho@588: } else { renatofilho@588: if (gst_element_link (vcolorspace, vencode) == FALSE) { renatofilho@588: g_warning ("Fail to link colorspace and video encode element."); renatofilho@588: goto error; renatofilho@588: } renatofilho@588: } renatofilho@588: renatofilho@588: gst_element_link (vencode, vqueue_src); renatofilho@588: renatofilho@588: // ghost pad the video bin renatofilho@588: vpad = gst_element_get_pad (vqueue, "sink"); renatofilho@588: gst_element_add_pad (vbin, gst_ghost_pad_new ("sink", vpad)); renatofilho@588: gst_object_unref (vpad); renatofilho@588: renatofilho@588: vpad = gst_element_get_pad (vqueue_src, "src"); renatofilho@588: gst_element_add_pad (vbin, gst_ghost_pad_new ("src", vpad)); renatofilho@588: gst_object_unref (vpad); renatofilho@588: renatofilho@588: return vbin; renatofilho@588: renatofilho@588: error: renatofilho@588: if (vpad != NULL) renatofilho@588: gst_object_unref (vpad); renatofilho@588: renatofilho@588: if (vbin != NULL) renatofilho@588: gst_object_unref (vbin); renatofilho@588: renatofilho@588: if (vqueue != NULL) renatofilho@588: gst_object_unref (vqueue); renatofilho@588: renatofilho@588: if (vencode != NULL) renatofilho@588: gst_object_unref (vencode); renatofilho@588: renatofilho@588: if (vqueue_src != NULL) renatofilho@588: gst_object_unref (vqueue_src); renatofilho@588: renatofilho@588: if (vcolorspace != NULL) renatofilho@588: gst_object_unref (vcolorspace); renatofilho@588: renatofilho@588: return NULL; renatofilho@588: } renatofilho@588: renatofilho@588: renatofilho@600: renatofilho@600: void renatofilho@588: g_mencoder_setup_stream (GMencoder *self, renatofilho@616: const gchar* mux_name, renatofilho@588: const gchar* video_encode, renatofilho@588: gchar** video_encode_prop, renatofilho@588: gdouble video_fps, renatofilho@588: gdouble video_rate, renatofilho@588: guint video_width, renatofilho@600: guint video_height, renatofilho@588: const gchar* audio_encode, renatofilho@588: gchar** audio_encode_prop, renatofilho@588: guint audio_rate, renatofilho@600: const gchar* out_uri) renatofilho@600: { renatofilho@600: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@600: if (priv->ready == TRUE) { renatofilho@600: g_warning ("Stream already configured. You need close stream first."); renatofilho@600: return; renatofilho@600: } renatofilho@600: renatofilho@634: _close_output (self); renatofilho@634: _open_output (self, out_uri); renatofilho@634: renatofilho@634: priv->sources = 0; renatofilho@600: priv->pipe = _create_pipeline (self, renatofilho@600: video_encode, renatofilho@600: mux_name, renatofilho@600: video_encode_prop, renatofilho@600: video_fps, renatofilho@600: video_rate, renatofilho@600: video_width, renatofilho@600: video_height, renatofilho@600: audio_encode, renatofilho@600: audio_encode_prop, renatofilho@600: audio_rate); renatofilho@600: renatofilho@600: } renatofilho@600: renatofilho@600: renatofilho@600: gboolean renatofilho@600: g_mencoder_append_uri (GMencoder *self, renatofilho@600: const gchar* uri) renatofilho@600: { renatofilho@600: GstPad *pad_src; renatofilho@600: GstPad *pad_sink; renatofilho@600: GstElement *src; renatofilho@600: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@600: gboolean ret = FALSE; renatofilho@634: GstElement *ap = NULL; renatofilho@634: GstElement *vp = NULL; renatofilho@634: renatofilho@600: renatofilho@600: g_return_val_if_fail (priv->pipe != NULL, FALSE); renatofilho@600: g_return_val_if_fail (priv->ready == FALSE, FALSE); renatofilho@600: renatofilho@634: #ifndef SUPPORT_MULT_INPUT renatofilho@634: g_return_val_if_fail (priv->sources < 1, FALSE); renatofilho@634: #endif renatofilho@634: renatofilho@600: src = _create_source (uri); renatofilho@600: if (src == NULL) renatofilho@600: return FALSE; renatofilho@600: renatofilho@600: gst_bin_add (GST_BIN (priv->pipe), src); renatofilho@600: renatofilho@634: #ifdef SUPPORT_MULT_INPUT renatofilho@600: ap = gst_bin_get_by_name (GST_BIN (priv->pipe), "ap"); renatofilho@600: vp = gst_bin_get_by_name (GST_BIN (priv->pipe), "vp"); renatofilho@634: #else renatofilho@634: ap = gst_bin_get_by_name (GST_BIN (priv->pipe), "abin"); renatofilho@634: vp = gst_bin_get_by_name (GST_BIN (priv->pipe), "vbin"); renatofilho@634: #endif renatofilho@634: renatofilho@634: if ((vp == NULL) || (ap == NULL)) { renatofilho@634: g_warning ("Fail to get output bin"); renatofilho@600: goto error; renatofilho@634: } renatofilho@600: renatofilho@600: pad_src = gst_element_get_pad (src, "src_audio"); renatofilho@600: pad_sink = gst_element_get_compatible_pad (ap, renatofilho@600: pad_src, renatofilho@600: gst_pad_get_caps (pad_src)); renatofilho@600: renatofilho@600: if ((pad_sink == NULL) || (pad_src == NULL)) renatofilho@600: goto error; renatofilho@600: renatofilho@600: GstPadLinkReturn lret = gst_pad_link (pad_src, pad_sink); renatofilho@600: if (lret != GST_PAD_LINK_OK) renatofilho@600: goto error; renatofilho@600: renatofilho@600: gst_object_unref (pad_src); renatofilho@600: gst_object_unref (pad_sink); renatofilho@600: renatofilho@600: pad_src = gst_element_get_pad (src, "src_video"); renatofilho@600: pad_sink = gst_element_get_compatible_pad (vp, renatofilho@600: pad_src, renatofilho@600: gst_pad_get_caps (pad_src)); renatofilho@600: renatofilho@600: if ((pad_src == NULL) || (pad_sink == NULL)) renatofilho@600: goto error; renatofilho@600: renatofilho@600: if (gst_pad_link (pad_src, pad_sink) != GST_PAD_LINK_OK) { renatofilho@600: g_warning ("invalid source. video"); renatofilho@600: goto error; renatofilho@600: } renatofilho@600: renatofilho@634: priv->sources++; renatofilho@600: ret = TRUE; renatofilho@634: g_debug ("Uri: [%s] OK ", uri); renatofilho@600: error: renatofilho@600: renatofilho@600: if ((src != NULL) && (ret == FALSE)) { renatofilho@600: gst_bin_remove (GST_BIN (priv->pipe), src); renatofilho@600: gst_object_unref (src); renatofilho@600: } renatofilho@600: renatofilho@600: if (ap != NULL) renatofilho@600: gst_object_unref (ap); renatofilho@600: renatofilho@600: if (vp != NULL) renatofilho@600: gst_object_unref (vp); renatofilho@600: renatofilho@600: if (pad_src != NULL) renatofilho@600: gst_object_unref (pad_src); renatofilho@600: renatofilho@600: if (pad_sink != NULL) renatofilho@600: gst_object_unref (pad_sink); renatofilho@600: renatofilho@600: return ret; renatofilho@600: } renatofilho@600: renatofilho@600: renatofilho@600: renatofilho@600: void renatofilho@600: g_mencoder_remove_uri (GMencoder *self, renatofilho@600: const gchar* uri) renatofilho@600: { renatofilho@600: // GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@600: //TODO: remove src renatofilho@600: } renatofilho@600: renatofilho@600: void renatofilho@600: g_mencoder_play_stream (GMencoder *self) renatofilho@600: { renatofilho@600: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@600: renatofilho@600: g_return_if_fail (priv->ready == FALSE); renatofilho@600: priv->ready = TRUE; renatofilho@600: gst_element_set_state (priv->pipe, GST_STATE_PLAYING); renatofilho@654: renatofilho@654: priv->tick_id = g_timeout_add (500, _tick_cb, self); renatofilho@600: } renatofilho@600: renatofilho@600: void renatofilho@600: g_mencoder_pause_stream (GMencoder *self) renatofilho@600: { renatofilho@600: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@600: renatofilho@600: g_return_if_fail (priv->ready == TRUE); renatofilho@600: gst_element_set_state (priv->pipe, GST_STATE_PAUSED); renatofilho@600: } renatofilho@600: renatofilho@600: void renatofilho@600: g_mencoder_close_stream (GMencoder *self) renatofilho@600: { renatofilho@600: renatofilho@600: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@600: renatofilho@634: g_debug ("g_mencoder_close_stream"); renatofilho@654: if (priv->tick_id != 0) { renatofilho@654: g_source_remove (priv->tick_id); renatofilho@654: priv->tick_id = 0; renatofilho@654: } renatofilho@654: renatofilho@600: if (priv->pipe != NULL) { renatofilho@600: gst_element_set_state (priv->pipe, GST_STATE_NULL); renatofilho@600: gst_object_unref (priv->pipe); renatofilho@600: priv->pipe = NULL; renatofilho@600: priv->abin = NULL; renatofilho@600: priv->vbin = NULL; renatofilho@600: priv->sink = NULL; renatofilho@600: } renatofilho@600: priv->ready = FALSE; renatofilho@654: renatofilho@654: renatofilho@600: } renatofilho@600: renatofilho@600: static GstElement* renatofilho@600: _create_pipeline (GMencoder *self, renatofilho@600: const gchar* video_encode, renatofilho@600: const gchar* mux_name, renatofilho@600: gchar** video_encode_prop, renatofilho@600: gdouble video_fps, renatofilho@600: gdouble video_rate, renatofilho@600: guint video_width, renatofilho@600: guint video_height, renatofilho@600: const gchar* audio_encode, renatofilho@600: gchar** audio_encode_prop, renatofilho@600: guint audio_rate) renatofilho@588: { renatofilho@588: GstBus *bus = NULL; renatofilho@588: GstElement *pipe = NULL; renatofilho@600: GstElement *sink = NULL; renatofilho@588: GstElement *mux = NULL; renatofilho@588: GstElement *abin = NULL; renatofilho@600: GstElement *vbin = NULL; renatofilho@634: GstPad *aux_pad = NULL; renatofilho@634: GstPad *mux_pad = NULL; renatofilho@634: #ifdef SUPPORT_MULT_INPUT renatofilho@600: GstElement *ap = NULL; renatofilho@600: GstElement *vp = NULL; renatofilho@634: #endif renatofilho@634: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@588: renatofilho@600: pipe = gst_pipeline_new ("pipe"); renatofilho@588: renatofilho@634: #ifdef SUPPORT_MULT_INPUT renatofilho@634: ap = gst_element_factory_make ("concatmux", "ap"); renatofilho@614: vp = gst_element_factory_make ("concatmux", "vp"); renatofilho@634: gst_bin_add_many (GST_BIN (pipe), ap, vp, NULL); renatofilho@634: #endif renatofilho@588: renatofilho@600: mux = gst_element_factory_make ((mux_name ? mux_name : "ffmux_mpeg"), "mux"); renatofilho@588: if (mux == NULL) renatofilho@588: goto error; renatofilho@588: renatofilho@634: renatofilho@634: sink = gst_element_factory_make ("fdsink", "sink"); renatofilho@600: if (sink == NULL) renatofilho@588: goto error; renatofilho@588: renatofilho@600: g_object_set (G_OBJECT(sink), renatofilho@634: "fd", priv->fd, renatofilho@634: NULL); renatofilho@600: renatofilho@600: abin = _create_audio_bin (audio_encode, audio_encode_prop, audio_rate); renatofilho@588: if (abin == NULL) renatofilho@588: goto error; renatofilho@588: renatofilho@600: vbin = _create_video_bin (video_encode, video_encode_prop, video_fps, video_rate, video_width, video_height); renatofilho@588: if (vbin == NULL) renatofilho@588: goto error; renatofilho@588: renatofilho@588: // Finish Pipe renatofilho@634: gst_bin_add_many (GST_BIN (pipe), abin, vbin, mux, sink, NULL); renatofilho@600: renatofilho@600: renatofilho@634: #ifdef SUPPORT_MULT_INPUT renatofilho@634: if (gst_element_link (ap, abin) == FALSE) { renatofilho@634: g_warning ("Fail to link concat and abin"); renatofilho@634: goto error; renatofilho@634: } renatofilho@634: renatofilho@634: if (gst_element_link (vp, vbin) == FALSE) { renatofilho@634: g_warning ("Fail to link concat and vbin"); renatofilho@634: } renatofilho@634: #endif renatofilho@588: renatofilho@588: //Link bins with mux renatofilho@588: aux_pad = gst_element_get_pad (abin, "src"); renatofilho@617: mux_pad = gst_element_get_compatible_pad (mux, aux_pad, GST_PAD_CAPS (aux_pad)); renatofilho@600: if (mux_pad == NULL) { renatofilho@617: g_warning ("Mux element no have audio PAD"); renatofilho@600: goto error; renatofilho@600: } renatofilho@588: GstPadLinkReturn ret = gst_pad_link (aux_pad, mux_pad); renatofilho@588: if (ret != GST_PAD_LINK_OK) { renatofilho@588: g_warning ("Fail link audio and mux: %d", ret); renatofilho@588: goto error; renatofilho@588: renatofilho@588: } renatofilho@588: gst_object_unref (aux_pad); renatofilho@588: gst_object_unref (mux_pad); renatofilho@588: renatofilho@588: aux_pad = gst_element_get_pad (vbin, "src"); renatofilho@617: mux_pad = gst_element_get_compatible_pad (mux, aux_pad, GST_PAD_CAPS (aux_pad)); renatofilho@600: if (mux_pad == NULL) { renatofilho@617: g_warning ("Mux element no have video PAD"); renatofilho@600: goto error; renatofilho@600: } renatofilho@588: ret = gst_pad_link (aux_pad, mux_pad); renatofilho@588: if (ret != GST_PAD_LINK_OK) { renatofilho@588: g_warning ("Fail link video and mux: %d", ret); renatofilho@588: goto error; renatofilho@588: } renatofilho@588: gst_object_unref (aux_pad); renatofilho@588: gst_object_unref (mux_pad); renatofilho@588: aux_pad = NULL; renatofilho@588: mux_pad = NULL; renatofilho@588: renatofilho@588: //Link mux with sink renatofilho@600: gst_element_link (mux, sink); renatofilho@588: renatofilho@588: bus = gst_pipeline_get_bus (GST_PIPELINE (pipe)); renatofilho@588: gst_bus_add_watch (bus, _pipeline_bus_cb, self); renatofilho@588: gst_object_unref (bus); renatofilho@600: return pipe; renatofilho@588: renatofilho@588: error: renatofilho@588: g_warning ("Invalid uri"); renatofilho@588: renatofilho@588: if (pipe != NULL) { renatofilho@588: gst_object_unref (pipe); renatofilho@588: } renatofilho@588: renatofilho@588: renatofilho@588: if (mux != NULL) { renatofilho@588: gst_object_unref (mux); renatofilho@588: } renatofilho@588: renatofilho@588: if (mux_pad != NULL) { renatofilho@588: gst_object_unref (mux_pad); renatofilho@588: } renatofilho@588: renatofilho@588: if (aux_pad != NULL) { renatofilho@588: gst_object_unref (mux_pad); renatofilho@588: } renatofilho@588: renatofilho@600: if (sink != NULL) { renatofilho@600: gst_object_unref (sink); renatofilho@588: } renatofilho@588: renatofilho@588: if (abin != NULL) { renatofilho@588: gst_object_unref (abin); renatofilho@588: } renatofilho@588: renatofilho@588: if (vbin != NULL) { renatofilho@588: gst_object_unref (vbin); renatofilho@588: } renatofilho@588: renatofilho@588: return FALSE; renatofilho@588: } renatofilho@588: renatofilho@600: renatofilho@600: static void renatofilho@600: _close_output (GMencoder *self) renatofilho@588: { renatofilho@600: renatofilho@600: } renatofilho@600: renatofilho@600: static GstElement* renatofilho@600: _create_source (const gchar* uri) renatofilho@600: { renatofilho@600: renatofilho@600: GstElement *bsrc; renatofilho@600: GstElement *src; renatofilho@600: GstElement *aqueue; renatofilho@600: GstElement *vqueue; renatofilho@600: GstElement *decode; renatofilho@600: GstPad *src_pad; renatofilho@600: renatofilho@600: renatofilho@600: bsrc = gst_bin_new (NULL); renatofilho@600: renatofilho@634: //src = gst_element_factory_make ("gnomevfssrc", "src"); renatofilho@634: //g_object_set (G_OBJECT (src), "location", uri, NULL); renatofilho@634: src = gst_element_make_from_uri (GST_URI_SRC, uri, "src"); renatofilho@600: if (src == NULL) renatofilho@600: goto error; renatofilho@600: renatofilho@600: decode = gst_element_factory_make ("decodebin2", "decode"); renatofilho@600: if (decode == NULL) renatofilho@600: goto error; renatofilho@600: renatofilho@600: aqueue = gst_element_factory_make ("queue", "aqueue"); renatofilho@600: if (aqueue == NULL) renatofilho@600: goto error; renatofilho@600: renatofilho@600: vqueue = gst_element_factory_make ("queue", "vqueue"); renatofilho@600: if (vqueue == NULL) renatofilho@600: goto error; renatofilho@600: renatofilho@600: gst_bin_add_many (GST_BIN (bsrc), src, decode, aqueue, vqueue, NULL); renatofilho@600: gst_element_link (src, decode); renatofilho@600: renatofilho@600: g_signal_connect (G_OBJECT (decode), renatofilho@600: "new-decoded-pad", renatofilho@600: G_CALLBACK (_decodebin_new_pad_cb), renatofilho@600: bsrc); renatofilho@600: renatofilho@600: g_signal_connect (G_OBJECT (decode), renatofilho@600: "unknown-type", renatofilho@600: G_CALLBACK (_decodebin_unknown_type_cb), renatofilho@600: pipe); renatofilho@600: renatofilho@600: src_pad = gst_element_get_pad (aqueue, "src"); renatofilho@600: gst_element_add_pad (bsrc, gst_ghost_pad_new("src_audio", src_pad)); renatofilho@600: gst_object_unref (src_pad); renatofilho@600: renatofilho@600: src_pad = gst_element_get_pad (vqueue, "src"); renatofilho@600: gst_element_add_pad (bsrc, gst_ghost_pad_new("src_video", src_pad)); renatofilho@600: gst_object_unref (src_pad); renatofilho@600: renatofilho@600: return bsrc; renatofilho@600: renatofilho@600: error: renatofilho@600: if (src != NULL) { renatofilho@600: gst_object_unref (src); renatofilho@600: } renatofilho@600: renatofilho@600: if (decode != NULL) { renatofilho@600: gst_object_unref (decode); renatofilho@600: } renatofilho@600: renatofilho@600: if (aqueue != NULL) { renatofilho@600: gst_object_unref (aqueue); renatofilho@600: } renatofilho@600: renatofilho@600: if (vqueue != NULL) { renatofilho@600: gst_object_unref (vqueue); renatofilho@600: } renatofilho@600: renatofilho@600: return NULL; renatofilho@600: } renatofilho@600: renatofilho@600: static void renatofilho@600: _open_output (GMencoder *self, renatofilho@600: const gchar* uri) renatofilho@600: { renatofilho@600: gchar** i; renatofilho@588: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self); renatofilho@588: renatofilho@600: i = g_strsplit (uri, "://", 0); renatofilho@600: if (strcmp (i[0], "fd") == 0) { renatofilho@600: priv->fd = atoi (i[1]); renatofilho@600: } else if (strcmp (i[0], "file") == 0) { renatofilho@600: priv->fd = open (i[1], O_WRONLY | O_CREAT | O_TRUNC); renatofilho@600: } else { renatofilho@600: g_warning ("Output uri not supported"); renatofilho@600: } renatofilho@588: renatofilho@600: g_strfreev (i); renatofilho@588: } renatofilho@588: renatofilho@588: static gboolean renatofilho@588: _pipeline_bus_cb (GstBus *bus, renatofilho@588: GstMessage *msg, renatofilho@588: gpointer user_data) renatofilho@588: { renatofilho@600: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (user_data); renatofilho@600: renatofilho@588: switch (GST_MESSAGE_TYPE (msg)) renatofilho@588: { renatofilho@588: case GST_MESSAGE_STATE_CHANGED: renatofilho@588: { renatofilho@588: GstState oldstate; renatofilho@588: GstState newstate; renatofilho@588: GstState pendingstate; renatofilho@588: renatofilho@588: renatofilho@588: gst_message_parse_state_changed (msg, &oldstate, renatofilho@588: &newstate, &pendingstate); renatofilho@588: renatofilho@588: if (pendingstate != GST_STATE_VOID_PENDING) renatofilho@588: break; renatofilho@588: renatofilho@588: if ((oldstate == GST_STATE_READY) && renatofilho@588: (newstate == GST_STATE_PAUSED)) { renatofilho@588: if (priv->ready) renatofilho@588: g_signal_emit (user_data, g_mencoder_signals[PAUSED], 0); renatofilho@588: } else if ((oldstate == GST_STATE_PAUSED) && renatofilho@588: (newstate == GST_STATE_PLAYING)) { renatofilho@588: g_signal_emit (user_data, g_mencoder_signals[PLAYING], 0); renatofilho@588: } else if ((oldstate == GST_STATE_READY) && renatofilho@588: (newstate == GST_STATE_NULL)) { renatofilho@588: g_signal_emit (user_data, g_mencoder_signals[STOPED], 0); renatofilho@588: } renatofilho@588: break; renatofilho@588: } renatofilho@588: case GST_MESSAGE_ERROR: renatofilho@588: { renatofilho@588: GError *error; renatofilho@588: gchar *debug; renatofilho@588: gchar *err_str; renatofilho@588: renatofilho@588: gst_message_parse_error (msg, &error, &debug); renatofilho@588: err_str = g_strdup_printf ("Error [%d] %s (%s)", error->code, renatofilho@588: error->message, renatofilho@588: debug); renatofilho@588: g_signal_emit (user_data, g_mencoder_signals[ERROR], 0, err_str); renatofilho@600: priv->ready = FALSE; renatofilho@588: g_free (err_str); renatofilho@588: g_clear_error (&error); renatofilho@588: g_free (debug); renatofilho@588: break; renatofilho@588: } renatofilho@588: renatofilho@588: case GST_MESSAGE_EOS: renatofilho@600: priv->ready = FALSE; renatofilho@588: g_signal_emit (user_data, g_mencoder_signals[EOS], 0); renatofilho@588: break; renatofilho@654: renatofilho@588: default: renatofilho@654: { renatofilho@588: break; renatofilho@654: } renatofilho@588: } renatofilho@588: return TRUE; renatofilho@588: } renatofilho@588: renatofilho@600: renatofilho@600: renatofilho@588: static void renatofilho@588: _decodebin_new_pad_cb (GstElement* object, renatofilho@588: GstPad* pad, renatofilho@588: gboolean flag, renatofilho@588: gpointer user_data) renatofilho@588: { renatofilho@588: GstCaps *caps; renatofilho@588: gchar *str_caps = NULL; renatofilho@600: GstElement *sink_element; renatofilho@600: GstPad *sink_pad; renatofilho@588: renatofilho@588: caps = gst_pad_get_caps (pad); renatofilho@588: str_caps = gst_caps_to_string (caps); renatofilho@634: g_debug ("New pad : %s", str_caps); renatofilho@588: if (strstr (str_caps, "audio") != NULL) { renatofilho@600: sink_element = gst_bin_get_by_name (GST_BIN (user_data), "aqueue"); renatofilho@588: } else if (strstr (str_caps, "video") != NULL) { renatofilho@600: sink_element = gst_bin_get_by_name (GST_BIN (user_data), "vqueue"); renatofilho@588: } else { renatofilho@588: g_warning ("invalid caps %s", str_caps); renatofilho@588: } renatofilho@588: renatofilho@600: sink_pad = gst_element_get_pad (sink_element, "sink"); renatofilho@600: gst_pad_link (pad, sink_pad); renatofilho@600: renatofilho@600: gst_object_unref (sink_element); renatofilho@600: gst_object_unref (sink_pad); renatofilho@588: g_free (str_caps); renatofilho@588: gst_caps_unref (caps); renatofilho@588: } renatofilho@588: renatofilho@588: static void renatofilho@588: _decodebin_unknown_type_cb (GstElement* object, renatofilho@588: GstPad* pad, renatofilho@588: GstCaps* caps, renatofilho@588: gpointer user_data) renatofilho@588: { renatofilho@588: g_warning ("Unknown Type"); renatofilho@600: //priv->ready = FALSE; renatofilho@588: } renatofilho@654: renatofilho@654: static gboolean renatofilho@654: _tick_cb (gpointer user_data) renatofilho@654: { renatofilho@654: GstFormat format = GST_FORMAT_TIME; renatofilho@654: gint64 cur = 0; renatofilho@654: gint64 duration = 0; renatofilho@654: renatofilho@654: GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (user_data); renatofilho@654: renatofilho@654: if (gst_element_query_duration (priv->pipe, &format, &duration)) { renatofilho@654: gst_element_query_position (priv->pipe, &format, &cur); renatofilho@654: g_print ("PROGRESS:%lli%\n", (100 * cur) / duration); renatofilho@654: } renatofilho@654: renatofilho@654: return TRUE; renatofilho@654: }