From: Ronald S. B. <rb...@pd...> - 2003-12-27 17:42:45
|
CVS Root: /home/cvs/gstreamer Module: gst-record Changes by: rbultje Date: Sat Dec 27 2003 09:42:04 PST Log message: 2003-12-27 Ronald Bultje <rb...@ro...> * Makefile.am: Change in style (newlines for each SUBDIR). * TODO: Matroskamux now has caps in its pad template. * configure.ac: Change i18n pkgname, add src/gst/Makefile. * src/Makefile.am: * src/debug.c: * src/debug.h: * src/gst/Makefile.am: * src/gst/debug.c: * src/gst/debug.h: * src/gst/list.c: * src/gst/list.h: * src/gst/marshal.list: * src/gst/record.c: * src/gst/record.h: * src/list.c: * src/list.h: * src/marshal.list: * src/record.c: * src/record.h: Moved all non-UI GStreamer bits to their own subdirectory. * src/gst/assistant.c: * src/gst/assistant.h: * src/gst/manager.c: * src/gst/manager.h: * src/main.c: (main): Two new (private) elements. A manager is a managing bin, it takes care of private events such as endless-stream-EOS handling and timestamp shifts. This should eventually be done in a cleaner fashion in the core, but this is more than good enough for now. EOS is used to end recording from a source that won't emit EOS itself (e.g. v4lsrc), timestamp shifts is used for pipelines where part is used longer and started earlier than others (think a display + capture pipeline, where the display starts before the capture, but capture should still start at time=0). * src/window.c: (cb_sliders): Show window after reposition, this prevents 'flicker' effects. Modified files: . : ChangeLog Makefile.am TODO configure.ac src : Makefile.am main.c window.c Added files: src/gst : Makefile.am assistant.c assistant.h debug.c debug.h list.c list.h manager.c manager.h marshal.list record.c record.h Removed files: src : debug.c debug.h list.c list.h marshal.list record.c record.h Links: http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/ChangeLog.diff?r1=1.7&r2=1.8 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/Makefile.am.diff?r1=1.5&r2=1.6 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/TODO.diff?r1=1.24&r2=1.25 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/configure.ac.diff?r1=1.11&r2=1.12 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/Makefile.am.diff?r1=1.8&r2=1.9 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/debug.c http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/debug.h http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/list.c http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/list.h http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/main.c.diff?r1=1.3&r2=1.4 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/marshal.list http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/record.c http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/record.h http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/window.c.diff?r1=1.11&r2=1.12 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/Makefile.am?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/assistant.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/assistant.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/debug.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/debug.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/list.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/list.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/manager.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/manager.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/marshal.list?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/record.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-record/src/gst/record.h?rev=1.1&content-type=text/vnd.viewcvs-markup ====Begin Diffs==== Index: ChangeLog =================================================================== RCS file: /home/cvs/gstreamer/gst-record/ChangeLog,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- ChangeLog 21 Dec 2003 18:08:38 -0000 1.7 +++ ChangeLog 27 Dec 2003 17:41:51 -0000 1.8 @@ -1,3 +1,45 @@ +2003-12-27 Ronald Bultje <rb...@ro...> + + * Makefile.am: + Change in style (newlines for each SUBDIR). + * TODO: + Matroskamux now has caps in its pad template. + * configure.ac: + Change i18n pkgname, add src/gst/Makefile. + * src/Makefile.am: + * src/debug.c: + * src/debug.h: + * src/gst/Makefile.am: + * src/gst/debug.c: + * src/gst/debug.h: + * src/gst/list.c: + * src/gst/list.h: + * src/gst/marshal.list: + * src/gst/record.c: + * src/gst/record.h: + * src/list.c: + * src/list.h: + * src/marshal.list: + * src/record.c: + * src/record.h: + Moved all non-UI GStreamer bits to their own subdirectory. + * src/gst/assistant.c: + * src/gst/assistant.h: + * src/gst/manager.c: + * src/gst/manager.h: + * src/main.c: (main): + Two new (private) elements. A manager is a managing bin, it takes + care of private events such as endless-stream-EOS handling and + timestamp shifts. This should eventually be done in a cleaner + fashion in the core, but this is more than good enough for now. EOS + is used to end recording from a source that won't emit EOS itself + (e.g. v4lsrc), timestamp shifts is used for pipelines where part + is used longer and started earlier than others (think a display + + capture pipeline, where the display starts before the capture, but + capture should still start at time=0). + * src/window.c: (cb_sliders): + Show window after reposition, this prevents 'flicker' effects. + 2003-12-21 Ronald Bultje <rb...@ro...> * TODO: Index: Makefile.am =================================================================== RCS file: /home/cvs/gstreamer/gst-record/Makefile.am,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- Makefile.am 26 Oct 2003 13:18:48 -0000 1.5 +++ Makefile.am 27 Dec 2003 17:41:51 -0000 1.6 @@ -1,4 +1,9 @@ -SUBDIRS = data docs pixmaps po src +SUBDIRS = \ + data \ + docs \ + pixmaps \ + po \ + src INTLTOOL_BUILT = \ intltool-extract \ Index: TODO =================================================================== RCS file: /home/cvs/gstreamer/gst-record/TODO,v retrieving revision 1.24 retrieving revision 1.25 diff -u -d -r1.24 -r1.25 --- TODO 21 Dec 2003 18:08:38 -0000 1.24 +++ TODO 27 Dec 2003 17:41:51 -0000 1.25 @@ -31,7 +31,6 @@ * on exit, my soundcard sometimes hangs (without obvious reason). Even kill -9 doesn't work, process is 'D' (ps -ax). Kernel bug? * recording twice with the same AVI element fails. This is a bug in avimux. -* matroskamux doesn't have caps on its template. * .schemas file doesn't work (according to Benjamin, this is a GConf-editor bug). * tables in preferences screen - when given a padding of x (>0) - will have Index: configure.ac =================================================================== RCS file: /home/cvs/gstreamer/gst-record/configure.ac,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- configure.ac 9 Nov 2003 17:05:28 -0000 1.11 +++ configure.ac 27 Dec 2003 17:41:51 -0000 1.12 @@ -26,7 +26,7 @@ dnl i18n AC_PROG_INTLTOOL([0.23]) -GETTEXT_PACKAGE=$PACKAGE_TARNAME +GETTEXT_PACKAGE=$PACKAGE AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [The gettext package name]) @@ -111,6 +111,7 @@ pixmaps/Makefile po/Makefile.in src/Makefile +src/gst/Makefile gst-record.spec ]) Index: Makefile.am =================================================================== RCS file: /home/cvs/gstreamer/gst-record/src/Makefile.am,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- Makefile.am 20 Dec 2003 18:03:08 -0000 1.8 +++ Makefile.am 27 Dec 2003 17:41:51 -0000 1.9 @@ -1,15 +1,14 @@ +SUBDIRS = \ + gst + bin_PROGRAMS = gst-record gst_record_SOURCES = \ configuration.c \ - debug.c \ druid.c \ - list.c \ main.c \ - marshal.c \ parameter.c \ preferences.c \ - record.c \ sliders.c \ stats.c \ tv.c \ @@ -17,6 +16,7 @@ gst_record_CFLAGS = \ -I$(top_srcdir)/pixmaps \ + -I$(srcdir)/gst \ $(GLIB_CFLAGS) \ $(GST_CFLAGS) \ $(GSTLIBS_CFLAGS) \ @@ -27,6 +27,9 @@ -DDATA_DIR=\""$(datadir)"\" \ -DPIX_DIR=\""$(datadir)/pixmaps"\" +gst_record_LDADD = \ + gst/libgstrecelements.la + gst_record_LDFLAGS = \ $(GLIB_LIBS) \ $(GNOME_LIBS) \ @@ -36,33 +39,12 @@ noinst_HEADERS = \ configuration.h \ - debug.h \ druid.h \ keys.h \ - list.h \ parameter.h \ preferences.h \ - record.h \ sliders.h \ stats.h \ stock.h \ tv.h \ window.h - -BUILT_SOURCES = \ - marshal.c \ - marshal.h -built_headers = \ - marshal.h - -EXTRA_DIST = \ - marshal.list - -marshal.h: marshal.list - glib-genmarshal --header --prefix=gst_rec_marshal $^ > marshal.h.tmp - mv marshal.h.tmp marshal.h - -marshal.c: marshal.list - echo "#include \"marshal.h\"" >> marshal.c.tmp - glib-genmarshal --body --prefix=gst_rec_marshal $^ >> marshal.c.tmp - mv marshal.c.tmp marshal.c --- debug.c DELETED --- --- debug.h DELETED --- --- list.c DELETED --- --- list.h DELETED --- Index: main.c =================================================================== RCS file: /home/cvs/gstreamer/gst-record/src/main.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- main.c 20 Dec 2003 18:03:08 -0000 1.3 +++ main.c 27 Dec 2003 17:41:51 -0000 1.4 @@ -30,6 +30,7 @@ #include "debug.h" #include "list.h" +#include "manager.h" #include "stock.h" #include "window.h" @@ -89,6 +90,7 @@ NULL); /* init ourselves */ + gst_rec_elements_init (); register_stock_icons (); gst_rec_debug_init (); gst_rec_lists_init (); @@ -97,8 +99,8 @@ appfile = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP, PIX_DIR"/gst-record.png", TRUE, NULL); - gnome_window_icon_set_default_from_file (appfile); - g_free (appfile); + gnome_window_icon_set_default_from_file (appfile); + g_free (appfile); create_window (); gtk_main (); --- marshal.list DELETED --- --- record.c DELETED --- --- record.h DELETED --- Index: window.c =================================================================== RCS file: /home/cvs/gstreamer/gst-record/src/window.c,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- window.c 21 Dec 2003 18:08:38 -0000 1.11 +++ window.c 27 Dec 2003 17:41:51 -0000 1.12 @@ -357,9 +357,9 @@ window = gst_rec_sliders_new (gst_rec_get_video_source (coll->rec), gst_rec_get_audio_source (coll->rec), TRUE); if (window) { - gtk_widget_show (window); - + gtk_widget_realize (window); gst_rec_sliders_attach (GST_REC_SLIDERS (window), widget); + gtk_widget_show (window); gst_rec_sliders_run (GST_REC_SLIDERS (window)); } } --- NEW FILE: Makefile.am --- noinst_LTLIBRARIES = \ libgstrecelements.la libgstrecelements_la_SOURCES = \ assistant.c \ debug.c \ list.c \ manager.c \ marshal.c \ record.c libgstrecelements_la_CFLAGS = \ $(GLIB_CFLAGS) \ $(GST_CFLAGS) \ $(GSTLIBS_CFLAGS) $(X_CFLAGS) libgstrecelements_la_LIBADD = \ $(GLIB_LIBS) \ $(GST_LIBS) \ $(GSTLIBS_LIBS) $(X_LIBS) noinst_HEADERS = \ assistant.h \ debug.h \ list.h \ manager.h \ record.h BUILT_SOURCES = \ marshal.c \ marshal.h built_headers = \ marshal.h EXTRA_DIST = \ marshal.list marshal.h: marshal.list glib-genmarshal --header --prefix=gst_rec_marshal $^ > marshal.h.tmp mv marshal.h.tmp marshal.h marshal.c: marshal.list echo "#include \"marshal.h\"" >> marshal.c.tmp glib-genmarshal --body --prefix=gst_rec_marshal $^ >> marshal.c.tmp mv marshal.c.tmp marshal.c --- NEW FILE: assistant.c --- /* GStreamer Recorder * (c) 2003 Ronald Bultje <rb...@ro...> * * assistant.c: handle actual events from managing bin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "assistant.h" /* GObject init */ static void gst_rec_assistant_class_init (GstRecAssistantClass *klass); static void gst_rec_assistant_base_init (GstRecAssistantClass *klass); static void gst_rec_assistant_init (GstRecAssistant *assistant); /* pad proxy stuff */ static GstCaps *gst_rec_assistant_getcaps (GstPad *pad, GstCaps *caps); static GstPadLinkReturn gst_rec_assistant_link (GstPad *pad, GstCaps *caps); static void gst_rec_assistant_chain (GstPad *pad, GstData *data); /* the event handler - it's all about him */ static gboolean gst_rec_assistant_src_event (GstPad *pad, GstEvent *event); /* keep track of state for reset */ static GstElementStateReturn gst_rec_assistant_change_state (GstElement *element); static GstElementClass *parent_class = NULL; GType gst_rec_assistant_get_type (void) { static GType gst_rec_assistant_type = 0; if (!gst_rec_assistant_type) { static const GTypeInfo gst_rec_assistant_info = { sizeof (GstRecAssistantClass), (GBaseInitFunc) gst_rec_assistant_base_init, NULL, (GClassInitFunc) gst_rec_assistant_class_init, NULL, NULL, sizeof (GstRecAssistant), 0, (GInstanceInitFunc) gst_rec_assistant_init, NULL }; gst_rec_assistant_type = g_type_register_static (GST_TYPE_ELEMENT, "GstRecAssistant", &gst_rec_assistant_info, 0); } return gst_rec_assistant_type; } static void gst_rec_assistant_base_init (GstRecAssistantClass *klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); static GstElementDetails gst_avi_demux_details = GST_ELEMENT_DETAILS ( "Managing bin assistant", "Generic", "Manages private gst-rec events forwarded from the containing bin", "Ronald Bultje <rb...@ro...>" ); gst_element_class_set_details (element_class, &gst_avi_demux_details); } static void gst_rec_assistant_class_init (GstRecAssistantClass *klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); parent_class = g_type_class_ref (GST_TYPE_ELEMENT); element_class->change_state = gst_rec_assistant_change_state; } static void gst_rec_assistant_init (GstRecAssistant *assistant) { GST_FLAG_SET (assistant, GST_ELEMENT_EVENT_AWARE); assistant->sinkpad = gst_pad_new ("sink", GST_PAD_SINK); gst_pad_set_chain_function (assistant->sinkpad, gst_rec_assistant_chain); gst_pad_set_link_function (assistant->sinkpad, gst_rec_assistant_link); gst_pad_set_getcaps_function (assistant->sinkpad, gst_rec_assistant_getcaps); gst_element_add_pad (GST_ELEMENT (assistant), assistant->sinkpad); assistant->srcpad = gst_pad_new ("src", GST_PAD_SRC); gst_pad_set_event_function (assistant->srcpad, gst_rec_assistant_src_event); gst_pad_set_link_function (assistant->srcpad, gst_rec_assistant_link); gst_pad_set_getcaps_function (assistant->srcpad, gst_rec_assistant_getcaps); gst_element_add_pad (GST_ELEMENT (assistant), assistant->srcpad); assistant->time_shift = 0; assistant->seek_time = GST_CLOCK_TIME_NONE; assistant->eos_time = GST_CLOCK_TIME_NONE; assistant->eos = FALSE; } static GstPad * gst_rec_assistant_otherpad (GstPad *pad) { GstRecAssistant *assistant = GST_REC_ASSISTANT (gst_pad_get_parent (pad)); if (pad == assistant->srcpad) return assistant->sinkpad; else if (pad == assistant->sinkpad) return assistant->srcpad; g_assert (0); } static GstCaps * gst_rec_assistant_getcaps (GstPad *pad, GstCaps *caps) { GstPad *otherpad = GST_PAD_PEER (gst_rec_assistant_otherpad (pad)); if (otherpad) return gst_pad_get_caps (otherpad); return NULL; } static GstPadLinkReturn gst_rec_assistant_link (GstPad *pad, GstCaps *caps) { return gst_pad_proxy_link (gst_rec_assistant_otherpad (pad), caps); } static void gst_rec_assistant_chain (GstPad *pad, GstData *data) { GstBuffer *buf; GstRecAssistant *assistant = GST_REC_ASSISTANT (gst_pad_get_parent (pad)); if (GST_IS_EVENT (data)) { gst_pad_event_default (pad, GST_EVENT (data)); return; } buf = GST_BUFFER (data); /* do we have a new time diff? */ if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { if (assistant->seek_time != GST_CLOCK_TIME_NONE) { assistant->time_shift = assistant->seek_time - GST_BUFFER_TIMESTAMP (buf); assistant->seek_time = GST_CLOCK_TIME_NONE; } GST_BUFFER_TIMESTAMP (buf) += assistant->time_shift; /* are we beyond EOS already? */ if (assistant->eos_time != GST_CLOCK_TIME_NONE && GST_BUFFER_TIMESTAMP (buf) > assistant->eos_time) { assistant->eos = TRUE; } } /* are we supposed to EOS? */ if (assistant->eos) { gst_buffer_unref (buf); gst_pad_event_default (pad, gst_event_new (GST_EVENT_EOS)); } else { gst_pad_push (assistant->srcpad, data); } } static gboolean gst_rec_assistant_src_event (GstPad *pad, GstEvent *event) { GstRecAssistant *assistant = GST_REC_ASSISTANT (gst_pad_get_parent (pad)); gboolean res = FALSE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: assistant->eos = TRUE; res = TRUE; break; case GST_EVENT_SEEK_SEGMENT: case GST_EVENT_SEEK: switch (GST_EVENT_SEEK_FORMAT (event)) { case GST_FORMAT_TIME: if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK_SEGMENT) { assistant->eos_time = GST_EVENT_SEEK_ENDOFFSET (event); } assistant->seek_time = GST_EVENT_SEEK_OFFSET (event); res = TRUE; break; default: break; } break; default: break; } } static GstElementStateReturn gst_rec_assistant_change_state (GstElement *element) { GstRecAssistant *assistant = GST_REC_ASSISTANT (element); switch (GST_STATE_PENDING (element)) { case GST_STATE_PAUSED_TO_PLAYING: assistant->eos = FALSE; break; case GST_STATE_PAUSED_TO_READY: assistant->seek_time = GST_CLOCK_TIME_NONE; assistant->eos_time = GST_CLOCK_TIME_NONE; assistant->time_shift = 0; break; default: break; } if (GST_ELEMENT_CLASS (parent_class)->change_state) return GST_ELEMENT_CLASS (parent_class)->change_state (element); return GST_STATE_SUCCESS; } --- NEW FILE: assistant.h --- /* GStreamer Recorder * (c) 2003 Ronald Bultje <rb...@ro...> * * assistant.h: child of a managing bin, the actual event handler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __GST_REC_ASSISTANT_H__ #define __GST_REC_ASSISTANT_H__ #include <gst/gst.h> #define GST_REC_TYPE_ASSISTANT \ (gst_rec_assistant_get_type()) #define GST_REC_ASSISTANT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_REC_TYPE_ASSISTANT, \ GstRecAssistant)) #define GST_REC_ASSISTANT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), GST_REC_TYPE_ASSISTANT, \ GstRecAssistantClass)) #define GST_REC_IS_ASSISTANT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_REC_TYPE_ASSISTANT)) #define GST_REC_IS_ASSISTANT_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GST_REC_TYPE_ASSISTANT)) typedef struct _GstRecAssistant { GstElement parent; /* data pads */ GstPad *sinkpad, *srcpad; /* are we supposed to emit EOS next? */ gboolean eos; /* and are we time-shifting? */ GstClockTimeDiff time_shift; GstClockTime seek_time, eos_time; } GstRecAssistant; typedef struct _GstRecAssistantClass { GstElementClass parent; } GstRecAssistantClass; GType gst_rec_assistant_get_type (void); #endif /* __GST_REC_ASSISTANT_H__ */ --- NEW FILE: debug.c --- /* GStreamer Recorder * (c) 2003 Ronald Bultje <rb...@ro...> * * debug.c: debugging * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <glib.h> #include <gst/gst.h> #include "debug.h" GST_DEBUG_CATEGORY (gst_rec_debug_cat); void gst_rec_debug_init (void) { GST_DEBUG_CATEGORY_INIT (gst_rec_debug_cat, "GST_REC", 0, "GstRec messages"); } --- NEW FILE: debug.h --- /* GStreamer Recorder * (c) 2003 Ronald Bultje <rb...@ro...> * * debug.h: debug definitions * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __GST_REC_DEBUG_H__ #define __GST_REC_DEBUG_H__ #include <glib.h> #include <gst/gst.h> G_BEGIN_DECLS GST_DEBUG_CATEGORY_EXTERN (gst_rec_debug_cat); #ifdef G_HAVE_ISO_VARARGS #define GST_REC_ERROR(...) GST_CAT_ERROR (gst_rec_debug_cat, __VA_ARGS__) #define GST_REC_WARNING(...) GST_CAT_WARNING (gst_rec_debug_cat, __VA_ARGS__) #define GST_REC_INFO(...) GST_CAT_INFO (gst_rec_debug_cat, __VA_ARGS__) #define GST_REC_DEBUG(...) GST_CAT_DEBUG (gst_rec_debug_cat, __VA_ARGS__) #define GST_REC_LOG(...) GST_CAT_LOG (gst_rec_debug_cat, __VA_ARGS__) #elif defined(G_HAVE_GNUC_VARARGS) #define GST_REC_ERROR(args...) GST_CAT_ERROR (gst_rec_debug_cat, ##args) #define GST_REC_WARNING(args...) GST_CAT_WARNING (gst_rec_debug_cat, ##args) #define GST_REC_INFO(args...) GST_CAT_INFO (gst_rec_debug_cat, ##args) #define GST_REC_DEBUG(args...) GST_CAT_DEBUG (gst_rec_debug_cat, ##args) #define GST_REC_LOG(args...) GST_CAT_LOG (gst_rec_debug_cat, ##args) #endif void gst_rec_debug_init (void); G_END_DECLS #endif /* __GST_REC_DEBUG_H__ */ --- NEW FILE: list.c --- /* GStreamer Recorder * (c) 2003 Ronald Bultje <rb...@ro...> * * list.c: lists of elements (as template) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <string.h> #include <glib.h> #include <gst/gst.h> #include <gst/propertyprobe/propertyprobe.h> #include "debug.h" #include "list.h" #include "record.h" /* * Internal lists of sources/encoders/muxers. */ static GList *video_source_factories = NULL, *audio_source_factories = NULL, *video_encoder_factories = NULL, *audio_encoder_factories = NULL, *muxer_factories = NULL; /* * Types */ static GObjectClass *src_parent_class = NULL, *enc_parent_class = NULL, *mux_parent_class = NULL; static void gst_rec_source_init (GstRecSource *src) { src->name = NULL; src->factory = NULL; src->device = NULL; } static void gst_rec_source_dispose (GObject *obj) { GstRecSource *src = GST_REC_SOURCE (obj); g_free (src->name); g_free (src->device); } static void gst_rec_source_class_init (GObjectClass *klass) { src_parent_class = g_type_class_ref (G_TYPE_OBJECT); klass->dispose = gst_rec_source_dispose; } GType gst_rec_source_get_type (void) { static GType gst_rec_source_type = 0; if (!gst_rec_source_type) { static const GTypeInfo gst_rec_source_info = { sizeof (GObjectClass), NULL, NULL, (GClassInitFunc) gst_rec_source_class_init, NULL, NULL, sizeof (GstRecSource), 0, (GInstanceInitFunc) gst_rec_source_init, NULL }; gst_rec_source_type = g_type_register_static (G_TYPE_OBJECT, "GstRecSource", &gst_rec_source_info, 0); } return gst_rec_source_type; } static GstRecSource * gst_rec_source_new (gchar *name, GstElementFactory *factory, gchar *device) { GstRecSource *src = g_object_new (GST_REC_TYPE_SOURCE, NULL); src->name = name; src->factory = factory; src->device = device; return src; } static void gst_rec_encoder_init (GstRecEncoder *enc) { enc->name = NULL; enc->factory = NULL; enc->filter = NULL; } static void gst_rec_encoder_dispose (GObject *obj) { GstRecEncoder *enc = GST_REC_ENCODER (obj); g_free (enc->name); gst_caps_sink (enc->filter); } static void gst_rec_encoder_class_init (GObjectClass *klass) { enc_parent_class = g_type_class_ref (G_TYPE_OBJECT); klass->dispose = gst_rec_encoder_dispose; } GType gst_rec_encoder_get_type (void) { static GType gst_rec_encoder_type = 0; if (!gst_rec_encoder_type) { static const GTypeInfo gst_rec_encoder_info = { sizeof (GObjectClass), NULL, NULL, (GClassInitFunc) gst_rec_encoder_class_init, NULL, NULL, sizeof (GstRecEncoder), 0, (GInstanceInitFunc) gst_rec_encoder_init, NULL }; gst_rec_encoder_type = g_type_register_static (G_TYPE_OBJECT, "GstRecEncoder", &gst_rec_encoder_info, 0); } return gst_rec_encoder_type; } static GstRecEncoder * gst_rec_encoder_new (gchar *name, GstElementFactory *factory, GstCaps *filter) { GstRecEncoder *enc = g_object_new (GST_REC_TYPE_ENCODER, NULL); enc->name = name; enc->factory = factory; enc->filter = filter; return enc; } static void gst_rec_muxer_dispose (GObject *obj) { GstRecMuxer *mux = GST_REC_MUXER (obj); g_free (mux->name); } static void gst_rec_muxer_class_init (GObjectClass *klass) { mux_parent_class = g_type_class_ref (G_TYPE_OBJECT); klass->dispose = gst_rec_muxer_dispose; } static void gst_rec_muxer_init (GstRecMuxer *mux) { mux->name = NULL; mux->factory = NULL; } GType gst_rec_muxer_get_type (void) { static GType gst_rec_muxer_type = 0; if (!gst_rec_muxer_type) { static const GTypeInfo gst_rec_muxer_info = { sizeof (GObjectClass), NULL, NULL, (GClassInitFunc) gst_rec_muxer_class_init, NULL, NULL, sizeof (GstRecMuxer), 0, (GInstanceInitFunc) gst_rec_muxer_init, NULL }; gst_rec_muxer_type = g_type_register_static (G_TYPE_OBJECT, "GstRecMuxer", &gst_rec_muxer_info, 0); } return gst_rec_muxer_type; } static GstRecMuxer * gst_rec_muxer_new (gchar *name, GstElementFactory *factory) { GstRecMuxer *mux = g_object_new (GST_REC_TYPE_MUXER, NULL); mux->name = name; mux->factory = factory; return mux; } /* * Lists. */ static GList * gst_rec_list_any_sources (GList *factories) { GstRecSource *source; GList *sources = NULL, *item; GST_REC_DEBUG ("Finding sources"); for (item = factories; item != NULL; item = item->next) { GstElementFactory *factory = item->data; GstElement *element; GstPropertyProbe *probe; const GParamSpec *pspec; GObjectClass *klass; const gchar *longname; /* first see if we can actually create a device here */ element = gst_element_factory_create (factory, "test"); longname = gst_element_factory_get_longname (factory); if (!element) { GST_REC_WARNING ("Failed to create instance of factory '%s' (%s)", longname, GST_PLUGIN_FEATURE (factory)->name); continue; } klass = G_OBJECT_GET_CLASS (element); /* do we have a "device" property? */ if (!g_object_class_find_property (klass, "device") || !GST_IS_PROPERTY_PROBE (element) || !(probe = GST_PROPERTY_PROBE (element)) || !(pspec = gst_property_probe_get_property (probe, "device"))) { GST_REC_DEBUG ("Found source '%s' (%s) - no device", longname, GST_PLUGIN_FEATURE (factory)->name); source = gst_rec_source_new (g_strdup (longname), factory, NULL); sources = g_list_append (sources, source); } else { gint n; gchar *name; GValueArray *array; array = gst_property_probe_probe_and_get_values (probe, pspec); if (array != NULL) { for (n = 0; n < array->n_values; n++) { GValue *device; device = g_value_array_get_nth (array, n); g_object_set_property (G_OBJECT (element), "device", device); if (gst_element_set_state (element, GST_STATE_READY) != GST_STATE_SUCCESS) { GST_REC_WARNING ("Found source '%s' (%s) - device %s failed to open", longname, GST_PLUGIN_FEATURE (factory)->name, g_value_get_string (device)); continue; } g_object_get (G_OBJECT (element), "device-name", &name, NULL); gst_element_set_state (element, GST_STATE_NULL); GST_REC_DEBUG ("Found source '%s' (%s) - device %s '%s'", longname, GST_PLUGIN_FEATURE (factory)->name, g_value_get_string (device), name); source = gst_rec_source_new (g_strdup_printf ("%s [%s]", name, longname), factory, g_strdup (g_value_get_string (device))); sources = g_list_append (sources, source); } } } gst_object_unref (GST_OBJECT (element)); } /* An empty one ("None") */ source = gst_rec_source_new (g_strdup ("None"), NULL, NULL); sources = g_list_append (sources, source); /* FIXME: sort, based on ranks (another FIXME. ;)). */ return sources; } GList * gst_rec_list_video_sources (void) { /* caching increases speed */ static GList *video_sources = NULL; if (!video_sources) video_sources = gst_rec_list_any_sources (video_source_factories); g_list_foreach (video_sources, (GFunc) g_object_ref, NULL); return g_list_copy (video_sources); } GList * gst_rec_list_audio_sources (void) { /* caching increases speed */ static GList *audio_sources = NULL; if (!audio_sources) audio_sources = gst_rec_list_any_sources (audio_source_factories); g_list_foreach (audio_sources, (GFunc) g_object_ref, NULL); return g_list_copy (audio_sources); } GList * gst_rec_list_muxers (GstElement *video_source, GstElement *audio_source) { GstRecMuxer *muxer; GList *muxers = NULL, *item; GstElementFactory *video_factory = NULL, *audio_factory = NULL; /* shortcuts */ if (video_source) video_factory = gst_element_get_factory (video_source); if (audio_source) audio_factory = gst_element_get_factory (audio_source); GST_REC_DEBUG ("Finding muxers for video source '%s' (%s) " "and audio source '%s' (%s)", (video_factory != NULL) ? gst_element_factory_get_longname (video_factory) : "NULL", (video_factory != NULL) ? GST_PLUGIN_FEATURE (video_factory)->name : "none", (audio_factory != NULL) ? gst_element_factory_get_longname (audio_factory) : "NULL", (audio_factory != NULL) ? GST_PLUGIN_FEATURE (audio_factory)->name : "none"); for (item = muxer_factories; item != NULL; item = item->next) { GstElementFactory *factory = item->data; /* FIXME: check whether the given muxers fits at all * (i.e., check against given sources and against all * known encoders) */ GST_REC_DEBUG ("Found muxer '%s' (%s)", gst_element_factory_get_longname (factory), GST_PLUGIN_FEATURE (factory)->name); muxer = gst_rec_muxer_new (g_strdup (gst_element_factory_get_longname (factory)), factory); muxers = g_list_append (muxers, muxer); } /* FIXME: sort, based on ranks (another FIXME. ;)). */ return muxers; } /* * Try to make a nice human-readable string from the caps. */ static gchar * gst_rec_caps_format (GstCaps *caps) { const gchar *mime = gst_caps_get_mime (caps); gchar *name = NULL; if (!strcmp (mime, "video/x-raw-yuv")) { guint32 fcc; gst_caps_get_fourcc_int (caps, "format", &fcc); switch (fcc) { case GST_MAKE_FOURCC ('Y','U','Y','2'): name = g_strdup ("Raw packed YUV-4:2:2 (YUY2)"); break; case GST_MAKE_FOURCC ('I','4','2','0'): name = g_strdup ("Raw planar YUV-4:2:0 (I420)"); break; default: break; } } else if (!strcmp (mime, "video/x-raw-rgb")) { gint depth; gst_caps_get_int (caps, "depth", &depth); switch (depth) { case 24: case 32: name = g_strdup_printf ("Raw %d-bits RGB", depth); break; default: break; } } else if (!strcmp (mime, "video/x-jpeg")) { name = g_strdup ("Hardware Motion-JPEG"); } else if (!strcmp (mime, "audio/x-raw-int")) { gboolean sign; gint depth, width; gst_caps_get (caps, "signed", &sign, "depth", &depth, "width", &width, NULL); if (width == depth) { if (width == 16 && sign) { name = g_strdup ("Raw 16-bits signed audio"); } else if (width == 8 && !sign) { name = g_strdup ("Raw 8-bits unsigned audio"); } } } return name; } static GList * gst_rec_list_any_encoders (GstElement *source, GstElement *muxer, gchar *pad_prefix, GList *encoderslist) { GList *muxtempllist; GList *encoders = NULL; GstRecEncoder *encoder; GstCaps *source_caps; GstElementFactory *source_factory, *muxer_factory; if (!source || !muxer) return NULL; /* factory shortcuts */ source_factory = gst_element_get_factory (source); muxer_factory = gst_element_get_factory (muxer); GST_REC_DEBUG ("Making encoder list for source '%s' (%s) " "and muxer '%s' (%s)", gst_element_factory_get_longname (source_factory), GST_PLUGIN_FEATURE (source_factory)->name, gst_element_factory_get_longname (muxer_factory), GST_PLUGIN_FEATURE (muxer_factory)->name); /* get pad (usually template) caps */ source_caps = gst_pad_get_caps (gst_element_get_pad (source, "src")); if (!source_caps) { GST_REC_WARNING ("Source '%s' (%s) gave NULL caps", gst_element_factory_get_longname (source_factory), GST_PLUGIN_FEATURE (source_factory)->name); return NULL; } /* go through all template video sink pads of the muxer */ for (muxtempllist = muxer_factory->padtemplates; muxtempllist != NULL; muxtempllist = muxtempllist->next) { GstPadTemplate *muxer_template = muxtempllist->data; if (!strncmp (muxer_template->name_template, pad_prefix, strlen (pad_prefix)) && muxer_template->direction == GST_PAD_SINK) { GstCaps *muxer_caps = gst_pad_template_get_caps (muxer_template), *return_caps; /* make sure we've got something to calculate with */ if (!muxer_caps) continue; /* check for direct source->muxer connection. */ if ((return_caps = gst_caps_intersect (source_caps, muxer_caps))) { GstCaps *one; return_caps = gst_caps_copy_on_write (return_caps); for (one = return_caps; one != NULL; one = one->next) { if (!strncmp (pad_prefix, "video_", strlen (pad_prefix))) { gst_props_remove_entry_by_name (one->properties, "width"); gst_props_remove_entry_by_name (one->properties, "height"); gst_props_remove_entry_by_name (one->properties, "framerate"); } else if (!strncmp (pad_prefix, "audio_", strlen (pad_prefix))) { gst_props_remove_entry_by_name (one->properties, "rate"); gst_props_remove_entry_by_name (one->properties, "channels"); } } one = gst_caps_normalize (return_caps); gst_caps_unref (return_caps); for (return_caps = one; one != NULL; one = one->next) { gchar *str1 = gst_rec_caps_format (one); if (str1 != NULL) { gchar *str2 = gst_caps_to_string (one); /* we've got a hit! */ GST_REC_DEBUG ("Adding filter '%s' (%s) to list", str1, str2); g_free (str2); /* add to list */ encoder = gst_rec_encoder_new (str1, NULL, gst_caps_copy_1 (one)); encoders = g_list_append (encoders, encoder); } } gst_caps_unref (return_caps); } /* traverse list */ for ( ; encoderslist != NULL; encoderslist = encoderslist->next) { GstElementFactory *encoder_factory = encoderslist->data; GList *enctempllist; GstPadTemplate *encoder_src_template = NULL, *encoder_sink_template = NULL; GstCaps *encoder_src_caps, *encoder_sink_caps, *return_src_caps = NULL, *return_sink_caps = NULL; /* find the proper src/sink template */ for (enctempllist = encoder_factory->padtemplates; enctempllist != NULL; enctempllist = enctempllist->next) { GstPadTemplate *template = enctempllist->data; if (template->direction == GST_PAD_SRC) { encoder_src_template = template; } else { encoder_sink_template = template; } } /* make sure we filled in everything */ if (!encoder_src_template || !encoder_sink_template) continue; /* fill in caps */ encoder_src_caps = gst_pad_template_get_caps (encoder_src_template); encoder_sink_caps = gst_pad_template_get_caps (encoder_sink_template); if (encoder_src_caps != NULL && encoder_sink_caps != NULL && (return_src_caps = gst_caps_intersect (encoder_src_caps, muxer_caps)) && (return_sink_caps = gst_caps_intersect (source_caps, encoder_sink_caps))) { /* we've got a hit! */ GST_REC_DEBUG ("Adding encoder '%s' (%s) to list", gst_element_factory_get_longname (encoder_factory), GST_PLUGIN_FEATURE (encoder_factory)->name); /* add to list */ encoder = gst_rec_encoder_new ( g_strdup (gst_element_factory_get_longname (encoder_factory)), encoder_factory, NULL); encoders = g_list_append (encoders, encoder); } /* free memory */ if (encoder_src_caps) gst_caps_unref (encoder_src_caps); if (encoder_sink_caps) gst_caps_unref (encoder_sink_caps); if (return_src_caps) gst_caps_unref (return_src_caps); if (return_sink_caps) gst_caps_unref (return_sink_caps); } /* free more memory */ gst_caps_unref (muxer_caps); } } /* free last memory */ gst_caps_sink (source_caps); /* FIXME: sort, based on ranks (another FIXME. ;)). */ /* and return our list */ return encoders; } GList * gst_rec_list_video_encoders (GstElement *video_source, GstElement *muxer) { return gst_rec_list_any_encoders (video_source, muxer, "video_", video_encoder_factories); } GList * gst_rec_list_audio_encoders (GstElement *audio_source, GstElement *muxer) { return gst_rec_list_any_encoders (audio_source, muxer, "audio_", audio_encoder_factories); } /* * Free returned lists. */ void gst_rec_list_replace (GList **old, GList *new) { if (*old) { g_list_foreach (*old, (GFunc) g_object_unref, NULL); g_list_free (*old); } *old = new; } /* * Config entries. */ gchar * gst_rec_source_to_config (GstRecSource *templ) { if (!templ->factory) return g_strdup (""); if (templ->device) return g_strdup_printf ("%s device=%s", GST_PLUGIN_FEATURE (templ->factory)->name, templ->device); return g_strdup (GST_PLUGIN_FEATURE (templ->factory)->name); } gchar * gst_rec_encoder_to_config (GstRecEncoder *templ) { if (!templ->factory) return g_strdup (""); return g_strdup (GST_PLUGIN_FEATURE (templ->factory)->name); } gchar * gst_rec_muxer_to_config (GstRecMuxer *templ) { if (!templ->factory) return g_strdup (""); return g_strdup (GST_PLUGIN_FEATURE (templ->factory)->name); } gchar * gst_rec_filter_to_config (GstRecEncoder *templ) { if (!templ->filter) return g_strdup (""); return gst_caps_to_string (templ->filter); } /* * Elements. */ static GstElement * gst_rec_config_to_element (const gchar *config, const gchar *name) { GstElement *element; if (!config) return NULL; element = gst_parse_launch (config, NULL); /* Users might do really clumsy things, like giving multiple * elements and assuming we like that. Well, we **DON'T**. * Stay off this GConf config stuff, guys, it's not meant * to be changed into random sequences of characters to see * whether the application will survive. */ if (!element || GST_IS_BIN (element)) { if (element) gst_object_unref (GST_OBJECT (element)); return NULL; } gst_element_set_name (element, name); return element; } GstElement * gst_rec_config_to_source (const gchar *config, gboolean audio) { static guint audio_counter = 0, video_counter = 0; gchar *name; GstElement *element; if (audio) { name = g_strdup_printf ("audio-source-%u", audio_counter++); } else { name = g_strdup_printf ("video-source-%u", video_counter++); } element = gst_rec_config_to_element (config, name); g_free (name); return element; } GstElement * gst_rec_config_to_encoder (const gchar *config, gboolean audio) { static guint audio_counter = 0, video_counter = 0; gchar *name; GstElement *element; if (audio) { name = g_strdup_printf ("audio-encoder-%u", audio_counter++); } else { name = g_strdup_printf ("video-encoder-%u", video_counter++); } element = gst_rec_config_to_element (config, name); g_free (name); return element; } GstElement * gst_rec_config_to_muxer (const gchar *config) { static guint counter = 0; gchar *name; GstElement *element; name = g_strdup_printf ("muxer-%u", counter++); element = gst_rec_config_to_element (config, name); g_free (name); return element; } GstCaps * gst_rec_config_to_filter (const gchar *config, gboolean audio) { static guint audio_counter = 0, video_counter = 0; gchar *name; GstCaps *caps = gst_caps_from_string ((gchar *) config); if (!caps) return NULL; if (audio) { name = g_strdup_printf ("audio-filter-%u", audio_counter++); } else { name = g_strdup_printf ("video-filter-%u", video_counter++); } gst_caps_set_name (caps, name); g_free (name); return caps; } /* * Get the GstRec{Muxer,{Video,Audio}{Source,Encoder,Filter}} * that makes for the currently selected one. */ GstRecSource * gst_rec_list_find_source (GstRec *rec, GList *list, GstElement *current) { GstElementFactory *factory = NULL; if (current) factory = gst_element_get_factory (current); for ( ; list != NULL; list = list->next) { GstRecSource *source = list->data; if (source->factory != factory) continue; if (source->device) { gchar *device; g_object_get (G_OBJECT (current), "device", &device, NULL); if (strcmp (source->device, device)) continue; } return source; } return NULL; } GstRecEncoder * gst_rec_list_find_encoder (GstRec *rec, GList *list, GstElement *current, GstCaps *filter) { GstElementFactory *factory = NULL; if (current) factory = gst_element_get_factory (current); for ( ; list != NULL; list = list->next) { GstRecEncoder *encoder = list->data; if (encoder->factory == factory && encoder->filter == filter) return encoder; } return NULL; } GstRecMuxer * gst_rec_list_find_muxer (GstRec *rec, GList *list) { GstElement *current = gst_rec_get_muxer (rec); GstElementFactory *factory = NULL; if (current) factory = gst_element_get_factory (current); for ( ; list != NULL; list = list->next) { GstRecMuxer *muxer = list->data; if (muxer->factory == factory) return muxer; } return NULL; } /* * Initialize internal lists of sources/codecs/muxers. */ void gst_rec_lists_init (void) { const GList *elements; GstElementFactory *factory; GList **addlist; const gchar *klass; GST_REC_DEBUG ("Building initial list of typed elements"); /* build up a tree of elements internally */ elements = gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY); while (elements != NULL) { factory = (GstElementFactory *) elements->data; klass = gst_element_factory_get_klass (factory); if (!strncmp (klass, "Source/Video", 12)) { addlist = &video_source_factories; } else if (!strncmp (klass, "Source/Audio", 12)) { addlist = &audio_source_factories; } else if (!strncmp (klass, "Codec/Audio/Encoder", 19)) { addlist = &audio_encoder_factories; } else if (!strncmp (klass, "Codec/Video/Encoder", 19)) { addlist = &video_encoder_factories; } else if (!strncmp (klass, "Codec/Muxer", 11)) { addlist = &muxer_factories; } else { goto next; } g_object_ref (G_OBJECT (factory)); *addlist = g_list_append (*addlist, factory); next: elements = elements->next; } } --- NEW FILE: list.h --- /* GStreamer Recorder * (c) 2003 Ronald Bultje <rb...@ro...> * * list.h: definition of lists of elements * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __GST_REC_LIST_H__ #define __GST_REC_LIST_H__ #include <glib.h> #include <gst/gst.h> #include "record.h" G_BEGIN_DECLS /* * These are basically element templates, but there's some * application-specific information added to them with * respect to the default GStreamer element factories. */ #define GST_REC_TYPE_SOURCE \ (gst_rec_source_get_type()) #define GST_REC_SOURCE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_REC_TYPE_SOURCE, GstRecSource)) #define GST_REC_VIDEO_SOURCE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_REC_TYPE_SOURCE, GstRecVideoSource)) #define GST_REC_AUDIO_SOURCE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_REC_TYPE_SOURCE, GstRecAudioSource)) #define GST_REC_IS_SOURCE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_REC_TYPE_SOURCE)) typedef struct _GstRecSource { GObject parent; /* descriptive name of the video source */ gchar *name; /* the element's factory to create instances */ GstElementFactory *factory; /* the device that belongs to it. Can be NULL */ gchar *device; } GstRecSource, GstRecVideoSource, GstRecAudioSource; #define GST_REC_TYPE_ENCODER \ (gst_rec_encoder_get_type()) #define GST_REC_ENCODER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_REC_TYPE_ENCODER, GstRecEncoder)) #define GST_REC_VIDEO_ENCODER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_REC_TYPE_ENCODER, GstRecVideoEncoder)) #define GST_REC_AUDIO_ENCODER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_REC_TYPE_ENCODER, GstRecAudioEncoder)) #define GST_REC_IS_ENCODER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_REC_TYPE_ENCODER)) typedef struct _GstRecEncoder { GObject parent; /* descriptive name of the audio source */ gchar *name; /* the element's factory to create instances, if any */ GstElementFactory *factory; /* filter caps, if any */ GstCaps *filter; } GstRecEncoder, GstRecVideoEncoder, GstRecAudioEncoder; #define GST_REC_TYPE_MUXER \ (gst_rec_muxer_get_type()) #define GST_REC_MUXER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_REC_TYPE_MUXER, GstRecMuxer)) #define GST_REC_IS_MUXER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_REC_TYPE_MUXER)) typedef struct _GstRecMuxer { GObject parent; /* descriptive name of the audio source */ gchar *name; /* the element's factory to create instances, if any */ GstElementFactory *factory; } GstRecMuxer; GType gst_rec_source_get_type (void); GType gst_rec_encoder_get_type (void); GType gst_rec_muxer_get_type (void); /* * Enumerate different possible elements (plus, optionally, * devices associated with specific instances). * Free using gst_rec_list_replace (&list, NULL). */ GList *gst_rec_list_video_sources (void); GList *gst_rec_list_audio_sources (void); GList *gst_rec_list_muxers (GstElement *video_source, GstElement *audio_source); GList *gst_rec_list_video_encoders (GstElement *video_source, GstElement *muxer); GList *gst_rec_list_audio_encoders (GstElement *audio_source, GstElement *muxer); /* * Get rid of memory. */ void gst_rec_list_replace (GList **old, GList *new); /* * Create config entries ready to be written to (e.g.) GConf. */ gchar *gst_rec_source_to_config (GstRecSource *templ); gchar *gst_rec_encoder_to_config (GstRecEncoder *templ); gchar *gst_rec_muxer_to_config (GstRecMuxer *templ); gchar *gst_rec_filter_to_config (GstRecEncoder *templ); #define gst_rec_video_source_to_config(templ) \ gst_rec_source_to_config (templ) #define gst_rec_audio_source_to_config(templ) \ gst_rec_source_to_config (templ) #define gst_rec_video_encoder_to_config(templ) \ gst_rec_encoder_to_config (templ) #define gst_rec_audio_encoder_to_config(templ) \ gst_rec_encoder_to_config (templ) #define gst_rec_video_filter_to_config(templ) \ gst_rec_filter_to_config (templ) #define gst_rec_audio_filter_to_config(templ) \ gst_rec_filter_to_config (templ) /* * Create an element based on a GConf-sort config entry. */ GstElement *gst_rec_config_to_source (const gchar *config, gboolean audio); GstElement *gst_rec_config_to_encoder (const gchar *config, gboolean audio); GstElement *gst_rec_config_to_muxer (const gchar *config); GstCaps * gst_rec_config_to_filter (const gchar *config, gboolean audio); #define gst_rec_config_to_video_source(config) \ gst_rec_config_to_source (config, FALSE) #define gst_rec_config_to_audio_source(config) \ gst_rec_config_to_source (config, TRUE) #define gst_rec_config_to_video_encoder(config) \ gst_rec_config_to_encoder (config, FALSE) #define gst_rec_config_to_audio_encoder(config) \ gst_rec_config_to_encoder (config, TRUE) #define gst_rec_config_to_video_filter(config) \ gst_rec_config_to_filter (config, FALSE) #define gst_rec_config_to_audio_filter(config) \ gst_rec_config_to_filter (config, TRUE) /* * Get the GstRec{Muxer,{Video,Audio}{Source,Encoder,Filter}} * that makes for the currently selected one. */ GstRecSource * gst_rec_list_find_source (GstRec *rec, GList *list, GstElement *current); GstRecEncoder *gst_rec_list_find_encoder (GstRec *rec, GList *list, GstElement *current, GstCaps *filter); GstRecMuxer * gst_rec_list_find_muxer (GstRec *rec, GList *list); #define gst_rec_list_find_video_source(rec,list) \ ((GstRecVideoSource *) gst_rec_list_find_source (rec, list, \ gst_rec_get_video_source (rec))) #define gst_rec_list_find_audio_source(rec,list) \ ((GstRecAudioSource *) gst_rec_list_find_source (rec, list, \ gst_rec_get_audio_source (rec))) #define gst_rec_list_find_video_encoder(rec,list) \ ((GstRecVideoEncoder *) gst_rec_list_find_encoder (rec, list, \ gst_rec_get_video_encoder (rec), \ gst_rec_get_video_filter (rec))) #define gst_rec_list_find_audio_encoder(rec,list) \ ((GstRecAudioEncoder *) gst_rec_list_find_encoder (rec, list, \ gst_rec_get_audio_encoder (rec), \ gst_rec_get_audio_filter (rec))) /* * Internal init call. Don't touch. */ void gst_rec_lists_init (void); G_END_DECLS #endif /* __GST_REC_LIST_H__ */ --- NEW FILE: manager.c --- /* GStreamer Recorder * (c) 2003 Ronald Bultje <rb...@ro...> * * manager.c: managing bin. * This element will handle private events, such as EOS halfway * a stream, seeks which aren't anything else than a timestamp * shift (on elements not supporting seek) and some more. This * can be considered an application-extension to GStreamer events, * outside it's original goal, and thus not useful inside gst-core. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "assistant.h" #include "manager.h" static void gst_rec_manager_class_init (GstRecManagerClass *klass); static void gst_rec_manager_base_init (GstRecManagerClass *klass); static void gst_rec_manager_init (GstRecManager *manager); static void gst_rec_manager_child_eos (GstElement *element, gpointer data); static void gst_rec_manager_child_add (GstBin *bin, GstElement *child); static void gst_rec_manager_child_del (GstBin *bin, GstElement *child); static GstBinClass *parent_class = NULL; GType gst_rec_manager_get_type (void) { static GType gst_rec_manager_type = 0; if (!gst_rec_manager_type) { static const GTypeInfo gst_rec_manager_info = { sizeof (GstRecManagerClass), (GBaseInitFunc) gst_rec_manager_base_init, NULL, (GClassInitFunc) gst_rec_manager_class_init, NULL, NULL, sizeof (GstRecManager), 0, (GInstanceInitFunc) gst_rec_manager_init, NULL }; gst_rec_manager_type = g_type_register_static (GST_TYPE_BIN, "GstRecManager", &gst_rec_manager_info, 0); } return gst_rec_manager_type; } static void gst_rec_manager_base_init (GstRecManagerClass *klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); static GstElementDetails gst_avi_demux_details = GST_ELEMENT_DETAILS ( "Managing bin", "Generic/Bin", "Manages private gst-rec events for the contained element", "Ronald Bultje <rb...@ro...>" ); gst_element_class_set_details (element_class, &gst_avi_demux_details); } static void gst_rec_manager_class_init (GstRecManagerClass *klass) { GstBinClass *bin_class = GST_BIN_CLASS (klass); parent_class = g_type_class_ref (GST_TYPE_BIN); bin_class->add_element = gst_rec_manager_child_add; bin_class->remove_element = gst_rec_manager_child_del; } static void gst_rec_manager_init (GstRecManager *manager) { GstElement *assistant; GstPad *pad; static guint num = 0; gchar *name; name = g_strdup_printf ("event-handler-%u", num++); assistant = gst_element_factory_make ("assistant", name); g_free (name); pad = gst_element_get_pad (assistant, "src"); gst_bin_add (GST_BIN (manager), assistant); g_signal_connect (G_OBJECT (assistant), "eos", G_CALLBACK (gst_rec_manager_child_eos), manager); manager->srcpad = gst_ghost_pad_new ("src", pad); gst_element_add_pad (GST_ELEMENT (manager), manager->srcpad); manager->assistant = assistant; } static void gst_rec_manager_child_eos (GstElement *element, gpointer data) { gst_element_set_eos (GST_ELEMENT (data)); } static void gst_rec_manager_child_add (GstBin *bin, GstElement *child) { GstRecManager *manager = GST_REC_MANAGER (bin); guint length = g_list_length ((GList *) gst_bin_get_list (bin)); /* Right after creation, the first element to be added is the * assistant. This may never be removed until disposal. After * that, one child can be added (no more than one!). */ g_assert (length < 2); if (length == 0) g_assert (G_OBJECT_TYPE (child) == GST_REC_TYPE_ASSISTANT); else /* length == 1 */ { g_assert (G_OBJECT_TYPE (child) != GST_REC_TYPE_ASSISTANT); /* link pad with assistant */ gst_element_link (child, manager->assistant); } if (parent_class->add_element) parent_class->add_element (bin, child); } static void gst_rec_manager_child_del (GstBin *bin, ... [truncated message content] |