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