12 #include "gmencoder.h"
14 #define G_MENCODER_GET_PRIVATE(obj) \
15 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), G_TYPE_MENCODER, GMencoderPrivate))
17 // #define SUPPORT_MULT_INPUT 0
19 typedef struct _GMencoderPrivate GMencoderPrivate;
20 typedef struct _SetupInfo SetupInfo;
25 gchar **video_encode_prop;
31 gchar **audio_encode_prop;
36 struct _GMencoderPrivate {
61 static void g_mencoder_class_init(GMencoderClass * klass);
62 static void g_mencoder_init(GMencoder * object);
63 static void g_mencoder_dispose(GObject * object);
64 static void g_mencoder_finalize(GObject * object);
65 static GstElement *_create_audio_bin(const gchar * encode,
66 gchar ** encode_prop, gint rate);
67 static GstElement *_create_video_bin(const gchar * encode,
70 gint rate, guint width, guint height);
73 _pipeline_bus_cb(GstBus * bus, GstMessage * msg, gpointer user_data);
75 static void _decodebin_new_pad_cb(GstElement * object,
77 gboolean flag, gpointer user_data);
79 static void _decodebin_unknown_type_cb(GstElement * object,
84 static void _close_output(GMencoder * self);
85 static gboolean _open_output(GMencoder * self, const gchar * uri);
87 static GstElement *_create_source(const gchar * uri);
88 static GstElement *_create_pipeline(GMencoder * self,
89 const gchar * video_encode,
90 const gchar * mux_name,
91 gchar ** video_encode_prop,
96 const gchar * audio_encode,
97 gchar ** audio_encode_prop,
100 static gboolean _tick_cb(gpointer data);
102 static guint g_mencoder_signals[LAST_SIGNAL] = { 0 };
104 G_DEFINE_TYPE(GMencoder, g_mencoder, G_TYPE_OBJECT)
105 static void g_mencoder_class_init(GMencoderClass * klass)
107 GObjectClass *object_class;
108 object_class = (GObjectClass *) klass;
109 g_type_class_add_private(klass, sizeof(GMencoderPrivate));
111 object_class->dispose = g_mencoder_dispose;
112 object_class->finalize = g_mencoder_finalize;
114 g_mencoder_signals[PAUSED] =
115 g_signal_new("paused",
116 G_OBJECT_CLASS_TYPE(object_class),
119 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
121 g_mencoder_signals[PLAYING] =
122 g_signal_new("playing",
123 G_OBJECT_CLASS_TYPE(object_class),
126 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
128 g_mencoder_signals[STOPED] =
129 g_signal_new("stoped",
130 G_OBJECT_CLASS_TYPE(object_class),
133 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
135 g_mencoder_signals[EOS] =
137 G_OBJECT_CLASS_TYPE(object_class),
140 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
142 g_mencoder_signals[ERROR] =
143 g_signal_new("error",
144 G_OBJECT_CLASS_TYPE(object_class),
147 g_cclosure_marshal_VOID__STRING,
148 G_TYPE_NONE, 1, G_TYPE_STRING);
152 g_mencoder_init(GMencoder * self)
154 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
155 priv->info = g_new0(SetupInfo, 1);
159 g_mencoder_dispose(GObject * object)
164 g_mencoder_finalize(GObject * object)
167 g_mencoder_close_stream(G_MENCODER(object));
173 return g_object_new(G_TYPE_MENCODER, NULL);
178 _obj_set_prop(GObject * obj, const gchar * prop_name,
179 const gchar * prop_val)
183 GParamSpec *s = NULL;
184 GObjectClass *k = G_OBJECT_GET_CLASS(obj);
187 g_value_init(&v, G_TYPE_STRING);
188 g_value_set_string(&v, prop_val);
190 s = g_object_class_find_property(k, prop_name);
192 g_print("Invalid property name: %s\n", prop_name);
196 g_value_init(&p, s->value_type);
197 switch (s->value_type) {
199 g_value_set_int(&p, atoi(prop_val));
202 g_value_set_string(&p, prop_val);
208 g_object_set_property(obj, prop_name, &p);
214 _create_element_with_prop(const gchar * factory_name,
215 const gchar * element_name, gchar ** prop)
220 ret = gst_element_factory_make(factory_name, element_name);
225 for (i = 0; i < g_strv_length(prop); i++) {
226 if (prop[i] != NULL) {
227 char **v = g_strsplit(prop[i], "=", 2);
228 if (g_strv_length(v) == 2) {
229 _obj_set_prop(G_OBJECT(ret), v[0], v[1]);
241 _create_audio_bin(const gchar * encode, gchar ** encode_prop, gint rate)
243 GstElement *abin = NULL;
244 GstElement *aqueue = NULL;
245 GstElement *aconvert = NULL;
246 GstElement *aencode = NULL;
247 GstElement *aqueue_src = NULL;
250 // audio/x-raw-int ! queue ! audioconvert ! faac ! rtpmp4gpay !
251 // udpsink name=upd_audio host=224.0.0.1 port=5002
252 abin = gst_bin_new("abin");
253 aqueue = gst_element_factory_make("queue", "aqueue");
254 aconvert = gst_element_factory_make("audioconvert", "aconvert");
256 _create_element_with_prop((encode ? encode : "lame"), "aencode",
258 aqueue_src = gst_element_factory_make("queue", "aqueue_src");
260 if ((abin == NULL) || (aqueue == NULL) || (aconvert == NULL)
261 || (aencode == NULL) || (aqueue_src == NULL)) {
262 g_warning("Audio elements not found");
266 g_object_set(G_OBJECT(aencode), "bitrate", 32, NULL);
268 * if (rate > 0) { g_object_set (G_OBJECT (aencode), "bitrate", 32,
272 gst_bin_add_many(GST_BIN(abin), aqueue, aconvert, aencode, aqueue_src,
274 if (gst_element_link_many(aqueue, aconvert, aencode, aqueue_src, NULL)
276 g_warning("Not Link audio elements");
278 // TODO: apply audio rate
280 // ghost pad the audio bin
281 apad = gst_element_get_pad(aqueue, "sink");
282 gst_element_add_pad(abin, gst_ghost_pad_new("sink", apad));
283 gst_object_unref(apad);
285 apad = gst_element_get_pad(aqueue_src, "src");
286 gst_element_add_pad(abin, gst_ghost_pad_new("src", apad));
287 gst_object_unref(apad);
292 gst_object_unref(abin);
295 gst_object_unref(aqueue);
297 if (aconvert != NULL)
298 gst_object_unref(aconvert);
301 gst_object_unref(aencode);
303 if (aqueue_src != NULL)
304 gst_object_unref(aqueue_src);
307 gst_object_unref(apad);
315 // queue ! videoscale ! video/x-raw-yuv,width=240,height=144 ! colorspace
316 // ! rate ! encode ! queue
318 _create_video_bin(const gchar * encode,
319 gchar ** encode_prop,
320 gdouble fps, gint rate, guint width, guint height)
322 GstElement *vbin = NULL;
323 GstElement *vqueue = NULL;
324 GstElement *vqueue_src = NULL;
325 GstElement *vcolorspace = NULL;
326 GstElement *vencode = NULL;
327 GstElement *vrate = NULL;
330 vbin = gst_bin_new("vbin");
331 vqueue = gst_element_factory_make("queue", "vqueue");
333 gst_element_factory_make("ffmpegcolorspace", "colorspace");
335 vencode = _create_element_with_prop((encode !=
337 "ffenc_mpeg1video"), "vencode",
339 vqueue_src = gst_element_factory_make("queue", "queue_src");
341 if ((vbin == NULL) || (vqueue == NULL) || (vcolorspace == NULL)
342 || (vencode == NULL) || (vqueue_src == NULL)) {
343 g_warning("Video elements not found");
347 gst_bin_add_many(GST_BIN(vbin), vqueue, vcolorspace, vencode,
350 if ((width > 0) && (height > 0)) {
354 gst_element_factory_make("videoscale", "vscale");
356 gst_bin_add(GST_BIN(vbin), vscale);
358 vcaps = gst_caps_new_simple("video/x-raw-yuv",
359 "width", G_TYPE_INT, width,
360 "height", G_TYPE_INT, height, NULL);
362 gst_element_link(vqueue, vscale);
364 if (gst_element_link_filtered(vscale, vcolorspace, vcaps) == FALSE) {
365 g_warning("Fail to resize video");
366 gst_object_unref(vcaps);
367 gst_object_unref(vscale);
370 gst_caps_unref(vcaps);
372 gst_element_link(vqueue, vcolorspace);
376 // Changing the video fps
378 vrate = gst_element_factory_make("videorate", "vrate");
380 gst_bin_add(GST_BIN(vbin), vrate);
382 if (gst_element_link(vcolorspace, vrate) == FALSE) {
383 g_warning("Fail to link video elements");
387 vcaps = gst_caps_new_simple("video/x-raw-yuv",
388 "framerate", GST_TYPE_FRACTION,
389 (int) (fps * 1000), 1000, NULL);
391 if (gst_element_link_filtered(vrate, vencode, vcaps) == FALSE) {
392 g_warning("Fail to link vrate with vencode.");
395 gst_caps_unref(vcaps);
397 if (gst_element_link(vcolorspace, vencode) == FALSE) {
398 g_warning("Fail to link colorspace and video encode element.");
403 gst_element_link(vencode, vqueue_src);
405 // ghost pad the video bin
406 vpad = gst_element_get_pad(vqueue, "sink");
407 gst_element_add_pad(vbin, gst_ghost_pad_new("sink", vpad));
408 gst_object_unref(vpad);
410 vpad = gst_element_get_pad(vqueue_src, "src");
411 gst_element_add_pad(vbin, gst_ghost_pad_new("src", vpad));
412 gst_object_unref(vpad);
418 gst_object_unref(vpad);
421 gst_object_unref(vbin);
424 gst_object_unref(vqueue);
427 gst_object_unref(vencode);
429 if (vqueue_src != NULL)
430 gst_object_unref(vqueue_src);
432 if (vcolorspace != NULL)
433 gst_object_unref(vcolorspace);
441 g_mencoder_setup_stream(GMencoder * self,
442 const gchar * mux_name,
443 const gchar * video_encode,
444 gchar ** video_encode_prop,
449 const gchar * audio_encode,
450 gchar ** audio_encode_prop,
451 guint audio_rate, const gchar * out_uri)
453 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
454 if (priv->ready == TRUE) {
456 ("Stream already configured. You need close stream first.");
461 if (_open_output(self, out_uri) == FALSE)
465 priv->pipe = _create_pipeline(self,
473 audio_encode, audio_encode_prop,
476 return (priv->pipe != NULL);
481 g_mencoder_append_uri(GMencoder * self, const gchar * uri)
486 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
487 gboolean ret = FALSE;
488 GstElement *ap = NULL;
489 GstElement *vp = NULL;
492 g_return_val_if_fail(priv->pipe != NULL, FALSE);
493 g_return_val_if_fail(priv->ready == FALSE, FALSE);
495 #ifndef SUPPORT_MULT_INPUT
496 g_return_val_if_fail(priv->sources < 1, FALSE);
499 src = _create_source(uri);
503 priv->src = gst_bin_get_by_name(GST_BIN(src), "src");
505 gst_bin_add(GST_BIN(priv->pipe), src);
507 #ifdef SUPPORT_MULT_INPUT
508 ap = gst_bin_get_by_name(GST_BIN(priv->pipe), "ap");
509 vp = gst_bin_get_by_name(GST_BIN(priv->pipe), "vp");
511 ap = gst_bin_get_by_name(GST_BIN(priv->pipe), "abin");
512 vp = gst_bin_get_by_name(GST_BIN(priv->pipe), "vbin");
515 if ((vp == NULL) || (ap == NULL)) {
516 g_warning("Fail to get output bin");
520 pad_src = gst_element_get_pad(src, "src_audio");
521 pad_sink = gst_element_get_compatible_pad(ap,
523 gst_pad_get_caps(pad_src));
525 if ((pad_sink == NULL) || (pad_src == NULL))
528 GstPadLinkReturn lret = gst_pad_link(pad_src, pad_sink);
529 if (lret != GST_PAD_LINK_OK)
532 gst_object_unref(pad_src);
533 gst_object_unref(pad_sink);
535 pad_src = gst_element_get_pad(src, "src_video");
536 pad_sink = gst_element_get_compatible_pad(vp,
538 gst_pad_get_caps(pad_src));
540 if ((pad_src == NULL) || (pad_sink == NULL))
543 if (gst_pad_link(pad_src, pad_sink) != GST_PAD_LINK_OK) {
544 g_warning("invalid source. video");
552 if ((src != NULL) && (ret == FALSE)) {
553 gst_bin_remove(GST_BIN(priv->pipe), src);
554 gst_object_unref(src);
558 gst_object_unref(ap);
561 gst_object_unref(vp);
564 gst_object_unref(pad_src);
566 if (pad_sink != NULL)
567 gst_object_unref(pad_sink);
575 g_mencoder_remove_uri(GMencoder * self, const gchar * uri)
577 // GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE (self);
582 g_mencoder_play_stream(GMencoder * self)
584 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
585 g_return_if_fail(priv->ready == FALSE);
587 gst_element_set_state(priv->pipe, GST_STATE_PLAYING);
588 priv->tick_id = g_timeout_add(500, _tick_cb, self);
592 g_mencoder_pause_stream(GMencoder * self)
594 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
595 g_return_if_fail(priv->ready == TRUE);
596 gst_element_set_state(priv->pipe, GST_STATE_PAUSED);
600 g_mencoder_close_stream(GMencoder * self)
603 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
604 if (priv->tick_id != 0) {
605 g_source_remove(priv->tick_id);
609 if (priv->pipe != NULL) {
610 // TODO: fixe pipeline dispose
611 // gst_element_set_state (priv->pipe, GST_STATE_NULL);
612 // g_debug ("SETING STATE TO NULL: OK");
613 // gst_element_set_state (priv->pipe, GST_STATE_NULL);
614 // gst_object_unref (priv->pipe);
615 gst_object_unref(priv->src);
626 _create_pipeline(GMencoder * self,
627 const gchar * video_encode,
628 const gchar * mux_name,
629 gchar ** video_encode_prop,
634 const gchar * audio_encode,
635 gchar ** audio_encode_prop, guint audio_rate)
638 GstElement *pipe = NULL;
639 GstElement *sink = NULL;
640 GstElement *mux = NULL;
641 GstElement *abin = NULL;
642 GstElement *vbin = NULL;
643 GstElement *queue = NULL;
644 GstPad *aux_pad = NULL;
645 GstPad *mux_pad = NULL;
646 #ifdef SUPPORT_MULT_INPUT
647 GstElement *ap = NULL;
648 GstElement *vp = NULL;
650 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
652 pipe = gst_pipeline_new("pipe");
654 #ifdef SUPPORT_MULT_INPUT
655 ap = gst_element_factory_make("concatmux", "ap");
656 vp = gst_element_factory_make("concatmux", "vp");
657 gst_bin_add_many(GST_BIN(pipe), ap, vp, NULL);
661 gst_element_factory_make((mux_name ? mux_name : "ffmux_mpeg"),
666 queue = gst_element_factory_make("queue", "queueu_sink");
669 sink = gst_element_factory_make("fdsink", "sink");
673 g_object_set(G_OBJECT(sink), "fd", priv->fd, "sync", FALSE, NULL);
675 abin = _create_audio_bin(audio_encode, audio_encode_prop, audio_rate);
680 _create_video_bin(video_encode, video_encode_prop, video_fps,
681 video_rate, video_width, video_height);
686 gst_bin_add_many(GST_BIN(pipe), abin, vbin, mux, queue, sink, NULL);
689 #ifdef SUPPORT_MULT_INPUT
690 if (gst_element_link(ap, abin) == FALSE) {
691 g_warning("Fail to link concat and abin");
695 if (gst_element_link(vp, vbin) == FALSE) {
696 g_warning("Fail to link concat and vbin");
700 // Link bins with mux
701 aux_pad = gst_element_get_pad(abin, "src");
703 gst_element_get_compatible_pad(mux, aux_pad,
704 GST_PAD_CAPS(aux_pad));
705 if (mux_pad == NULL) {
706 g_warning("Mux element no have audio PAD");
709 GstPadLinkReturn ret = gst_pad_link(aux_pad, mux_pad);
710 if (ret != GST_PAD_LINK_OK) {
711 g_warning("Fail link audio and mux: %d", ret);
715 gst_object_unref(aux_pad);
716 gst_object_unref(mux_pad);
718 aux_pad = gst_element_get_pad(vbin, "src");
720 gst_element_get_compatible_pad(mux, aux_pad,
721 GST_PAD_CAPS(aux_pad));
722 if (mux_pad == NULL) {
723 g_warning("Mux element no have video PAD");
726 ret = gst_pad_link(aux_pad, mux_pad);
727 if (ret != GST_PAD_LINK_OK) {
728 g_warning("Fail link video and mux: %d", ret);
731 gst_object_unref(aux_pad);
732 gst_object_unref(mux_pad);
736 // Link mux with sink
737 gst_element_link_many(mux, queue, sink, NULL);
739 bus = gst_pipeline_get_bus(GST_PIPELINE(pipe));
740 gst_bus_add_watch(bus, _pipeline_bus_cb, self);
741 gst_object_unref(bus);
745 g_warning("Invalid uri");
748 gst_object_unref(pipe);
753 gst_object_unref(mux);
756 if (mux_pad != NULL) {
757 gst_object_unref(mux_pad);
760 if (aux_pad != NULL) {
761 gst_object_unref(mux_pad);
765 gst_object_unref(sink);
769 gst_object_unref(abin);
773 gst_object_unref(vbin);
781 _close_output(GMencoder * self)
786 _create_source(const gchar * uri)
789 GstElement *bsrc = NULL;
790 GstElement *src = NULL;
791 GstElement *queue = NULL;
792 GstElement *aqueue = NULL;
793 GstElement *vqueue = NULL;
794 GstElement *decode = NULL;
795 GstPad *src_pad = NULL;
798 bsrc = gst_bin_new(NULL);
800 // src = gst_element_factory_make ("gnomevfssrc", "src");
801 // g_object_set (G_OBJECT (src), "location", uri, NULL);
802 src = gst_element_make_from_uri(GST_URI_SRC, uri, "src");
806 decode = gst_element_factory_make("decodebin", "decode");
810 queue = gst_element_factory_make("queue", "queue_src");
811 aqueue = gst_element_factory_make("queue", "aqueue");
815 vqueue = gst_element_factory_make("queue", "vqueue");
819 gst_bin_add_many(GST_BIN(bsrc), src, queue, decode, aqueue, vqueue,
821 gst_element_link_many(src, queue, decode, NULL);
823 g_signal_connect(G_OBJECT(decode),
825 G_CALLBACK(_decodebin_new_pad_cb), bsrc);
827 g_signal_connect(G_OBJECT(decode),
829 G_CALLBACK(_decodebin_unknown_type_cb), pipe);
831 src_pad = gst_element_get_pad(aqueue, "src");
832 gst_element_add_pad(bsrc, gst_ghost_pad_new("src_audio", src_pad));
833 gst_object_unref(src_pad);
835 src_pad = gst_element_get_pad(vqueue, "src");
836 gst_element_add_pad(bsrc, gst_ghost_pad_new("src_video", src_pad));
837 gst_object_unref(src_pad);
843 gst_object_unref(src);
846 if (decode != NULL) {
847 gst_object_unref(decode);
850 if (aqueue != NULL) {
851 gst_object_unref(aqueue);
854 if (vqueue != NULL) {
855 gst_object_unref(vqueue);
862 _open_output(GMencoder * self, const gchar * uri)
866 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(self);
868 i = g_strsplit(uri, "://", 0);
869 if (strcmp(i[0], "fd") == 0) {
870 priv->fd = atoi(i[1]);
871 } else if (strcmp(i[0], "file") == 0) {
872 if (g_file_test (i[1], G_FILE_TEST_EXISTS)) {
873 if (unlink (i[1]) != 0) {
874 g_warning ("Fail to write in : %s", uri);
879 priv->fd = open(i[1], O_WRONLY | O_CREAT | O_TRUNC,
880 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
882 if (priv->fd == -1) {
883 g_warning ("Fail to open : %s", uri);
887 g_warning("Output uri not supported");
897 _pipeline_bus_cb(GstBus * bus, GstMessage * msg, gpointer user_data)
899 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data);
901 switch (GST_MESSAGE_TYPE(msg)) {
903 case GST_MESSAGE_STATE_CHANGED:
907 GstState pendingstate;
910 gst_message_parse_state_changed(msg, &oldstate,
911 &newstate, &pendingstate);
913 if (pendingstate != GST_STATE_VOID_PENDING)
916 if ((oldstate == GST_STATE_READY)
917 && (newstate == GST_STATE_PAUSED)) {
919 g_signal_emit(user_data, g_mencoder_signals[PAUSED],
921 } else if ((oldstate == GST_STATE_PAUSED)
922 && (newstate == GST_STATE_PLAYING)) {
923 g_signal_emit(user_data, g_mencoder_signals[PLAYING], 0);
924 } else if ((oldstate == GST_STATE_READY) &&
925 (newstate == GST_STATE_NULL)) {
926 g_signal_emit(user_data, g_mencoder_signals[STOPED], 0);
931 case GST_MESSAGE_ERROR:
937 if (priv->tick_id != 0) {
938 g_source_remove(priv->tick_id);
942 gst_message_parse_error(msg, &error, &debug);
943 err_str = g_strdup_printf("Error [%d] %s (%s)", error->code,
944 error->message, debug);
946 g_signal_emit(user_data, g_mencoder_signals[ERROR], 0,
949 g_clear_error(&error);
954 case GST_MESSAGE_EOS:
956 g_signal_emit(user_data, g_mencoder_signals[EOS], 0);
959 case GST_MESSAGE_DURATION:
963 gst_message_parse_duration(msg, &format, &duration);
964 if (format == GST_FORMAT_BYTES)
965 priv->duration = duration;
979 _decodebin_new_pad_cb(GstElement * object,
980 GstPad * pad, gboolean flag, gpointer user_data)
983 gchar *str_caps = NULL;
984 GstElement *sink_element;
987 caps = gst_pad_get_caps(pad);
988 str_caps = gst_caps_to_string(caps);
989 if (strstr(str_caps, "audio") != NULL) {
990 sink_element = gst_bin_get_by_name(GST_BIN(user_data), "aqueue");
991 } else if (strstr(str_caps, "video") != NULL) {
992 sink_element = gst_bin_get_by_name(GST_BIN(user_data), "vqueue");
994 g_warning("invalid caps %s", str_caps);
997 sink_pad = gst_element_get_pad(sink_element, "sink");
998 gst_pad_link(pad, sink_pad);
1000 gst_object_unref(sink_element);
1001 gst_object_unref(sink_pad);
1003 gst_caps_unref(caps);
1007 _decodebin_unknown_type_cb(GstElement * object,
1008 GstPad * pad, GstCaps * caps,
1011 g_warning("Unknown Type");
1012 // priv->ready = FALSE;
1016 _tick_cb(gpointer user_data)
1018 GstFormat format = GST_FORMAT_BYTES;
1021 GMencoderPrivate *priv = G_MENCODER_GET_PRIVATE(user_data);
1023 if (priv->duration == 0) {
1025 if (gst_element_query_duration(priv->src, &format, &d))
1029 if (priv->duration != 0) {
1030 gst_element_query_position(priv->src, &format, &cur);
1031 g_print("PROGRESS:%lli\n", (99 * cur) / priv->duration);