From 4a583683e5cdf35d6f059952cbae4416120f8749 Mon Sep 17 00:00:00 2001 From: Erik Walthinsen Date: Fri, 25 May 2001 21:00:07 +0000 Subject: Merged from INCSCHED on 200505251!!! Original commit message from CVS: Merged from INCSCHED on 200505251!!! --- .gitignore | 2 + AUTHORS | 2 + Makefile.am | 7 +- autogen.sh | 15 - configure.in | 77 ++- docs/gst/tmpl/cothreads.sgml | 2 +- docs/gst/tmpl/gstelement.sgml | 18 - docs/gst/tmpl/gstfakesrc.sgml | 5 + docs/gst/tmpl/gstthread.sgml | 2 + docs/random/matth/scheduling.txt | 125 ++++ examples/Makefile.am | 2 +- examples/autoplug/autoplug.c | 210 ++++--- examples/mixer/mixer.c | 155 +++-- gst/Makefile.am | 3 +- gst/autoplug/Makefile.am | 10 +- gst/autoplug/autoplugtest.c | 92 +++ gst/autoplug/gstautoplugcache.c | 371 +++++++++++ gst/autoplug/gstautoplugger.c | 606 ++++++++++++++++++ gst/autoplug/gststaticautoplug.c | 4 +- gst/autoplug/gststaticautoplugrender.c | 18 +- gst/cothreads.c | 93 ++- gst/cothreads.h | 19 +- gst/elements/gstfakesrc.c | 23 +- gst/elements/gstfakesrc.h | 1 + gst/elements/gstsinesrc.c | 2 +- gst/gst.c | 32 +- gst/gst.h | 2 + gst/gstbin.c | 532 ++++++---------- gst/gstbin.h | 15 +- gst/gstbuffer.c | 2 +- gst/gstcaps.c | 12 +- gst/gstclock.c | 17 +- gst/gstelement.c | 272 +++++++-- gst/gstelement.h | 66 +- gst/gstelementfactory.c | 12 +- gst/gstinfo.c | 340 +++++++---- gst/gstinfo.h | 232 ++++--- gst/gstobject.c | 165 ++++- gst/gstobject.h | 40 +- gst/gstpad.c | 248 ++++++-- gst/gstpad.h | 32 +- gst/gstpipeline.c | 19 +- gst/gstprops.c | 23 +- gst/gstqueue.c | 251 +++++--- gst/gstqueue.h | 18 +- gst/gstscheduler.c | 850 ++++++++++++++++++++++++-- gst/gstscheduler.h | 99 +++ gst/gstthread.c | 474 +++++++++++--- gst/gstthread.h | 10 +- gst/gsttype.c | 8 +- gst/gsttypefind.c | 18 +- gst/gsttypes.h | 17 + gst/gstxml.c | 2 +- gstplay/Makefile.am | 1 + gstplay/gstmediaplay.c | 11 +- gstplay/gstmediaplay.glade | 2 + gstplay/gstplay.c | 184 +++--- gstplay/gstplay.h | 8 +- gstplay/gstplayprivate.h | 5 +- gstreamer-uninstalled.pc.in | 11 + gstreamer.pc.in | 11 + gstreamer.spec.in | 1 + libs/idct/gstidct.c | 14 +- plugins/elements/gstfakesrc.c | 23 +- plugins/elements/gstfakesrc.h | 1 + plugins/elements/gstqueue.c | 251 +++++--- plugins/elements/gstqueue.h | 18 +- plugins/elements/gstsinesrc.c | 2 +- test/.gitignore | 1 + test/Makefile.am | 12 - test/avi2mpg.c | 2 +- test/cobin.c | 2 - test/dvshow.c | 2 +- test/fake.c | 4 +- test/mpeg2parse2.c | 1 + test/mpeg2parse3.c | 118 ++-- test/mpeg2parse4.c | 224 +++++++ test/video2mp1.c | 2 +- test/videotest2.c | 2 +- tests/Makefile.am | 4 +- tests/incsched.c | 135 ++++ tests/mp1vid.c | 78 +++ tests/old/examples/Makefile.am | 2 +- tests/old/examples/autoplug/autoplug.c | 210 ++++--- tests/old/examples/mixer/mixer.c | 155 +++-- tests/old/testsuite/refcounting/Makefile.am | 18 + tests/old/testsuite/refcounting/bin.c | 272 +++++++++ tests/old/testsuite/refcounting/element.c | 116 ++++ tests/old/testsuite/refcounting/element_pad.c | 134 ++++ tests/old/testsuite/refcounting/mem.c | 22 + tests/old/testsuite/refcounting/mem.h | 1 + tests/old/testsuite/refcounting/object.c | 156 +++++ tests/old/testsuite/refcounting/pad.c | 223 +++++++ tests/old/testsuite/refcounting/thread.c | 281 +++++++++ tests/reaping.c | 35 ++ tests/states.c | 12 +- tests/threadlock.c | 51 ++ testsuite/refcounting/Makefile.am | 18 + testsuite/refcounting/bin.c | 272 +++++++++ testsuite/refcounting/element.c | 116 ++++ testsuite/refcounting/element_pad.c | 134 ++++ testsuite/refcounting/mem.c | 22 + testsuite/refcounting/mem.h | 1 + testsuite/refcounting/object.c | 156 +++++ testsuite/refcounting/pad.c | 223 +++++++ testsuite/refcounting/thread.c | 281 +++++++++ tools/.gitignore | 2 + tools/Makefile.am | 3 +- tools/gstreamer-complete.c | 203 ++++++ tools/gstreamer-compprep.c | 91 +++ tools/gstreamer-inspect.c | 65 +- 111 files changed, 8347 insertions(+), 1730 deletions(-) create mode 100644 docs/random/matth/scheduling.txt create mode 100644 gst/autoplug/autoplugtest.c create mode 100644 gst/autoplug/gstautoplugcache.c create mode 100644 gst/autoplug/gstautoplugger.c create mode 100644 gst/gsttypes.h create mode 100644 gstreamer-uninstalled.pc.in create mode 100644 gstreamer.pc.in create mode 100644 test/mpeg2parse4.c create mode 100644 tests/incsched.c create mode 100644 tests/mp1vid.c create mode 100644 tests/old/testsuite/refcounting/Makefile.am create mode 100644 tests/old/testsuite/refcounting/bin.c create mode 100644 tests/old/testsuite/refcounting/element.c create mode 100644 tests/old/testsuite/refcounting/element_pad.c create mode 100644 tests/old/testsuite/refcounting/mem.c create mode 100644 tests/old/testsuite/refcounting/mem.h create mode 100644 tests/old/testsuite/refcounting/object.c create mode 100644 tests/old/testsuite/refcounting/pad.c create mode 100644 tests/old/testsuite/refcounting/thread.c create mode 100644 tests/reaping.c create mode 100644 tests/threadlock.c create mode 100644 testsuite/refcounting/Makefile.am create mode 100644 testsuite/refcounting/bin.c create mode 100644 testsuite/refcounting/element.c create mode 100644 testsuite/refcounting/element_pad.c create mode 100644 testsuite/refcounting/mem.c create mode 100644 testsuite/refcounting/mem.h create mode 100644 testsuite/refcounting/object.c create mode 100644 testsuite/refcounting/pad.c create mode 100644 testsuite/refcounting/thread.c create mode 100644 tools/gstreamer-complete.c create mode 100644 tools/gstreamer-compprep.c diff --git a/.gitignore b/.gitignore index 320b264a3..a7d8dfa5b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ config.sub configure gstreamer-[0-9]* gstreamer-config +gstreamer.pc +gstreamer-uninstalled.pc gstreamer.spec libtool ltconfig diff --git a/AUTHORS b/AUTHORS index 2e4f2e612..4105ade40 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,6 +3,8 @@ Matt Howell Brent Bradburn Wim Taymans Richard Boulton +Zaheer Merali + - thread synchronization rework David I. Lehn - debian packaging - various fixes diff --git a/Makefile.am b/Makefile.am index ebdf9fbc8..14cbc0de7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,9 +26,14 @@ bin_SCRIPTS = gstreamer-config m4datadir = $(datadir)/aclocal m4data_DATA = gstreamer.m4 +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = gstreamer.pc + man_MANS = gstreamer-config.1 -EXTRA_DIST = gstreamer.spec.in gstreamer-config.in gstreamer.m4 LICENSE REQUIREMENTS $(man_MANS) +EXTRA_DIST = gstreamer.spec.in gstreamer-config.in gstreamer.m4 \ + gstreamer.pc.in gstreamer-uninstall.pc.in \ + LICENSE REQUIREMENTS ABOUT-NLS $(man_MANS) dist-hook: cp gstreamer.spec $(distdir) diff --git a/autogen.sh b/autogen.sh index e674cdf1b..1a0a240dd 100755 --- a/autogen.sh +++ b/autogen.sh @@ -79,24 +79,9 @@ autoheader autoconf automake --add-missing -if [ "x$1" = "x--autogen-recurse" ];then - exit # the rest will happen later -fi - -#for dir in `find * -name autogen.sh -print | grep -v '^autogen.sh$' | \ -# sed 's/autogen.sh$//'`;do -# echo "Recursively running autogen.sh in $dir" -# pushd $dir > /dev/null -# ./autogen.sh --autogen-recurse "$@" -# popd > /dev/null -#done - # now remove the cache, because it can be considered dangerous in this case rm -f config.cache -# For busy application developers (Hadess) -# ./configure --enable-maintainer-mode --enable-debug --enable-debug-verbose --disable-docs-build "$@" - ./configure --enable-maintainer-mode --enable-plugin-srcdir --enable-debug --enable-debug-verbose "$@" echo diff --git a/configure.in b/configure.in index 17c1c532b..5f56a4d4c 100644 --- a/configure.in +++ b/configure.in @@ -417,35 +417,75 @@ AC_SUBST(X_LIBS) dnl Check for the Xv library xvsave_LIBS=${LIBS} -AC_CHECK_LIB(Xv, XvQueryExtension, HAVE_LIBXV=yes, HAVE_LIBXV=no, $X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS) +AC_CHECK_LIB(Xv, XvQueryExtension, + HAVE_LIBXV=yes + AC_DEFINE(HAVE_LIBXV), + HAVE_LIBXV=no, + $X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS +) LIBS=${xvsave_LIBS} AC_CHECK_HEADER(X11/extensions/Xv.h, :, HAVE_LIBXV=no) AC_CHECK_HEADER(X11/extensions/Xvlib.h, :, HAVE_LIBXV=no) dnl Check for OSS audio -AC_CHECK_HEADER(sys/soundcard.h, HAVE_OSS=yes, HAVE_OSS=no) +AC_CHECK_HEADER(sys/soundcard.h, + AC_DEFINE(HAVE_OSS) + HAVE_OSS=yes, [] +) dnl Check for xaudio -AC_CHECK_HEADER(xaudio/decoder.h, HAVE_XAUDIO=yes, HAVE_XAUDIO=no) +AC_CHECK_HEADER(xaudio/decoder.h, + AC_DEFINE(HAVE_XAUDIO) + HAVE_XAUDIO="yes", + AC_MSG_WARN( +***** NOTE: These plugins won't be built: gstxa +) + HAVE_XAUDIO="no", +) dnl Check for libmad AC_MSG_CHECKING(MAD library) -AC_CHECK_LIB(mad, mad_decoder_finish, HAVE_LIBMAD=yes, HAVE_LIBMAD=no, ) -AC_CHECK_HEADER(mad.h, :, HAVE_LIBMAD=no) +AC_CHECK_LIB(mad, mad_decoder_finish, + HAVE_LIBMAD=yes + AC_DEFINE(HAVE_LIBMAD), + AC_MSG_WARN( +***** NOTE: These plugins won't be built: mad +) + HAVE_LIBMAD=no, +) dnl Check for libvorbis AC_MSG_CHECKING(Vorbis library) -AC_CHECK_LIB(vorbis, ogg_sync_init, HAVE_VORBIS=yes, HAVE_VORBIS=no, ) -AC_CHECK_HEADER(vorbis/codec.h, :, HAVE_VORBIS=no) +AC_CHECK_LIB(vorbis, ogg_sync_init, + HAVE_VORBIS=yes + AC_DEFINE(HAVE_VORBIS), + AC_MSG_WARN( +***** NOTE: These plugins won't be built: vorbisdec vorbisenc +) + HAVE_VORBIS=no, +) dnl Check for libjpeg AC_MSG_CHECKING(libjpeg library) -AC_CHECK_LIB(jpeg, jpeg_set_defaults, HAVE_LIBJPEG=yes, HAVE_LIBJPEG=no, ) -AC_CHECK_HEADER(jpeglib.h, :, HAVE_LIBJPEG=no) +AC_CHECK_LIB(jpeg, jpeg_set_defaults, + HAVE_LIBJPEG=yes + AC_DEFINE(HAVE_LIBJPEG), + AC_MSG_WARN( +***** NOTE: These plugins won't be built: jpegdec jpegenc +) + HAVE_LIBJPEG=no, +) -dnl Check for libHermes +dnl Check for Hermes AC_MSG_CHECKING(Hermes library) -AC_CHECK_LIB(Hermes, Hermes_ConverterInstance, HAVE_LIBHERMES=yes, HAVE_LIBHERMES=no, ) +AC_CHECK_LIB(Hermes, Hermes_ConverterInstance, + HAVE_LIBHERMES=yes + AC_DEFINE(HAVE_LIBHERMES), + AC_MSG_WARN( +***** NOTE: These plugins won't be built: colorspace +) + HAVE_LIBHERMES=no, +) AC_CHECK_HEADER(Hermes/Hermes.h, :, HAVE_LIBHERMES=no) dnl Check for cdparanoia @@ -672,13 +712,13 @@ esac], [:]) dnl Default value AC_ARG_ENABLE(docs-build, -[ --disable-docs-build disable all building of documentation], +[ --enable-docs-build enable building of documentation], [case "${enableval}" in yes) BUILD_DOCS=yes ;; no) BUILD_DOCS=no ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-docs-build) ;; esac], -[BUILD_DOCS=yes]) dnl Default value +[BUILD_DOCS=no]) dnl Default value AC_ARG_ENABLE(plugin-docs, [ --enable-plugin-docs enable the building of plugin documentation @@ -889,7 +929,11 @@ dnl ############################## dnl # Set up the defaults cflags # dnl ############################## dnl CC="kgcc" -CFLAGS="$CORE_CFLAGS $CFLAGS -O6 -Wall" +if test "x$USE_PROFILING" = xyes; then + CFLAGS="$CORE_CFLAGS $CFLAGS -Wall" +else + CFLAGS="$CORE_CFLAGS $CFLAGS -O6 -Wall" +fi LIBS="$CORE_LIBS $LIBS" AC_SUBST(CORE_LIBS) AC_SUBST(CORE_CFLAGS) @@ -1041,6 +1085,7 @@ tests/Makefile tests/sched/Makefile tests/eos/Makefile testsuite/Makefile +testsuite/refcounting/Makefile testsuite/capsnego/Makefile tests/nego/Makefile examples/Makefile @@ -1073,5 +1118,7 @@ docs/fwg/Makefile debian/Makefile stamp.h gstreamer-config -gstreamer.spec]) +gstreamer.spec +gstreamer.pc +gstreamer-uninstalled.pc]) AC_OUTPUT_COMMANDS([chmod +x gstreamer-config]) diff --git a/docs/gst/tmpl/cothreads.sgml b/docs/gst/tmpl/cothreads.sgml index 4e5e52dba..8131449ea 100644 --- a/docs/gst/tmpl/cothreads.sgml +++ b/docs/gst/tmpl/cothreads.sgml @@ -61,9 +61,9 @@ The maximum number of cothreads we are going to support. @argv: @flags: @sp: +@jmp: @top_sp: @pc: -@jmp: diff --git a/docs/gst/tmpl/gstelement.sgml b/docs/gst/tmpl/gstelement.sgml index 05b2defa6..f1471bdaf 100644 --- a/docs/gst/tmpl/gstelement.sgml +++ b/docs/gst/tmpl/gstelement.sgml @@ -267,24 +267,6 @@ circumstances. @Returns: - - - - - -@element: -@manager: - - - - - - - -@element: -@Returns: - - diff --git a/docs/gst/tmpl/gstfakesrc.sgml b/docs/gst/tmpl/gstfakesrc.sgml index dfda3f403..197eaebd6 100644 --- a/docs/gst/tmpl/gstfakesrc.sgml +++ b/docs/gst/tmpl/gstfakesrc.sgml @@ -46,3 +46,8 @@ The GstFakeSrc generates empty buffers. (fakesrc) + + + + + diff --git a/docs/gst/tmpl/gstthread.sgml b/docs/gst/tmpl/gstthread.sgml index a9e66520b..bc4f3af83 100644 --- a/docs/gst/tmpl/gstthread.sgml +++ b/docs/gst/tmpl/gstthread.sgml @@ -24,8 +24,10 @@ Thread flags: @GST_THREAD_CREATE: The thread is being created. +@GST_THREAD_STATE_STARTED: @GST_THREAD_STATE_SPINNING: The thread is runnning @GST_THREAD_STATE_REAPING: The thread is ending. +@GST_THREAD_STATE_ELEMENT_CHANGED: @GST_THREAD_FLAG_LAST: subclass use this to start their enumeration diff --git a/docs/random/matth/scheduling.txt b/docs/random/matth/scheduling.txt new file mode 100644 index 000000000..7b82d1da4 --- /dev/null +++ b/docs/random/matth/scheduling.txt @@ -0,0 +1,125 @@ +----------------------------------------------------------- +- GStreamer Scheduling / Synchronization (incsched) Notes - +----------------------------------------------------------- + +These notes describe deadlock scenarios and proposed solutions for +GStreamer. This will be implemented in the INCSCHED1 branch. + +I. Miscelaneous proposals +II. Liveness problems (sometimes deadlock ;) and propsed solutions +III. State transition approach and responsibility + +MattH. + +-------------------------------- +- I. Miscalenous proposals - +-------------------------------- + +1. Change the names of GstThread and GstQueue to GstPtThread and GstPtQueue + for pthread versions of Thread and Queue. + +2. Change GstPtQueue to check its pads' peers' managers and make sure + they are different. If not, fail and generate error message. (This + ensures a GstPtQueue straddles a pthread boundary.) + +3. Change state transitions to NULL <-> READY <-> PAUSED <-> PLAYING. + + +--------------------------------------------------- +- II. Deadlock Scenarios and Proposed Solutions - +- (in the order they will be implemented) - +--------------------------------------------------- + +1. A downstream element "waits" for a buffer from its upstream element, + a state change happens and "pauses" the upstream element -- the + downstream element is blocked and cannot execute its change_state. + + Note that this can only happen within a single GstPtQueue! Either a + downstream element calls Pull, finds no buffer, and does a + wait_cond(new buffer) or an upstream element calls Push, finds no + room, and does a wait_cond(new room). Thus, GstPtQueue contains all + the cond_wait / signal code. + + => The managing container (thread, pipeline) "wakes" up any sleep + conditions of its "bottom half". (In the scenario described, it + wakes the blocked downstream element's call to Pull.) The GstPtQueue + cond_wait section determines that it woke up due to a pending state + change and does a cothread_switch(0) to return to the main loop, + which then executes the state transition. + + Note that a managing container will have only one sleep condition + in its "bottom half." + + +2. Element "blocked" on getting I/O and cannot execute its change_state. + + => We will provide an I/O library for the elements to use that does + not actually block. (A retry-loop with timeout or select() on + 2 -- or more -- file descriptors: one the one you want I/O from, + the other one that GStreamer uses to "wake" everyone up.) The + I/O library determines that it was woken due to a pending state + change and does a cothread_switch(0) to return to the main loop, + which then executes the state transition. + + Note that a managing container will have only one elements in + the middle of doing blocking I/O. + + +3. Element using a library (code out of its control) which blocks for + some reason (e.g., using real blocking I/O) so main loop never gets + run to execute change_state. + + => Build in some timeout in the manging container (the "top half") + when waiting for bottom half to respond to pending state. If + managing container times out, kill the element's thread with a + signal (or series of signals -- escalating priority). This + requires that the element (the "bottom half") have matching + signal handler(s) that execute(s) the state-transition. + + +-------------------------------------------------------- +- III. State-transition Approach and Responsibility - +-------------------------------------------------------- + +A. The "top half" context of the managing container. (This is likely the + context of the application.) + + Call change_state on the managing container (GstPipeline, GstPtThread). + If its "bottom half" (main_loop) is asleep, signal the condition to + wake it up. Then do a cond_wait for the "bottom half" to execute the + state transition and return (once the state has been changed). + + +B. The main_loop (the "bottom half") of the managing container. + + Needs to check for pending state transition after every switch back from + one of its elements. If a pending state is found, it calls change_state + on each of its elements, signals the "top half" that the state has been + changed, then continues executing the plan (if Playing) or puts itself + to sleep (Paused, Ready). + + +C. Element. + + Implement a change_state function to make transition for that element. + The elements' change_state is what actually changes the state variable + and notifies the scheduler that the state was changed. This function + may also do things like close or open resources. + + NOTE: when an element goes through certain state transitions (e.g., from + Paused to Ready) its state (stack) will be wiped out. If it wants to + preserve any state or data, it needs to store the information in a safe + place. + + +D. Cothread Scheduler. + + Gets notified of state transition by elements' change_state functions + then (re)set the plan accordingly. Assuming + NULL <-> READY <-> PAUSED <-> PLAYING, some would be + + + Paused -> Playing: jump back where you were in the Plan and continue + its execution + + + Ready -> Paused: reset the cothread pointers foreach cothread in the + Plan (don't run) diff --git a/examples/Makefile.am b/examples/Makefile.am index c251eabb6..a97088356 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -8,4 +8,4 @@ endif SUBDIRS = $(GNOME_SUBDS) \ helloworld helloworld2 \ queue queue2 queue3 queue4 \ - launch thread xml plugins typefind + launch thread xml plugins typefind mixer diff --git a/examples/autoplug/autoplug.c b/examples/autoplug/autoplug.c index e9135fb8d..70ac4b7a2 100644 --- a/examples/autoplug/autoplug.c +++ b/examples/autoplug/autoplug.c @@ -2,94 +2,36 @@ #include static void -gst_play_have_type (GstElement *sink, GstElement *sink2, gpointer data) +autoplug_have_size (GstElement *element, guint width, guint height, + GtkWidget *socket) { - GST_DEBUG (0,"GstPipeline: play have type %p\n", (gboolean *)data); - - *(gboolean *)data = TRUE; -} - -gboolean -idle_func (gpointer data) -{ - return gst_bin_iterate (GST_BIN (data)); -} - -static GstCaps* -gst_play_typefind (GstBin *bin, GstElement *element) -{ - gboolean found = FALSE; - GstElement *typefind; - GstCaps *caps = NULL; - - GST_DEBUG (0,"GstPipeline: typefind for element \"%s\" %p\n", - GST_ELEMENT_NAME(element), &found); - - typefind = gst_elementfactory_make ("typefind", "typefind"); - g_return_val_if_fail (typefind != NULL, FALSE); - - gtk_signal_connect (GTK_OBJECT (typefind), "have_type", - GTK_SIGNAL_FUNC (gst_play_have_type), &found); - - gst_pad_connect (gst_element_get_pad (element, "src"), - gst_element_get_pad (typefind, "sink")); - - gst_bin_add (bin, typefind); - - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); - - // push a buffer... the have_type signal handler will set the found flag - gst_bin_iterate (bin); - - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL); - - caps = gst_pad_get_caps (gst_element_get_pad (element, "src")); - - gst_pad_disconnect (gst_element_get_pad (element, "src"), - gst_element_get_pad (typefind, "sink")); - gst_bin_remove (bin, typefind); - gst_object_unref (GST_OBJECT (typefind)); - - return caps; + gtk_widget_set_usize(socket,width,height); } -int main(int argc,char *argv[]) +static void +gst_play_have_type (GstElement *typefind, GstCaps *caps, GstElement *pipeline) { - GstElement *disksrc, *osssink, *videosink; - GstElement *bin; + GstElement *osssink, *videosink; GtkWidget *appwindow; - GstCaps *srccaps; GstElement *new_element; GstAutoplug *autoplug; GtkWidget *socket; + GstElement *autobin; + GstElement *disksrc; + GstElement *cache; - g_thread_init(NULL); - gst_init(&argc,&argv); - gnome_init("autoplug","0.0.1", argc,argv); - - if (argc != 2) { - g_print("usage: %s \n", argv[0]); - exit(-1); - } - - /* create a new bin to hold the elements */ - bin = gst_pipeline_new("pipeline"); - g_assert(bin != NULL); - - /* create a disk reader */ - disksrc = gst_elementfactory_make("disksrc", "disk_source"); - g_assert(disksrc != NULL); - gtk_object_set(GTK_OBJECT(disksrc),"location", argv[1],NULL); + GST_DEBUG (0,"GstPipeline: play have type\n"); - gst_bin_add (GST_BIN (bin), disksrc); + gst_element_set_state (pipeline, GST_STATE_PAUSED); - srccaps = gst_play_typefind (GST_BIN (bin), disksrc); + disksrc = gst_bin_get_by_name (GST_BIN (pipeline), "disk_source"); + autobin = gst_bin_get_by_name (GST_BIN (pipeline), "autobin"); + cache = gst_bin_get_by_name (GST_BIN (autobin), "cache"); - if (!srccaps) { - g_print ("could not autoplug, unknown media type...\n"); - exit (-1); - } - + // disconnect the typefind from the pipeline and remove it + gst_element_disconnect (cache, "src", typefind, "sink"); + gst_bin_remove (GST_BIN (autobin), typefind); + /* and an audio sink */ osssink = gst_elementfactory_make("osssink", "play_audio"); g_assert(osssink != NULL); @@ -102,7 +44,7 @@ int main(int argc,char *argv[]) g_assert (autoplug != NULL); new_element = gst_autoplug_to_renderers (autoplug, - srccaps, + caps, videosink, osssink, NULL); @@ -112,42 +54,122 @@ int main(int argc,char *argv[]) exit (-1); } - gst_bin_remove (GST_BIN (bin), disksrc); - // FIXME hack, reparent the disksrc so the scheduler doesn't break - bin = gst_pipeline_new("pipeline"); + gst_element_set_name (new_element, "new_element"); - gst_bin_add (GST_BIN (bin), disksrc); - gst_bin_add (GST_BIN (bin), new_element); + gst_bin_add (GST_BIN (autobin), new_element); - gst_element_connect (disksrc, "src", new_element, "sink"); + gtk_object_set (GTK_OBJECT (cache), "reset", TRUE, NULL); - appwindow = gnome_app_new("autoplug demo","autoplug demo"); + gst_element_connect (cache, "src", new_element, "sink"); + + appwindow = gnome_app_new ("autoplug demo","autoplug demo"); socket = gtk_socket_new (); gtk_widget_show (socket); - gnome_app_set_contents(GNOME_APP(appwindow), + gnome_app_set_contents (GNOME_APP (appwindow), GTK_WIDGET (socket)); gtk_widget_realize (socket); gtk_socket_steal (GTK_SOCKET (socket), gst_util_get_int_arg (GTK_OBJECT (videosink), "xid")); + gtk_widget_set_usize(socket,320,240); - gtk_widget_show_all(appwindow); + gtk_widget_show_all (appwindow); - xmlSaveFile("xmlTest.gst", gst_xml_write(GST_ELEMENT(bin))); + gtk_signal_connect (GTK_OBJECT (videosink), "have_size", + GTK_SIGNAL_FUNC (autoplug_have_size), socket); - /* start playing */ - gst_element_set_state(GST_ELEMENT(bin), GST_STATE_PLAYING); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + xmlSaveFile("xmlTest.gst", gst_xml_write (GST_ELEMENT (pipeline))); +} + +gboolean +idle_func (gpointer data) +{ + return gst_bin_iterate (GST_BIN (data)); +} + +static void +gst_play_cache_empty (GstElement *element, GstElement *pipeline) +{ + GstElement *autobin; + GstElement *disksrc; + GstElement *cache; + GstElement *new_element; + + fprintf (stderr, "have cache empty\n"); + + gst_element_set_state (pipeline, GST_STATE_PAUSED); + + disksrc = gst_bin_get_by_name (GST_BIN (pipeline), "disk_source"); + autobin = gst_bin_get_by_name (GST_BIN (pipeline), "autobin"); + cache = gst_bin_get_by_name (GST_BIN (autobin), "cache"); + new_element = gst_bin_get_by_name (GST_BIN (autobin), "new_element"); + + gst_element_disconnect (disksrc, "src", cache, "sink"); + gst_element_disconnect (cache, "src", new_element, "sink"); + gst_bin_remove (GST_BIN (autobin), cache); + gst_element_connect (disksrc, "src", new_element, "sink"); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + fprintf (stderr, "done with cache_empty\n"); +} + +int main(int argc,char *argv[]) +{ + GstElement *disksrc; + GstElement *pipeline; + GstElement *autobin; + GstElement *typefind; + GstElement *cache; + + g_thread_init(NULL); + gst_init(&argc,&argv); + gnome_init("autoplug","0.0.1", argc,argv); + + if (argc != 2) { + g_print("usage: %s \n", argv[0]); + exit(-1); + } + + /* create a new pipeline to hold the elements */ + pipeline = gst_pipeline_new("pipeline"); + g_assert(pipeline != NULL); - gtk_idle_add(idle_func, bin); + /* create a disk reader */ + disksrc = gst_elementfactory_make("disksrc", "disk_source"); + g_assert(disksrc != NULL); + gtk_object_set(GTK_OBJECT(disksrc),"location", argv[1],NULL); + gst_bin_add (GST_BIN (pipeline), disksrc); + + autobin = gst_bin_new ("autobin"); + cache = gst_elementfactory_make ("autoplugcache", "cache"); + gtk_signal_connect (GTK_OBJECT (cache), "cache_empty", GTK_SIGNAL_FUNC (gst_play_cache_empty), pipeline); + + typefind = gst_elementfactory_make ("typefind", "typefind"); + gtk_signal_connect (GTK_OBJECT (typefind), "have_type", GTK_SIGNAL_FUNC (gst_play_have_type), pipeline); + gst_bin_add (GST_BIN (autobin), cache); + gst_bin_add (GST_BIN (autobin), typefind); + + gst_element_connect (cache, "src", typefind, "sink"); + gst_element_add_ghost_pad (autobin, gst_element_get_pad (cache, "sink"), "sink"); + + gst_bin_add (GST_BIN( pipeline), autobin); + gst_element_connect (disksrc, "src", autobin, "sink"); + + /* start playing */ + gst_element_set_state( GST_ELEMENT (pipeline), GST_STATE_PLAYING); - gst_main(); + gtk_idle_add (idle_func, pipeline); + gst_main (); - /* stop the bin */ - gst_element_set_state(GST_ELEMENT(bin), GST_STATE_NULL); + /* stop the pipeline */ + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); - gst_pipeline_destroy(bin); + gst_object_unref (GST_OBJECT (pipeline)); exit(0); } diff --git a/examples/mixer/mixer.c b/examples/mixer/mixer.c index 205d0738a..4461d4796 100644 --- a/examples/mixer/mixer.c +++ b/examples/mixer/mixer.c @@ -4,10 +4,13 @@ * demonstrates the adder plugin and the volume envelope plugin * work in progress but do try it out * - * Latest change : 16/04/2001 - * multiple input channels allowed - * volume envelope adapted - * Version : 0.3 + * Latest change : 28/04/2001 + * trying to adapt to incsched + * delayed start for channels > 1 + * now works by quickhacking the + * adder plugin to set + * GST_ELEMENT_COTHREAD_STOPPING + * Version : 0.5 */ #include @@ -15,8 +18,10 @@ #include "mixer.h" #include +//#define WITH_BUG +//#define WITH_BUG2 //#define DEBUG - +//#define AUTOPLUG /* define if you want autoplugging of input channels */ /* function prototypes */ input_channel_t* create_input_channel (int id, char* location); @@ -35,55 +40,50 @@ void eos(GstElement *element) // playing = FALSE; } -static void -gst_play_have_type (GstElement *sink, GstElement *sink2, gpointer data) -{ - GST_DEBUG (0,"GstPipeline: play have type %p\n", (gboolean *)data); - - *(gboolean *)data = TRUE; -} - static GstCaps* gst_play_typefind (GstBin *bin, GstElement *element) { - gboolean found = FALSE; GstElement *typefind; + GstElement *pipeline; GstCaps *caps = NULL; - GST_DEBUG (0,"GstPipeline: typefind for element \"%s\" %p\n", - GST_ELEMENT_NAME(element), &found); + GST_DEBUG (0,"GstPipeline: typefind for element \"%s\"\n", + GST_ELEMENT_NAME(element)); + + pipeline = gst_pipeline_new ("autoplug_pipeline"); typefind = gst_elementfactory_make ("typefind", "typefind"); g_return_val_if_fail (typefind != NULL, FALSE); - gtk_signal_connect (GTK_OBJECT (typefind), "have_type", - GTK_SIGNAL_FUNC (gst_play_have_type), &found); - gst_pad_connect (gst_element_get_pad (element, "src"), gst_element_get_pad (typefind, "sink")); gst_bin_add (bin, typefind); + gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (bin)); - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); + gst_element_set_state (pipeline, GST_STATE_PLAYING); // push a buffer... the have_type signal handler will set the found flag - gst_bin_iterate (bin); + gst_bin_iterate (GST_BIN (pipeline)); - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL); + gst_element_set_state (pipeline, GST_STATE_NULL); caps = gst_pad_get_caps (gst_element_get_pad (element, "src")); gst_pad_disconnect (gst_element_get_pad (element, "src"), gst_element_get_pad (typefind, "sink")); gst_bin_remove (bin, typefind); + gst_bin_remove (GST_BIN (pipeline), GST_ELEMENT (bin)); gst_object_unref (GST_OBJECT (typefind)); + gst_object_unref (GST_OBJECT (pipeline)); return caps; } int main(int argc,char *argv[]) { - int i; + int i, j; int num_channels; + gboolean done; char buffer[20]; @@ -108,38 +108,41 @@ int main(int argc,char *argv[]) /* set up output channel and main bin */ /* create adder */ - adder = gst_elementfactory_make("adder", "adderel"); + adder = gst_elementfactory_make ("adder", "adderel"); /* create an audio sink */ - audiosink = gst_elementfactory_make("esdsink", "play_audio"); + audiosink = gst_elementfactory_make ("esdsink", "play_audio"); /* create main bin */ - main_bin = gst_bin_new("bin"); + main_bin = gst_pipeline_new("bin"); /* connect adder and output to bin */ - - gst_bin_add(GST_BIN(main_bin), adder); - gst_bin_add(GST_BIN(main_bin), audiosink); + GST_INFO (0, "main: adding adder to bin"); + gst_bin_add (GST_BIN(main_bin), adder); + GST_INFO (0, "main: adding audiosink to bin"); + gst_bin_add (GST_BIN(main_bin), audiosink); /* connect adder and audiosink */ gst_pad_connect(gst_element_get_pad(adder,"src"), gst_element_get_pad(audiosink,"sink")); - /* create input channels, add to bin and connect */ - + /* start looping */ input_channels = NULL; for (i = 1; i < argc; ++i) { printf ("Opening channel %d from file %s...\n", i, argv[i]); channel_in = create_input_channel (i, argv[i]); - input_channels = g_list_append (input_channels, channel_in); - gst_bin_add(GST_BIN(main_bin), channel_in->pipe); + input_channels = g_list_append (input_channels, channel_in); + + if (i > 1) gst_element_set_state (main_bin, GST_STATE_PAUSED); + gst_bin_add (GST_BIN(main_bin), channel_in->pipe); /* request pads and connect to adder */ + GST_INFO (0, "requesting pad\n"); pad = gst_element_request_pad_by_name (adder, "sink%d"); - g_print ("\tGot new adder sink pad %s\n", gst_pad_get_name (pad)); + printf ("\tGot new adder sink pad %s\n", gst_pad_get_name (pad)); sprintf (buffer, "channel%d", i); gst_pad_connect (gst_element_get_pad (channel_in->pipe, buffer), pad); @@ -178,24 +181,32 @@ int main(int argc,char *argv[]) env_register_cp (channel_in->volenv, num_channels * 10.0 - 5.0, 0.0000001); /* start fade in */ } env_register_cp (channel_in->volenv, num_channels * 10.0 , 1.0 / num_channels); /* to end level */ - } - - /* sleep a few seconds doesn't seem to help anyway */ - printf ("Sleeping a few seconds ...\n"); - sleep (2); - printf ("Waking up ...\n"); + xmlSaveFile("mixer.xml", gst_xml_write(GST_ELEMENT(main_bin))); - - /* start playing */ - gst_element_set_state(main_bin, GST_STATE_PLAYING); + /* start playing */ + gst_element_set_state(main_bin, GST_STATE_PLAYING); - playing = TRUE; + // write out the schedule + gst_schedule_show(GST_ELEMENT_SCHED(main_bin)); + playing = TRUE; - while (playing) { + j = 0; + //printf ("main: start iterating from 0"); + while (playing && j < 100) + { +// printf ("main: iterating %d\n", j); + gst_bin_iterate(GST_BIN(main_bin)); + //fprintf(stderr,"after iterate()\n"); + ++j; + } + } + printf ("main: all the channels are open\n"); + while (playing) + { gst_bin_iterate(GST_BIN(main_bin)); + //fprintf(stderr,"after iterate()\n"); } - /* stop the bin */ gst_element_set_state(main_bin, GST_STATE_NULL); @@ -228,11 +239,10 @@ create_input_channel (int id, char* location) GstAutoplug *autoplug; GstCaps *srccaps; GstElement *new_element; + GstElement *decoder; -#ifdef DEBUG - printf ("DEBUG : c_i_p : creating channel with id %d for file %s\n", + GST_DEBUG (0, "c_i_p : creating channel with id %d for file %s\n", id, location); -#endif /* allocate channel */ @@ -245,23 +255,21 @@ create_input_channel (int id, char* location) /* create channel */ -#ifdef DEBUG - printf ("DEBUG : c_i_p : creating pipeline\n"); -#endif + GST_DEBUG (0, "c_i_p : creating pipeline\n"); - channel->pipe = gst_bin_new ("pipeline"); + sprintf (buffer, "pipeline%d", id); + channel->pipe = gst_bin_new (buffer); g_assert(channel->pipe != NULL); /* create elements */ -#ifdef DEBUG - printf ("DEBUG : c_i_p : creating disksrc\n"); -#endif + GST_DEBUG(0, "c_i_p : creating disksrc\n"); sprintf (buffer, "disksrc%d", id); channel->disksrc = gst_elementfactory_make ("disksrc", buffer); g_assert(channel->disksrc != NULL); - + + GST_DEBUG(0, "c_i_p : setting location\n"); gtk_object_set(GTK_OBJECT(channel->disksrc),"location", location, NULL); /* add disksrc to the bin before autoplug */ @@ -286,8 +294,24 @@ create_input_channel (int id, char* location) printf ("DEBUG : c_i_p : getting srccaps\n"); #endif +#ifdef WITH_BUG srccaps = gst_play_typefind (GST_BIN (channel->pipe), channel->disksrc); +#endif +#ifdef WITH_BUG2 + { + GstElement *pipeline; + pipeline = gst_pipeline_new ("autoplug_pipeline"); + + gst_bin_add (GST_BIN (pipeline), channel->pipe); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_bin_remove (GST_BIN (pipeline), channel->pipe); + + } +#endif + +#ifdef AUTOPLUG if (!srccaps) { g_print ("could not autoplug, unknown media type...\n"); exit (-1); @@ -311,7 +335,24 @@ create_input_channel (int id, char* location) g_print ("could not autoplug, no suitable codecs found...\n"); exit (-1); } + +#else + + new_element = gst_bin_new ("autoplug_bin"); + + /* static plug, use mad plugin and assume mp3 input */ + decoder = gst_elementfactory_make ("mad", "mpg123"); + + gst_bin_add (GST_BIN (new_element), decoder); + + gst_element_add_ghost_pad (new_element, + gst_element_get_pad (decoder, "sink"), "sink"); + gst_element_add_ghost_pad (new_element, + gst_element_get_pad (decoder, "src"), "src_00"); +#endif + xmlSaveFile ("mixer.gst", gst_xml_write (new_element)); + gst_bin_add (GST_BIN(channel->pipe), channel->volenv); gst_bin_add (GST_BIN (channel->pipe), new_element); diff --git a/gst/Makefile.am b/gst/Makefile.am index cfd5ac69a..edd8c0df7 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -90,6 +90,7 @@ libgstincludedir = $(includedir)/gst libgstinclude_HEADERS = \ cothreads.h \ gst.h \ + gsttypes.h \ gstautoplug.h \ gstbin.h \ gstbuffer.h \ @@ -128,7 +129,7 @@ noinst_HEADERS = \ gstsparc.h \ gstpropsprivate.h -CFLAGS = $(LIBGST_CFLAGS) +CFLAGS = $(LIBGST_CFLAGS) -D_GNU_SOURCE LIBS = $(LIBGST_LIBS) libgst_la_LDFLAGS = -version-info $(GST_LIBVERSION) diff --git a/gst/autoplug/Makefile.am b/gst/autoplug/Makefile.am index 8050727ae..043317a26 100644 --- a/gst/autoplug/Makefile.am +++ b/gst/autoplug/Makefile.am @@ -1,6 +1,7 @@ filterdir = $(libdir)/gst -filter_LTLIBRARIES = libgststaticautoplug.la libgststaticautoplugrender.la +filter_LTLIBRARIES = libgststaticautoplug.la libgststaticautoplugrender.la \ + libgstautoplugcache.la libgstautoplugger.la libgststaticautoplug_la_SOURCES = \ gststaticautoplug.c @@ -8,5 +9,12 @@ libgststaticautoplug_la_SOURCES = \ libgststaticautoplugrender_la_SOURCES = \ gststaticautoplugrender.c +libgstautoplugcache_la_SOURCES = gstautoplugcache.c +libgstautoplugger_la_SOURCES = gstautoplugger.c + libgststaticautoplug_la_LDFLAGS = -version-info $(GST_LIBVERSION) libgststaticautoplugrender_la_LDFLAGS = -version-info $(GST_LIBVERSION) +libgstautoplugcache_la_LDFLAGS = -version-info $(GST_LIBVERSION) + +noinst_PROGRAMS = autoplugtest +autoplugtest_LDADD = $(GST_LIBS) diff --git a/gst/autoplug/autoplugtest.c b/gst/autoplug/autoplugtest.c new file mode 100644 index 000000000..748175ffd --- /dev/null +++ b/gst/autoplug/autoplugtest.c @@ -0,0 +1,92 @@ +#include +#include + +GstElement *pipeline, *src, *autobin, *cache, *typefind, *decoder, *sink; + +void cache_empty(GstElement *element, gpointer private) { + fprintf(stderr,"have cache empty\n"); + + gst_element_set_state (pipeline, GST_STATE_PAUSED); + + gst_element_disconnect(src,"src",cache,"sink"); + gst_schedule_show (GST_ELEMENT_SCHED(pipeline)); + gst_element_disconnect(cache,"src",decoder,"sink"); + gst_schedule_show (GST_ELEMENT_SCHED(pipeline)); + gst_bin_remove (GST_BIN(autobin), cache); + gst_schedule_show (GST_ELEMENT_SCHED(pipeline)); + gst_element_connect(src,"src",decoder,"sink"); + gst_schedule_show (GST_ELEMENT_SCHED(pipeline)); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + gst_schedule_show (GST_ELEMENT_SCHED(pipeline)); + + fprintf(stderr,"done with cache_empty\n"); +} + +void have_type(GstElement *element, GstCaps *caps, GstCaps **private_caps) { + fprintf(stderr,"have caps, mime type is %s\n",gst_caps_get_mime(caps)); + + gst_element_set_state (pipeline, GST_STATE_PAUSED); + + // disconnect the typefind from the pipeline and remove it + gst_element_disconnect(cache,"src",typefind,"sink"); + gst_bin_remove(GST_BIN(autobin),typefind); + + gst_schedule_show (GST_ELEMENT_SCHED(pipeline)); + + if (strstr(gst_caps_get_mime(caps),"mp3")) { + decoder = gst_elementfactory_make ("mad","decoder"); + sink = gst_elementfactory_make ("osssink","sink"); + gst_bin_add(GST_BIN(autobin),decoder); + gst_bin_add(GST_BIN(autobin),sink); + gst_element_connect(decoder,"src",sink,"sink"); + + gtk_object_set (GTK_OBJECT(cache), "reset", TRUE, NULL); + + gst_element_connect(cache,"src",decoder,"sink"); + } + else if (strstr(gst_caps_get_mime(caps),"x-ogg")) { + decoder = gst_elementfactory_make ("vorbisdec","decoder"); + sink = gst_elementfactory_make ("osssink","sink"); + gst_bin_add(GST_BIN(autobin),decoder); + gst_bin_add(GST_BIN(autobin),sink); + gst_element_connect(decoder,"src",sink,"sink"); + + gtk_object_set (GTK_OBJECT(cache), "reset", TRUE, NULL); + + gst_element_connect(cache,"src",decoder,"sink"); + } + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + fprintf(stderr,"done with have_type signal\n"); +} + +int main (int argc,char *argv[]) { + GstCaps *caps; + int i; + + gst_init(&argc,&argv); + + pipeline = gst_pipeline_new("pipeline"); + src = gst_elementfactory_make ("disksrc","src"); + gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL); + gst_bin_add (GST_BIN(pipeline),src); + + autobin = gst_bin_new("autobin"); + cache = gst_elementfactory_make ("autoplugcache","cache"); + gtk_signal_connect (GTK_OBJECT(cache),"cache_empty",GTK_SIGNAL_FUNC(cache_empty),NULL); + typefind = gst_elementfactory_make ("typefind", "typefind"); + gtk_signal_connect (GTK_OBJECT(typefind),"have_type",GTK_SIGNAL_FUNC(have_type),&caps); + gst_bin_add (GST_BIN(autobin),cache); + gst_bin_add (GST_BIN(autobin),typefind); + gst_element_connect(cache,"src",typefind,"sink"); + gst_element_add_ghost_pad(autobin,gst_element_get_pad(cache,"sink"),"sink"); + + gst_bin_add (GST_BIN(pipeline), autobin); + gst_element_connect (src,"src",autobin,"sink"); + + gst_element_set_state(pipeline,GST_STATE_PLAYING); + + while (1) + gst_bin_iterate(GST_BIN(pipeline)); +} diff --git a/gst/autoplug/gstautoplugcache.c b/gst/autoplug/gstautoplugcache.c new file mode 100644 index 000000000..094867238 --- /dev/null +++ b/gst/autoplug/gstautoplugcache.c @@ -0,0 +1,371 @@ +/* GStreamer + * Copyright (C) 2001 RidgeRun, Inc. (www.ridgerun.com) + * + * gstautoplugcache.c: Data cache for the dynamic autoplugger + * + * 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. + */ + +#include + +GstElementDetails gst_autoplugcache_details = { + "AutoplugCache", + "Connection", + "Data cache for the dynamic autoplugger", + VERSION, + "Erik Walthinsen ", + "(C) 2001 RidgeRun, Inc. (www.ridgerun.com)", +}; + +#define GST_TYPE_AUTOPLUGCACHE \ + (gst_autoplugcache_get_type()) +#define GST_AUTOPLUGCACHE(obj) \ + (GTK_CHECK_CAST((obj),GST_TYPE_AUTOPLUGCACHE,GstAutoplugCache)) +#define GST_AUTOPLUGCACHE_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_AUTOPLUGCACHE,GstAutoplugCacheClass)) +#define GST_IS_AUTOPLUGCACHE(obj) \ + (GTK_CHECK_TYPE((obj),GST_TYPE_AUTOPLUGCACHE)) +#define GST_IS_AUTOPLUGCACHE_CLASS(obj) \ + (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_AUTOPLUGCACHE)) + +typedef struct _GstAutoplugCache GstAutoplugCache; +typedef struct _GstAutoplugCacheClass GstAutoplugCacheClass; + +struct _GstAutoplugCache { + GstElement element; + + GstPad *sinkpad, *srcpad; + + gboolean caps_proxy; + + GList *cache; + GList *cache_start; + gint buffer_count; + GList *current_playout; + gboolean fire_empty; + gboolean fire_first; +}; + +struct _GstAutoplugCacheClass { + GstElementClass parent_class; + + void (*first_buffer) (GstElement *element, GstBuffer *buf); + void (*cache_empty) (GstElement *element); +}; + + +/* Cache signals and args */ +enum { + FIRST_BUFFER, + CACHE_EMPTY, + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_BUFFER_COUNT, + ARG_CAPS_PROXY, + ARG_RESET +}; + + +static void gst_autoplugcache_class_init (GstAutoplugCacheClass *klass); +static void gst_autoplugcache_init (GstAutoplugCache *cache); + +static void gst_autoplugcache_set_arg (GtkObject *object, GtkArg *arg, guint id); +static void gst_autoplugcache_get_arg (GtkObject *object, GtkArg *arg, guint id); + +static void gst_autoplugcache_loop (GstElement *element); + +static GstPadNegotiateReturn gst_autoplugcache_nego_src (GstPad *pad, GstCaps **caps, gpointer *data); +static GstPadNegotiateReturn gst_autoplugcache_nego_sink (GstPad *pad, GstCaps **caps, gpointer *data); +static GstElementStateReturn gst_autoplugcache_change_state (GstElement *element); + + +static GstElementClass *parent_class = NULL; +static guint gst_autoplugcache_signals[LAST_SIGNAL] = { 0 }; + +GtkType +gst_autoplugcache_get_type(void) { + static GtkType autoplugcache_type = 0; + + if (!autoplugcache_type) { + static const GtkTypeInfo autoplugcache_info = { + "GstAutoplugCache", + sizeof(GstAutoplugCache), + sizeof(GstAutoplugCacheClass), + (GtkClassInitFunc)gst_autoplugcache_class_init, + (GtkObjectInitFunc)gst_autoplugcache_init, + (GtkArgSetFunc)gst_autoplugcache_set_arg, + (GtkArgGetFunc)gst_autoplugcache_get_arg, + (GtkClassInitFunc)NULL, + }; + autoplugcache_type = gtk_type_unique (GST_TYPE_ELEMENT, &autoplugcache_info); + } + return autoplugcache_type; +} + +static void +gst_autoplugcache_class_init (GstAutoplugCacheClass *klass) +{ + GtkObjectClass *gtkobject_class; + GstElementClass *gstelement_class; + + gtkobject_class = (GtkObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = gtk_type_class (GST_TYPE_ELEMENT); + + gst_autoplugcache_signals[FIRST_BUFFER] = + gtk_signal_new ("first_buffer", GTK_RUN_LAST, gtkobject_class->type, + GTK_SIGNAL_OFFSET (GstAutoplugCacheClass, first_buffer), + gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); + gst_autoplugcache_signals[CACHE_EMPTY] = + gtk_signal_new ("cache_empty", GTK_RUN_LAST, gtkobject_class->type, + GTK_SIGNAL_OFFSET (GstAutoplugCacheClass, cache_empty), + gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); + gtk_object_class_add_signals (gtkobject_class, gst_autoplugcache_signals, LAST_SIGNAL); + + gtk_object_add_arg_type ("GstAutoplugCache::buffer_count", GTK_TYPE_INT, + GTK_ARG_READABLE, ARG_BUFFER_COUNT); + gtk_object_add_arg_type ("GstAutoplugCache::caps_proxy", GTK_TYPE_BOOL, + GTK_ARG_READWRITE, ARG_CAPS_PROXY); + gtk_object_add_arg_type ("GstAutoplugCache::reset", GTK_TYPE_BOOL, + GTK_ARG_WRITABLE, ARG_RESET); + + gtkobject_class->set_arg = gst_autoplugcache_set_arg; + gtkobject_class->get_arg = gst_autoplugcache_get_arg; + + gstelement_class->change_state = gst_autoplugcache_change_state; +} + +static void +gst_autoplugcache_init (GstAutoplugCache *cache) +{ + gst_element_set_loop_function(GST_ELEMENT(cache), GST_DEBUG_FUNCPTR(gst_autoplugcache_loop)); + + cache->sinkpad = gst_pad_new ("sink", GST_PAD_SINK); +// gst_pad_set_negotiate_function (cache->sinkpad, gst_autoplugcache_nego_sink); + gst_element_add_pad (GST_ELEMENT(cache), cache->sinkpad); + + cache->srcpad = gst_pad_new ("src", GST_PAD_SRC); +// gst_pad_set_negotiate_function (cache->srcpad, gst_autoplugcache_nego_src); + gst_element_add_pad (GST_ELEMENT(cache), cache->srcpad); + + cache->caps_proxy = FALSE; + + // provide a zero basis for the cache + cache->cache = g_list_prepend(NULL, NULL); + cache->cache_start = cache->cache; + cache->buffer_count = 0; + cache->current_playout = 0; + cache->fire_empty = FALSE; + cache->fire_first = FALSE; +} + +static void +gst_autoplugcache_loop (GstElement *element) +{ + GstAutoplugCache *cache; + GstBuffer *buf = NULL; + + cache = GST_AUTOPLUGCACHE (element); + + /* Theory: + * The cache is a doubly-linked list. The front of the list is the most recent + * buffer, the end of the list is the first buffer. The playout pointer always + * points to the latest buffer sent out the end. cache points to the front + * (most reccent) of the list at all times. cache_start points to the first + * buffer, i.e. the end of the list. + * If the playout pointer does not have a prev (towards the most recent) buffer + * (== NULL), a buffer must be pulled from the sink pad and added to the cache. + * When the playout pointer gets reset (as in a set_arg), the cache is walked + * without problems, because the playout pointer has a non-NULL next. When + * the playout pointer hits the end of cache again it has to start pulling. + */ + + do { + // the first time through, the current_playout pointer is going to be NULL + if (cache->current_playout == NULL) { + // get a buffer + buf = gst_pad_pull (cache->sinkpad); + + // add it to the cache, though cache == NULL + gst_buffer_ref (buf); + cache->cache = g_list_prepend (cache->cache, buf); + cache->buffer_count++; + + // set the current_playout pointer + cache->current_playout = cache->cache; + + gtk_signal_emit (GTK_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], buf); + + // send the buffer on its way + gst_pad_push (cache->srcpad, buf); + } + + // the steady state is where the playout is at the front of the cache + else if (g_list_previous(cache->current_playout) == NULL) { + + // if we've been told to fire an empty signal (after a reset) + if (cache->fire_empty) { + int oldstate = GST_STATE(cache); + fprintf(stderr,"at front of cache, about to pull, but firing signal\n"); + gst_object_ref (GST_OBJECT (cache)); + gtk_signal_emit (GTK_OBJECT(cache), gst_autoplugcache_signals[CACHE_EMPTY], NULL); + if (GST_STATE(cache) != oldstate) { + gst_object_ref (GST_OBJECT (cache)); + GST_DEBUG(GST_CAT_AUTOPLUG, "state changed during signal, aborting\n"); + cothread_switch(cothread_current_main()); + } + gst_object_unref (GST_OBJECT (cache)); + } + + // get a buffer + buf = gst_pad_pull (cache->sinkpad); + + // add it to the front of the cache + gst_buffer_ref (buf); + cache->cache = g_list_prepend (cache->cache, buf); + cache->buffer_count++; + + // set the current_playout pointer + cache->current_playout = cache->cache; + + // send the buffer on its way + gst_pad_push (cache->srcpad, buf); + } + + // otherwise we're trundling through existing cached buffers + else { + // move the current_playout pointer + cache->current_playout = g_list_previous (cache->current_playout); + + if (cache->fire_first) { + gtk_signal_emit (GTK_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], buf); + cache->fire_first = FALSE; + } + + // push that buffer + gst_pad_push (cache->srcpad, GST_BUFFER(cache->current_playout->data)); + } + } while (!GST_FLAG_IS_SET (element, GST_ELEMENT_COTHREAD_STOPPING)); +} + +static GstPadNegotiateReturn +gst_autoplugcache_nego_src (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstAutoplugCache *cache = GST_AUTOPLUGCACHE (GST_PAD_PARENT (pad)); + + return gst_pad_negotiate_proxy (pad, cache->sinkpad, caps); +} + +static GstPadNegotiateReturn +gst_autoplugcache_nego_sink (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstAutoplugCache *cache = GST_AUTOPLUGCACHE (GST_PAD_PARENT (pad)); + + return gst_pad_negotiate_proxy (pad, cache->srcpad, caps); +} + + +static GstElementStateReturn +gst_autoplugcache_change_state (GstElement *element) +{ + // FIXME this should do something like free the cache on ->NULL + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; +} + +static void +gst_autoplugcache_set_arg (GtkObject *object, GtkArg *arg, guint id) +{ + GstAutoplugCache *cache; + + cache = GST_AUTOPLUGCACHE (object); + + switch (id) { + case ARG_CAPS_PROXY: + cache->caps_proxy = GTK_VALUE_BOOL(*arg); +GST_DEBUG(0,"caps_proxy is %d\n",cache->caps_proxy); + if (cache->caps_proxy) { + gst_pad_set_negotiate_function (cache->sinkpad, GST_DEBUG_FUNCPTR(gst_autoplugcache_nego_sink)); + gst_pad_set_negotiate_function (cache->srcpad, GST_DEBUG_FUNCPTR(gst_autoplugcache_nego_src)); + } else { + gst_pad_set_negotiate_function (cache->sinkpad, NULL); + gst_pad_set_negotiate_function (cache->srcpad, NULL); + } + break; + case ARG_RESET: + // no idea why anyone would set this to FALSE, but just in case ;-) + if (GTK_VALUE_BOOL(*arg)) { + fprintf(stderr,"resetting playout pointer\n"); + // reset the playout pointer to the begining again + cache->current_playout = cache->cache_start; + // now we can fire a signal when the cache runs dry + cache->fire_empty = TRUE; + // also set it up to fire the first_buffer signal again + cache->fire_first = TRUE; + } + break; + default: + break; + } +} + +static void +gst_autoplugcache_get_arg (GtkObject *object, GtkArg *arg, guint id) +{ + GstAutoplugCache *cache; + + cache = GST_AUTOPLUGCACHE (object); + + switch (id) { + case ARG_BUFFER_COUNT: + GTK_VALUE_INT(*arg) = cache->buffer_count; + break; + case ARG_CAPS_PROXY: + GTK_VALUE_BOOL(*arg) = cache->caps_proxy; + default: + arg->type = GTK_TYPE_INVALID; + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new ("autoplugcache", GST_TYPE_AUTOPLUGCACHE, + &gst_autoplugcache_details); + g_return_val_if_fail (factory != NULL, FALSE); + + gst_plugin_add_factory (plugin, factory); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "autoplugcache", + plugin_init +}; + diff --git a/gst/autoplug/gstautoplugger.c b/gst/autoplug/gstautoplugger.c new file mode 100644 index 000000000..883d8cdc1 --- /dev/null +++ b/gst/autoplug/gstautoplugger.c @@ -0,0 +1,606 @@ +/* GStreamer + * Copyright (C) 2001 RidgeRun, Inc. (www.ridgerun.com) + * + * gstautoplugger.c: Data for the dynamic autopluggerger + * + * 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. + */ + +#include + +GstElementDetails gst_autoplugger_details = { + "Dynamic autoplugger", + "Autoplugger", + "Magic element that converts from any type to any other", + VERSION, + "Erik Walthinsen ", + "(C) 2001 RidgeRun, Inc. (www.ridgerun.com)", +}; + +#define GST_TYPE_AUTOPLUGGER \ + (gst_autoplugger_get_type()) +#define GST_AUTOPLUGGER(obj) \ + (GTK_CHECK_CAST((obj),GST_TYPE_AUTOPLUGGER,GstAutoplugger)) +#define GST_AUTOPLUGGER_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_AUTOPLUGGER,GstAutopluggerClass)) +#define GST_IS_AUTOPLUGGER(obj) \ + (GTK_CHECK_TYPE((obj),GST_TYPE_AUTOPLUGGER)) +#define GST_IS_AUTOPLUGGER_CLASS(obj) \ + (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_AUTOPLUGGER)) + +typedef struct _GstAutoplugger GstAutoplugger; +typedef struct _GstAutopluggerClass GstAutopluggerClass; + +struct _GstAutoplugger { + GstBin bin; + gint paused; + + GstElement *cache; + gboolean cache_first_buffer; + GstPad *cache_sinkpad, *cache_srcpad; + + GstElement *typefind; + GstPad *typefind_sinkpad; + + GstPad *sinkpadpeer, *srcpadpeer; + GstCaps *sinkcaps, *srccaps; + + GstCaps *sinktemplatecaps; + + GstAutoplug *autoplug; + GstElement *autobin; + + gboolean disable_nocaps; +}; + +struct _GstAutopluggerClass { + GstBinClass parent_class; +}; + + +/* signals and args */ +enum { + LAST_SIGNAL +}; + +enum { + ARG_0, +}; + + +static void gst_autoplugger_class_init (GstAutopluggerClass *klass); +static void gst_autoplugger_init (GstAutoplugger *queue); + +static void gst_autoplugger_set_arg (GtkObject *object, GtkArg *arg, guint id); +static void gst_autoplugger_get_arg (GtkObject *object, GtkArg *arg, guint id); + +//static GstElementStateReturn gst_autoplugger_change_state (GstElement *element); + + +static void gst_autoplugger_external_sink_caps_changed (GstPad *pad, GstCaps *caps, GstAutoplugger *autoplugger); +static void gst_autoplugger_external_src_caps_changed (GstPad *pad, GstCaps *caps, GstAutoplugger *autoplugger); +static void gst_autoplugger_external_sink_caps_nego_failed (GstPad *pad, gboolean *result, GstAutoplugger *autoplugger); +static void gst_autoplugger_external_src_caps_nego_failed (GstPad *pad, gboolean *result, GstAutoplugger *autoplugger); +static void gst_autoplugger_external_sink_connected (GstPad *pad, GstPad *peerpad, GstAutoplugger *autoplugger); +static void gst_autoplugger_external_src_connected (GstPad *pad, GstPad *peerpad, GstAutoplugger *autoplugger); + +static void gst_autoplugger_cache_first_buffer (GstElement *element,GstBuffer *buf,GstAutoplugger *autoplugger); +static void gst_autoplugger_cache_empty (GstElement *element, GstAutoplugger *autoplugger); +static void gst_autoplugger_typefind_have_type (GstElement *element, GstCaps *caps, GstAutoplugger *autoplugger); + +static GstElementClass *parent_class = NULL; +//static guint gst_autoplugger_signals[LAST_SIGNAL] = { 0 }; + +GtkType +gst_autoplugger_get_type(void) { + static GtkType autoplugger_type = 0; + + if (!autoplugger_type) { + static const GtkTypeInfo autoplugger_info = { + "GstAutoplugger", + sizeof(GstAutoplugger), + sizeof(GstAutopluggerClass), + (GtkClassInitFunc)gst_autoplugger_class_init, + (GtkObjectInitFunc)gst_autoplugger_init, + (GtkArgSetFunc)gst_autoplugger_set_arg, + (GtkArgGetFunc)gst_autoplugger_get_arg, + (GtkClassInitFunc)NULL, + }; + autoplugger_type = gtk_type_unique (GST_TYPE_BIN, &autoplugger_info); + } + return autoplugger_type; +} + +static void +gst_autoplugger_class_init (GstAutopluggerClass *klass) +{ + GtkObjectClass *gtkobject_class; + GstElementClass *gstelement_class; + + gtkobject_class = (GtkObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = gtk_type_class (GST_TYPE_ELEMENT); + +/* + gst_autoplugger_signals[_EMPTY] = + gtk_signal_new ("_empty", GTK_RUN_LAST, gtkobject_class->type, + GTK_SIGNAL_OFFSET (GstAutopluggerClass, _empty), + gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); + gtk_object_class_add_signals (gtkobject_class, gst_autoplugger_signals, LAST_SIGNAL); +*/ + +/* + gtk_object_add_arg_type ("GstAutoplugger::buffer_count", GTK_TYPE_INT, + GTK_ARG_READABLE, ARG_BUFFER_COUNT); + gtk_object_add_arg_type ("GstAutoplugger::reset", GTK_TYPE_BOOL, + GTK_ARG_WRITABLE, ARG_RESET); +*/ + + gtkobject_class->set_arg = gst_autoplugger_set_arg; + gtkobject_class->get_arg = gst_autoplugger_get_arg; + +// gstelement_class->change_state = gst_autoplugger_change_state; +} + +static void +gst_autoplugger_init (GstAutoplugger *autoplugger) +{ + // create the autoplugger cache, which is the fundamental unit of the autopluggerger + // FIXME we need to find a way to set element's name before _init + // FIXME ... so we can name the subelements uniquely + autoplugger->cache = gst_elementfactory_make("autoplugcache", "unnamed_autoplugcache"); + g_return_if_fail (autoplugger->cache != NULL); + + GST_DEBUG(GST_CAT_AUTOPLUG, "turning on caps nego proxying in cache\n"); + gtk_object_set(GTK_OBJECT(autoplugger->cache),"caps_proxy",TRUE,NULL); + + // attach signals to the cache + gtk_signal_connect (GTK_OBJECT (autoplugger->cache), "first_buffer", + GTK_SIGNAL_FUNC (gst_autoplugger_cache_first_buffer), autoplugger); + + // add the cache to self + gst_bin_add (GST_BIN(autoplugger), autoplugger->cache); + + // get the cache's pads so we can attach stuff to them + autoplugger->cache_sinkpad = gst_element_get_pad (autoplugger->cache, "sink"); + autoplugger->cache_srcpad = gst_element_get_pad (autoplugger->cache, "src"); + + // attach handlers to the typefind pads + gtk_signal_connect (GTK_OBJECT (autoplugger->cache_sinkpad), "caps_changed", + GTK_SIGNAL_FUNC (gst_autoplugger_external_sink_caps_changed), autoplugger); + gtk_signal_connect (GTK_OBJECT (autoplugger->cache_srcpad), "caps_changed", + GTK_SIGNAL_FUNC (gst_autoplugger_external_src_caps_changed), autoplugger); + gtk_signal_connect (GTK_OBJECT (autoplugger->cache_sinkpad), "caps_nego_failed", + GTK_SIGNAL_FUNC (gst_autoplugger_external_sink_caps_nego_failed), autoplugger); + gtk_signal_connect (GTK_OBJECT (autoplugger->cache_srcpad), "caps_nego_failed", + GTK_SIGNAL_FUNC (gst_autoplugger_external_src_caps_nego_failed), autoplugger); +// gtk_signal_connect (GTK_OBJECT (autoplugger->cache_sinkpad), "connected", +// GTK_SIGNAL_FUNC (gst_autoplugger_external_sink_connected), autoplugger); +// gtk_signal_connect (GTK_OBJECT (autoplugger->cache_srcpad), "connected", +// GTK_SIGNAL_FUNC (gst_autoplugger_external_src_connected), autoplugger); + + // ghost both of these pads to the outside world + gst_element_add_ghost_pad (GST_ELEMENT(autoplugger), autoplugger->cache_sinkpad, "sink"); + gst_element_add_ghost_pad (GST_ELEMENT(autoplugger), autoplugger->cache_srcpad, "src"); +} + + +static void +gst_autoplugger_external_sink_connected(GstPad *pad, GstPad *peerpad, GstAutoplugger *autoplugger) +{ + GstPadTemplate *peertemplate; + GstCaps *peercaps, *peertemplatecaps; + + GST_INFO(GST_CAT_AUTOPLUG, "have cache:sink connected"); +// autoplugger->sinkpadpeer = peerpad; + + if (autoplugger->sinkpadpeer) { + peercaps = GST_PAD_CAPS(autoplugger->sinkpadpeer); + if (peercaps) + GST_INFO(GST_CAT_AUTOPLUG, "there are some caps on this pad's peer: %s", + gst_caps_get_mime(peercaps)); + peertemplate = GST_PAD_PADTEMPLATE(autoplugger->sinkpadpeer); + if (peertemplate) { + peertemplatecaps = GST_PADTEMPLATE_CAPS(peertemplate); + if (peertemplatecaps) { + GST_INFO(GST_CAT_AUTOPLUG, "there are some caps on this pad's peer's padtemplate %s", + gst_caps_get_mime(peertemplatecaps)); + } + } + } +} + +static void +gst_autoplugger_external_src_connected(GstPad *pad, GstPad *peerpad, GstAutoplugger *autoplugger) +{ + GstPadTemplate *peertemplate; + GstCaps *peercaps, *peertemplatecaps; + + GST_INFO(GST_CAT_AUTOPLUG, "have cache:src connected"); +// autoplugger->srcpadpeer = peerpad; + + if (autoplugger->srcpadpeer) { + peercaps = GST_PAD_CAPS(autoplugger->srcpadpeer); + if (peercaps) + GST_INFO(GST_CAT_AUTOPLUG, "there are some caps on this pad's peer: %s", + gst_caps_get_mime(peercaps)); + peertemplate = GST_PAD_PADTEMPLATE(autoplugger->srcpadpeer); + if (peertemplate) { + peertemplatecaps = GST_PADTEMPLATE_CAPS(peertemplate); + if (peertemplatecaps) { + GST_INFO(GST_CAT_AUTOPLUG, "there are some caps on this pad's peer's padtemplate %s", + gst_caps_get_mime(peertemplatecaps)); + autoplugger->sinktemplatecaps = peertemplatecaps; +// GST_DEBUG(GST_CAT_AUTOPLUG, "turning on caps nego proxying in cache\n"); +// gtk_object_set(GTK_OBJECT(autoplugger->cache),"caps_proxy",TRUE,NULL); + } + } + } +} + + +static void +gst_autoplugger_external_sink_caps_changed(GstPad *pad, GstCaps *caps, GstAutoplugger *autoplugger) +{ + GST_INFO(GST_CAT_AUTOPLUG, "have cache:sink caps of %s\n",gst_caps_get_mime(caps)); + autoplugger->sinkcaps = caps; +} + +static void +gst_autoplugger_external_src_caps_changed(GstPad *pad, GstCaps *caps, GstAutoplugger *autoplugger) +{ + GST_INFO(GST_CAT_AUTOPLUG, "have cache:src caps of %s\n",gst_caps_get_mime(caps)); + autoplugger->srccaps = caps; +} + + +static gboolean +gst_autoplugger_autoplug(GstAutoplugger *autoplugger,GstPad *srcpad,GstCaps *srccaps,GstCaps *sinkcaps) +{ + GstPad *sinkpad; + + sinkpad = GST_PAD(GST_PAD_PEER(srcpad)); + GST_DEBUG(GST_CAT_AUTOPLUG,"disconnecting %s:%s and %s:%s to autoplug between them\n", + GST_DEBUG_PAD_NAME(srcpad),GST_DEBUG_PAD_NAME(sinkpad)); + GST_DEBUG(GST_CAT_AUTOPLUG,"srcpadcaps are of type %s\n",gst_caps_get_mime(srccaps)); + GST_DEBUG(GST_CAT_AUTOPLUG,"sinkpadcaps are of type %s\n",gst_caps_get_mime(sinkcaps)); + + // disconnect the pads + GST_DEBUG(GST_CAT_AUTOPLUG, "disconnecting the pads that will be joined by an autobin\n"); + gst_pad_disconnect(srcpad,sinkpad); + + if (!autoplugger->autoplug) { + autoplugger->autoplug = gst_autoplugfactory_make("static"); + g_return_val_if_fail(autoplugger->autoplug != NULL, FALSE); + } + GST_DEBUG(GST_CAT_AUTOPLUG, "building autoplugged bin between caps\n"); + autoplugger->autobin = gst_autoplug_to_caps(autoplugger->autoplug, + srccaps,sinkcaps,NULL); + g_return_val_if_fail(autoplugger->autobin != NULL, FALSE); + gst_bin_add(GST_BIN(autoplugger),autoplugger->autobin); + +gst_schedule_show(GST_ELEMENT_SCHED(autoplugger)); + + // FIXME this is a hack +// GST_DEBUG(GST_CAT_AUTOPLUG, "copying failed caps to srcpad %s:%s to ensure renego\n",GST_DEBUG_PAD_NAME(autoplugger->cache_srcpad)); +// gst_pad_set_caps(srcpad,srccaps); + + if (GST_PAD_CAPS(srcpad) == NULL) GST_DEBUG(GST_CAT_AUTOPLUG,"no caps on cache:src!\n"); + + // attach the autoplugged bin + GST_DEBUG(GST_CAT_AUTOPLUG, "attaching the autoplugged bin between the two pads\n"); + gst_pad_connect(srcpad,gst_element_get_pad(autoplugger->autobin,"sink")); +gst_schedule_show(GST_ELEMENT_SCHED(autoplugger)); + gst_pad_connect(gst_element_get_pad(autoplugger->autobin,"src_00"),sinkpad); +gst_schedule_show(GST_ELEMENT_SCHED(autoplugger)); + + // FIXME try to force the renego +// GST_DEBUG(GST_CAT_AUTOPLUG, "trying to force everyone to nego\n"); +// gst_pad_renegotiate(gst_element_get_pad(autoplugger->autobin,"sink")); +// gst_pad_renegotiate(sinkpad); + + return TRUE; +} + +static void +gst_autoplugger_external_sink_caps_nego_failed(GstPad *pad, gboolean *result, GstAutoplugger *autoplugger) +{ + GstPad *srcpad_peer; + GstPadTemplate *srcpad_peer_template; + GstCaps *srcpad_peer_caps; + GstPad *sinkpad_peer; + GstCaps *sinkpad_peer_caps; + + GST_INFO(GST_CAT_AUTOPLUG, "have caps nego failure on sinkpad %s:%s!!!",GST_DEBUG_PAD_NAME(pad)); + + autoplugger->paused++; + if (autoplugger->paused == 1) + // try to PAUSE the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PAUSED); + + srcpad_peer = GST_PAD(GST_PAD_PEER(autoplugger->cache_srcpad)); + g_return_if_fail(srcpad_peer != NULL); + srcpad_peer_template = GST_PAD_PADTEMPLATE(srcpad_peer); + g_return_if_fail(srcpad_peer_template != NULL); + srcpad_peer_caps = GST_PADTEMPLATE_CAPS(srcpad_peer_template); + g_return_if_fail(srcpad_peer_caps != NULL); + + sinkpad_peer = GST_PAD(GST_PAD_PEER(pad)); + g_return_if_fail(sinkpad_peer != NULL); + sinkpad_peer_caps = GST_PAD_CAPS(sinkpad_peer); + g_return_if_fail(sinkpad_peer_caps != NULL); + + if (gst_autoplugger_autoplug(autoplugger,autoplugger->cache_srcpad,sinkpad_peer_caps,srcpad_peer_caps)) + *result = TRUE; + + // force renego + gst_pad_renegotiate(GST_PAD(GST_PAD_PEER(autoplugger->cache_sinkpad))); + + autoplugger->paused--; + if (autoplugger->paused == 0) + // try to PLAY the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PLAYING); + + GST_INFO(GST_CAT_AUTOPLUG, "done dealing with caps nego failure on sinkpad %s:%s",GST_DEBUG_PAD_NAME(pad)); +} + +static void +gst_autoplugger_external_src_caps_nego_failed(GstPad *pad, gboolean *result, GstAutoplugger *autoplugger) +{ + GstCaps *srcpad_caps; + GstPad *srcpad_peer; + GstPadTemplate *srcpad_peer_template; + GstCaps *srcpad_peer_caps; + + GST_INFO(GST_CAT_AUTOPLUG, "have caps nego failure on srcpad %s:%s!!!",GST_DEBUG_PAD_NAME(pad)); + + autoplugger->paused++; + if (autoplugger->paused == 1) + // try to PAUSE the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PAUSED); + + srcpad_caps = GST_PAD_CAPS(autoplugger->cache_srcpad); + + srcpad_peer = GST_PAD(GST_PAD_PEER(autoplugger->cache_srcpad)); + g_return_if_fail(srcpad_peer != NULL); + srcpad_peer_template = GST_PAD_PADTEMPLATE(srcpad_peer); + g_return_if_fail(srcpad_peer_template != NULL); + srcpad_peer_caps = GST_PADTEMPLATE_CAPS(srcpad_peer_template); + g_return_if_fail(srcpad_peer_caps != NULL); + + if (gst_autoplugger_autoplug(autoplugger,autoplugger->cache_srcpad,srcpad_caps,srcpad_peer_caps)) + *result = TRUE; + + autoplugger->paused--; + if (autoplugger->paused == 0) + // try to PLAY the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PLAYING); + + autoplugger->disable_nocaps = TRUE; + + GST_INFO(GST_CAT_AUTOPLUG, "done dealing with caps nego failure on srcpad %s:%s",GST_DEBUG_PAD_NAME(pad)); +} + + +static void +gst_autoplugger_cache_empty(GstElement *element, GstAutoplugger *autoplugger) +{ + GstPad *cache_sinkpad_peer,*cache_srcpad_peer; + + GST_INFO(GST_CAT_AUTOPLUG, "autoplugger cache has hit empty, we can now remove it"); + + autoplugger->paused++; + if (autoplugger->paused == 1) + // try to PAUSE the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PAUSED); + + // disconnect the cache from its peers + GST_DEBUG(GST_CAT_AUTOPLUG, "disconnecting autoplugcache from its peers\n"); + cache_sinkpad_peer = GST_PAD (GST_PAD_PEER(autoplugger->cache_sinkpad)); + cache_srcpad_peer = GST_PAD (GST_PAD_PEER(autoplugger->cache_srcpad)); + gst_pad_disconnect(cache_sinkpad_peer,autoplugger->cache_sinkpad); + gst_pad_disconnect(autoplugger->cache_srcpad,cache_srcpad_peer); + + // remove the cache from self + GST_DEBUG(GST_CAT_AUTOPLUG, "removing the cache from the autoplugger\n"); + gst_bin_remove (GST_BIN(autoplugger), autoplugger->cache); + + // connect the two pads + GST_DEBUG(GST_CAT_AUTOPLUG, "reconnecting the autoplugcache's former peers\n"); + gst_pad_connect(cache_sinkpad_peer,cache_srcpad_peer); + + autoplugger->paused--; + if (autoplugger->paused == 0) + // try to PLAY the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PLAYING); + + xmlSaveFile("autoplugger.gst", gst_xml_write(GST_ELEMENT_SCHED(autoplugger)->parent)); + + GST_INFO(GST_CAT_AUTOPLUG, "autoplugger_cache_empty finished"); +} + +static void +gst_autoplugger_typefind_have_type(GstElement *element, GstCaps *caps, GstAutoplugger *autoplugger) +{ + GST_INFO(GST_CAT_AUTOPLUG, "typefind claims to have a type: %s",gst_caps_get_mime(caps)); + +gst_schedule_show(GST_ELEMENT_SCHED(autoplugger)); + + autoplugger->paused++; + if (autoplugger->paused == 1) + // try to PAUSE the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PAUSED); + + // first disconnect the typefind and shut it down + GST_DEBUG(GST_CAT_AUTOPLUG, "disconnecting typefind from the cache\n"); + gst_pad_disconnect(autoplugger->cache_srcpad,autoplugger->typefind_sinkpad); + gst_bin_remove(GST_BIN(autoplugger),autoplugger->typefind); + + // FIXME FIXME now we'd compare caps and see if we need to autoplug something in the middle, but for + // now we're going to just reconnect where we left off + // FIXME FIXME FIXME!!!: this should really be done in the caps failure!!! +/* + if (!autoplugger->autoplug) { + autoplugger->autoplug = gst_autoplugfactory_make("static"); + } + autoplugger->autobin = gst_autoplug_to_caps(autoplugger->autoplug, + caps,autoplugger->sinktemplatecaps,NULL); + g_return_if_fail(autoplugger->autobin != NULL); + gst_bin_add(GST_BIN(autoplugger),autoplugger->autobin); + +// // re-attach the srcpad's original peer to the cache +// GST_DEBUG(GST_CAT_AUTOPLUG, "reconnecting the cache to the downstream peer\n"); +// gst_pad_connect(autoplugger->cache_srcpad,autoplugger->srcpadpeer); + + // attach the autoplugged bin + GST_DEBUG(GST_CAT_AUTOPLUG, "attaching the autoplugged bin between cache and downstream peer\n"); + gst_pad_connect(autoplugger->cache_srcpad,gst_element_get_pad(autoplugger->autobin,"sink")); + gst_pad_connect(gst_element_get_pad(autoplugger->autobin,"src_00"),autoplugger->srcpadpeer); +*/ + + // FIXME set the caps on the new connection +// GST_DEBUG(GST_CAT_AUTOPLUG,"forcing caps on the typefound pad\n"); +// gst_pad_set_caps(autoplugger->cache_srcpad,caps); + + // reattach the original outside srcpad + GST_DEBUG(GST_CAT_AUTOPLUG,"re-attaching downstream peer to autoplugcache\n"); + gst_pad_connect(autoplugger->cache_srcpad,autoplugger->srcpadpeer); + + // now reset the autoplugcache + GST_DEBUG(GST_CAT_AUTOPLUG, "resetting the cache to send first buffer(s) again\n"); + gtk_object_set(GTK_OBJECT(autoplugger->cache),"reset",TRUE,NULL); + + // attach the cache_empty handler + // FIXME this is the wrong place, it shouldn't be done until we get successful caps nego! + gtk_signal_connect(GTK_OBJECT(autoplugger->cache),"cache_empty", + GTK_SIGNAL_FUNC(gst_autoplugger_cache_empty),autoplugger); + + autoplugger->paused--; + if (autoplugger->paused == 0) + // try to PLAY the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PLAYING); + + GST_INFO(GST_CAT_AUTOPLUG, "typefind_have_type finished"); +gst_schedule_show(GST_ELEMENT_SCHED(autoplugger)); +} + +static void +gst_autoplugger_cache_first_buffer(GstElement *element,GstBuffer *buf,GstAutoplugger *autoplugger) +{ + GST_INFO(GST_CAT_AUTOPLUG, "have first buffer through cache"); + autoplugger->cache_first_buffer = TRUE; + + // if there are no established caps, worry + if (!autoplugger->sinkcaps) { + GST_INFO(GST_CAT_AUTOPLUG, "have no caps for the buffer, Danger Will Robinson!"); + +if (autoplugger->disable_nocaps) { + GST_DEBUG(GST_CAT_AUTOPLUG, "not dealing with lack of caps this time\n"); + return; +} + +gst_schedule_show(GST_ELEMENT_SCHED(autoplugger)); + + autoplugger->paused++; + if (autoplugger->paused == 1) + // try to PAUSE the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PAUSED); + + // detach the srcpad + GST_DEBUG(GST_CAT_AUTOPLUG, "disconnecting cache from its downstream peer\n"); + autoplugger->srcpadpeer = GST_PAD(GST_PAD_PEER(autoplugger->cache_srcpad)); + gst_pad_disconnect(autoplugger->cache_srcpad,autoplugger->srcpadpeer); + + // instantiate the typefind and set up the signal handlers + if (!autoplugger->typefind) { + GST_DEBUG(GST_CAT_AUTOPLUG, "creating typefind and setting signal handler\n"); + autoplugger->typefind = gst_elementfactory_make("typefind","unnamed_typefind"); + autoplugger->typefind_sinkpad = gst_element_get_pad(autoplugger->typefind,"sink"); + gtk_signal_connect(GTK_OBJECT(autoplugger->typefind),"have_type", + GTK_SIGNAL_FUNC (gst_autoplugger_typefind_have_type), autoplugger); + } + // add it to self and attach it + GST_DEBUG(GST_CAT_AUTOPLUG, "adding typefind to self and connecting to cache\n"); + gst_bin_add(GST_BIN(autoplugger),autoplugger->typefind); + gst_pad_connect(autoplugger->cache_srcpad,autoplugger->typefind_sinkpad); + + // bring the typefind into playing state + GST_DEBUG(GST_CAT_AUTOPLUG, "setting typefind state to PLAYING\n"); + gst_element_set_state(autoplugger->cache,GST_STATE_PLAYING); + + autoplugger->paused--; + if (autoplugger->paused == 0) + // try to PLAY the whole thing + gst_element_set_state(GST_ELEMENT_SCHED(autoplugger)->parent,GST_STATE_PLAYING); + + GST_INFO(GST_CAT_AUTOPLUG,"here we go into nothingness, hoping the typefind will return us to safety"); +gst_schedule_show(GST_ELEMENT_SCHED(autoplugger)); + } else { +// // attach the cache_empty handler, since the cache simply isn't needed +// gtk_signal_connect(GTK_OBJECT(autoplugger->cache),"cache_empty", +// GTK_SIGNAL_FUNC(gst_autoplugger_cache_empty),autoplugger); + } +} + +static void +gst_autoplugger_set_arg (GtkObject *object, GtkArg *arg, guint id) +{ + GstAutoplugger *autoplugger; + + autoplugger = GST_AUTOPLUGGER (object); + + switch (id) { + default: + break; + } +} + +static void +gst_autoplugger_get_arg (GtkObject *object, GtkArg *arg, guint id) +{ + GstAutoplugger *autoplugger; + + autoplugger = GST_AUTOPLUGGER (object); + + switch (id) { + default: + arg->type = GTK_TYPE_INVALID; + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new ("autoplugger", GST_TYPE_AUTOPLUGGER, + &gst_autoplugger_details); + g_return_val_if_fail (factory != NULL, FALSE); + + gst_plugin_add_factory (plugin, factory); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "autoplugger", + plugin_init +}; + diff --git a/gst/autoplug/gststaticautoplug.c b/gst/autoplug/gststaticautoplug.c index 11403a069..3edf83e78 100644 --- a/gst/autoplug/gststaticautoplug.c +++ b/gst/autoplug/gststaticautoplug.c @@ -124,7 +124,7 @@ gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest) if (gst_caps_check_compatibility (gst_padtemplate_get_caps (srctemp), gst_padtemplate_get_caps (desttemp))) { GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, - "factory \"%s\" can connect with factory \"%s\"", src->name, dest->name); + "factory \"%s\" can connect with factory \"%s\"\n", src->name, dest->name); return TRUE; } } @@ -134,7 +134,7 @@ gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest) srctemps = g_list_next (srctemps); } GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, - "factory \"%s\" cannot connect with factory \"%s\"", src->name, dest->name); + "factory \"%s\" cannot connect with factory \"%s\"\n", src->name, dest->name); return FALSE; } diff --git a/gst/autoplug/gststaticautoplugrender.c b/gst/autoplug/gststaticautoplugrender.c index c544f7f18..cbfc798fa 100644 --- a/gst/autoplug/gststaticautoplugrender.c +++ b/gst/autoplug/gststaticautoplugrender.c @@ -123,7 +123,7 @@ gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest) desttemp->direction == GST_PAD_SINK) { if (gst_caps_check_compatibility (GST_PADTEMPLATE_CAPS (srctemp), GST_PADTEMPLATE_CAPS (desttemp))) { GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, - "factory \"%s\" can connect with factory \"%s\"", src->name, dest->name); + "factory \"%s\" can connect with factory \"%s\"\n", src->name, dest->name); return TRUE; } } @@ -133,7 +133,7 @@ gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest) srctemps = g_list_next (srctemps); } GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, - "factory \"%s\" cannot connect with factory \"%s\"", src->name, dest->name); + "factory \"%s\" cannot connect with factory \"%s\"\n", src->name, dest->name); return FALSE; } @@ -154,12 +154,21 @@ gst_autoplug_pads_autoplug_func (GstElement *src, GstPad *pad, GstElement *sink) if (gst_pad_get_direction(sinkpad) == GST_PAD_SINK && !GST_PAD_CONNECTED (pad) && !GST_PAD_CONNECTED(sinkpad)) { + GstElementState state = GST_STATE (gst_element_get_parent (src)); + + if (state == GST_STATE_PLAYING) + gst_element_set_state (GST_ELEMENT (gst_element_get_parent (src)), GST_STATE_PAUSED); + if ((connected = gst_pad_connect (pad, sinkpad))) { + if (state == GST_STATE_PLAYING) + gst_element_set_state (GST_ELEMENT (gst_element_get_parent (src)), GST_STATE_PLAYING); break; } else { GST_DEBUG (0,"pads incompatible %s, %s\n", GST_PAD_NAME (pad), GST_PAD_NAME (sinkpad)); } + if (state == GST_STATE_PLAYING) + gst_element_set_state (GST_ELEMENT (gst_element_get_parent (src)), GST_STATE_PLAYING); } sinkpads = g_list_next(sinkpads); } @@ -423,12 +432,13 @@ differ: // create a new queue and add to the previous bin queue = gst_elementfactory_make("queue", g_strconcat("queue_", GST_ELEMENT_NAME(element), NULL)); GST_DEBUG (0,"adding element \"%s\"\n", GST_ELEMENT_NAME (element)); - gst_bin_add(GST_BIN(thebin), queue); - gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (queue)); // this will be the new bin for all following elements thebin = gst_elementfactory_make("thread", g_strconcat("thread_", GST_ELEMENT_NAME(element), NULL)); + gst_bin_add(GST_BIN(thebin), queue); + gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (queue)); + srcpad = gst_element_get_pad(queue, "src"); gst_autoplug_pads_autoplug(thesrcelement, queue); diff --git a/gst/cothreads.c b/gst/cothreads.c index 2de2b3ee6..478dba0d3 100644 --- a/gst/cothreads.c +++ b/gst/cothreads.c @@ -67,6 +67,11 @@ cothread_init (void) { cothread_context *ctx = (cothread_context *)malloc(sizeof(cothread_context)); + // we consider the initiating process to be cothread 0 + ctx->nthreads = 1; + ctx->current = 0; + ctx->data = g_hash_table_new(g_str_hash, g_str_equal); + GST_INFO (GST_CAT_COTHREADS,"initializing cothreads"); if (_cothread_key == -1) { @@ -89,12 +94,14 @@ cothread_init (void) ctx->threads[0]->sp = (void *)CURRENT_STACK_FRAME; ctx->threads[0]->pc = 0; - GST_INFO (GST_CAT_COTHREADS,"0th thread is %p at sp:%p",ctx->threads[0], ctx->threads[0]->sp); + // initialize the lock +#ifdef COTHREAD_ATOMIC + atomic_set (&ctx->threads[0]->lock, 0); +#else + ctx->threads[0]->lock = g_mutex_new(); +#endif - // we consider the initiating process to be cothread 0 - ctx->nthreads = 1; - ctx->current = 0; - ctx->data = g_hash_table_new(g_str_hash, g_str_equal); + GST_INFO (GST_CAT_COTHREADS,"0th thread is %p at sp:%p",ctx->threads[0], ctx->threads[0]->sp); return ctx; } @@ -118,7 +125,7 @@ cothread_create (cothread_context *ctx) } GST_DEBUG (0,"pthread_self() %ld\n",pthread_self()); //if (0) { - if (pthread_self() == 0) { + if (pthread_self() == 0) { // FIXME uh, what does this test really do? s = (cothread_state *)malloc(COTHREAD_STACKSIZE); GST_DEBUG (0,"new stack (case 1) at %p\n",s); } else { @@ -145,6 +152,13 @@ cothread_create (cothread_context *ctx) // is this needed anymore? s->top_sp = s->sp; + // initialize the lock +#ifdef COTHREAD_ATOMIC + atomic_set (s->lock, 0); +#else + s->lock = g_mutex_new(); +#endif + GST_INFO (GST_CAT_COTHREADS,"created cothread #%d: %p at sp:%p", ctx->nthreads, s, s->sp); ctx->threads[ctx->nthreads++] = s; @@ -186,6 +200,18 @@ cothread_main(cothread_context *ctx) return ctx->threads[0]; } +/** + * cothread_current)main: + * + * Returns: the #cothread_state of the main (0th) thread in the current pthread + */ +cothread_state* +cothread_current_main(void) +{ + cothread_context *ctx = pthread_getspecific(_cothread_key); + return ctx->threads[0]; +} + static void cothread_stub (void) { @@ -195,6 +221,11 @@ cothread_stub (void) GST_DEBUG_ENTER(""); thread->flags |= COTHREAD_STARTED; +//#ifdef COTHREAD_ATOMIC +// // do something here to lock +//#else +// g_mutex_lock(thread->lock); +//#endif while (1) { thread->func(thread->argc,thread->argv); // we do this to avoid ever returning, we just switch to 0th thread @@ -281,8 +312,22 @@ cothread_switch (cothread_state *thread) #endif if (current == thread) goto selfswitch; + // unlock the current thread, we're out of that context now +#ifdef COTHREAD_ATOMIC + // do something to unlock the cothread +#else + g_mutex_unlock(current->lock); +#endif + + // lock the next cothread before we even switch to it +#ifdef COTHREAD_ATOMIC + // do something to lock the cothread +#else + g_mutex_lock(thread->lock); +#endif + // find the number of the thread to switch to - GST_INFO (GST_CAT_COTHREAD_SWITCH,"switching from cothread %d to to cothread #%d", + GST_INFO (GST_CAT_COTHREAD_SWITCH,"switching from cothread #%d to cothread #%d", ctx->current,thread->threadnum); ctx->current = thread->threadnum; @@ -319,7 +364,7 @@ cothread_switch (cothread_state *thread) #ifdef COTHREAD_PARANOID nothread: - g_print("cothread: there's no thread, strange...\n"); + g_print("cothread: can't switch to NULL cothread!\n"); return; nocontext: g_print("cothread: there's no context, help!\n"); @@ -332,3 +377,35 @@ selfswitch: g_print("cothread: trying to switch to same thread, legal but not necessary\n"); return; } + + +void +cothread_lock (cothread_state *thread) +{ +#ifdef COTHREAD_ATOMIC + // do something to lock the cothread +#else + g_mutex_lock(thread->lock); +#endif +} + +gboolean +cothread_trylock (cothread_state *thread) +{ +#ifdef COTHREAD_ATOMIC + // do something to try to lock the cothread +#else + return g_mutex_trylock(thread->lock); +#endif +} + +void +cothread_unlock (cothread_state *thread) +{ +#ifdef COTHREAD_ATOMIC + // do something to unlock the cothread +#else + g_mutex_unlock(thread->lock); +#endif +} + diff --git a/gst/cothreads.h b/gst/cothreads.h index d48b1abbd..d95d7cf73 100644 --- a/gst/cothreads.h +++ b/gst/cothreads.h @@ -26,6 +26,12 @@ #include #include +#ifdef HAVE_ATOMIC_H +#include +#endif + +#undef COTHREAD_ATOMIC + #ifndef CURRENT_STACK_FRAME #define CURRENT_STACK_FRAME ({ char __csf; &__csf; }) #endif /* CURRENT_STACK_FRAME */ @@ -47,10 +53,16 @@ struct _cothread_state { int flags; void *sp; + jmp_buf jmp; /* is this needed any more? */ void *top_sp; void *pc; - jmp_buf jmp; + +#ifdef COTHREAD_ATOMIC + atomic_t lock; +#else + GMutex *lock; +#endif }; @@ -63,6 +75,11 @@ void cothread_switch (cothread_state *thread); void cothread_set_data (cothread_state *thread, gchar *key, gpointer data); gpointer cothread_get_data (cothread_state *thread, gchar *key); +void cothread_lock (cothread_state *thread); +gboolean cothread_trylock (cothread_state *thread); +void cothread_unlock (cothread_state *thread); + cothread_state* cothread_main (cothread_context *ctx); +cothread_state* cothread_current_main (void); #endif /* __COTHREAD_H__ */ diff --git a/gst/elements/gstfakesrc.c b/gst/elements/gstfakesrc.c index 5fa6af217..97570bbb8 100644 --- a/gst/elements/gstfakesrc.c +++ b/gst/elements/gstfakesrc.c @@ -49,6 +49,7 @@ enum { ARG_OUTPUT, ARG_PATTERN, ARG_NUM_BUFFERS, + ARG_EOS, ARG_SILENT }; @@ -125,6 +126,8 @@ gst_fakesrc_class_init (GstFakeSrcClass *klass) GTK_ARG_READWRITE, ARG_PATTERN); gtk_object_add_arg_type ("GstFakeSrc::num_buffers", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_NUM_BUFFERS); + gtk_object_add_arg_type ("GstFakeSrc::eos", GTK_TYPE_BOOL, + GTK_ARG_READWRITE, ARG_EOS); gtk_object_add_arg_type ("GstFakeSrc::silent", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_SILENT); @@ -222,6 +225,10 @@ gst_fakesrc_set_arg (GtkObject *object, GtkArg *arg, guint id) case ARG_NUM_BUFFERS: src->num_buffers = GTK_VALUE_INT (*arg); break; + case ARG_EOS: + src->eos = GTK_VALUE_BOOL (*arg); +GST_INFO (0, "will EOS on next buffer"); + break; case ARG_SILENT: src->silent = GTK_VALUE_BOOL (*arg); break; @@ -256,6 +263,8 @@ gst_fakesrc_get_arg (GtkObject *object, GtkArg *arg, guint id) case ARG_NUM_BUFFERS: GTK_VALUE_INT (*arg) = src->num_buffers; break; + case ARG_EOS: + GTK_VALUE_BOOL (*arg) = src->eos; case ARG_SILENT: GTK_VALUE_BOOL (*arg) = src->silent; break; @@ -295,6 +304,12 @@ gst_fakesrc_get(GstPad *pad) src->num_buffers--; } + if (src->eos) { + GST_INFO (0, "fakesrc is setting eos on pad"); + gst_pad_set_eos (pad); + return NULL; + } + if (!src->silent) g_print("fakesrc: ******* (%s:%s)> \n",GST_DEBUG_PAD_NAME(pad)); buf = gst_buffer_new(); @@ -336,7 +351,13 @@ gst_fakesrc_loop(GstElement *element) } else { if (src->num_buffers > 0) - src->num_buffers--; + src->num_buffers--; + } + + if (src->eos) { + GST_INFO (0, "fakesrc is setting eos on pad"); + gst_pad_set_eos (pad); + return; } buf = gst_buffer_new(); diff --git a/gst/elements/gstfakesrc.h b/gst/elements/gstfakesrc.h index a795c1932..ba92f631b 100644 --- a/gst/elements/gstfakesrc.h +++ b/gst/elements/gstfakesrc.h @@ -65,6 +65,7 @@ struct _GstFakeSrc { GstElement element; gboolean loop_based; + gboolean eos; gint numsrcpads; GSList *srcpads; GstFakeSrcOutputType output; diff --git a/gst/elements/gstsinesrc.c b/gst/elements/gstsinesrc.c index 8cddd5d7b..6a00e1eef 100644 --- a/gst/elements/gstsinesrc.c +++ b/gst/elements/gstsinesrc.c @@ -407,4 +407,4 @@ gst_sinesrc_factory_init (GstElementFactory *factory) gst_elementfactory_add_padtemplate (factory, src_temp); return TRUE; -} \ No newline at end of file +} diff --git a/gst/gst.c b/gst/gst.c index be7eeb4fa..e859b0439 100644 --- a/gst/gst.c +++ b/gst/gst.c @@ -174,21 +174,6 @@ gst_init_check (int *argc, (*argv)[i] = NULL; } - else if (!strncmp ("--gst-mask=", (*argv)[i], 11)) { - guint32 val; - - // handle either 0xHEX or dec - if (*((*argv)[i]+12) == 'x') { - sscanf ((*argv)[i]+13, "%08x", &val); - } else { - sscanf ((*argv)[i]+11, "%d", &val); - } - - gst_debug_set_categories (val); - gst_info_set_categories (val); - - (*argv)[i] = NULL; - } else if (!strncmp ("--gst-plugin-spew", (*argv)[i], 17)) { _gst_plugin_spew = TRUE; @@ -247,10 +232,19 @@ gst_init_check (int *argc, g_print ("--------------------------------------------------------\n"); for (i = 0; i #include +#include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include diff --git a/gst/gstbin.c b/gst/gstbin.c index dc0331e07..a45f13489 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -46,7 +46,6 @@ static gboolean gst_bin_change_state_type (GstBin *bin, GstElementState state, GtkType type); -static void gst_bin_create_plan_func (GstBin *bin); static gboolean gst_bin_iterate_func (GstBin *bin); static xmlNodePtr gst_bin_save_thyself (GstObject *object, xmlNodePtr parent); @@ -113,8 +112,6 @@ gst_bin_class_init (GstBinClass *klass) gtk_object_class_add_signals (gtkobject_class, gst_bin_signals, LAST_SIGNAL); klass->change_state_type = gst_bin_change_state_type; - klass->create_plan = gst_bin_create_plan_func; - klass->schedule = gst_bin_schedule_func; klass->iterate = gst_bin_iterate_func; gstobject_class->save_thyself = gst_bin_save_thyself; @@ -155,6 +152,105 @@ gst_bin_new (const gchar *name) return gst_elementfactory_make ("bin", name); } +static inline void +gst_bin_reset_element_sched (GstElement *element, GstSchedule *sched) +{ + GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "resetting element's scheduler"); + + // first remove the element from its current schedule, if any +// if (GST_ELEMENT_SCHED(element)) +// GST_SCHEDULE_REMOVE_ELEMENT (GST_ELEMENT_SCHED(element), element); + // then set the new manager + gst_element_set_sched (element,sched); + + // and add it to the new scheduler +// if (sched) +// GST_SCHEDULE_ADD_ELEMENT (sched, element); +} + +void +gst_bin_set_element_sched (GstElement *element,GstSchedule *sched) +{ + GList *children; + GstElement *child; + + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_ELEMENT(element)); + g_return_if_fail (sched != NULL); + g_return_if_fail (GST_IS_SCHEDULE(sched)); + + GST_INFO (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p",GST_ELEMENT_NAME(element), + sched); + + // if it's actually a Bin + if (GST_IS_BIN(element)) { + + if (GST_FLAG_IS_SET(element,GST_BIN_FLAG_MANAGER)) { + GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "child is already a manager, not resetting"); + return; + } + + GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting children's schedule to parent's"); + GST_SCHEDULE_ADD_ELEMENT (sched, element); + + // set the children's schedule + children = GST_BIN(element)->children; + while (children) { + child = GST_ELEMENT (children->data); + children = g_list_next(children); + + gst_bin_set_element_sched (child, sched); + } + + // otherwise, if it's just a regular old element + } else { + GST_SCHEDULE_ADD_ELEMENT (sched, element); + } +} + + +void +gst_bin_unset_element_sched (GstElement *element) +{ + GList *children; + GstElement *child; + + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_ELEMENT(element)); + + GST_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from it sched %p", + GST_ELEMENT_NAME(element),GST_ELEMENT_SCHED(element)); + + // if it's actually a Bin + if (GST_IS_BIN(element)) { + + if (GST_FLAG_IS_SET(element,GST_BIN_FLAG_MANAGER)) { + GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "child is already a manager, not unsetting sched"); + return; + } + + // FIXME this check should be irrelevant + if (GST_ELEMENT_SCHED (element)) + GST_SCHEDULE_REMOVE_ELEMENT (GST_ELEMENT_SCHED(element), element); + + // for each child, remove them from their schedule + children = GST_BIN(element)->children; + while (children) { + child = GST_ELEMENT (children->data); + children = g_list_next(children); + + gst_bin_unset_element_sched (child); + } + + // otherwise, if it's just a regular old element + } else { + // FIXME this check should be irrelevant + if (GST_ELEMENT_SCHED (element)) + GST_SCHEDULE_REMOVE_ELEMENT (GST_ELEMENT_SCHED(element), element); + } +} + + /** * gst_bin_add: * @bin: #GstBin to add element to @@ -172,19 +268,30 @@ gst_bin_add (GstBin *bin, g_return_if_fail (element != NULL); g_return_if_fail (GST_IS_ELEMENT (element)); - // must be NULL or PAUSED state in order to modify bin - g_return_if_fail ((GST_STATE (bin) == GST_STATE_NULL) || - (GST_STATE (bin) == GST_STATE_PAUSED)); + GST_DEBUG (GST_CAT_PARENTAGE, "adding element \"%s\" to bin \"%s\"\n", + GST_ELEMENT_NAME(element),GST_ELEMENT_NAME(bin)); + + // must be not be in PLAYING state in order to modify bin +// g_return_if_fail (GST_STATE (bin) != GST_STATE_PLAYING); + + // the element must not already have a parent + g_return_if_fail (GST_ELEMENT_PARENT(element) == NULL); + + // then check to see if the element's name is already taken in the bin + g_return_if_fail (gst_object_check_uniqueness (bin->children, GST_ELEMENT_NAME(element)) == TRUE); + // set the element's parent and add the element to the bin's list of children + gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin)); bin->children = g_list_append (bin->children, element); bin->numchildren++; - gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin)); - GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "added child %s", GST_ELEMENT_NAME (element)); + ///// now we have to deal with manager stuff + // we can only do this if there's a scheduler: + // if we're not a manager, and aren't attached to anything, we have no sched (yet) + if (GST_ELEMENT_SCHED(bin) != NULL) + gst_bin_set_element_sched (element, GST_ELEMENT_SCHED(bin)); - /* we know we have at least one child, we just added one... */ -// if (GST_STATE(element) < GST_STATE_READY) -// gst_bin_change_state_norecurse(bin,GST_STATE_READY); + GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "added child \"%s\"", GST_ELEMENT_NAME (element)); gtk_signal_emit (GTK_OBJECT (bin), gst_bin_signals[OBJECT_ADDED], element); } @@ -206,24 +313,32 @@ gst_bin_remove (GstBin *bin, g_return_if_fail (GST_IS_ELEMENT (element)); g_return_if_fail (bin->children != NULL); - // must be NULL or PAUSED state in order to modify bin - g_return_if_fail ((GST_STATE (bin) == GST_STATE_NULL) || - (GST_STATE (bin) == GST_STATE_PAUSED)); + // must not be in PLAYING state in order to modify bin + g_return_if_fail (GST_STATE (bin) != GST_STATE_PLAYING); + // the element must have its parent set to the current bin + g_return_if_fail (GST_ELEMENT_PARENT(element) == (GstObject *)bin); + + // the element must be in the bin's list of children if (g_list_find(bin->children, element) == NULL) { // FIXME this should be a warning!!! GST_ERROR_OBJECT(bin,element,"no such element in bin"); return; } - gst_object_unparent (GST_OBJECT (element)); + // remove this element from the list of managed elements + gst_bin_unset_element_sched (element); + + // now remove the element from the list of elements bin->children = g_list_remove (bin->children, element); bin->numchildren--; GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "removed child %s", GST_ELEMENT_NAME (element)); + gst_object_unparent (GST_OBJECT (element)); + /* if we're down to zero children, force state to NULL */ - if (bin->numchildren == 0) + if (bin->numchildren == 0 && GST_ELEMENT_SCHED (bin) != NULL) gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL); } @@ -236,62 +351,47 @@ gst_bin_change_state (GstElement *element) GstElement *child; GstElementStateReturn ret; - GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME (element)); +// GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME (element)); g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE); bin = GST_BIN (element); -// GST_DEBUG (0,"currently %d(%s), %d(%s) pending\n",GST_STATE (element), -// _gst_print_statename (GST_STATE (element)), GST_STATE_PENDING (element), -// _gst_print_statename (GST_STATE_PENDING (element))); +// GST_DEBUG (GST_CAT_STATES,"currently %d(%s), %d(%s) pending\n",GST_STATE (element), +// gst_element_statename (GST_STATE (element)), GST_STATE_PENDING (element), +// gst_element_statename (GST_STATE_PENDING (element))); - GST_INFO_ELEMENT (GST_CAT_STATES, element, "changing bin's state from %s to %s", - _gst_print_statename (GST_STATE (element)), - _gst_print_statename (GST_STATE_PENDING (element))); + GST_INFO_ELEMENT (GST_CAT_STATES, element, "changing childrens' state from %s to %s", + gst_element_statename (GST_STATE (element)), + gst_element_statename (GST_STATE_PENDING (element))); // g_return_val_if_fail(bin->numchildren != 0, GST_STATE_FAILURE); - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_NULL_TO_READY: - { - GstObject *parent; - - parent = gst_object_get_parent (GST_OBJECT (element)); - - if (!parent || !GST_IS_BIN (parent)) - gst_bin_create_plan (bin); - else - GST_DEBUG (0,"not creating plan for '%s'\n",GST_ELEMENT_NAME (bin)); - - break; - } - case GST_STATE_READY_TO_NULL: - GST_FLAG_UNSET (bin, GST_BIN_FLAG_MANAGER); - default: - break; - } // g_print("-->\n"); children = bin->children; while (children) { child = GST_ELEMENT (children->data); - GST_DEBUG (0,"setting state on '%s'\n",GST_ELEMENT_NAME (child)); +// GST_DEBUG (GST_CAT_STATES,"setting state on '%s'\n",GST_ELEMENT_NAME (child)); switch (gst_element_set_state (child, GST_STATE_PENDING (element))) { case GST_STATE_FAILURE: GST_STATE_PENDING (element) = GST_STATE_NONE_PENDING; - GST_DEBUG (0,"child '%s' failed to go to state %d(%s)\n", GST_ELEMENT_NAME (child), - GST_STATE_PENDING (element), _gst_print_statename (GST_STATE_PENDING (element))); + GST_DEBUG (GST_CAT_STATES,"child '%s' failed to go to state %d(%s)\n", GST_ELEMENT_NAME (child), + GST_STATE_PENDING (element), gst_element_statename (GST_STATE_PENDING (element))); return GST_STATE_FAILURE; break; case GST_STATE_ASYNC: - GST_DEBUG (0,"child '%s' is changing state asynchronously\n", GST_ELEMENT_NAME (child)); + GST_DEBUG (GST_CAT_STATES,"child '%s' is changing state asynchronously\n", GST_ELEMENT_NAME (child)); break; } // g_print("\n"); children = g_list_next (children); } -// g_print("<-- \"%s\"\n",gst_object_get_name(GST_OBJECT(bin))); +// g_print("<-- \"%s\"\n",GST_OBJECT_NAME(bin)); + + GST_INFO_ELEMENT (GST_CAT_STATES, element, "done changing bin's state from %s to %s", + gst_element_statename (GST_STATE (element)), + gst_element_statename (GST_STATE_PENDING (element))); ret = gst_bin_change_state_norecurse (bin); return ret; @@ -301,10 +401,10 @@ gst_bin_change_state (GstElement *element) static GstElementStateReturn gst_bin_change_state_norecurse (GstBin *bin) { - - if (GST_ELEMENT_CLASS (parent_class)->change_state) + if (GST_ELEMENT_CLASS (parent_class)->change_state) { + GST_DEBUG_ELEMENT (GST_CAT_STATES, bin, "setting bin's own state\n"); return GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT (bin)); - else + } else return GST_STATE_FAILURE; } @@ -317,7 +417,7 @@ gst_bin_change_state_type(GstBin *bin, GstElement *child; // g_print("gst_bin_change_state_type(\"%s\",%d,%d);\n", -// gst_object_get_name(GST_OBJECT(bin)),state,type); +// GST_OBJECT_NAME(bin))),state,type); g_return_val_if_fail (GST_IS_BIN (bin), FALSE); g_return_val_if_fail (bin->numchildren != 0, FALSE); @@ -359,7 +459,7 @@ gst_bin_set_state_type (GstBin *bin, { GstBinClass *oclass; - GST_DEBUG (0,"gst_bin_set_state_type(\"%s\",%d,%d)\n", + GST_DEBUG (GST_CAT_STATES,"gst_bin_set_state_type(\"%s\",%d,%d)\n", GST_ELEMENT_NAME (bin), state,type); g_return_val_if_fail (bin != NULL, FALSE); @@ -376,19 +476,30 @@ static void gst_bin_real_destroy (GtkObject *object) { GstBin *bin = GST_BIN (object); - GList *children; + GList *children, *orig; GstElement *child; - GST_DEBUG (0,"in gst_bin_real_destroy()\n"); + GST_DEBUG (GST_CAT_REFCOUNTING,"destroy()\n"); - children = bin->children; - while (children) { - child = GST_ELEMENT (children->data); - gst_element_destroy (child); - children = g_list_next (children); + if (bin->children) { + orig = children = g_list_copy (bin->children); + while (children) { + child = GST_ELEMENT (children->data); + //gst_object_unref (GST_OBJECT (child)); + //gst_object_unparent (GST_OBJECT (child)); + gst_bin_remove (bin, child); + children = g_list_next (children); + } + g_list_free (orig); + g_list_free (bin->children); } + bin->children = NULL; + bin->numchildren = 0; + + g_cond_free (bin->eoscond); - g_list_free (bin->children); + if (GTK_OBJECT_CLASS (parent_class)->destroy) + GTK_OBJECT_CLASS (parent_class)->destroy (object); } /** @@ -416,7 +527,7 @@ gst_bin_get_by_name (GstBin *bin, children = bin->children; while (children) { child = GST_ELEMENT (children->data); - if (!strcmp (gst_object_get_name (GST_OBJECT (child)),name)) + if (!strcmp (GST_OBJECT_NAME(child),name)) return child; if (GST_IS_BIN (child)) { GstElement *res = gst_bin_get_by_name (GST_BIN (child), name); @@ -521,7 +632,7 @@ gst_bin_restore_thyself (GstObject *object, childlist = field->xmlChildrenNode; while (childlist) { if (!strcmp (childlist->name, "element")) { - GstElement *element = gst_element_load_thyself (childlist, GST_OBJECT (bin)); + GstElement *element = gst_element_restore_thyself (childlist, GST_OBJECT (bin)); gst_bin_add (bin, element); } @@ -569,23 +680,6 @@ gst_bin_iterate (GstBin *bin) return eos; } -/** - * gst_bin_create_plan: - * @bin: #GstBin to create the plan for - * - * Let the bin figure out how to handle its children. - */ -void -gst_bin_create_plan (GstBin *bin) -{ - GstBinClass *oclass; - - oclass = GST_BIN_CLASS (GTK_OBJECT (bin)->klass); - - if (oclass->create_plan) - (oclass->create_plan) (bin); -} - /* out internal element fired EOS, we decrement the number of pending EOS childs */ static void gst_bin_received_eos (GstElement *element, GstBin *bin) @@ -601,290 +695,22 @@ gst_bin_received_eos (GstElement *element, GstBin *bin) GST_UNLOCK (bin); } -/** - * gst_bin_schedule: - * @bin: #GstBin to schedule - * - * Let the bin figure out how to handle its children. - */ -void -gst_bin_schedule (GstBin *bin) -{ - GstBinClass *oclass; - - oclass = GST_BIN_CLASS (GTK_OBJECT (bin)->klass); - - if (oclass->schedule) - (oclass->schedule) (bin); -} - typedef struct { gulong offset; gulong size; } region_struct; -static void -gst_bin_create_plan_func (GstBin *bin) -{ - GstElement *manager; - GList *elements; - GstElement *element; -#ifdef GST_DEBUG_ENABLED - const gchar *elementname; -#endif - GSList *pending = NULL; - GstBin *pending_bin; - - GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME (bin)); - - GST_INFO_ELEMENT (GST_CAT_PLANNING, bin, "creating plan"); - - // first figure out which element is the manager of this and all child elements - // if we're a managing bin ourselves, that'd be us - if (GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER)) { - manager = GST_ELEMENT (bin); - GST_DEBUG (0,"setting manager to self\n"); - // otherwise, it's what our parent says it is - } else { - manager = gst_element_get_manager (GST_ELEMENT (bin)); - if (!manager) { - GST_DEBUG (0,"manager not set for element \"%s\" assuming manager is self\n", GST_ELEMENT_NAME (bin)); - manager = GST_ELEMENT (bin); - GST_FLAG_SET (bin, GST_BIN_FLAG_MANAGER); - } - GST_DEBUG (0,"setting manager to \"%s\"\n", GST_ELEMENT_NAME (manager)); - } - gst_element_set_manager (GST_ELEMENT (bin), manager); - - // perform the first recursive pass of plan generation - // we set the manager of every element but those who manage themselves - // the need for cothreads is also determined recursively - GST_DEBUG (0,"performing first-phase recursion\n"); - bin->need_cothreads = bin->use_cothreads; - if (bin->need_cothreads) - GST_DEBUG (0,"requiring cothreads because we're forced to\n"); - - elements = bin->children; - while (elements) { - element = GST_ELEMENT (elements->data); - elements = g_list_next (elements); -#ifdef GST_DEBUG_ENABLED - elementname = GST_ELEMENT_NAME (element); -#endif - GST_DEBUG (0,"have element \"%s\"\n",elementname); - - // first set their manager - GST_DEBUG (0,"setting manager of \"%s\" to \"%s\"\n",elementname,GST_ELEMENT_NAME (manager)); - gst_element_set_manager (element, manager); - - // we do recursion and such for Bins - if (GST_IS_BIN (element)) { - // recurse into the child Bin - GST_DEBUG (0,"recursing into child Bin \"%s\" with manager \"%s\"\n",elementname, - GST_ELEMENT_NAME (element->manager)); - gst_bin_create_plan (GST_BIN (element)); - GST_DEBUG (0,"after recurse got manager \"%s\"\n", - GST_ELEMENT_NAME (element->manager)); - // check to see if it needs cothreads and isn't self-managing - if (((GST_BIN (element))->need_cothreads) && !GST_FLAG_IS_SET(element,GST_BIN_FLAG_MANAGER)) { - GST_DEBUG (0,"requiring cothreads because child bin \"%s\" does\n",elementname); - bin->need_cothreads = TRUE; - } - } else { - // then we need to determine whether they need cothreads - // if it's a loop-based element, use cothreads - if (element->loopfunc != NULL) { - GST_DEBUG (0,"requiring cothreads because \"%s\" is a loop-based element\n",elementname); - GST_FLAG_SET (element, GST_ELEMENT_USE_COTHREAD); - // if it's a 'complex' element, use cothreads - } else if (GST_FLAG_IS_SET (element, GST_ELEMENT_COMPLEX)) { - GST_DEBUG (0,"requiring cothreads because \"%s\" is complex\n",elementname); - GST_FLAG_SET (element, GST_ELEMENT_USE_COTHREAD); - // if the element has more than one sink pad, use cothreads - } else if (element->numsinkpads > 1) { - GST_DEBUG (0,"requiring cothreads because \"%s\" has more than one sink pad\n",elementname); - GST_FLAG_SET (element, GST_ELEMENT_USE_COTHREAD); - } - if (GST_FLAG_IS_SET (element, GST_ELEMENT_USE_COTHREAD)) - bin->need_cothreads = TRUE; - } - } - - - // if we're not a manager thread, we're done. - if (!GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER)) { - GST_DEBUG_LEAVE("(\"%s\")",GST_ELEMENT_NAME (bin)); - return; - } - - // clear previous plan state - g_list_free (bin->managed_elements); - bin->managed_elements = NULL; - bin->num_managed_elements = 0; - - // find all the managed children - // here we pull off the trick of walking an entire arbitrary tree without recursion - GST_DEBUG (0,"attempting to find all the elements to manage\n"); - pending = g_slist_prepend (pending, bin); - do { - // retrieve the top of the stack and pop it - pending_bin = GST_BIN (pending->data); - pending = g_slist_remove (pending, pending_bin); - - // walk the list of elements, find bins, and do stuff - GST_DEBUG (0,"checking Bin \"%s\" for managed elements\n", - GST_ELEMENT_NAME (pending_bin)); - elements = pending_bin->children; - while (elements) { - element = GST_ELEMENT (elements->data); - elements = g_list_next (elements); -#ifdef GST_DEBUG_ENABLED - elementname = GST_ELEMENT_NAME (element); -#endif - - // if it's ours, add it to the list - if (element->manager == GST_ELEMENT(bin)) { - // if it's a Bin, add it to the list of Bins to check - if (GST_IS_BIN (element)) { - GST_DEBUG (0,"flattened recurse into \"%s\"\n",elementname); - pending = g_slist_prepend (pending, element); - - // otherwise add it to the list of elements - } else { - GST_DEBUG (0,"found element \"%s\" that I manage\n",elementname); - bin->managed_elements = g_list_prepend (bin->managed_elements, element); - bin->num_managed_elements++; - } - } - // else it's not ours and we need to wait for EOS notifications - else { - GST_DEBUG (0,"setting up EOS signal from \"%s\" to \"%s\"\n", elementname, - gst_element_get_name (GST_ELEMENT(bin)->manager)); - gtk_signal_connect (GTK_OBJECT (element), "eos", gst_bin_received_eos, GST_ELEMENT(bin)->manager); - bin->eos_providers = g_list_prepend (bin->eos_providers, element); - bin->num_eos_providers++; - } - } - } while (pending); - - GST_DEBUG (0,"have %d elements to manage, implementing plan\n",bin->num_managed_elements); - - gst_bin_schedule(bin); - -// g_print ("gstbin \"%s\", eos providers:%d\n", -// GST_ELEMENT_NAME (bin), -// bin->num_eos_providers); - - GST_DEBUG_LEAVE("(\"%s\")",GST_ELEMENT_NAME (bin)); -} - static gboolean gst_bin_iterate_func (GstBin *bin) { - GList *chains; - _GstBinChain *chain; - GList *entries; - GstElement *entry; - GList *pads; - GstPad *pad; - GstBuffer *buf = NULL; - gint num_scheduled = 0; - gboolean eos = FALSE; - - GST_DEBUG_ENTER("(\"%s\")", GST_ELEMENT_NAME (bin)); - - g_return_val_if_fail (bin != NULL, TRUE); - g_return_val_if_fail (GST_IS_BIN (bin), TRUE); - g_return_val_if_fail (GST_STATE (bin) == GST_STATE_PLAYING, TRUE); - - // step through all the chains - chains = bin->chains; - while (chains) { - chain = (_GstBinChain *)(chains->data); - chains = g_list_next (chains); - - if (!chain->need_scheduling) continue; - - if (chain->need_cothreads) { - GList *entries; - - // all we really have to do is switch to the first child - // FIXME this should be lots more intelligent about where to start - GST_DEBUG (0,"starting iteration via cothreads\n"); - - entries = chain->elements; - entry = NULL; - - // find an element with a threadstate to start with - while (entries) { - entry = GST_ELEMENT (entries->data); - - if (entry->threadstate) - break; - entries = g_list_next (entries); - } - // if we couldn't find one, bail out - if (entries == NULL) - GST_ERROR(GST_ELEMENT(bin),"no cothreaded elements found!"); - - GST_FLAG_SET (entry, GST_ELEMENT_COTHREAD_STOPPING); - GST_DEBUG (0,"set COTHREAD_STOPPING flag on \"%s\"(@%p)\n", - GST_ELEMENT_NAME (entry),entry); - cothread_switch (entry->threadstate); - - } else { - GST_DEBUG (0,"starting iteration via chain-functions\n"); - - entries = chain->entries; - - g_assert (entries != NULL); - - while (entries) { - entry = GST_ELEMENT (entries->data); - entries = g_list_next (entries); - - GST_DEBUG (0,"have entry \"%s\"\n",GST_ELEMENT_NAME (entry)); - - if (GST_IS_BIN (entry)) { - gst_bin_iterate (GST_BIN (entry)); - } else { - pads = entry->pads; - while (pads) { - pad = GST_PAD (pads->data); - if (GST_RPAD_DIRECTION(pad) == GST_PAD_SRC) { - GST_DEBUG (0,"calling getfunc of %s:%s\n",GST_DEBUG_PAD_NAME(pad)); - if (GST_REAL_PAD(pad)->getfunc == NULL) - fprintf(stderr, "error, no getfunc in \"%s\"\n", GST_ELEMENT_NAME (entry)); - else - buf = (GST_REAL_PAD(pad)->getfunc)(pad); - if (buf) gst_pad_push(pad,buf); - } - pads = g_list_next (pads); - } - } - } - } - num_scheduled++; - } - - // check if nothing was scheduled that was ours.. - if (!num_scheduled) { - // are there any other elements that are still busy? - if (bin->num_eos_providers) { - GST_LOCK (bin); - GST_DEBUG (0,"waiting for eos providers\n"); - g_cond_wait (bin->eoscond, GST_GET_LOCK(bin)); - GST_DEBUG (0,"num eos providers %d\n", bin->num_eos_providers); - GST_UNLOCK (bin); - } - else { - gst_element_signal_eos (GST_ELEMENT (bin)); - eos = TRUE; - } + // only iterate if this is the manager bin + if (GST_ELEMENT_SCHED(bin)->parent == GST_ELEMENT (bin)) { + return GST_SCHEDULE_ITERATE(GST_ELEMENT_SCHED(bin)); + } else { + GST_DEBUG (GST_CAT_SCHEDULING, "this bin can't be iterated on!\n"); } - GST_DEBUG_LEAVE("(%s)", GST_ELEMENT_NAME (bin)); - return !eos; + return FALSE; } diff --git a/gst/gstbin.h b/gst/gstbin.h index 23b826fc6..da5d8aa83 100644 --- a/gst/gstbin.h +++ b/gst/gstbin.h @@ -47,6 +47,8 @@ extern GstElementDetails gst_bin_details; typedef enum { /* this bin is a manager of child elements, i.e. a pipeline or thread */ GST_BIN_FLAG_MANAGER = GST_ELEMENT_FLAG_LAST, + /* this bin is actually a meta-bin, and may need to be scheduled */ + GST_BIN_SELF_SCHEDULABLE, /* we prefer to have cothreads when its an option, over chain-based */ GST_BIN_FLAG_PREFER_COTHREADS, @@ -55,8 +57,8 @@ typedef enum { GST_BIN_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 4, } GstBinFlags; -typedef struct _GstBin GstBin; -typedef struct _GstBinClass GstBinClass; +//typedef struct _GstBin GstBin; +//typedef struct _GstBinClass GstBinClass; typedef struct __GstBinChain _GstBinChain; struct _GstBin { @@ -94,9 +96,6 @@ struct _GstBinClass { gboolean (*change_state_type) (GstBin *bin, GstElementState state, GtkType type); - /* create a plan for the execution of the bin */ - void (*create_plan) (GstBin *bin); - void (*schedule) (GstBin *bin); /* run a full iteration of operation */ gboolean (*iterate) (GstBin *bin); }; @@ -116,6 +115,10 @@ GtkType gst_bin_get_type (void); GstElement* gst_bin_new (const gchar *name); #define gst_bin_destroy(bin) gst_object_destroy(GST_OBJECT(bin)) +void gst_bin_set_element_manager (GstElement *element, GstElement *manager); +void gst_bin_add_managed_element (GstBin *bin, GstElement *element); +void gst_bin_remove_managed_element (GstBin *bin, GstElement *element); + /* add and remove elements from the bin */ void gst_bin_add (GstBin *bin, GstElement *element); @@ -129,8 +132,6 @@ GstElement* gst_bin_get_by_name_recurse_up (GstBin *bin, const gchar *name); GList* gst_bin_get_list (GstBin *bin); -void gst_bin_create_plan (GstBin *bin); -void gst_bin_schedule (GstBin *bin); gboolean gst_bin_set_state_type (GstBin *bin, GstElementState state, GtkType type); diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 23f68e73a..7a44084ca 100644 --- a/gst/gstbuffer.c +++ b/gst/gstbuffer.c @@ -448,7 +448,7 @@ gst_buffer_copy (GstBuffer *buffer) // copy the absolute size newbuf->size = buffer->size; // allocate space for the copy - newbuf->data = (guchar *)g_malloc (buffer->data); + newbuf->data = (guchar *)g_malloc (buffer->size); // copy the data straight across memcpy(newbuf,buffer->data,buffer->size); // the new maxsize is the same as the size, since we just malloc'd it diff --git a/gst/gstcaps.c b/gst/gstcaps.c index df122b58b..0ef1c5543 100644 --- a/gst/gstcaps.c +++ b/gst/gstcaps.c @@ -476,7 +476,7 @@ static gboolean gst_caps_check_compatibility_func (GstCaps *fromcaps, GstCaps *tocaps) { if (fromcaps->id != tocaps->id) { - GST_DEBUG (0,"gstcaps: mime types differ (%s to %s)\n", + GST_DEBUG (GST_CAT_CAPS,"mime types differ (%s to %s)\n", gst_type_find_by_id (fromcaps->id)->mime, gst_type_find_by_id (tocaps->id)->mime); return FALSE; @@ -487,13 +487,13 @@ gst_caps_check_compatibility_func (GstCaps *fromcaps, GstCaps *tocaps) return gst_props_check_compatibility (fromcaps->properties, tocaps->properties); } else { - GST_DEBUG (0,"gstcaps: no source caps\n"); + GST_DEBUG (GST_CAT_CAPS,"no source caps\n"); return FALSE; } } else { // assume it accepts everything - GST_DEBUG (0,"gstcaps: no caps\n"); + GST_DEBUG (GST_CAT_CAPS,"no caps\n"); return TRUE; } } @@ -512,17 +512,17 @@ gst_caps_check_compatibility (GstCaps *fromcaps, GstCaps *tocaps) { if (fromcaps == NULL) { if (tocaps == NULL) { - GST_DEBUG (0,"gstcaps: no caps\n"); + GST_DEBUG (GST_CAT_CAPS,"no caps\n"); return TRUE; } else { - GST_DEBUG (0,"gstcaps: no src but destination caps\n"); + GST_DEBUG (GST_CAT_CAPS,"no source but destination caps\n"); return FALSE; } } else { if (tocaps == NULL) { - GST_DEBUG (0,"gstcaps: src caps and no dest caps\n"); + GST_DEBUG (GST_CAT_CAPS,"source caps and no destination caps\n"); return TRUE; } } diff --git a/gst/gstclock.c b/gst/gstclock.c index 8040baf33..0c3caad5b 100644 --- a/gst/gstclock.c +++ b/gst/gstclock.c @@ -71,7 +71,7 @@ void gst_clock_register (GstClock *clock, GstObject *obj) { if ((GST_ELEMENT(obj))->numsrcpads == 0) { - GST_DEBUG (0,"gst_clock: setting registered sink object 0x%p\n", obj); + GST_DEBUG (GST_CAT_CLOCK,"gst_clock: setting registered sink object 0x%p\n", obj); clock->sinkobjects = g_list_append (clock->sinkobjects, obj); clock->num++; } @@ -88,7 +88,8 @@ gst_clock_set (GstClock *clock, GstClockTime time) g_mutex_lock (clock->lock); clock->start_time = now - time; g_mutex_unlock (clock->lock); - GST_DEBUG (0,"gst_clock: setting clock to %llu %llu %llu\n", time, now, clock->start_time); + GST_DEBUG (GST_CAT_CLOCK,"gst_clock: setting clock to %llu %llu %llu\n", + time, now, clock->start_time); } GstClockTimeDiff @@ -115,7 +116,7 @@ gst_clock_reset (GstClock *clock) clock->start_time = ((guint64)tfnow.tv_sec)*1000000LL+tfnow.tv_usec; clock->current_time = clock->start_time; clock->adjust = 0LL; - GST_DEBUG (0,"gst_clock: setting start clock %llu\n", clock->start_time); + GST_DEBUG (GST_CAT_CLOCK,"gst_clock: setting start clock %llu\n", clock->start_time); g_mutex_unlock (clock->lock); } @@ -133,7 +134,8 @@ gst_clock_wait (GstClock *clock, GstClockTime time, GstObject *obj) diff = GST_CLOCK_DIFF (time, now); // if we are not behind wait a bit - GST_DEBUG (0,"gst_clock: %s waiting for time %08llu %08llu %08lld\n", GST_OBJECT_NAME (obj), time, now, diff); + GST_DEBUG (GST_CAT_CLOCK,"gst_clock: %s waiting for time %08llu %08llu %08lld\n", + GST_OBJECT_NAME (obj), time, now, diff); g_mutex_unlock (clock->lock); if (diff > 10000 ) { @@ -143,8 +145,9 @@ gst_clock_wait (GstClock *clock, GstClockTime time, GstObject *obj) if (!tfnow.tv_sec) { select(0, NULL, NULL, NULL, &tfnow); } - else GST_DEBUG (0,"gst_clock: %s waiting %u %llu %llu %llu seconds\n", GST_OBJECT_NAME (obj), - (int)tfnow.tv_sec, now, diff, time); + else GST_DEBUG (GST_CAT_CLOCK,"gst_clock: %s waiting %u %llu %llu %llu seconds\n", + GST_OBJECT_NAME (obj), (int)tfnow.tv_sec, now, diff, time); } - GST_DEBUG (0,"gst_clock: %s waiting for time %08llu %08llu %08lld done \n", GST_OBJECT_NAME (obj), time, now, diff); + GST_DEBUG (GST_CAT_CLOCK,"gst_clock: %s waiting for time %08llu %08llu %08lld done \n", + GST_OBJECT_NAME (obj), time, now, diff); } diff --git a/gst/gstelement.c b/gst/gstelement.c index 7241b406c..37e5e0318 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -26,14 +26,16 @@ #include "gstelement.h" #include "gstextratypes.h" #include "gstbin.h" +#include "gstscheduler.h" #include "gstutils.h" - /* Element signals and args */ enum { STATE_CHANGE, NEW_PAD, + PAD_REMOVED, NEW_GHOST_PAD, + GHOST_PAD_REMOVED, ERROR, EOS, LAST_SIGNAL @@ -48,6 +50,10 @@ enum { static void gst_element_class_init (GstElementClass *klass); static void gst_element_init (GstElement *element); +static void gst_element_set_arg (GtkObject *object, GtkArg *arg, guint id); +static void gst_element_get_arg (GtkObject *object, GtkArg *arg, guint id); + +static void gst_element_shutdown (GtkObject *object); static void gst_element_real_destroy (GtkObject *object); static GstElementStateReturn gst_element_change_state (GstElement *element); @@ -67,8 +73,8 @@ GtkType gst_element_get_type(void) { sizeof(GstElementClass), (GtkClassInitFunc)gst_element_class_init, (GtkObjectInitFunc)gst_element_init, - (GtkArgSetFunc)NULL, - (GtkArgGetFunc)NULL, + (GtkArgSetFunc)gst_element_set_arg, + (GtkArgGetFunc)gst_element_get_arg, (GtkClassInitFunc)NULL, }; element_type = gtk_type_unique(GST_TYPE_OBJECT,&element_info); @@ -97,11 +103,21 @@ gst_element_class_init (GstElementClass *klass) GTK_SIGNAL_OFFSET (GstElementClass, new_pad), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GST_TYPE_PAD); + gst_element_signals[PAD_REMOVED] = + gtk_signal_new ("pad_removed", GTK_RUN_LAST, gtkobject_class->type, + GTK_SIGNAL_OFFSET (GstElementClass, pad_removed), + gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, + GST_TYPE_PAD); gst_element_signals[NEW_GHOST_PAD] = gtk_signal_new ("new_ghost_pad", GTK_RUN_LAST, gtkobject_class->type, GTK_SIGNAL_OFFSET (GstElementClass, new_ghost_pad), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GST_TYPE_PAD); + gst_element_signals[GHOST_PAD_REMOVED] = + gtk_signal_new ("ghost_pad_removed", GTK_RUN_LAST, gtkobject_class->type, + GTK_SIGNAL_OFFSET (GstElementClass, ghost_pad_removed), + gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, + GST_TYPE_PAD); gst_element_signals[ERROR] = gtk_signal_new ("error", GTK_RUN_LAST, gtkobject_class->type, GTK_SIGNAL_OFFSET (GstElementClass, error), @@ -115,11 +131,15 @@ gst_element_class_init (GstElementClass *klass) gtk_object_class_add_signals (gtkobject_class, gst_element_signals, LAST_SIGNAL); - gtkobject_class->destroy = gst_element_real_destroy; + gtkobject_class->set_arg = GST_DEBUG_FUNCPTR(gst_element_set_arg); + gtkobject_class->get_arg = GST_DEBUG_FUNCPTR(gst_element_get_arg); + gtkobject_class->shutdown = GST_DEBUG_FUNCPTR(gst_element_shutdown); + gtkobject_class->destroy = GST_DEBUG_FUNCPTR(gst_element_real_destroy); - gstobject_class->save_thyself = gst_element_save_thyself; + gstobject_class->save_thyself = GST_DEBUG_FUNCPTR(gst_element_save_thyself); + gstobject_class->restore_thyself = GST_DEBUG_FUNCPTR(gst_element_restore_thyself); - klass->change_state = gst_element_change_state; + klass->change_state = GST_DEBUG_FUNCPTR(gst_element_change_state); klass->elementfactory = NULL; } @@ -134,8 +154,38 @@ gst_element_init (GstElement *element) element->pads = NULL; element->loopfunc = NULL; element->threadstate = NULL; + element->sched = NULL; } + +static void +gst_element_set_arg (GtkObject *object, GtkArg *arg, guint id) +{ + GstElementClass *oclass = GST_ELEMENT_CLASS (object->klass); + + GST_SCHEDULE_LOCK_ELEMENT ( GST_ELEMENT_SCHED(object), GST_ELEMENT(object) ); + + if (oclass->set_arg) + (oclass->set_arg)(object,arg,id); + + GST_SCHEDULE_UNLOCK_ELEMENT ( GST_ELEMENT_SCHED(object), GST_ELEMENT(object) ); +} + + +static void +gst_element_get_arg (GtkObject *object, GtkArg *arg, guint id) +{ + GstElementClass *oclass = GST_ELEMENT_CLASS (object->klass); + + GST_SCHEDULE_LOCK_ELEMENT (GST_ELEMENT_SCHED(object), GST_ELEMENT(object) ); + + if (oclass->get_arg) + (oclass->get_arg)(object,arg,id); + + GST_SCHEDULE_UNLOCK_ELEMENT (GST_ELEMENT_SCHED(object), GST_ELEMENT(object) ); +} + + /** * gst_element_new: * @@ -237,9 +287,15 @@ gst_element_add_pad (GstElement *element, GstPad *pad) g_return_if_fail (pad != NULL); g_return_if_fail (GST_IS_PAD (pad)); + // first check to make sure the pad's parent is already set + g_return_if_fail (GST_PAD_PARENT (pad) == NULL); + + // then check to see if there's already a pad by that name here + g_return_if_fail (gst_object_check_uniqueness (element->pads, GST_PAD_NAME(pad)) == TRUE); + /* set the pad's parent */ - GST_DEBUG (0,"setting parent of pad '%s'(%p) to '%s'(%p)\n", - GST_PAD_NAME (pad), pad, GST_ELEMENT_NAME (element), element); + GST_DEBUG (GST_CAT_ELEMENT_PADS,"setting parent of pad '%s' to '%s'\n", + GST_PAD_NAME (pad), GST_ELEMENT_NAME (element)); gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (element)); /* add it to the list */ @@ -254,6 +310,36 @@ gst_element_add_pad (GstElement *element, GstPad *pad) gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[NEW_PAD], pad); } +/** + * gst_element_remove_pad: + * @element: element to remove pad from + * @pad: pad to remove + * + * Remove a pad (connection point) from the element, + */ +void +gst_element_remove_pad (GstElement *element, GstPad *pad) +{ + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_ELEMENT (element)); + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + + g_return_if_fail (GST_PAD_PARENT (pad) == element); + + /* add it to the list */ + element->pads = g_list_remove (element->pads, pad); + element->numpads--; + if (gst_pad_get_direction (pad) == GST_PAD_SRC) + element->numsrcpads--; + else + element->numsinkpads--; + + gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[PAD_REMOVED], pad); + + gst_object_unparent (GST_OBJECT (pad)); +} + /** * gst_element_add_ghost_pad: * @element: element to add ghost pad to @@ -273,17 +359,22 @@ gst_element_add_ghost_pad (GstElement *element, GstPad *pad, gchar *name) g_return_if_fail (pad != NULL); g_return_if_fail (GST_IS_PAD (pad)); - GST_DEBUG(0,"creating new ghost pad called %s, from pad %s:%s\n",name,GST_DEBUG_PAD_NAME(pad)); + // then check to see if there's already a pad by that name here + g_return_if_fail (gst_object_check_uniqueness (element->pads, name) == TRUE); + + GST_DEBUG(GST_CAT_ELEMENT_PADS,"creating new ghost pad called %s, from pad %s:%s\n", + name,GST_DEBUG_PAD_NAME(pad)); ghostpad = gst_ghost_pad_new (name, pad); /* add it to the list */ - GST_DEBUG(0,"adding ghost pad %s to element %s\n", name, GST_ELEMENT_NAME (element)); + GST_DEBUG(GST_CAT_ELEMENT_PADS,"adding ghost pad %s to element %s\n", + name, GST_ELEMENT_NAME (element)); element->pads = g_list_append (element->pads, ghostpad); element->numpads++; // set the parent of the ghostpad gst_object_set_parent (GST_OBJECT (ghostpad), GST_OBJECT (element)); - GST_DEBUG(0,"added ghostpad %s:%s\n",GST_DEBUG_PAD_NAME(ghostpad)); + GST_DEBUG(GST_CAT_ELEMENT_PADS,"added ghostpad %s:%s\n",GST_DEBUG_PAD_NAME(ghostpad)); /* emit the NEW_GHOST_PAD signal */ gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[NEW_GHOST_PAD], ghostpad); @@ -331,21 +422,18 @@ gst_element_get_pad (GstElement *element, const gchar *name) if (!element->numpads) return NULL; - GST_DEBUG(GST_CAT_ELEMENT_PADS,"searching for pad '%s' in element %s\n", - name, GST_ELEMENT_NAME (element)); - // look through the list, matching by name walk = element->pads; while (walk) { GstPad *pad = GST_PAD(walk->data); - if (!strcmp (gst_object_get_name (GST_OBJECT(pad)), name)) { - GST_DEBUG(GST_CAT_ELEMENT_PADS,"found pad '%s'\n",name); + if (!strcmp (GST_PAD_NAME(pad), name)) { + GST_INFO(GST_CAT_ELEMENT_PADS,"found pad %s:%s",GST_DEBUG_PAD_NAME(pad)); return pad; } walk = g_list_next (walk); } - GST_DEBUG(GST_CAT_ELEMENT_PADS,"no such pad '%s'\n",name); + GST_INFO(GST_CAT_ELEMENT_PADS,"no such pad '%s' in element \"%s\"",name,GST_ELEMENT_NAME(element)); return NULL; } @@ -440,7 +528,7 @@ gst_element_get_padtemplate_by_compatible (GstElement *element, GstPadTemplate * GstPadTemplate *newtempl = NULL; GList *padlist; - GST_DEBUG(0,"gst_element_get_padtemplate_by_compatible()\n"); + GST_DEBUG(GST_CAT_ELEMENT_PADS,"gst_element_get_padtemplate_by_compatible()\n"); g_return_val_if_fail (element != NULL, NULL); g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); @@ -457,19 +545,19 @@ gst_element_get_padtemplate_by_compatible (GstElement *element, GstPadTemplate * // Check direction (must be opposite) // Check caps - GST_DEBUG(0,"checking direction and caps\n"); + GST_DEBUG(GST_CAT_CAPS,"checking direction and caps\n"); if (padtempl->direction == GST_PAD_SRC && compattempl->direction == GST_PAD_SINK) { - GST_DEBUG(0,"compatible direction: found src pad template\n"); + GST_DEBUG(GST_CAT_CAPS,"compatible direction: found src pad template\n"); compat = gst_caps_check_compatibility(GST_PADTEMPLATE_CAPS (padtempl), GST_PADTEMPLATE_CAPS (compattempl)); - GST_DEBUG(0,"caps are %scompatible\n", (compat?"":"not ")); + GST_DEBUG(GST_CAT_CAPS,"caps are %scompatible\n", (compat?"":"not ")); } else if (padtempl->direction == GST_PAD_SINK && compattempl->direction == GST_PAD_SRC) { - GST_DEBUG(0,"compatible direction: found sink pad template\n"); + GST_DEBUG(GST_CAT_CAPS,"compatible direction: found sink pad template\n"); compat = gst_caps_check_compatibility(GST_PADTEMPLATE_CAPS (compattempl), GST_PADTEMPLATE_CAPS (padtempl)); - GST_DEBUG(0,"caps are %scompatible\n", (compat?"":"not ")); + GST_DEBUG(GST_CAT_CAPS,"caps are %scompatible\n", (compat?"":"not ")); } if (compat) { @@ -659,7 +747,7 @@ gst_element_disconnect (GstElement *src, const gchar *srcpadname, void gst_element_error (GstElement *element, const gchar *error) { - g_error("GstElement: error in element '%s': %s\n", gst_object_get_name (GST_OBJECT (element)), error); + g_error("GstElement: error in element '%s': %s\n", GST_ELEMENT_NAME(element), error); /* FIXME: this is not finished!!! */ @@ -689,6 +777,11 @@ gst_element_set_state (GstElement *element, GstElementState state) g_return_val_if_fail (element != NULL, GST_STATE_FAILURE); g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE); + g_return_val_if_fail (element->sched != NULL, GST_STATE_FAILURE); + + GST_DEBUG_ELEMENT (GST_CAT_STATES,element, "setting state from %s to %s\n", + gst_element_statename(GST_STATE(element)), + gst_element_statename(state)); /* start with the current state */ curpending = GST_STATE(element); @@ -702,6 +795,9 @@ gst_element_set_state (GstElement *element, GstElementState state) /* set the pending state variable */ // FIXME: should probably check to see that we don't already have one GST_STATE_PENDING (element) = curpending; + if (curpending != state) + GST_DEBUG_ELEMENT (GST_CAT_STATES,element,"intermediate: setting state to %s\n", + gst_element_statename(curpending)); /* call the state change function so it can set the state */ oclass = GST_ELEMENT_CLASS (GTK_OBJECT (element)->klass); @@ -711,7 +807,7 @@ gst_element_set_state (GstElement *element, GstElementState state) /* if that outright didn't work, we need to bail right away */ /* NOTE: this will bail on ASYNC as well! */ if (return_val == GST_STATE_FAILURE) { -// GST_DEBUG (0,"have async return from '%s'\n",GST_ELEMENT_NAME (element)); + GST_DEBUG_ELEMENT (GST_CAT_STATES,element,"have failed change_state return\n"); return return_val; } } @@ -741,6 +837,7 @@ gst_element_get_factory (GstElement *element) return oclass->elementfactory; } + /** * gst_element_change_state: * @element: element to change state of @@ -757,15 +854,48 @@ gst_element_change_state (GstElement *element) g_return_val_if_fail (element != NULL, GST_STATE_FAILURE); g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE); -// g_print("gst_element_change_state(\"%s\",%d)\n", -// element->name,state); +// GST_DEBUG_ELEMENT (GST_CAT_STATES, element, "default handler sets state to %s\n", +// gst_element_statename(GST_STATE_PENDING(element))); + + if (GST_STATE_TRANSITION(element) == GST_STATE_PAUSED_TO_PLAYING) { + g_return_val_if_fail(GST_ELEMENT_SCHED(element), GST_STATE_FAILURE); + if (GST_ELEMENT_PARENT(element)) + fprintf(stderr,"PAUSED->PLAYING: element \"%s\" has parent \"%s\" and sched %p\n", +GST_ELEMENT_NAME(element),GST_ELEMENT_NAME(GST_ELEMENT_PARENT(element)),GST_ELEMENT_SCHED(element)); + GST_SCHEDULE_ENABLE_ELEMENT (element->sched,element); + } + else if (GST_STATE_TRANSITION(element) == GST_STATE_PLAYING_TO_PAUSED) { + if (GST_ELEMENT_PARENT(element)) + fprintf(stderr,"PLAYING->PAUSED: element \"%s\" has parent \"%s\" and sched %p\n", +GST_ELEMENT_NAME(element),GST_ELEMENT_NAME(GST_ELEMENT_PARENT(element)),GST_ELEMENT_SCHED(element)); + GST_SCHEDULE_DISABLE_ELEMENT (element->sched,element); + } GST_STATE (element) = GST_STATE_PENDING (element); GST_STATE_PENDING (element) = GST_STATE_NONE_PENDING; + // note: queues' state_change is a special case because it needs to lock + // for synchronization (from another thread). since this signal may block + // or (worse) make another state change, the queue needs to unlock before + // calling. thus, gstqueue.c::gst_queue_state_change() blocks, unblocks, + // unlocks, then emits this. gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[STATE_CHANGE], GST_STATE (element)); - return TRUE; + return GST_STATE_SUCCESS; +} + +static void +gst_element_shutdown (GtkObject *object) +{ + GstElement *element = GST_ELEMENT (object); + + GST_DEBUG_ELEMENT (GST_CAT_REFCOUNTING, element, "shutdown\n"); + + if (GST_IS_BIN (GST_OBJECT_PARENT (element))) + gst_bin_remove (GST_BIN (GST_OBJECT_PARENT (element)), element); + + if (GTK_OBJECT_CLASS (parent_class)->shutdown) + GTK_OBJECT_CLASS (parent_class)->shutdown (object); } static void @@ -775,16 +905,29 @@ gst_element_real_destroy (GtkObject *object) GList *pads; GstPad *pad; -// g_print("in gst_element_real_destroy()\n"); - - pads = element->pads; - while (pads) { - pad = GST_PAD (pads->data); - gst_pad_destroy (pad); - pads = g_list_next (pads); + GST_DEBUG_ELEMENT (GST_CAT_REFCOUNTING, element, "destroy\n"); + + if (element->pads) { + GList *orig; + orig = pads = g_list_copy (element->pads); + while (pads) { + pad = GST_PAD (pads->data); + //gst_object_destroy (GST_OBJECT (pad)); + gst_object_ref (GST_OBJECT (pad)); + gst_element_remove_pad (element, pad); + gst_object_unref (GST_OBJECT (pad)); + pads = g_list_next (pads); + } + g_list_free (orig); + g_list_free (element->pads); + element->pads = NULL; } - g_list_free (element->pads); + element->numsrcpads = 0; + element->numsinkpads = 0; + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + GTK_OBJECT_CLASS (parent_class)->destroy (object); } /* @@ -830,7 +973,7 @@ gst_element_save_thyself (GstObject *object, oclass = GST_ELEMENT_CLASS (GTK_OBJECT (element)->klass); - xmlNewChild(parent, NULL, "name", gst_object_get_name (GST_OBJECT (element))); + xmlNewChild(parent, NULL, "name", GST_ELEMENT_NAME(element)); if (oclass->elementfactory != NULL) { GstElementFactory *factory = (GstElementFactory *)oclass->elementfactory; @@ -839,6 +982,9 @@ gst_element_save_thyself (GstObject *object, xmlNewChild (parent, NULL, "version", factory->details->version); } +// if (element->manager) +// xmlNewChild(parent, NULL, "manager", GST_ELEMENT_NAME(element->manager)); + // output all args to the element type = GTK_OBJECT_TYPE (element); while (type != GTK_TYPE_INVALID) { @@ -918,7 +1064,7 @@ gst_element_save_thyself (GstObject *object, } /** - * gst_element_load_thyself: + * gst_element_restore_thyself: * @self: the xml node * @parent: the parent of this object when it's loaded * @@ -927,7 +1073,7 @@ gst_element_save_thyself (GstObject *object, * Returns: the new element */ GstElement* -gst_element_load_thyself (xmlNodePtr self, GstObject *parent) +gst_element_restore_thyself (xmlNodePtr self, GstObject *parent) { xmlNodePtr children = self->xmlChildrenNode; GstElement *element; @@ -948,7 +1094,7 @@ gst_element_load_thyself (xmlNodePtr self, GstObject *parent) g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (type != NULL, NULL); - GST_INFO (GST_CAT_XML,"loading \"%s\" of type \"%s\"\n", name, type); + GST_INFO (GST_CAT_XML,"loading \"%s\" of type \"%s\"", name, type); element = gst_elementfactory_make (type, name); @@ -1002,32 +1148,33 @@ gst_element_load_thyself (xmlNodePtr self, GstObject *parent) } /** - * gst_element_set_manager: + * gst_element_set_sched: * @element: Element to set manager of. - * @manager: Element to be the manager. + * @sched: @GstSchedule to set. * - * Sets the manager of the element. For internal use only, unless you're + * Sets the scheduler of the element. For internal use only, unless you're * writing a new bin subclass. */ void -gst_element_set_manager (GstElement *element, - GstElement *manager) +gst_element_set_sched (GstElement *element, + GstSchedule *sched) { - element->manager = manager; + GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting scheduler to %p",sched); + element->sched = sched; } /** - * gst_element_get_manager: + * gst_element_get_sched: * @element: Element to get manager of. * - * Returns the manager of the element. + * Returns the scheduler of the element. * - * Returns: Element's manager + * Returns: Element's scheduler */ -GstElement* -gst_element_get_manager (GstElement *element) +GstSchedule* +gst_element_get_sched (GstElement *element) { - return element->manager; + return element->sched; } /** @@ -1070,3 +1217,24 @@ gst_element_signal_eos (GstElement *element) GST_FLAG_SET(element,GST_ELEMENT_COTHREAD_STOPPING); } + +const gchar *gst_element_statename(int state) { + switch (state) { +#ifdef GST_DEBUG_COLOR + case GST_STATE_NONE_PENDING: return "NONE_PENDING";break; + case GST_STATE_NULL: return "\033[01;37mNULL\033[00m";break; + case GST_STATE_READY: return "\033[01;31mREADY\033[00m";break; + case GST_STATE_PLAYING: return "\033[01;32mPLAYING\033[00m";break; + case GST_STATE_PAUSED: return "\033[01;33mPAUSED\033[00m";break; + default: return "\033[01;37;41mUNKNOWN!\033[00m"; +#else + case GST_STATE_NONE_PENDING: return "NONE_PENDING";break; + case GST_STATE_NULL: return "NULL";break; + case GST_STATE_READY: return "READY";break; + case GST_STATE_PLAYING: return "PLAYING";break; + case GST_STATE_PAUSED: return "PAUSED";break; + default: return "UNKNOWN!"; +#endif + } + return ""; +} diff --git a/gst/gstelement.h b/gst/gstelement.h index cc296c941..212dd4cdc 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -46,8 +46,8 @@ typedef enum { GST_STATE_NONE_PENDING = 0, GST_STATE_NULL = (1 << 0), GST_STATE_READY = (1 << 1), - GST_STATE_PLAYING = (1 << 2), - GST_STATE_PAUSED = (1 << 3), + GST_STATE_PAUSED = (1 << 2), + GST_STATE_PLAYING = (1 << 3), } GstElementState; typedef enum { @@ -56,28 +56,19 @@ typedef enum { GST_STATE_ASYNC = 2, } GstElementStateReturn; -static inline char *_gst_print_statename(int state) { - switch (state) { - case GST_STATE_NONE_PENDING: return "NONE_PENDING";break; - case GST_STATE_NULL: return "NULL";break; - case GST_STATE_READY: return "READY";break; - case GST_STATE_PLAYING: return "PLAYING";break; - case GST_STATE_PAUSED: return "PAUSED";break; - default: return ""; - } - return ""; -} +// NOTE: this probably should be done with an #ifdef to decide whether to safe-cast +// or to just do the non-checking cast. #define GST_STATE(obj) (GST_ELEMENT(obj)->current_state) #define GST_STATE_PENDING(obj) (GST_ELEMENT(obj)->pending_state) // Note: using 8 bit shift mostly "just because", it leaves us enough room to grow #define GST_STATE_TRANSITION(obj) ((GST_STATE(obj)<<8) | GST_STATE_PENDING(obj)) #define GST_STATE_NULL_TO_READY ((GST_STATE_NULL<<8) | GST_STATE_READY) -#define GST_STATE_READY_TO_PLAYING ((GST_STATE_READY<<8) | GST_STATE_PLAYING) -#define GST_STATE_PLAYING_TO_PAUSED ((GST_STATE_PLAYING<<8) | GST_STATE_PAUSED) +#define GST_STATE_READY_TO_PAUSED ((GST_STATE_READY<<8) | GST_STATE_PAUSED) #define GST_STATE_PAUSED_TO_PLAYING ((GST_STATE_PAUSED<<8) | GST_STATE_PLAYING) -#define GST_STATE_PLAYING_TO_READY ((GST_STATE_PLAYING<<8) | GST_STATE_READY) +#define GST_STATE_PLAYING_TO_PAUSED ((GST_STATE_PLAYING<<8) | GST_STATE_PAUSED) +#define GST_STATE_PAUSED_TO_READY ((GST_STATE_PAUSED<<8) | GST_STATE_READY) #define GST_STATE_READY_TO_NULL ((GST_STATE_READY<<8) | GST_STATE_NULL) #define GST_TYPE_ELEMENT \ @@ -104,6 +95,9 @@ typedef enum { /***** !!!!! need to have a flag that says that an element must *not* be an entry into a scheduling chain !!!!! *****/ + /* this element for some reason doesn't obey COTHREAD_STOPPING, or + has some other reason why it can't be the entry */ + GST_ELEMENT_NO_ENTRY, /* there is a new loopfunction ready for placement */ GST_ELEMENT_NEW_LOOPFUNC, @@ -116,7 +110,7 @@ typedef enum { GST_ELEMENT_EOS, /* use some padding for future expansion */ - GST_ELEMENT_FLAG_LAST = GST_OBJECT_FLAG_LAST + 8, + GST_ELEMENT_FLAG_LAST = GST_OBJECT_FLAG_LAST + 12, } GstElementFlags; #define GST_ELEMENT_IS_THREAD_SUGGESTED(obj) (GST_FLAG_IS_SET(obj,GST_ELEMENT_THREAD_SUGGESTED)) @@ -125,10 +119,12 @@ typedef enum { #define GST_ELEMENT_NAME(obj) (GST_OBJECT_NAME(obj)) #define GST_ELEMENT_PARENT(obj) (GST_OBJECT_PARENT(obj)) +#define GST_ELEMENT_MANAGER(obj) (((GstElement*)(obj))->manager) +#define GST_ELEMENT_SCHED(obj) (((GstElement*)(obj))->sched) #define GST_ELEMENT_PADS(obj) ((obj)->pads) -typedef struct _GstElement GstElement; -typedef struct _GstElementClass GstElementClass; +//typedef struct _GstElement GstElement; +//typedef struct _GstElementClass GstElementClass; typedef struct _GstElementDetails GstElementDetails; typedef struct _GstElementFactory GstElementFactory; @@ -149,6 +145,7 @@ struct _GstElement { GList *pads; GstElement *manager; + GstSchedule *sched; }; struct _GstElementClass { @@ -158,11 +155,21 @@ struct _GstElementClass { GstElementFactory *elementfactory; /* signal callbacks */ - void (*state_change) (GstElement *element,GstElementState state); - void (*new_pad) (GstElement *element,GstPad *pad); - void (*new_ghost_pad) (GstElement *element,GstPad *pad); - void (*error) (GstElement *element,gchar *error); - void (*eos) (GstElement *element); + void (*state_change) (GstElement *element,GstElementState state); + void (*new_pad) (GstElement *element,GstPad *pad); + void (*pad_removed) (GstElement *element,GstPad *pad); + void (*new_ghost_pad) (GstElement *element,GstPad *pad); + void (*ghost_pad_removed) (GstElement *element,GstPad *pad); + void (*error) (GstElement *element,gchar *error); + void (*eos) (GstElement *element); + + /* local pointers for get/set */ + void (*set_arg) (GtkObject *object, + GtkArg *arg, + guint arg_id); + void (*get_arg) (GtkObject *object, + GtkArg *arg, + guint arg_id); /* change the element state */ GstElementStateReturn (*change_state) (GstElement *element); @@ -202,10 +209,11 @@ const gchar* gst_element_get_name (GstElement *element); void gst_element_set_parent (GstElement *element, GstObject *parent); GstObject* gst_element_get_parent (GstElement *element); -void gst_element_set_manager (GstElement *element, GstElement *manager); -GstElement* gst_element_get_manager (GstElement *element); +void gst_element_set_sched (GstElement *element, GstSchedule *sched); +GstSchedule* gst_element_get_sched (GstElement *element); void gst_element_add_pad (GstElement *element, GstPad *pad); +void gst_element_remove_pad (GstElement *element, GstPad *pad); GstPad* gst_element_get_pad (GstElement *element, const gchar *name); GList* gst_element_get_pad_list (GstElement *element); GList* gst_element_get_padtemplate_list (GstElement *element); @@ -232,7 +240,7 @@ void gst_element_error (GstElement *element, const gchar *error); GstElementFactory* gst_element_get_factory (GstElement *element); /* XML write and read */ -GstElement* gst_element_load_thyself (xmlNodePtr self, GstObject *parent); +GstElement* gst_element_restore_thyself (xmlNodePtr self, GstObject *parent); /* @@ -264,9 +272,13 @@ GstElement* gst_elementfactory_create (GstElementFactory *factory, /* FIXME this name is wrong, probably so is the one above it */ GstElement* gst_elementfactory_make (const gchar *factoryname, const gchar *name); + xmlNodePtr gst_elementfactory_save_thyself (GstElementFactory *factory, xmlNodePtr parent); GstElementFactory* gst_elementfactory_load_thyself (xmlNodePtr parent); + +const gchar * gst_element_statename (int state); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gst/gstelementfactory.c b/gst/gstelementfactory.c index 997cb2055..9c4bc42e7 100644 --- a/gst/gstelementfactory.c +++ b/gst/gstelementfactory.c @@ -68,8 +68,6 @@ gst_elementfactory_find (const gchar *name) g_return_val_if_fail(name != NULL, NULL); - GST_DEBUG (0,"gstelementfactory: find \"%s\"\n", name); - walk = _gst_elementfactories; while (walk) { factory = (GstElementFactory *)(walk->data); @@ -78,6 +76,8 @@ gst_elementfactory_find (const gchar *name) walk = g_list_next(walk); } + // this should be an ERROR + GST_DEBUG (GST_CAT_ELEMENTFACTORY,"no such elementfactoryfactory \"%s\"\n", name); return NULL; } @@ -148,7 +148,8 @@ gst_elementfactory_create (GstElementFactory *factory, g_return_val_if_fail(factory != NULL, NULL); g_return_val_if_fail(name != NULL, NULL); - GST_DEBUG (0,"gstelementfactory: create \"%s\" \"%s\"\n", factory->name, name); + GST_DEBUG (GST_CAT_ELEMENTFACTORY,"creating element from factory \"%s\" with name \"%s\"\n", + factory->name, name); // it's not loaded, try to load the plugin if (factory->type == 0) { @@ -160,12 +161,11 @@ gst_elementfactory_create (GstElementFactory *factory, // create an instance of the element element = GST_ELEMENT(gtk_type_new(factory->type)); g_assert(element != NULL); - gst_object_ref(GST_OBJECT(element)); // attempt to set the elemenfactory class pointer if necessary oclass = GST_ELEMENT_CLASS(GTK_OBJECT(element)->klass); if (oclass->elementfactory == NULL) { - GST_DEBUG (0,"gstelementfactory: class %s\n", factory->name); + GST_DEBUG (GST_CAT_ELEMENTFACTORY,"class %s\n", factory->name); oclass->elementfactory = factory; } @@ -194,7 +194,7 @@ gst_elementfactory_make (const gchar *factoryname, const gchar *name) g_return_val_if_fail(factoryname != NULL, NULL); g_return_val_if_fail(name != NULL, NULL); - GST_DEBUG (0,"gstelementfactory: make \"%s\" \"%s\"\n", factoryname, name); +// GST_DEBUG (GST_CAT_ELEMENTFACTORY,"gstelementfactory: make \"%s\" \"%s\"\n", factoryname, name); //gst_plugin_load_elementfactory(factoryname); factory = gst_elementfactory_find(factoryname); diff --git a/gst/gstinfo.c b/gst/gstinfo.c index 15263722c..46ccbde76 100644 --- a/gst/gstinfo.c +++ b/gst/gstinfo.c @@ -20,6 +20,7 @@ * Boston, MA 02111-1307, USA. */ +#include #include "gst_private.h" #include "gstelement.h" #include "gstpad.h" @@ -27,19 +28,7 @@ extern gchar *_gst_progname; -/***** DEBUG system *****/ -GHashTable *__gst_function_pointers = NULL; - - - -/***** INFO system *****/ -GstInfoHandler _gst_info_handler = gst_default_info_handler; -#ifdef GST_INFO_ENABLED_VERBOSE -guint32 _gst_info_categories = 0xffffffff; -#else -guint32 _gst_info_categories = 0x00000001; -#endif - +/***** Categories and colorization *****/ static gchar *_gst_info_category_strings[] = { "GST_INIT", "COTHREADS", @@ -48,9 +37,9 @@ static gchar *_gst_info_category_strings[] = { "AUTOPLUG_ATTEMPT", "PARENTAGE", "STATES", - "PLANING", + "PLANNING", "SCHEDULING", - "OPERATION", + "DATAFLOW", "BUFFER", "CAPS", "CLOCK", @@ -66,48 +55,195 @@ static gchar *_gst_info_category_strings[] = { "TYPES", "XML", "NEGOTIATION", + "REFCOUNTING", }; -const gchar *_gst_category_colors[] = { - [GST_CAT_GST_INIT] = "00;37", - [GST_CAT_COTHREADS] = "00;32", - [GST_CAT_COTHREAD_SWITCH] = "00;32", - [GST_CAT_AUTOPLUG] = "00;34", - [GST_CAT_AUTOPLUG_ATTEMPT] = "00;34", - [GST_CAT_PARENTAGE] = "", - [GST_CAT_STATES] = "00;31", - [GST_CAT_PLANNING] = "00;35", - [GST_CAT_SCHEDULING] = "00;35", - [GST_CAT_DATAFLOW] = "00;32", - [GST_CAT_BUFFER] = "00;32", - [GST_CAT_CAPS] = "", - [GST_CAT_CLOCK] = "", - [GST_CAT_ELEMENT_PADS] = "", - [GST_CAT_ELEMENTFACTORY] = "", - [GST_CAT_PADS] = "", - [GST_CAT_PIPELINE] = "", - [GST_CAT_PLUGIN_LOADING] = "00;36", - [GST_CAT_PLUGIN_ERRORS] = "05;31", - [GST_CAT_PLUGIN_INFO] = "00;36", - [GST_CAT_PROPERTIES] = "", - [GST_CAT_THREAD] = "00;31", - [GST_CAT_TYPES] = "", - [GST_CAT_XML] = "", - [GST_CAT_NEGOTIATION] = "", - - [31] = "", -}; +/** + * gst_get_category_name: + * @category: the category to return the name of + * + * Returns: string containing the name of the category + */ +const gchar * +gst_get_category_name (gint category) { + if ((category >= 0) && (category < GST_CAT_MAX_CATEGORY)) + return _gst_info_category_strings[category]; + else + return NULL; +} + +/* + * Attribute codes: + * 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed + * Text color codes: + * 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white + * Background color codes: + * 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white + */ +const gchar *_gst_category_colors[32] = { + [GST_CAT_GST_INIT] = "07;37", + [GST_CAT_COTHREADS] = "00;32", + [GST_CAT_COTHREAD_SWITCH] = "00;37;42", + [GST_CAT_AUTOPLUG] = "00;34", + [GST_CAT_AUTOPLUG_ATTEMPT] = "00;36;44", + [GST_CAT_PARENTAGE] = "01;37;41", // !! + [GST_CAT_STATES] = "00;31", + [GST_CAT_PLANNING] = "07;35", + [GST_CAT_SCHEDULING] = "00;35", + [GST_CAT_DATAFLOW] = "00;32", + [GST_CAT_BUFFER] = "00;32", + [GST_CAT_CAPS] = "04;34", + [GST_CAT_CLOCK] = "00;33", // !! + [GST_CAT_ELEMENT_PADS] = "01;37;41", // !! + [GST_CAT_ELEMENTFACTORY] = "01;37;41", // !! + [GST_CAT_PADS] = "01;37;41", // !! + [GST_CAT_PIPELINE] = "01;37;41", // !! + [GST_CAT_PLUGIN_LOADING] = "00;36", + [GST_CAT_PLUGIN_ERRORS] = "05;31", + [GST_CAT_PLUGIN_INFO] = "00;36", + [GST_CAT_PROPERTIES] = "00;37;44", // !! + [GST_CAT_THREAD] = "00;31", + [GST_CAT_TYPES] = "01;37;41", // !! + [GST_CAT_XML] = "01;37;41", // !! + [GST_CAT_NEGOTIATION] = "07;34", + [GST_CAT_REFCOUNTING] = "00;34:42", + + [31] = "", +}; -/* colorization hash */ +/* colorization hash - DEPRACATED in favor of above */ inline gint _gst_debug_stringhash_color(gchar *file) { - int filecolor; + int filecolor = 0; while (file[0]) filecolor += *(char *)(file++); filecolor = (filecolor % 6) + 31; return filecolor; } + +/***** DEBUG system *****/ +GstDebugHandler _gst_debug_handler = gst_default_debug_handler; +guint32 _gst_debug_categories = 0x00000000; + +/** + * gst_default_debug_handler: + * @category: category of the DEBUG message + * @file: the file the DEBUG occurs in + * @function: the function the DEBUG occurs in + * @line: the line number in the file + * @debug_string: the current debug_string in the function, if any + * @element: pointer to the #GstElement in question + * @string: the actual DEBUG string + * + * Prints out the DEBUG mesage in a variant of the following form: + * + * DEBUG(pid:cid):gst_function:542(args): [elementname] something neat happened + */ +void +gst_default_debug_handler (gint category, gboolean incore, gchar *file, gchar *function, + gint line, gchar *debug_string, + void *element, gchar *string) +{ + gchar *empty = ""; + gchar *elementname = empty,*location = empty; + int pthread_id = getpid(); + int cothread_id = cothread_getcurrent(); +#ifdef GST_DEBUG_COLOR + int pthread_color = pthread_id%6 + 31; + int cothread_color = (cothread_id < 0) ? 37 : (cothread_id%6 + 31); +#endif + + if (debug_string == NULL) debug_string = ""; +// if (category != GST_CAT_GST_INIT) + location = g_strdup_printf("%s:%d%s:",function,line,debug_string); + if (element && GST_IS_ELEMENT (element)) +#ifdef GST_DEBUG_COLOR + elementname = g_strdup_printf (" \033[04m[%s]\033[00m", GST_OBJECT_NAME (element)); +#else + elementname = g_strdup_printf (" [%s]", GST_OBJECT_NAME (element)); +#endif + +#ifdef GST_DEBUG_COLOR + fprintf(stderr,"DEBUG(\033[00;%dm%5d\033[00m:\033[00;%dm%2d\033[00m)\033[" + "%s;%sm%s%s\033[00m %s", + pthread_color,pthread_id,cothread_color,cothread_id,incore?"00":"01", + _gst_category_colors[category],location,elementname,string); +#else + fprintf(stderr,"DEBUG(%5d:%2d)%s%s %s", + pthread_id,cothread_id,location,elementname,string); +#endif /* GST_DEBUG_COLOR */ + + if (location != empty) g_free(location); + if (elementname != empty) g_free(elementname); + + g_free(string); +} + + +/** + * gst_debug_set_categories: + * @categories: bitmask of DEBUG categories to enable + * + * Enable the output of DEBUG categories based on the given bitmask. + * The bit for any given category is (1 << GST_CAT_...). + */ +void +gst_debug_set_categories (guint32 categories) { + _gst_debug_categories = categories; + if (categories) + GST_INFO (0, "setting DEBUG categories to 0x%08X",categories); +} + +/** + * gst_debug_get_categories: + * + * Returns: the current bitmask of enabled DEBUG categories + * The bit for any given category is (1 << GST_CAT_...). + */ +guint32 +gst_debug_get_categories () { + return _gst_debug_categories; +} + +/** + * gst_debug_enable_category: + * @category: the category to enable + * + * Enables the given GST_CAT_... DEBUG category. + */ +void +gst_debug_enable_category (gint category) { + _gst_debug_categories |= (1 << category); + if (_gst_debug_categories) + GST_INFO (0, "setting DEBUG categories to 0x%08X",_gst_debug_categories); +} + +/** + * gst_debug_disable_category: + * @category: the category to disable + * + * Disables the given GST_CAT_... DEBUG category. + */ +void +gst_debug_disable_category (gint category) { + _gst_debug_categories &= ~ (1 << category); + if (_gst_debug_categories) + GST_INFO (0, "setting DEBUG categories to 0x%08X",_gst_debug_categories); +} + + + + +/***** INFO system *****/ +GstInfoHandler _gst_info_handler = gst_default_info_handler; +#ifdef GST_INFO_ENABLED_VERBOSE +guint32 _gst_info_categories = 0xffffffff; +#else +guint32 _gst_info_categories = 0x00000001; +#endif + + /** * gst_default_info_handler: * @category: category of the INFO message @@ -123,12 +259,18 @@ inline gint _gst_debug_stringhash_color(gchar *file) { * INFO:gst_function:542(args): [elementname] something neat happened */ void -gst_default_info_handler (gint category, gchar *file, gchar *function, +gst_default_info_handler (gint category, gboolean incore,gchar *file, gchar *function, gint line, gchar *debug_string, void *element, gchar *string) { gchar *empty = ""; gchar *elementname = empty,*location = empty; + int pthread_id = getpid(); + int cothread_id = cothread_getcurrent(); +#ifdef GST_DEBUG_COLOR + int pthread_color = pthread_id%6 + 31; + int cothread_color = (cothread_id < 0) ? 37 : (cothread_id%6 + 31); +#endif if (debug_string == NULL) debug_string = ""; if (category != GST_CAT_GST_INIT) @@ -138,15 +280,17 @@ gst_default_info_handler (gint category, gchar *file, gchar *function, #ifdef GST_DEBUG_ENABLED #ifdef GST_DEBUG_COLOR - fprintf(stderr,"INFO(%d:%d):\033[" GST_DEBUG_CHAR_MODE ";%sm%s%s\033[00m %s\n", - getpid(),cothread_getcurrent(),_gst_category_colors[category],location,elementname,string); + fprintf(stderr,"\033[01mINFO\033[00m (\033[00;%dm%5d\033[00m:\033[00;%dm%2d\033[00m)\033[" + GST_DEBUG_CHAR_MODE ";%sm%s%s\033[00m %s\n", + pthread_color,pthread_id,cothread_color,cothread_id, + _gst_category_colors[category],location,elementname,string); #else - fprintf(stderr,"INFO(%d:%d):%s%s %s\n", - getpid(),cothread_getcurrent(),location,elementname,string); + fprintf(stderr,"INFO (%5d:%2d)%s%s %s\n", + pthread_id,cothread_id,location,elementname,string); #endif /* GST_DEBUG_COLOR */ #else #ifdef GST_DEBUG_COLOR - fprintf(stderr,"INFO:\033[" GST_DEBUG_CHAR_MODE ";%sm%s%s\033[00m %s\n", + fprintf(stderr,"\033[01mINFO\033[00m:\033[" GST_DEBUG_CHAR_MODE ";%sm%s%s\033[00m %s\n", location,elementname,_gst_category_colors[category],string); #else fprintf(stderr,"INFO:%s%s %s\n", @@ -213,77 +357,6 @@ gst_info_disable_category (gint category) { -/***** DEBUG system *****/ -guint32 _gst_debug_categories = 0x00000000; - - -/** - * gst_debug_set_categories: - * @categories: bitmask of DEBUG categories to enable - * - * Enable the output of DEBUG categories based on the given bitmask. - * The bit for any given category is (1 << GST_CAT_...). - */ -void -gst_debug_set_categories (guint32 categories) { - _gst_debug_categories = categories; - if (categories) - GST_INFO (0, "setting DEBUG categories to 0x%08X",categories); -} - -/** - * gst_debug_get_categories: - * - * Returns: the current bitmask of enabled DEBUG categories - * The bit for any given category is (1 << GST_CAT_...). - */ -guint32 -gst_debug_get_categories () { - return _gst_debug_categories; -} - -/** - * gst_debug_enable_category: - * @category: the category to enable - * - * Enables the given GST_CAT_... DEBUG category. - */ -void -gst_debug_enable_category (gint category) { - _gst_debug_categories |= (1 << category); - if (_gst_debug_categories) - GST_INFO (0, "setting DEBUG categories to 0x%08X",_gst_debug_categories); -} - -/** - * gst_debug_disable_category: - * @category: the category to disable - * - * Disables the given GST_CAT_... DEBUG category. - */ -void -gst_debug_disable_category (gint category) { - _gst_debug_categories &= ~ (1 << category); - if (_gst_debug_categories) - GST_INFO (0, "setting DEBUG categories to 0x%08X",_gst_debug_categories); -} - -/** - * gst_get_category_name: - * @category: the category to return the name of - * - * Returns: string containing the name of the category - */ -const gchar * -gst_get_category_name (gint category) { - if ((category >= 0) && (category < GST_CAT_MAX_CATEGORY)) - return _gst_info_category_strings[category]; - else - return NULL; -} - - - /***** ERROR system *****/ GstErrorHandler _gst_error_handler = gst_default_error_handler; @@ -364,3 +437,24 @@ gst_default_error_handler (gchar *file, gchar *function, exit(1); } + + + +/***** DEBUG system *****/ +GHashTable *__gst_function_pointers = NULL; + +gchar * +_gst_debug_nameof_funcptr (void *ptr) +{ + gchar *ptrname; + Dl_info dlinfo; + if (__gst_function_pointers) { + if ((ptrname = g_hash_table_lookup(__gst_function_pointers,ptr))) + return g_strdup(ptrname); + } else if (dladdr(ptr,&dlinfo) && dlinfo.dli_sname) { + return g_strdup(dlinfo.dli_sname); + } else { + return g_strdup_printf("%p",ptr); + } + return NULL; +} diff --git a/gst/gstinfo.h b/gst/gstinfo.h index 620d809dd..71fdba9f4 100644 --- a/gst/gstinfo.h +++ b/gst/gstinfo.h @@ -34,6 +34,14 @@ #include "cothreads.h" +/***** are we in the core or not? *****/ +#ifdef __GST_PRIVATE_H__ + #define _GST_DEBUG_INCORE TRUE +#else + #define _GST_DEBUG_INCORE FALSE +#endif + + /* colorization stuff */ #ifdef GST_DEBUG_COLOR #ifdef __GST_PRIVATE_H__ /* FIXME this should be some libgst.la -specific thing */ @@ -47,13 +55,51 @@ gint _gst_debug_stringhash_color(gchar *file); +/********************************************************************** + * Categories + **********************************************************************/ + +const gchar * gst_get_category_name (gint category); + +enum { + GST_CAT_GST_INIT = 0, // Library initialization + GST_CAT_COTHREADS, // Cothread creation, etc. + GST_CAT_COTHREAD_SWITCH, // Cothread switching + GST_CAT_AUTOPLUG, // Successful autoplug results + GST_CAT_AUTOPLUG_ATTEMPT, // Attempted autoplug operations + GST_CAT_PARENTAGE, // GstBin parentage issues + GST_CAT_STATES, // State changes and such + GST_CAT_PLANNING, // Plan generation + GST_CAT_SCHEDULING, // Schedule construction + GST_CAT_DATAFLOW, // Events during actual data movement + GST_CAT_BUFFER, // Buffer creation/destruction + GST_CAT_CAPS, // Capabilities matching + GST_CAT_CLOCK, // Clocking + GST_CAT_ELEMENT_PADS, // Element pad management + GST_CAT_ELEMENTFACTORY, // Elementfactory stuff + GST_CAT_PADS, // Pad creation/connection + GST_CAT_PIPELINE, // Pipeline stuff + GST_CAT_PLUGIN_LOADING, // Plugin loading + GST_CAT_PLUGIN_ERRORS, // Errors during plugin loading + GST_CAT_PLUGIN_INFO, // Plugin state information + GST_CAT_PROPERTIES, // Properties + GST_CAT_THREAD, // Thread creation/management + GST_CAT_TYPES, // Typing + GST_CAT_XML, // XML load/save of everything + GST_CAT_NEGOTIATION, // Caps Negotiation stuff + GST_CAT_REFCOUNTING, // Ref Counting stuff + + GST_CAT_MAX_CATEGORY = 31 +}; + +extern const gchar *_gst_category_colors[32]; + + /********************************************************************** * DEBUG system **********************************************************************/ -extern guint32 _gst_debug_categories; - /* for include files that make too much noise normally */ #ifdef GST_DEBUG_FORCE_DISABLE #undef GST_DEBUG_ENABLED @@ -69,109 +115,69 @@ extern guint32 _gst_debug_categories; //#define GST_DEBUG_ENABLE_CATEGORIES 0x00000000 //#endif -/* fallback, this should probably be a 'weak' symbol or something */ -G_GNUC_UNUSED static gchar *_debug_string = NULL; -#ifdef GST_DEBUG_COLOR - #ifdef _GST_COLOR_CODE -#warning have a coded debug - #define GST_DEBUG_PREFIX(cat,format,args...) \ -"DEBUG(%d:%d)\033[" _GST_COLOR_CODE "m" __PRETTY_FUNCTION__ ":%d\033[00m" format , \ -getpid() , cothread_getcurrent() , __LINE__ , ## args - #else - #define GST_DEBUG_PREFIX(cat,format,args...) \ -"DEBUG(%d:%d)\033[" GST_DEBUG_CHAR_MODE ";%sm" __PRETTY_FUNCTION__ ":%d\033[00m" format , \ -getpid() , cothread_getcurrent() , _gst_category_colors[cat] , __LINE__ , ## args - #endif /* _GST_COLOR_CODE */ -#else - #define GST_DEBUG_PREFIX(cat,format,args...) \ -"DEBUG(%d:%d)" __PRETTY_FUNCTION__ ":%d" format , getpid() ,cothread_getcurrent() , __LINE__ , ## args -#endif +typedef void (*GstDebugHandler) (gint category,gboolean core,gchar *file,gchar *function, + gint line,gchar *debug_string, + void *element,gchar *string); +void gst_default_debug_handler (gint category,gboolean incore,gchar *file,gchar *function, + gint line,gchar *debug_string, + void *element,gchar *string); -#ifdef GST_DEBUG_ENABLED -#define GST_DEBUG(cat,format,args...) G_STMT_START{ \ - if (((1<path_string_separator = "/"; klass->signal_object = gtk_type_new (gst_signal_object_get_type ()); + + gtkobject_class->shutdown = gst_object_shutdown; + gtkobject_class->destroy = gst_object_real_destroy; + gtkobject_class->finalize = gst_object_finalize; } static void @@ -118,9 +126,12 @@ gst_object_init (GstObject *object) #ifdef HAVE_ATOMIC_H atomic_set(&(object->refcount),1); #else - object->refcount++; + object->refcount = 1; #endif object->parent = NULL; + + object->flags = 0; + GST_FLAG_SET (object, GST_FLOATING); } /** @@ -136,6 +147,122 @@ gst_object_new (void) return GST_OBJECT (gtk_type_new (gst_object_get_type ())); } +/** + * gst_object_ref: + * @object: GstObject to reference + * + * Increments the refence count on the object. + */ +GstObject* +gst_object_ref (GstObject *object) +{ + g_return_val_if_fail (GST_IS_OBJECT (object), NULL); + + GST_DEBUG (GST_CAT_REFCOUNTING, "ref '%s' %d->%d\n",GST_OBJECT_NAME(object), + GTK_OBJECT(object)->ref_count,GTK_OBJECT(object)->ref_count+1); + + gtk_object_ref (GTK_OBJECT (object)); + + return object; +} +#define gst_object_ref gst_object_ref + +/** + * gst_object_unref: + * @object: GstObject to unreference + * + * Decrements the refence count on the object. If reference count hits + * zero, destroy the object. + */ +void +gst_object_unref (GstObject *object) +{ + g_return_if_fail (GST_IS_OBJECT (object)); + + GST_DEBUG (GST_CAT_REFCOUNTING, "unref '%s' %d->%d\n",GST_OBJECT_NAME(object), + GTK_OBJECT(object)->ref_count,GTK_OBJECT(object)->ref_count-1); + + gtk_object_unref (GTK_OBJECT (object)); +} +#define gst_object_unref gst_object_unref + +/** + * gst_object_sink: + * @object: GstObject to sink + * + * Removes floating reference on an object. Any newly created object has + * a refcount of 1 and is FLOATING. This function should be used when + * creating a new object to symbolically 'take ownership of' the object. + */ +void +gst_object_sink (GstObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (GST_IS_OBJECT (object)); + + GST_DEBUG (GST_CAT_REFCOUNTING, "sink '%s'\n",GST_OBJECT_NAME(object)); + if (GST_OBJECT_FLOATING (object)) + { + GST_FLAG_UNSET (object, GST_FLOATING); + gst_object_unref (object); + } +} + +void +gst_object_destroy (GstObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (GST_IS_OBJECT (object)); + + GST_DEBUG (GST_CAT_REFCOUNTING, "destroy '%s'\n",GST_OBJECT_NAME(object)); + if (!GST_OBJECT_DESTROYED (object)) + { + /* need to hold a reference count around all class method + * invocations. + */ + gst_object_ref (object); + GTK_OBJECT (object)->klass->shutdown (GTK_OBJECT (object)); + gst_object_unref (object); + } +} + +static void +gst_object_shutdown (GtkObject *object) +{ + GST_DEBUG (GST_CAT_REFCOUNTING, "shutdown '%s'\n",GST_OBJECT_NAME(object)); + GST_FLAG_SET (GST_OBJECT (object), GST_DESTROYED); + parent_class->shutdown (GTK_OBJECT (object)); +} + +/* finilize is called when the object has to free its resources */ +static void +gst_object_real_destroy (GtkObject *gtk_object) +{ + GST_DEBUG (GST_CAT_REFCOUNTING, "destroy '%s'\n",GST_OBJECT_NAME(gtk_object)); + + GST_OBJECT_PARENT (gtk_object) = NULL; + + parent_class->destroy (gtk_object); +} + +/* finilize is called when the object has to free its resources */ +static void +gst_object_finalize (GtkObject *gtk_object) +{ + GstObject *object; + + object = GST_OBJECT (gtk_object); + + GST_DEBUG (GST_CAT_REFCOUNTING, "finalize '%s'\n",GST_OBJECT_NAME(object)); + + if (object->name != NULL) + g_free (object->name); + + g_mutex_free (object->lock); + + parent_class->finalize (gtk_object); +} + /** * gst_object_set_name: * @object: GstObject to set the name of @@ -315,26 +442,31 @@ gst_object_unref (GstObject *object) #endif /* gst_object_unref */ /** - * gst_object_sink: - * @object: GstObject to sink + * gst_object_check_uniqueness: + * @list: a list of #GstObject to check through + * @name: the name to search for * - * Removes floating reference on an object. Any newly created object has - * a refcount of 1 and is FLOATING. This function should be used when - * creating a new object to symbolically 'take ownership of' the object. + * This function checks through the list of objects to see if the name + * given appears in the list as the name of an object. It returns TRUE if + * the name does not exist in the list. + * + * Returns: TRUE if the name doesn't appear in the list, FALSE if it does. */ -#ifndef gst_object_sink -void -gst_object_sink (GstObject *object) +gboolean +gst_object_check_uniqueness (GList *list, const gchar *name) { - g_return_if_fail (object != NULL); - g_return_if_fail (GST_IS_OBJECT (object)); + GstObject *child; - if (GTK_OBJECT_FLOATING (object)) { - GTK_OBJECT_UNSET_FLAGS (object, GTK_FLOATING); - gst_object_unref (object); + while (list) { + child = GST_OBJECT (list->data); + list = g_list_next(list); + + if (strcmp(GST_OBJECT_NAME(child), name) == 0) return FALSE; } + + return TRUE; } -#endif /* gst_object_sink */ + /** * gst_object_save_thyself: @@ -528,6 +660,3 @@ gst_class_signal_emit_by_name (GstObject *object, gtk_signal_emit_by_name (oclass->signal_object, name, object, self); } - - - diff --git a/gst/gstobject.h b/gst/gstobject.h index 9e1e6bb95..f7e095f61 100644 --- a/gst/gstobject.h +++ b/gst/gstobject.h @@ -28,6 +28,8 @@ #include #include +#include + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -55,10 +57,16 @@ extern "C" { #define GST_IS_OBJECT_CLASS(obj) \ (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_OBJECT)) -typedef struct _GstObject GstObject; -typedef struct _GstObjectClass GstObjectClass; +//typedef struct _GstObject GstObject; +//typedef struct _GstObjectClass GstObjectClass; +// +typedef enum +{ + GST_DESTROYED = 0, + GST_FLOATING, -#define GST_OBJECT_FLAG_LAST 4 + GST_OBJECT_FLAG_LAST = 4, +} GstObjectFlags; struct _GstObject { GtkObject object; @@ -76,6 +84,8 @@ struct _GstObject { /* this objects parent */ GstObject *parent; + + guint32 flags; }; struct _GstObjectClass { @@ -89,19 +99,23 @@ struct _GstObjectClass { void (*object_saved) (GstObject *object, xmlNodePtr parent); /* functions go here */ + void (*destroy) (GstObject *object); + xmlNodePtr (*save_thyself) (GstObject *object, xmlNodePtr parent); void (*restore_thyself) (GstObject *object, xmlNodePtr self); }; -#define GST_OBJECT_NAME(obj) (const gchar*)(((GstObject *)(obj))->name) -#define GST_OBJECT_PARENT(obj) (((GstObject *)(obj))->parent) - - -#define GST_FLAGS(obj) GTK_OBJECT_FLAGS(obj) +#define GST_FLAGS(obj) (GST_OBJECT (obj)->flags) #define GST_FLAG_IS_SET(obj,flag) (GST_FLAGS (obj) & (1<<(flag))) #define GST_FLAG_SET(obj,flag) G_STMT_START{ (GST_FLAGS (obj) |= (1<<(flag))); }G_STMT_END #define GST_FLAG_UNSET(obj,flag) G_STMT_START{ (GST_FLAGS (obj) &= ~(1<<(flag))); }G_STMT_END +#define GST_OBJECT_NAME(obj) (const gchar*)(((GstObject *)(obj))->name) +#define GST_OBJECT_PARENT(obj) (((GstObject *)(obj))->parent) + +#define GST_OBJECT_DESTROYED(obj) (GST_FLAG_IS_SET (obj, GST_DESTROYED)) +#define GST_OBJECT_FLOATING(obj) (GST_FLAG_IS_SET (obj, GST_FLOATING)) + /* object locking */ #define GST_LOCK(obj) (g_mutex_lock(GST_OBJECT(obj)->lock)) #define GST_TRYLOCK(obj) (g_mutex_trylock(GST_OBJECT(obj)->lock)) @@ -122,15 +136,17 @@ void gst_object_set_parent (GstObject *object,GstObject *parent); GstObject* gst_object_get_parent (GstObject *object); void gst_object_unparent (GstObject *object); +gboolean gst_object_check_uniqueness (GList *list, const gchar *name); + xmlNodePtr gst_object_save_thyself (GstObject *object, xmlNodePtr parent); /* refcounting */ -#define gst_object_ref(object) gtk_object_ref(GTK_OBJECT(object)); -#define gst_object_unref(object) gtk_object_unref(GTK_OBJECT(object)); -#define gst_object_sink(object) gtk_object_sink(GTK_OBJECT(object)); +GstObject * gst_object_ref (GstObject *object); +void gst_object_unref (GstObject *object); +void gst_object_sink (GstObject *object); /* destroying an object */ -#define gst_object_destroy(object) gtk_object_destroy(GTK_OBJECT(object)) +void gst_object_destroy (GstObject *object); /* printing out the 'path' of the object */ gchar * gst_object_get_path_string (GstObject *object); diff --git a/gst/gstpad.c b/gst/gstpad.c index 48bd1e376..69913d25c 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -27,6 +27,7 @@ #include "gstelement.h" #include "gsttype.h" #include "gstbin.h" +#include "gstscheduler.h" /***** Start with the base GstPad class *****/ @@ -78,6 +79,9 @@ gst_pad_init (GstPad *pad) enum { REAL_SET_ACTIVE, REAL_CAPS_CHANGED, + REAL_CAPS_NEGO_FAILED, + REAL_CONNECTED, + REAL_DISCONNECTED, /* FILL ME */ REAL_LAST_SIGNAL }; @@ -97,7 +101,6 @@ static void gst_real_pad_get_arg (GtkObject *object,GtkArg *arg,guint id); static void gst_real_pad_destroy (GtkObject *object); static void gst_pad_push_func (GstPad *pad, GstBuffer *buf); -static gboolean gst_pad_eos_func (GstPad *pad); static GstPad *real_pad_parent_class = NULL; @@ -144,16 +147,31 @@ gst_real_pad_class_init (GstRealPadClass *klass) GTK_SIGNAL_OFFSET (GstRealPadClass, caps_changed), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); + gst_real_pad_signals[REAL_CAPS_NEGO_FAILED] = + gtk_signal_new ("caps_nego_failed", GTK_RUN_LAST, gtkobject_class->type, + GTK_SIGNAL_OFFSET (GstRealPadClass, caps_nego_failed), + gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); + gst_real_pad_signals[REAL_CONNECTED] = + gtk_signal_new ("connected", GTK_RUN_LAST, gtkobject_class->type, + GTK_SIGNAL_OFFSET (GstRealPadClass, connected), + gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); + gst_real_pad_signals[REAL_DISCONNECTED] = + gtk_signal_new ("disconnected", GTK_RUN_LAST, gtkobject_class->type, + GTK_SIGNAL_OFFSET (GstRealPadClass, disconnected), + gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); gtk_object_class_add_signals (gtkobject_class, gst_real_pad_signals, REAL_LAST_SIGNAL); gtk_object_add_arg_type ("GstRealPad::active", GTK_TYPE_BOOL, GTK_ARG_READWRITE, REAL_ARG_ACTIVE); - gtkobject_class->destroy = gst_real_pad_destroy; - gtkobject_class->set_arg = gst_real_pad_set_arg; - gtkobject_class->get_arg = gst_real_pad_get_arg; + gtkobject_class->destroy = GST_DEBUG_FUNCPTR(gst_real_pad_destroy); + gtkobject_class->set_arg = GST_DEBUG_FUNCPTR(gst_real_pad_set_arg); + gtkobject_class->get_arg = GST_DEBUG_FUNCPTR(gst_real_pad_get_arg); - gstobject_class->save_thyself = gst_pad_save_thyself; + gstobject_class->save_thyself = GST_DEBUG_FUNCPTR(gst_pad_save_thyself); gstobject_class->path_string_separator = "."; } @@ -167,7 +185,7 @@ gst_real_pad_init (GstRealPad *pad) pad->getfunc = NULL; pad->getregionfunc = NULL; pad->qosfunc = NULL; - pad->eosfunc = gst_pad_eos_func; + pad->eosfunc = GST_DEBUG_FUNCPTR(gst_pad_eos_func); pad->pushfunc = GST_DEBUG_FUNCPTR(gst_pad_push_func); pad->pullfunc = NULL; @@ -260,6 +278,8 @@ gst_pad_new_from_template (GstPadTemplate *templ, g_return_val_if_fail (templ != NULL, NULL); pad = gst_pad_new (name, templ->direction); + gst_object_ref (GST_OBJECT (templ)); + gst_object_sink (GST_OBJECT (templ)); GST_PAD_PADTEMPLATE(pad) = templ; return pad; @@ -330,8 +350,8 @@ void gst_pad_set_chain_function (GstPad *pad, g_return_if_fail (GST_IS_REAL_PAD (pad)); GST_RPAD_CHAINFUNC(pad) = chain; - GST_DEBUG (0,"chainfunc for %s:%s(@%p) at %p is set to %p\n", - GST_DEBUG_PAD_NAME(pad),pad,&GST_RPAD_CHAINFUNC(pad),chain); + GST_DEBUG (GST_CAT_PADS,"chainfunc for %s:%s set to %s\n", + GST_DEBUG_PAD_NAME(pad),GST_DEBUG_FUNCPTR_NAME(chain)); } /** @@ -349,8 +369,8 @@ gst_pad_set_get_function (GstPad *pad, g_return_if_fail (GST_IS_REAL_PAD (pad)); GST_RPAD_GETFUNC(pad) = get; - GST_DEBUG (0,"getfunc for %s:%s(@%p) at %p is set to %p\n", - GST_DEBUG_PAD_NAME(pad),pad,&GST_RPAD_GETFUNC(pad),get); + GST_DEBUG (GST_CAT_PADS,"getfunc for %s:%s set to %s\n", + GST_DEBUG_PAD_NAME(pad),GST_DEBUG_FUNCPTR_NAME(get)); } /** @@ -368,8 +388,8 @@ gst_pad_set_getregion_function (GstPad *pad, g_return_if_fail (GST_IS_REAL_PAD (pad)); GST_RPAD_GETREGIONFUNC(pad) = getregion; - GST_DEBUG (0,"getregionfunc for %s:%s(@%p) at %p is set to %p\n", - GST_DEBUG_PAD_NAME(pad),pad,&GST_RPAD_GETREGIONFUNC(pad),getregion); + GST_DEBUG (GST_CAT_PADS,"getregionfunc for %s:%s set to %s\n", + GST_DEBUG_PAD_NAME(pad),GST_DEBUG_FUNCPTR_NAME(getregion)); } /** @@ -387,8 +407,8 @@ gst_pad_set_qos_function (GstPad *pad, g_return_if_fail (GST_IS_REAL_PAD (pad)); GST_RPAD_QOSFUNC(pad) = qos; - GST_DEBUG (0,"qosfunc for %s:%s(@%p) at %p is set to %p\n", - GST_DEBUG_PAD_NAME(pad),pad,&GST_RPAD_QOSFUNC(pad),qos); + GST_DEBUG (GST_CAT_PADS,"qosfunc for %s:%s set to %s\n", + GST_DEBUG_PAD_NAME(pad),GST_DEBUG_FUNCPTR_NAME(qos)); } /** @@ -406,8 +426,8 @@ gst_pad_set_eos_function (GstPad *pad, g_return_if_fail (GST_IS_REAL_PAD (pad)); GST_RPAD_EOSFUNC(pad) = eos; - GST_DEBUG (0,"eosfunc for %s:%s(@%p) at %p is set to %p\n", - GST_DEBUG_PAD_NAME(pad),pad,&GST_RPAD_EOSFUNC(pad),eos); + GST_DEBUG (GST_CAT_PADS,"eosfunc for %s:%s set to %s\n", + GST_DEBUG_PAD_NAME(pad),GST_DEBUG_FUNCPTR_NAME(eos)); } /** @@ -425,11 +445,10 @@ gst_pad_set_negotiate_function (GstPad *pad, g_return_if_fail (GST_IS_REAL_PAD (pad)); GST_RPAD_NEGOTIATEFUNC(pad) = nego; - GST_DEBUG (0,"negotiatefunc for %s:%s(@%p) at %p is set to %p\n", - GST_DEBUG_PAD_NAME(pad),pad,&GST_RPAD_NEGOTIATEFUNC(pad),nego); + GST_DEBUG (GST_CAT_PADS,"negotiatefunc for %s:%s set to %s\n", + GST_DEBUG_PAD_NAME(pad),GST_DEBUG_FUNCPTR_NAME(nego)); } - /** * gst_pad_set_newcaps_function: * @pad: the pad to set the newcaps function for @@ -445,8 +464,8 @@ gst_pad_set_newcaps_function (GstPad *pad, g_return_if_fail (GST_IS_REAL_PAD (pad)); GST_RPAD_NEWCAPSFUNC (pad) = newcaps; - GST_DEBUG (0,"newcapsfunc for %s:%s(@%p) at %p is set to %p\n", - GST_DEBUG_PAD_NAME(pad),pad,&GST_RPAD_NEWCAPSFUNC(pad),newcaps); + GST_DEBUG (GST_CAT_PADS,"newcapsfunc for %s:%s set to %s\n", + GST_DEBUG_PAD_NAME(pad),GST_DEBUG_FUNCPTR_NAME(newcaps)); } /** @@ -464,18 +483,19 @@ gst_pad_set_bufferpool_function (GstPad *pad, g_return_if_fail (GST_IS_REAL_PAD (pad)); GST_RPAD_BUFFERPOOLFUNC (pad) = bufpool; - GST_DEBUG (0,"bufferpoolfunc for %s:%s(@%p) at %p is set to %p\n", - GST_DEBUG_PAD_NAME (pad), pad, &GST_RPAD_BUFFERPOOLFUNC (pad), bufpool); + GST_DEBUG (GST_CAT_PADS,"bufferpoolfunc for %s:%s set to %s\n", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME(bufpool)); } static void gst_pad_push_func(GstPad *pad, GstBuffer *buf) { if (GST_RPAD_CHAINFUNC(GST_RPAD_PEER(pad)) != NULL) { - GST_DEBUG (0,"calling chain function\n"); + GST_DEBUG (GST_CAT_DATAFLOW,"calling chain function %s\n", + GST_DEBUG_FUNCPTR_NAME(GST_RPAD_CHAINFUNC(GST_RPAD_PEER(pad)))); (GST_RPAD_CHAINFUNC(GST_RPAD_PEER(pad)))(pad,buf); } else { - GST_DEBUG (0,"got a problem here: default pad_push handler in place, no chain function\n"); + GST_DEBUG (GST_CAT_DATAFLOW,"got a problem here: default pad_push handler in place, no chain function\n"); } } @@ -495,7 +515,7 @@ gst_pad_handle_qos(GstPad *pad, GList *pads; GstPad *target_pad; - GST_DEBUG (0,"gst_pad_handle_qos(\"%s\",%08ld)\n", GST_OBJECT_NAME (GST_PAD_PARENT (pad)),qos_message); + GST_DEBUG (GST_CAT_PADS,"gst_pad_handle_qos(\"%s\",%08ld)\n", GST_OBJECT_NAME (GST_PAD_PARENT (pad)),qos_message); if (GST_RPAD_QOSFUNC(pad)) { (GST_RPAD_QOSFUNC(pad)) (pad,qos_message); @@ -503,7 +523,7 @@ gst_pad_handle_qos(GstPad *pad, element = GST_ELEMENT (GST_PAD_PARENT(GST_RPAD_PEER(pad))); pads = element->pads; - GST_DEBUG (0,"gst_pad_handle_qos recurse(\"%s\",%08ld)\n", GST_ELEMENT_NAME (element), qos_message); + GST_DEBUG (GST_CAT_PADS,"gst_pad_handle_qos recurse(\"%s\",%08ld)\n", GST_ELEMENT_NAME (element), qos_message); while (pads) { target_pad = GST_PAD (pads->data); if (GST_RPAD_DIRECTION(target_pad) == GST_PAD_SINK) { @@ -535,6 +555,9 @@ gst_pad_disconnect (GstPad *srcpad, g_return_if_fail (sinkpad != NULL); g_return_if_fail (GST_IS_PAD (sinkpad)); + GST_INFO (GST_CAT_ELEMENT_PADS, "disconnecting %s:%s(%p) and %s:%s(%p)", + GST_DEBUG_PAD_NAME(srcpad), srcpad, GST_DEBUG_PAD_NAME(sinkpad), sinkpad); + // now we need to deal with the real/ghost stuff realsrc = GST_PAD_REALIZE(srcpad); realsink = GST_PAD_REALIZE(sinkpad); @@ -542,6 +565,14 @@ gst_pad_disconnect (GstPad *srcpad, g_return_if_fail (GST_RPAD_PEER(realsrc) != NULL); g_return_if_fail (GST_RPAD_PEER(realsink) != NULL); + if ((GST_RPAD_DIRECTION(realsrc) == GST_PAD_SINK) && + (GST_RPAD_DIRECTION(realsink) == GST_PAD_SRC)) { + GstRealPad *temppad; + + temppad = realsrc; + realsrc = realsink; + realsink = temppad; + } g_return_if_fail ((GST_RPAD_DIRECTION(realsrc) == GST_PAD_SRC) && (GST_RPAD_DIRECTION(realsink) == GST_PAD_SINK)); @@ -549,6 +580,16 @@ gst_pad_disconnect (GstPad *srcpad, GST_RPAD_PEER(realsrc) = NULL; GST_RPAD_PEER(realsink) = NULL; + /* fire off a signal to each of the pads telling them that they've been disconnected */ + gtk_signal_emit(GTK_OBJECT(realsrc), gst_real_pad_signals[REAL_DISCONNECTED], realsink); + gtk_signal_emit(GTK_OBJECT(realsink), gst_real_pad_signals[REAL_DISCONNECTED], realsrc); + + // now tell the scheduler + if (realsrc->sched) + GST_SCHEDULE_PAD_DISCONNECT (realsrc->sched, (GstPad *)realsrc, (GstPad *)realsink); +// if (realsink->sched) +// GST_SCHEDULE_PAD_DISCONNECT (realsink->sched, (GstPad *)realsrc, (GstPad *)realsink); + GST_INFO (GST_CAT_ELEMENT_PADS, "disconnected %s:%s and %s:%s", GST_DEBUG_PAD_NAME(srcpad), GST_DEBUG_PAD_NAME(sinkpad)); } @@ -567,7 +608,6 @@ gst_pad_connect (GstPad *srcpad, GstPad *sinkpad) { GstRealPad *realsrc, *realsink; - GstRealPad *temppad; gboolean negotiated = FALSE; /* generic checks */ @@ -576,49 +616,69 @@ gst_pad_connect (GstPad *srcpad, g_return_val_if_fail(sinkpad != NULL, FALSE); g_return_val_if_fail(GST_IS_PAD(sinkpad), FALSE); - GST_INFO (GST_CAT_ELEMENT_PADS, "about to connect %s:%s and %s:%s", + GST_INFO (GST_CAT_PADS, "connecting %s:%s and %s:%s", GST_DEBUG_PAD_NAME(srcpad), GST_DEBUG_PAD_NAME(sinkpad)); // now we need to deal with the real/ghost stuff realsrc = GST_PAD_REALIZE(srcpad); realsink = GST_PAD_REALIZE(sinkpad); + if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) + GST_INFO (GST_CAT_PADS, "*actually* connecting %s:%s and %s:%s", + GST_DEBUG_PAD_NAME(realsrc), GST_DEBUG_PAD_NAME(realsink)); + g_return_val_if_fail(GST_RPAD_PEER(realsrc) == NULL, FALSE); g_return_val_if_fail(GST_RPAD_PEER(realsink) == NULL, FALSE); /* check for reversed directions and swap if necessary */ if ((GST_RPAD_DIRECTION(realsrc) == GST_PAD_SINK) && (GST_RPAD_DIRECTION(realsink) == GST_PAD_SRC)) { + GstRealPad *temppad; + temppad = realsrc; realsrc = realsink; realsink = temppad; } g_return_val_if_fail((GST_RPAD_DIRECTION(realsrc) == GST_PAD_SRC) && - (GST_RPAD_DIRECTION(realsink) == GST_PAD_SINK), FALSE); + (GST_RPAD_DIRECTION(realsink) == GST_PAD_SINK), FALSE); /* first set peers */ GST_RPAD_PEER(realsrc) = realsink; GST_RPAD_PEER(realsink) = realsrc; - /* FIXME: set connected flag */ - - GST_INFO (GST_CAT_ELEMENT_PADS, "connected %s:%s and %s:%s", - GST_DEBUG_PAD_NAME(srcpad), GST_DEBUG_PAD_NAME(sinkpad)); - if (GST_PAD_CAPS (srcpad)) { + GST_DEBUG(GST_CAT_PADS, "renegotiation from srcpad\n"); negotiated = gst_pad_renegotiate (srcpad); } else if (GST_PAD_CAPS (sinkpad)) { + GST_DEBUG(GST_CAT_PADS, "renegotiation from sinkpad\n"); negotiated = gst_pad_renegotiate (sinkpad); } - else + else { + GST_DEBUG(GST_CAT_PADS, "not renegotiating connection\n"); negotiated = TRUE; + } if (!negotiated) { + GST_INFO(GST_CAT_PADS, "pads %s:%s and %s:%s failed to negotiate, disconnecting", + GST_DEBUG_PAD_NAME(srcpad), GST_DEBUG_PAD_NAME(sinkpad)); gst_pad_disconnect (GST_PAD (realsrc), GST_PAD (realsink)); return FALSE; } + + /* fire off a signal to each of the pads telling them that they've been connected */ + gtk_signal_emit(GTK_OBJECT(realsrc), gst_real_pad_signals[REAL_CONNECTED], realsink); + gtk_signal_emit(GTK_OBJECT(realsink), gst_real_pad_signals[REAL_CONNECTED], realsrc); + + // now tell the scheduler(s) + if (realsrc->sched) + GST_SCHEDULE_PAD_CONNECT (realsrc->sched, (GstPad *)realsrc, (GstPad *)realsink); + else if (realsink->sched) + GST_SCHEDULE_PAD_CONNECT (realsink->sched, (GstPad *)realsrc, (GstPad *)realsink); + + GST_INFO (GST_CAT_PADS, "connected %s:%s and %s:%s", + GST_DEBUG_PAD_NAME(srcpad), GST_DEBUG_PAD_NAME(sinkpad)); return TRUE; } @@ -668,13 +728,31 @@ gst_pad_get_padtemplate (GstPad *pad) * * Returns: the parent object */ -GstObject* +GstElement* gst_pad_get_parent (GstPad *pad) { g_return_val_if_fail (pad != NULL, NULL); g_return_val_if_fail (GST_IS_PAD (pad), NULL); - return GST_OBJECT_PARENT (pad); + return GST_PAD_PARENT (pad); +} + +void +gst_pad_set_sched (GstPad *pad, GstSchedule *sched) +{ + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + + GST_RPAD_SCHED(pad) = sched; +} + +GstSchedule* +gst_pad_get_sched (GstPad *pad) +{ + g_return_val_if_fail (pad != NULL, NULL); + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + return GST_RPAD_SCHED(pad); } /** @@ -687,7 +765,7 @@ gst_pad_get_parent (GstPad *pad) * * Returns: the parent object */ -GstObject* +GstElement* gst_pad_get_real_parent (GstPad *pad) { g_return_val_if_fail (pad != NULL, NULL); @@ -892,7 +970,7 @@ gst_pad_check_compatibility (GstPad *srcpad, GstPad *sinkpad) } } else { - GST_DEBUG (0,"gstpad: could not check capabilities of pads (%s:%s) and (%s:%s) %p %p\n", + GST_DEBUG (GST_CAT_PADS,"could not check capabilities of pads (%s:%s) and (%s:%s) %p %p\n", GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad), GST_PAD_CAPS (srcpad), GST_PAD_CAPS (sinkpad)); return TRUE; @@ -940,25 +1018,51 @@ gst_pad_get_bufferpool (GstPad *pad) GST_DEBUG_ENTER("(%s:%s)",GST_DEBUG_PAD_NAME(pad)); if (peer->bufferpoolfunc) { - GST_DEBUG (0,"calling bufferpoolfunc &%s (@%p) of peer pad %s:%s\n", + GST_DEBUG (GST_CAT_PADS,"calling bufferpoolfunc &%s (@%p) of peer pad %s:%s\n", GST_DEBUG_FUNCPTR_NAME(peer->bufferpoolfunc),&peer->bufferpoolfunc,GST_DEBUG_PAD_NAME(((GstPad*)peer))); return (peer->bufferpoolfunc)(((GstPad*)peer)); } else { - GST_DEBUG (0,"no bufferpoolfunc for peer pad %s:%s at %p\n",GST_DEBUG_PAD_NAME(((GstPad*)peer)),&peer->bufferpoolfunc); + GST_DEBUG (GST_CAT_PADS,"no bufferpoolfunc for peer pad %s:%s at %p\n",GST_DEBUG_PAD_NAME(((GstPad*)peer)),&peer->bufferpoolfunc); return NULL; } } - -// FIXME this needs to be rethought soon static void gst_real_pad_destroy (GtkObject *object) { GstPad *pad = GST_PAD (object); -// g_print("in gst_pad_real_destroy()\n"); + GST_DEBUG (GST_CAT_REFCOUNTING, "destroy %s:%s\n", GST_DEBUG_PAD_NAME(pad)); + + if (GST_PAD (pad)->padtemplate) + gst_object_unref (GST_OBJECT (GST_PAD (pad)->padtemplate)); - g_list_free (GST_REAL_PAD(pad)->ghostpads); + if (GST_PAD_PEER (pad)) + gst_pad_disconnect (pad, GST_PAD (GST_PAD_PEER (pad))); + + if (GST_IS_ELEMENT (GST_OBJECT_PARENT (pad))) + gst_element_remove_pad (GST_ELEMENT (GST_OBJECT_PARENT (pad)), pad); + + // FIXME we should destroy the ghostpads, because they are nothing without the real pad + if (GST_REAL_PAD (pad)->ghostpads) { + GList *orig, *ghostpads; + + orig = ghostpads = g_list_copy (GST_REAL_PAD (pad)->ghostpads); + + while (ghostpads) { + GstPad *ghostpad = GST_PAD (ghostpads->data); + + if (GST_IS_ELEMENT (GST_OBJECT_PARENT (ghostpad))) + gst_element_remove_pad (GST_ELEMENT (GST_OBJECT_PARENT (ghostpad)), ghostpad); + + ghostpads = g_list_next (ghostpads); + } + g_list_free (orig); + g_list_free (GST_REAL_PAD(pad)->ghostpads); + } + + if (GTK_OBJECT_CLASS (real_pad_parent_class)->destroy) + GTK_OBJECT_CLASS (real_pad_parent_class)->destroy (object); } @@ -1170,9 +1274,22 @@ gst_pad_renegotiate (GstPad *pad) result = gst_pad_renegotiate_func (GST_PAD (currentpad), &data1, GST_PAD (otherpad), &data2, &newcaps); + if (!result) { + GST_DEBUG (GST_CAT_NEGOTIATION, "firing caps_nego_failed signal on %s:%s and %s:%s to give it a chance to succeed\n", + GST_DEBUG_PAD_NAME(currentpad),GST_DEBUG_PAD_NAME(otherpad)); + gtk_signal_emit (GTK_OBJECT(currentpad), + gst_real_pad_signals[REAL_CAPS_NEGO_FAILED],&result); + gtk_signal_emit (GTK_OBJECT(otherpad), + gst_real_pad_signals[REAL_CAPS_NEGO_FAILED],&result); + if (result) + GST_DEBUG (GST_CAT_NEGOTIATION, "caps_nego_failed handler claims success at renego, believing\n"); + } + if (result) { GST_DEBUG (GST_CAT_NEGOTIATION, "pads aggreed on caps :)\n"); + newcaps = GST_PAD_CAPS (pad); + /* here we have some sort of aggreement of the caps */ GST_PAD_CAPS (currentpad) = gst_caps_ref (newcaps); if (GST_RPAD_NEWCAPSFUNC (currentpad)) @@ -1181,6 +1298,13 @@ gst_pad_renegotiate (GstPad *pad) GST_PAD_CAPS (otherpad) = gst_caps_ref (newcaps); if (GST_RPAD_NEWCAPSFUNC (otherpad)) GST_RPAD_NEWCAPSFUNC (otherpad) (GST_PAD (otherpad), newcaps); + + GST_DEBUG (GST_CAT_NEGOTIATION, "firing caps_changed signal on %s:%s and %s:%s\n", + GST_DEBUG_PAD_NAME(currentpad),GST_DEBUG_PAD_NAME(otherpad)); + gtk_signal_emit (GTK_OBJECT(currentpad), + gst_real_pad_signals[REAL_CAPS_CHANGED],GST_PAD_CAPS(currentpad)); + gtk_signal_emit (GTK_OBJECT(otherpad), + gst_real_pad_signals[REAL_CAPS_CHANGED],GST_PAD_CAPS(otherpad)); } return result; @@ -1313,16 +1437,16 @@ gst_pad_push (GstPad *pad, GstBuffer *buf) { GstRealPad *peer = GST_RPAD_PEER (pad); - g_return_if_fail (peer != NULL); - GST_DEBUG_ENTER ("(%s:%s)", GST_DEBUG_PAD_NAME (pad)); + + g_return_if_fail (peer != NULL); if (peer->pushfunc) { - GST_DEBUG (0, "calling pushfunc &%s of peer pad %s:%s\n", + GST_DEBUG (GST_CAT_DATAFLOW, "calling pushfunc &%s of peer pad %s:%s\n", GST_DEBUG_FUNCPTR_NAME (peer->pushfunc), GST_DEBUG_PAD_NAME (((GstPad*)peer))); (peer->pushfunc) (((GstPad*)peer), buf); } else - GST_DEBUG (0, "no pushfunc\n"); + GST_DEBUG (GST_CAT_DATAFLOW, "no pushfunc\n"); } #endif @@ -1340,16 +1464,16 @@ gst_pad_pull (GstPad *pad) { GstRealPad *peer = GST_RPAD_PEER(pad); - g_return_val_if_fail (peer != NULL, NULL); - GST_DEBUG_ENTER("(%s:%s)",GST_DEBUG_PAD_NAME(pad)); + g_return_val_if_fail (peer != NULL, NULL); + if (peer->pullfunc) { - GST_DEBUG (0,"calling pullfunc &%s (@%p) of peer pad %s:%s\n", - GST_DEBUG_FUNCPTR_NAME(peer->pullfunc),&peer->pullfunc,GST_DEBUG_PAD_NAME(((GstPad*)peer))); + GST_DEBUG (GST_CAT_DATAFLOW,"calling pullfunc %s of peer pad %s:%s\n", + GST_DEBUG_FUNCPTR_NAME(peer->pullfunc),GST_DEBUG_PAD_NAME(peer)); return (peer->pullfunc)(((GstPad*)peer)); } else { - GST_DEBUG (0,"no pullfunc for peer pad %s:%s at %p\n",GST_DEBUG_PAD_NAME(((GstPad*)peer)),&peer->pullfunc); + GST_DEBUG (GST_CAT_DATAFLOW,"no pullfunc for peer pad %s:%s at %p\n",GST_DEBUG_PAD_NAME(((GstPad*)peer)),&peer->pullfunc); return NULL; } } @@ -1380,11 +1504,11 @@ gst_pad_pullregion (GstPad *pad, GstRegionType type, guint64 offset, guint64 len GST_DEBUG_ENTER("(%s:%s,%d,%lld,%lld)",GST_DEBUG_PAD_NAME(pad),type,offset,len); if (peer->pullregionfunc) { - GST_DEBUG (0,"calling pullregionfunc &%s of peer pad %s:%s\n", + GST_DEBUG (GST_CAT_DATAFLOW,"calling pullregionfunc &%s of peer pad %s:%s\n", GST_DEBUG_FUNCPTR_NAME(peer->pullregionfunc),GST_DEBUG_PAD_NAME(((GstPad*)peer))); return (peer->pullregionfunc)(((GstPad*)peer),type,offset,len); } else { - GST_DEBUG (0,"no pullregionfunc\n"); + GST_DEBUG (GST_CAT_DATAFLOW,"no pullregionfunc\n"); return NULL; } } @@ -1528,7 +1652,7 @@ gst_padtemplate_save_thyself (GstPadTemplate *templ, xmlNodePtr parent) xmlNodePtr subtree; guchar *presence; - GST_DEBUG (0,"saving padtemplate %s\n", templ->name_template); + GST_DEBUG (GST_CAT_XML,"saving padtemplate %s\n", templ->name_template); xmlNewChild(parent,NULL,"nametemplate", templ->name_template); xmlNewChild(parent,NULL,"direction", (templ->direction == GST_PAD_SINK? "sink":"src")); @@ -1616,7 +1740,7 @@ gst_padtemplate_load_thyself (xmlNodePtr parent) } -static gboolean +gboolean gst_pad_eos_func(GstPad *pad) { GstElement *element; @@ -1649,6 +1773,8 @@ gst_pad_eos_func(GstPad *pad) GST_INFO (GST_CAT_PADS,"set EOS on sink pad %s:%s",GST_DEBUG_PAD_NAME(pad)); GST_FLAG_SET (pad, GST_PAD_EOS); + gst_element_set_state (GST_ELEMENT(GST_PAD_PARENT(pad)), GST_STATE_READY); + return TRUE; } @@ -1676,6 +1802,8 @@ gst_pad_set_eos(GstPad *pad) GST_INFO (GST_CAT_PADS,"set EOS on src pad %s:%s",GST_DEBUG_PAD_NAME(pad)); GST_FLAG_SET (pad, GST_PAD_EOS); + gst_element_set_state (GST_ELEMENT(GST_PAD_PARENT(pad)), GST_STATE_READY); + gst_element_signal_eos (GST_ELEMENT (GST_PAD_PARENT (pad))); return TRUE; @@ -1783,7 +1911,7 @@ gst_ghost_pad_new (gchar *name, // FIXME need to ref the real pad here... ? - GST_DEBUG(0,"created ghost pad \"%s\"\n",name); + GST_DEBUG(GST_CAT_PADS,"created ghost pad \"%s\"\n",name); return GST_PAD(ghostpad); } diff --git a/gst/gstpad.h b/gst/gstpad.h index 14731e0f2..c5d722452 100644 --- a/gst/gstpad.h +++ b/gst/gstpad.h @@ -63,14 +63,14 @@ extern "C" { #define GST_IS_GHOST_PAD_CLASS(obj) (GTK_CHECK_CLASS_TYPE ((klass), GST_TYPE_GHOST_PAD)) -typedef struct _GstPad GstPad; -typedef struct _GstPadClass GstPadClass; +//typedef struct _GstPad GstPad; +//typedef struct _GstPadClass GstPadClass; typedef struct _GstRealPad GstRealPad; typedef struct _GstRealPadClass GstRealPadClass; typedef struct _GstGhostPad GstGhostPad; typedef struct _GstGhostPadClass GstGhostPadClass; -typedef struct _GstPadTemplate GstPadTemplate; -typedef struct _GstPadTemplateClass GstPadTemplateClass; +//typedef struct _GstPadTemplate GstPadTemplate; +//typedef struct _GstPadTemplateClass GstPadTemplateClass; typedef enum { @@ -142,6 +142,8 @@ struct _GstRealPad { guint64 offset; guint64 len; + GstSchedule *sched; + GstPadChainFunction chainfunc; GstPadGetFunction getfunc; GstPadGetRegionFunction getregionfunc; @@ -163,9 +165,13 @@ struct _GstRealPadClass { GstPadClass parent_class; /* signal callbacks */ - void (*set_active) (GstPad *pad, gboolean active); - void (*caps_changed) (GstPad *pad, GstCaps *newcaps); - void (*eos) (GstPad *pad); + void (*set_active) (GstPad *pad, gboolean active); + void (*caps_changed) (GstPad *pad, GstCaps *newcaps); + void (*caps_nego_failed) (GstPad *pad); + void (*connected) (GstPad *pad, GstPad *peer); + void (*disconnected) (GstPad *pad, GstPad *peer); + + void (*eos) (GstPad *pad); }; struct _GstGhostPad { @@ -182,7 +188,7 @@ struct _GstGhostPadClass { /***** helper macros *****/ /* GstPad */ #define GST_PAD_NAME(pad) (GST_OBJECT_NAME(pad)) -#define GST_PAD_PARENT(pad) (GST_OBJECT_PARENT(pad)) +#define GST_PAD_PARENT(pad) ((GstElement *)(GST_OBJECT_PARENT(pad))) #define GST_PAD_ELEMENT_PRIVATE(pad) (((GstPad *)(pad))->element_private) #define GST_PAD_PADTEMPLATE(pad) (((GstPad *)(pad))->padtemplate) @@ -191,6 +197,7 @@ struct _GstGhostPadClass { #define GST_RPAD_CAPS(pad) (((GstRealPad *)(pad))->caps) #define GST_RPAD_PEER(pad) (((GstRealPad *)(pad))->peer) #define GST_RPAD_BUFPEN(pad) (((GstRealPad *)(pad))->bufpen) +#define GST_RPAD_SCHED(pad) (((GstRealPad *)(pad))->sched) #define GST_RPAD_CHAINFUNC(pad) (((GstRealPad *)(pad))->chainfunc) #define GST_RPAD_GETFUNC(pad) (((GstRealPad *)(pad))->getfunc) #define GST_RPAD_GETREGIONFUNC(pad) (((GstRealPad *)(pad))->getregionfunc) @@ -312,8 +319,11 @@ void gst_pad_set_name (GstPad *pad, const gchar *name); const gchar* gst_pad_get_name (GstPad *pad); void gst_pad_set_parent (GstPad *pad, GstObject *parent); -GstObject* gst_pad_get_parent (GstPad *pad); -GstObject* gst_pad_get_real_parent (GstPad *pad); +GstElement* gst_pad_get_parent (GstPad *pad); +GstElement* gst_pad_get_real_parent (GstPad *pad); + +void gst_pad_set_sched (GstPad *pad, GstSchedule *sched); +GstSchedule* gst_pad_get_sched (GstPad *pad); void gst_pad_add_ghost_pad (GstPad *pad, GstPad *ghostpad); void gst_pad_remove_ghost_pad (GstPad *pad, GstPad *ghostpad); @@ -356,6 +366,7 @@ NULL ) #define gst_pad_eos(pad) (GST_RPAD_EOSFUNC(GST_RPAD_PEER(pad))(GST_PAD(GST_RPAD_PEER(pad)))) gboolean gst_pad_set_eos (GstPad *pad); +gboolean gst_pad_eos_func (GstPad *pad); void gst_pad_handle_qos (GstPad *pad, glong qos_message); void gst_pad_load_and_connect (xmlNodePtr self, GstObject *parent); @@ -379,6 +390,7 @@ GstCaps* gst_padtemplate_get_caps_by_name (GstPadTemplate *templ, const gchar * xmlNodePtr gst_padtemplate_save_thyself (GstPadTemplate *templ, xmlNodePtr parent); GstPadTemplate* gst_padtemplate_load_thyself (xmlNodePtr parent); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c index f2772d488..f967add7c 100644 --- a/gst/gstpipeline.c +++ b/gst/gstpipeline.c @@ -24,6 +24,11 @@ #include "gst_private.h" #include "gstpipeline.h" +#include "gstthread.h" +#include "gstutils.h" +#include "gsttype.h" +#include "gstautoplug.h" +#include "gstscheduler.h" GstElementDetails gst_pipeline_details = { @@ -95,6 +100,10 @@ gst_pipeline_init (GstPipeline *pipeline) { // we're a manager by default GST_FLAG_SET (pipeline, GST_BIN_FLAG_MANAGER); + + GST_ELEMENT_SCHED(pipeline) = gst_schedule_new(GST_ELEMENT(pipeline)); + GST_DEBUG(GST_CAT_PIPELINE, "pipeline's scheduler is %p\n",GST_ELEMENT_SCHED(pipeline)); +// gst_element_set_manager(GST_ELEMENT(pipeline),GST_ELEMENT(pipeline)); } @@ -107,16 +116,16 @@ gst_pipeline_init (GstPipeline *pipeline) * Returns: newly created GstPipeline */ GstElement* -gst_pipeline_new (const guchar *name) +gst_pipeline_new (const guchar *name) { return gst_elementfactory_make ("pipeline", name); } -static void -gst_pipeline_prepare (GstPipeline *pipeline) +static void +gst_pipeline_prepare (GstPipeline *pipeline) { - GST_DEBUG (0,"GstPipeline: preparing pipeline \"%s\" for playing\n", - GST_ELEMENT_NAME(GST_ELEMENT(pipeline))); + GST_DEBUG (GST_CAT_PIPELINE,"preparing pipeline \"%s\" for playing (DEPRACATED!!)\n", + GST_ELEMENT_NAME(GST_ELEMENT(pipeline))); } static GstElementStateReturn diff --git a/gst/gstprops.c b/gst/gstprops.c index 25d5a24cd..380b53119 100644 --- a/gst/gstprops.c +++ b/gst/gstprops.c @@ -53,19 +53,19 @@ gst_props_debug_entry (GstPropsEntry *entry) { switch (entry->propstype) { case GST_PROPS_INT_ID: - GST_DEBUG (0, "%d\n", entry->data.int_data); + GST_DEBUG (GST_CAT_PROPERTIES, "%d\n", entry->data.int_data); break; case GST_PROPS_FOURCC_ID: - GST_DEBUG (0, "%4.4s\n", (gchar*)&entry->data.fourcc_data); + GST_DEBUG (GST_CAT_PROPERTIES, "%4.4s\n", (gchar*)&entry->data.fourcc_data); break; case GST_PROPS_BOOL_ID: - GST_DEBUG (0, "%d\n", entry->data.bool_data); + GST_DEBUG (GST_CAT_PROPERTIES, "%d\n", entry->data.bool_data); break; case GST_PROPS_STRING_ID: - GST_DEBUG (0, "%s\n", entry->data.string_data.string); + GST_DEBUG (GST_CAT_PROPERTIES, "%s\n", entry->data.string_data.string); break; case GST_PROPS_INT_RANGE_ID: - GST_DEBUG (0, "%d-%d\n", entry->data.int_range_data.min, + GST_DEBUG (GST_CAT_PROPERTIES, "%d-%d\n", entry->data.int_range_data.min, entry->data.int_range_data.max); break; default: @@ -616,7 +616,7 @@ gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry * static gboolean gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2) { - GST_DEBUG (0,"compare: %s %s\n", g_quark_to_string (entry1->propid), + GST_DEBUG (GST_CAT_PROPERTIES,"compare: %s %s\n", g_quark_to_string (entry1->propid), g_quark_to_string (entry2->propid)); switch (entry1->propstype) { case GST_PROPS_LIST_ID: @@ -674,10 +674,13 @@ gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry switch (entry2->propstype) { // b <---> a - d case GST_PROPS_INT_RANGE_ID: + GST_DEBUG(GST_CAT_PROPERTIES,"%d <= %d <= %d ?\n",entry2->data.int_range_data.min, + entry1->data.int_data,entry2->data.int_range_data.max); return (entry2->data.int_range_data.min <= entry1->data.int_data && entry2->data.int_range_data.max >= entry1->data.int_data); // b <---> a case GST_PROPS_INT_ID: + GST_DEBUG(GST_CAT_PROPERTIES,"%d == %d ?\n",entry1->data.int_data,entry2->data.int_data); return (entry2->data.int_data == entry1->data.int_data); // b <---> a,b,c case GST_PROPS_LIST_ID: @@ -761,14 +764,14 @@ gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops) entry2 = (GstPropsEntry *)sinklist->data; while (entry1->propid < entry2->propid) { - GST_DEBUG (0,"source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid)); + GST_DEBUG (GST_CAT_PROPERTIES,"source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid)); more++; sourcelist = g_list_next (sourcelist); if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data; else goto end; } while (entry1->propid > entry2->propid) { - GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid)); + GST_DEBUG (GST_CAT_PROPERTIES,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid)); missing++; sinklist = g_list_next (sinklist); if (sinklist) entry2 = (GstPropsEntry *)sinklist->data; @@ -777,7 +780,7 @@ gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops) if (!gst_props_entry_check_compatibility (entry1, entry2)) { compatible = FALSE; - GST_DEBUG (0, "%s are not compatible\n:", + GST_DEBUG (GST_CAT_PROPERTIES, "%s are not compatible: \n", g_quark_to_string (entry1->propid)); gst_props_debug_entry (entry1); gst_props_debug_entry (entry2); @@ -790,7 +793,7 @@ gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops) GstPropsEntry *entry2; entry2 = (GstPropsEntry *)sinklist->data; missing++; - GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid)); + GST_DEBUG (GST_CAT_PROPERTIES,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid)); } end: diff --git a/gst/gstqueue.c b/gst/gstqueue.c index 37a4f7f3c..755c8f8c2 100644 --- a/gst/gstqueue.c +++ b/gst/gstqueue.c @@ -23,7 +23,7 @@ //#define DEBUG_ENABLED //#define STATUS_ENABLED #ifdef STATUS_ENABLED -#define STATUS(A) GST_DEBUG(0,A, gst_element_get_name(GST_ELEMENT(queue))) +#define STATUS(A) GST_DEBUG(GST_CAT_DATAFLOW, A, GST_ELEMENT_NAME(queue)) #else #define STATUS(A) #endif @@ -34,6 +34,7 @@ #include "gst_private.h" #include "gstqueue.h" +#include "gstscheduler.h" GstElementDetails gst_queue_details = { "Queue", @@ -47,15 +48,22 @@ GstElementDetails gst_queue_details = { /* Queue signals and args */ enum { - /* FILL ME */ + LOW_WATERMARK, + HIGH_WATERMARK, LAST_SIGNAL }; enum { ARG_0, + ARG_LEVEL_BUFFERS, + ARG_LEVEL_BYTES, + ARG_LEVEL_TIME, + ARG_SIZE_BUFFERS, + ARG_SIZE_BYTES, + ARG_SIZE_TIME, + ARG_LEAKY, ARG_LEVEL, ARG_MAX_LEVEL, - ARG_BLOCK, }; @@ -76,6 +84,23 @@ static void gst_queue_flush (GstQueue *queue); static GstElementStateReturn gst_queue_change_state (GstElement *element); + +static GtkType +queue_leaky_get_type(void) { + static GtkType queue_leaky_type = 0; + static GtkEnumValue queue_leaky[] = { + { GST_QUEUE_NO_LEAK, "0", "Not Leaky" }, + { GST_QUEUE_LEAK_UPSTREAM, "1", "Leaky on Upstream" }, + { GST_QUEUE_LEAK_DOWNSTREAM, "2", "Leaky on Downstream" }, + { 0, NULL, NULL }, + }; + if (!queue_leaky_type) { + queue_leaky_type = gtk_type_register_enum("GstQueueLeaky", queue_leaky); + } + return queue_leaky_type; +} +#define GST_TYPE_QUEUE_LEAKY (queue_leaky_get_type()) + static GstElementClass *parent_class = NULL; //static guint gst_queue_signals[LAST_SIGNAL] = { 0 }; @@ -111,12 +136,12 @@ gst_queue_class_init (GstQueueClass *klass) parent_class = gtk_type_class (GST_TYPE_ELEMENT); + gtk_object_add_arg_type ("GstQueue::leaky", GST_TYPE_QUEUE_LEAKY, + GTK_ARG_READWRITE, ARG_LEAKY); gtk_object_add_arg_type ("GstQueue::level", GTK_TYPE_INT, GTK_ARG_READABLE, ARG_LEVEL); gtk_object_add_arg_type ("GstQueue::max_level", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_MAX_LEVEL); - gtk_object_add_arg_type ("GstQueue::block", GTK_TYPE_BOOL, - GTK_ARG_READWRITE, ARG_BLOCK); gtkobject_class->set_arg = gst_queue_set_arg; gtkobject_class->get_arg = gst_queue_get_arg; @@ -144,14 +169,15 @@ gst_queue_init (GstQueue *queue) queue->queue = NULL; queue->level_buffers = 0; - queue->max_buffers = 100; - queue->block = TRUE; queue->level_bytes = 0; - queue->size_buffers = 0; - queue->size_bytes = 0; + queue->level_time = 0LL; + queue->size_buffers = 100; // 100 buffers + queue->size_bytes = 100 * 1024; // 100KB + queue->size_time = 1000000000LL; // 1sec queue->emptycond = g_cond_new (); queue->fullcond = g_cond_new (); + GST_DEBUG(GST_CAT_THREAD, "initialized queue's emptycond and fullcond\n"); } static GstBufferPool* @@ -206,10 +232,10 @@ gst_queue_handle_eos (GstPad *pad) queue = GST_QUEUE (GST_OBJECT_PARENT (pad)); - GST_DEBUG (0,"queue: %s received eos\n", GST_ELEMENT_NAME (queue)); + GST_DEBUG (GST_CAT_DATAFLOW,"%s received eos\n", GST_ELEMENT_NAME (queue)); GST_LOCK (queue); - GST_DEBUG (0,"queue: %s has %d buffers left\n", GST_ELEMENT_NAME (queue), + GST_DEBUG (GST_CAT_DATAFLOW,"%s has %d buffers left\n", GST_ELEMENT_NAME (queue), queue->level_buffers); GST_FLAG_SET (pad, GST_PAD_EOS); @@ -224,7 +250,7 @@ gst_queue_handle_eos (GstPad *pad) static void gst_queue_cleanup_buffers (gpointer data, const gpointer user_data) { - GST_DEBUG (0,"queue: %s cleaning buffer %p\n", (gchar *)user_data, data); + GST_DEBUG (GST_CAT_DATAFLOW,"%s cleaning buffer %p\n", (gchar *)user_data, data); gst_buffer_unref (GST_BUFFER (data)); } @@ -257,45 +283,79 @@ gst_queue_chain (GstPad *pad, GstBuffer *buf) /* we have to lock the queue since we span threads */ - GST_DEBUG (0,"queue: try have queue lock\n"); +// GST_DEBUG (GST_CAT_DATAFLOW,"trying to get lock on queue \"%s\"\n",name); GST_LOCK (queue); - GST_DEBUG (0,"queue: %s adding buffer %p %ld\n", name, buf, pthread_self ()); - GST_DEBUG (0,"queue: have queue lock\n"); if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLUSH)) { + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "buffer has FLUSH bit set, flushing queue\n"); gst_queue_flush (queue); } - GST_DEBUG (0,"queue: %s: chain %d %p\n", name, queue->level_buffers, buf); + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "adding buffer %p of size %d\n",buf,GST_BUFFER_SIZE(buf)); + + if (queue->level_buffers >= queue->size_buffers) { + // if this is a leaky queue... + if (queue->leaky) { + // if we leak on the upstream side, drop the current buffer + if (queue->leaky == GST_QUEUE_LEAK_UPSTREAM) { + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "queue is full, leaking buffer on upstream end\n"); + gst_buffer_unref(buf); + // now we have to clean up and exit right away + GST_UNLOCK (queue); + return; + } + // otherwise we have to push a buffer off the other end + else { + GSList *front; + GstBuffer *leakbuf; + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "queue is full, leaking buffer on downstream end\n"); + front = queue->queue; + leakbuf = (GstBuffer *)(front->data); + queue->level_buffers--; + queue->level_bytes -= GST_BUFFER_SIZE(leakbuf); + gst_buffer_unref(leakbuf); + queue->queue = g_slist_remove_link (queue->queue, front); + g_slist_free (front); + } + } - while (queue->level_buffers >= queue->max_buffers) { - GST_DEBUG (0,"queue: %s waiting %d\n", name, queue->level_buffers); - STATUS("%s: O\n"); - //g_cond_timed_wait (queue->fullcond, queue->fulllock, queue->timeval); - //FIXME need to signal other thread in case signals got lost? - g_cond_signal (queue->emptycond); - g_cond_wait (queue->fullcond, GST_OBJECT(queue)->lock); - STATUS("%s: O+\n"); - GST_DEBUG (0,"queue: %s waiting done %d\n", name, queue->level_buffers); + while (queue->level_buffers >= queue->size_buffers) { + // if there's a pending state change for this queue or its manager, switch + // back to iterator so bottom half of state change executes + if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING || +// GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING) +GST_STATE_PENDING(GST_SCHED_PARENT(GST_ELEMENT_SCHED(GST_PAD_PARENT(GST_PAD_PEER(queue->sinkpad))))) != +GST_STATE_NONE_PENDING) + { + GST_DEBUG(GST_CAT_DATAFLOW,"interrupted!!\n"); + if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING) + GST_DEBUG(GST_CAT_DATAFLOW,"GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING)\n"); + if (GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING) + GST_DEBUG(GST_CAT_DATAFLOW,"GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING\n"); + GST_UNLOCK(queue); + cothread_switch(cothread_current_main()); + } + + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "waiting for space, level is %d\n", queue->level_buffers); + g_cond_signal (queue->emptycond); + g_cond_wait (queue->fullcond, GST_OBJECT(queue)->lock); + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "done waiting, level is now %d\n", queue->level_buffers); + } } /* put the buffer on the tail of the list */ queue->queue = g_slist_append (queue->queue, buf); -// STATUS("%s: +\n"); - GST_DEBUG (0,"(%s:%s)+ ",GST_DEBUG_PAD_NAME(pad)); - - /* if we were empty, but aren't any more, signal a condition */ - tosignal = (queue->level_buffers >= 0); queue->level_buffers++; + queue->level_bytes += GST_BUFFER_SIZE(buf); +// GST_DEBUG (GST_CAT_DATAFLOW, "(%s:%s)+\n",GST_DEBUG_PAD_NAME(pad)); - /* we can unlock now */ - GST_DEBUG (0,"queue: %s chain %d end signal(%d,%p)\n", name, queue->level_buffers, tosignal, queue->emptycond); - - if (tosignal) { -// STATUS("%s: >\n"); + /* if we were empty, but aren't any more, signal a condition */ + if (queue->level_buffers == 1) + { + GST_DEBUG (GST_CAT_DATAFLOW,"%s signalling emptycond\n", name); g_cond_signal (queue->emptycond); -// STATUS("%s: >>\n"); } + GST_UNLOCK (queue); } @@ -307,6 +367,8 @@ gst_queue_get (GstPad *pad) GSList *front; const guchar *name; + g_assert(pad != NULL); + g_assert(GST_IS_PAD(pad)); g_return_val_if_fail (pad != NULL, NULL); g_return_val_if_fail (GST_IS_PAD (pad), NULL); @@ -314,63 +376,74 @@ gst_queue_get (GstPad *pad) name = GST_ELEMENT_NAME (queue); /* have to lock for thread-safety */ - GST_DEBUG (0,"queue: %s try have queue lock\n", name); + GST_DEBUG (GST_CAT_DATAFLOW,"%s try have queue lock\n", name); GST_LOCK (queue); - GST_DEBUG (0,"queue: %s push %d %ld %p\n", name, queue->level_buffers, pthread_self (), queue->emptycond); - GST_DEBUG (0,"queue: %s have queue lock\n", name); - - // we bail if there's nothing there - if (!queue->level_buffers && !queue->block) { - GST_UNLOCK(queue); - return NULL; - } + GST_DEBUG (GST_CAT_DATAFLOW,"%s push %d %ld %p\n", name, queue->level_buffers, pthread_self (), queue->emptycond); + GST_DEBUG (GST_CAT_DATAFLOW,"%s have queue lock\n", name); while (!queue->level_buffers) { - STATUS("queue: %s U released lock\n"); - //g_cond_timed_wait (queue->emptycond, queue->emptylock, queue->timeval); if (GST_FLAG_IS_SET (queue->sinkpad, GST_PAD_EOS)) { + GST_DEBUG (GST_CAT_DATAFLOW, "%s U released lock\n", name); + GST_UNLOCK(queue); gst_pad_set_eos (queue->srcpad); + // this return NULL shouldn't hurt anything... return NULL; } - //FIXME need to signal other thread in case signals got lost? + + // if there's a pending state change for this queue or its manager, switch + // back to iterator so bottom half of state change executes + if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING || +// GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING) +GST_STATE_PENDING(GST_SCHED_PARENT(GST_ELEMENT_SCHED(GST_PAD_PARENT(GST_PAD_PEER(queue->srcpad))))) != +GST_STATE_NONE_PENDING) + { + GST_DEBUG(GST_CAT_DATAFLOW,"interrupted!!\n"); + if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING) + GST_DEBUG(GST_CAT_DATAFLOW,"GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING)\n"); + if (GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING) + GST_DEBUG(GST_CAT_DATAFLOW,"GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING\n"); + GST_UNLOCK(queue); + cothread_switch(cothread_current_main()); + } + g_cond_signal (queue->fullcond); g_cond_wait (queue->emptycond, GST_OBJECT(queue)->lock); -// STATUS("queue: %s U- getting lock\n"); } front = queue->queue; buf = (GstBuffer *)(front->data); - GST_DEBUG (0,"retrieved buffer %p from queue\n",buf); + GST_DEBUG (GST_CAT_DATAFLOW,"retrieved buffer %p from queue\n",buf); queue->queue = g_slist_remove_link (queue->queue, front); g_slist_free (front); - queue->level_buffers--; -// STATUS("%s: -\n"); - GST_DEBUG (0,"(%s:%s)- ",GST_DEBUG_PAD_NAME(pad)); - - if (queue->level_buffers < queue->max_buffers) { -// STATUS("%s: < \n"); +// if (queue->level_buffers < queue->size_buffers) + if (queue->level_buffers == queue->size_buffers) + { + GST_DEBUG (GST_CAT_DATAFLOW,"%s signalling fullcond\n", name); g_cond_signal (queue->fullcond); -// STATUS("%s: << \n"); } - GST_UNLOCK(queue); -// GST_DEBUG (0,"queue: %s pushing %d %p \n", name, queue->level_buffers, buf); -// gst_pad_push (queue->srcpad, buf); -// GST_DEBUG (0,"queue: %s pushing %d done \n", name, queue->level_buffers); + queue->level_buffers--; + queue->level_bytes -= GST_BUFFER_SIZE(buf); + GST_DEBUG (GST_CAT_DATAFLOW,"(%s:%s)- ",GST_DEBUG_PAD_NAME(pad)); + + GST_UNLOCK(queue); return buf; - /* unlock now */ } static GstElementStateReturn gst_queue_change_state (GstElement *element) { GstQueue *queue; + GstElementStateReturn ret; g_return_val_if_fail (GST_IS_QUEUE (element), GST_STATE_FAILURE); queue = GST_QUEUE (element); - GST_DEBUG (0,"gstqueue: state pending %d\n", GST_STATE_PENDING (element)); + + // lock the queue so another thread (not in sync with this thread's state) + // can't call this queue's _get (or whatever) + GST_LOCK (queue); /* if going down into NULL state, clear out buffers*/ if (GST_STATE_PENDING (element) == GST_STATE_READY) { @@ -380,9 +453,41 @@ gst_queue_change_state (GstElement *element) /* if we haven't failed already, give the parent class a chance to ;-) */ if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); + { + gboolean valid_handler = FALSE; + guint state_change_id = gtk_signal_lookup("state_change", GTK_OBJECT_TYPE(element)); + + // determine whether we need to block the parent (element) class' + // STATE_CHANGE signal so we can UNLOCK before returning. we block + // it if we could find the state_change signal AND there's a signal + // handler attached to it. + // + // note: this assumes that change_state() *only* emits state_change signal. + // if element change_state() emits other signals, they need to be blocked + // as well. + if (state_change_id && + gtk_signal_handler_pending(GTK_OBJECT(element), state_change_id, FALSE)) + valid_handler = TRUE; + if (valid_handler) + gtk_signal_handler_block(GTK_OBJECT(element), state_change_id); + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + if (valid_handler) + gtk_signal_handler_unblock(GTK_OBJECT(element), state_change_id); + + // UNLOCK, *then* emit signal (if there's one there) + GST_UNLOCK(queue); + if (valid_handler) + gtk_signal_emit(GTK_OBJECT (element), state_change_id, GST_STATE(element)); + } + else + { + ret = GST_STATE_SUCCESS; + GST_UNLOCK(queue); + } - return GST_STATE_SUCCESS; + return ret; } @@ -397,11 +502,11 @@ gst_queue_set_arg (GtkObject *object, GtkArg *arg, guint id) queue = GST_QUEUE (object); switch(id) { - case ARG_MAX_LEVEL: - queue->max_buffers = GTK_VALUE_INT (*arg); + case ARG_LEAKY: + queue->leaky = GTK_VALUE_INT (*arg); break; - case ARG_BLOCK: - queue->block = GTK_VALUE_BOOL (*arg); + case ARG_MAX_LEVEL: + queue->size_buffers = GTK_VALUE_INT (*arg); break; default: break; @@ -419,14 +524,14 @@ gst_queue_get_arg (GtkObject *object, GtkArg *arg, guint id) queue = GST_QUEUE (object); switch (id) { + case ARG_LEAKY: + GTK_VALUE_INT (*arg) = queue->leaky; + break; case ARG_LEVEL: GTK_VALUE_INT (*arg) = queue->level_buffers; break; case ARG_MAX_LEVEL: - GTK_VALUE_INT (*arg) = queue->max_buffers; - break; - case ARG_BLOCK: - GTK_VALUE_BOOL (*arg) = queue->block; + GTK_VALUE_INT (*arg) = queue->size_buffers; break; default: arg->type = GTK_TYPE_INVALID; diff --git a/gst/gstqueue.h b/gst/gstqueue.h index 606346735..085d5ac12 100644 --- a/gst/gstqueue.h +++ b/gst/gstqueue.h @@ -47,6 +47,12 @@ GstElementDetails gst_queue_details; #define GST_IS_QUEUE_CLASS(obj) \ (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_QUEUE)) +enum { + GST_QUEUE_NO_LEAK = 0, + GST_QUEUE_LEAK_UPSTREAM = 1, + GST_QUEUE_LEAK_DOWNSTREAM = 2 +}; + typedef struct _GstQueue GstQueue; typedef struct _GstQueueClass GstQueueClass; @@ -60,12 +66,16 @@ struct _GstQueue { GSList *queue; gint level_buffers; /* number of buffers queued here */ - gint max_buffers; /* maximum number of buffers queued here */ - gboolean block; /* if set to FALSE, _get returns NULL if queue empty */ gint level_bytes; /* number of bytes queued here */ + guint64 level_time; /* amount of time queued here */ + gint size_buffers; /* size of queue in buffers */ gint size_bytes; /* size of queue in bytes */ + guint64 size_time; /* size of queue in time */ + gint leaky; /* whether the queue is leaky, and if so at which end */ + +// GMutex *lock; (optimization?) GCond *emptycond; GCond *fullcond; @@ -74,6 +84,10 @@ struct _GstQueue { struct _GstQueueClass { GstElementClass parent_class; + + /* signal callbacks */ + void (*low_watermark) (GstQueue *queue, gint level); + void (*high_watermark) (GstQueue *queue, gint level); }; GtkType gst_queue_get_type (void); diff --git a/gst/gstscheduler.c b/gst/gstscheduler.c index e4e1d79b6..0795c18cd 100644 --- a/gst/gstscheduler.c +++ b/gst/gstscheduler.c @@ -27,7 +27,7 @@ static int -gst_bin_loopfunc_wrapper (int argc,char *argv[]) +gst_schedule_loopfunc_wrapper (int argc,char *argv[]) { GstElement *element = GST_ELEMENT (argv); G_GNUC_UNUSED const gchar *name = GST_ELEMENT_NAME (element); @@ -35,10 +35,10 @@ gst_bin_loopfunc_wrapper (int argc,char *argv[]) GST_DEBUG_ENTER("(%d,'%s')",argc,name); do { - GST_DEBUG (0,"calling loopfunc %s for element %s\n", - GST_DEBUG_FUNCPTR_NAME (element->loopfunc),name); + GST_DEBUG (GST_CAT_DATAFLOW,"calling loopfunc %s for element %s\n", + GST_DEBUG_FUNCPTR_NAME (element->loopfunc),name); (element->loopfunc) (element); - GST_DEBUG (0,"element %s ended loop function\n", name); + GST_DEBUG (GST_CAT_DATAFLOW,"element %s ended loop function\n", name); } while (!GST_ELEMENT_IS_COTHREAD_STOPPING(element)); GST_FLAG_UNSET(element,GST_ELEMENT_COTHREAD_STOPPING); @@ -47,7 +47,7 @@ gst_bin_loopfunc_wrapper (int argc,char *argv[]) } static int -gst_bin_chain_wrapper (int argc,char *argv[]) +gst_schedule_chain_wrapper (int argc,char *argv[]) { GstElement *element = GST_ELEMENT (argv); G_GNUC_UNUSED const gchar *name = GST_ELEMENT_NAME (element); @@ -57,7 +57,8 @@ gst_bin_chain_wrapper (int argc,char *argv[]) GstBuffer *buf; GST_DEBUG_ENTER("(\"%s\")",name); - GST_DEBUG (0,"stepping through pads\n"); + + GST_DEBUG (GST_CAT_DATAFLOW,"stepping through pads\n"); do { pads = element->pads; while (pads) { @@ -66,11 +67,11 @@ gst_bin_chain_wrapper (int argc,char *argv[]) if (!GST_IS_REAL_PAD(pad)) continue; realpad = GST_REAL_PAD(pad); if (GST_RPAD_DIRECTION(realpad) == GST_PAD_SINK) { - GST_DEBUG (0,"pulling a buffer from %s:%s\n", name, GST_PAD_NAME (pad)); + GST_DEBUG (GST_CAT_DATAFLOW,"pulling a buffer from %s:%s\n", name, GST_PAD_NAME (pad)); buf = gst_pad_pull (pad); - GST_DEBUG (0,"calling chain function of %s:%s\n", name, GST_PAD_NAME (pad)); + GST_DEBUG (GST_CAT_DATAFLOW,"calling chain function of %s:%s\n", name, GST_PAD_NAME (pad)); if (buf) GST_RPAD_CHAINFUNC(realpad) (pad,buf); - GST_DEBUG (0,"calling chain function of %s:%s done\n", name, GST_PAD_NAME (pad)); + GST_DEBUG (GST_CAT_DATAFLOW,"calling chain function of %s:%s done\n", name, GST_PAD_NAME (pad)); } } } while (!GST_ELEMENT_IS_COTHREAD_STOPPING(element)); @@ -81,7 +82,7 @@ gst_bin_chain_wrapper (int argc,char *argv[]) } static int -gst_bin_src_wrapper (int argc,char *argv[]) +gst_schedule_src_wrapper (int argc,char *argv[]) { GstElement *element = GST_ELEMENT (argv); GList *pads; @@ -98,7 +99,7 @@ gst_bin_src_wrapper (int argc,char *argv[]) realpad = (GstRealPad*)(pads->data); pads = g_list_next(pads); if (GST_RPAD_DIRECTION(realpad) == GST_PAD_SRC) { - GST_DEBUG (0,"calling _getfunc for %s:%s\n",GST_DEBUG_PAD_NAME(realpad)); + GST_DEBUG (GST_CAT_DATAFLOW,"calling _getfunc for %s:%s\n",GST_DEBUG_PAD_NAME(realpad)); if (realpad->regiontype != GST_REGION_NONE) { g_return_val_if_fail (GST_RPAD_GETREGIONFUNC(realpad) != NULL, 0); // if (GST_RPAD_GETREGIONFUNC(realpad) == NULL) @@ -114,7 +115,7 @@ gst_bin_src_wrapper (int argc,char *argv[]) buf = GST_RPAD_GETFUNC(realpad) ((GstPad*)realpad); } - GST_DEBUG (0,"calling gst_pad_push on pad %s:%s\n",GST_DEBUG_PAD_NAME(realpad)); + GST_DEBUG (GST_CAT_DATAFLOW,"calling gst_pad_push on pad %s:%s\n",GST_DEBUG_PAD_NAME(realpad)); if (buf) gst_pad_push ((GstPad*)realpad, buf); } } @@ -126,40 +127,56 @@ gst_bin_src_wrapper (int argc,char *argv[]) } static void -gst_bin_pushfunc_proxy (GstPad *pad, GstBuffer *buf) +gst_schedule_pushfunc_proxy (GstPad *pad, GstBuffer *buf) { - cothread_state *threadstate = GST_ELEMENT (GST_PAD_PARENT (pad))->threadstate; + GstRealPad *peer = GST_RPAD_PEER(pad); + GST_DEBUG_ENTER("(%s:%s)",GST_DEBUG_PAD_NAME(pad)); GST_DEBUG (GST_CAT_DATAFLOW,"putting buffer %p in peer's pen\n",buf); // FIXME this should be bounded // loop until the bufferpen is empty so we can fill it up again while (GST_RPAD_BUFPEN(pad) != NULL) { - GST_DEBUG (GST_CAT_DATAFLOW, "switching to %p to empty bufpen\n",threadstate); - cothread_switch (threadstate); + GST_DEBUG (GST_CAT_DATAFLOW, "switching to %p to empty bufpen\n", + GST_ELEMENT (GST_PAD_PARENT (pad))->threadstate); + cothread_switch (GST_ELEMENT (GST_PAD_PARENT (pad))->threadstate); + + // we may no longer be the same pad, check. + if (GST_RPAD_PEER(peer) != (GstRealPad *)pad) { + GST_DEBUG (GST_CAT_DATAFLOW, "new pad in mid-switch!\n"); + pad = (GstPad *)GST_RPAD_PEER(peer); + } } // now fill the bufferpen and switch so it can be consumed GST_RPAD_BUFPEN(GST_RPAD_PEER(pad)) = buf; - GST_DEBUG (GST_CAT_DATAFLOW,"switching to %p (@%p)\n",threadstate,&(GST_ELEMENT(GST_PAD_PARENT(pad))->threadstate)); - cothread_switch (threadstate); + GST_DEBUG (GST_CAT_DATAFLOW,"switching to %p\n",GST_ELEMENT (GST_PAD_PARENT (pad))->threadstate); + cothread_switch (GST_ELEMENT (GST_PAD_PARENT (pad))->threadstate); GST_DEBUG (GST_CAT_DATAFLOW,"done switching\n"); } static GstBuffer* -gst_bin_pullfunc_proxy (GstPad *pad) +gst_schedule_pullfunc_proxy (GstPad *pad) { GstBuffer *buf; + GstRealPad *peer = GST_RPAD_PEER(pad); - cothread_state *threadstate = GST_ELEMENT(GST_PAD_PARENT(pad))->threadstate; GST_DEBUG_ENTER("(%s:%s)",GST_DEBUG_PAD_NAME(pad)); // FIXME this should be bounded // we will loop switching to the peer until it's filled up the bufferpen while (GST_RPAD_BUFPEN(pad) == NULL) { - GST_DEBUG (GST_CAT_DATAFLOW, "switching to %p to fill bufpen\n",threadstate); - cothread_switch (threadstate); + GST_DEBUG (GST_CAT_DATAFLOW, "switching to \"%s\": %p to fill bufpen\n", + GST_ELEMENT_NAME(GST_ELEMENT(GST_PAD_PARENT(pad))), + GST_ELEMENT(GST_PAD_PARENT(pad))->threadstate); + cothread_switch (GST_ELEMENT(GST_PAD_PARENT(pad))->threadstate); + + // we may no longer be the same pad, check. + if (GST_RPAD_PEER(peer) != (GstRealPad *)pad) { + GST_DEBUG (GST_CAT_DATAFLOW, "new pad in mid-switch!\n"); + pad = (GstPad *)GST_RPAD_PEER(peer); + } } GST_DEBUG (GST_CAT_DATAFLOW,"done switching\n"); @@ -170,11 +187,11 @@ gst_bin_pullfunc_proxy (GstPad *pad) } static GstBuffer* -gst_bin_pullregionfunc_proxy (GstPad *pad,GstRegionType type,guint64 offset,guint64 len) +gst_schedule_pullregionfunc_proxy (GstPad *pad,GstRegionType type,guint64 offset,guint64 len) { GstBuffer *buf; + GstRealPad *peer = GST_RPAD_PEER(pad); - cothread_state *threadstate = GST_ELEMENT(GST_PAD_PARENT(pad))->threadstate; GST_DEBUG_ENTER("%s:%s,%d,%lld,%lld",GST_DEBUG_PAD_NAME(pad),type,offset,len); // put the region info into the pad @@ -185,8 +202,15 @@ gst_bin_pullregionfunc_proxy (GstPad *pad,GstRegionType type,guint64 offset,guin // FIXME this should be bounded // we will loop switching to the peer until it's filled up the bufferpen while (GST_RPAD_BUFPEN(pad) == NULL) { - GST_DEBUG (GST_CAT_DATAFLOW, "switching to %p to fill bufpen\n",threadstate); - cothread_switch (threadstate); + GST_DEBUG (GST_CAT_DATAFLOW, "switching to %p to fill bufpen\n", + GST_ELEMENT(GST_PAD_PARENT(pad))->threadstate); + cothread_switch (GST_ELEMENT(GST_PAD_PARENT(pad))->threadstate); + + // we may no longer be the same pad, check. + if (GST_RPAD_PEER(peer) != (GstRealPad *)pad) { + GST_DEBUG (GST_CAT_DATAFLOW, "new pad in mid-switch!\n"); + pad = (GstPad *)GST_RPAD_PEER(peer); + } } GST_DEBUG (GST_CAT_DATAFLOW,"done switching\n"); @@ -198,18 +222,18 @@ gst_bin_pullregionfunc_proxy (GstPad *pad,GstRegionType type,guint64 offset,guin static void -gst_schedule_cothreaded_chain (GstBin *bin, _GstBinChain *chain) { +gst_schedule_cothreaded_chain (GstBin *bin, GstScheduleChain *chain) { GList *elements; GstElement *element; cothread_func wrapper_function; GList *pads; GstPad *pad; - GST_DEBUG (0,"chain is using cothreads\n"); + GST_DEBUG (GST_CAT_SCHEDULING,"chain is using COTHREADS\n"); // first create thread context if (bin->threadcontext == NULL) { - GST_DEBUG (0,"initializing cothread context\n"); + GST_DEBUG (GST_CAT_SCHEDULING,"initializing cothread context\n"); bin->threadcontext = cothread_init (); } @@ -224,19 +248,19 @@ gst_schedule_cothreaded_chain (GstBin *bin, _GstBinChain *chain) { // if the element has a loopfunc... if (element->loopfunc != NULL) { - wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_loopfunc_wrapper); - GST_DEBUG (0,"\nelement '%s' is a loop-based\n",GST_ELEMENT_NAME(element)); + wrapper_function = GST_DEBUG_FUNCPTR(gst_schedule_loopfunc_wrapper); + GST_DEBUG (GST_CAT_SCHEDULING,"element '%s' is a loop-based\n",GST_ELEMENT_NAME(element)); } else { // otherwise we need to decide what kind of cothread // if it's not DECOUPLED, we decide based on whether it's a source or not if (!GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { // if it doesn't have any sinks, it must be a source (duh) if (element->numsinkpads == 0) { - wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_src_wrapper); - GST_DEBUG (0,"\nelement '%s' is a source, using _src_wrapper\n",GST_ELEMENT_NAME(element)); + wrapper_function = GST_DEBUG_FUNCPTR(gst_schedule_src_wrapper); + GST_DEBUG (GST_CAT_SCHEDULING,"element '%s' is a source, using _src_wrapper\n",GST_ELEMENT_NAME(element)); } else { - wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_chain_wrapper); - GST_DEBUG (0,"\nelement '%s' is a filter, using _chain_wrapper\n",GST_ELEMENT_NAME(element)); + wrapper_function = GST_DEBUG_FUNCPTR(gst_schedule_chain_wrapper); + GST_DEBUG (GST_CAT_SCHEDULING,"element '%s' is a filter, using _chain_wrapper\n",GST_ELEMENT_NAME(element)); } } } @@ -250,13 +274,15 @@ gst_schedule_cothreaded_chain (GstBin *bin, _GstBinChain *chain) { // if the element is DECOUPLED or outside the manager, we have to chain if ((wrapper_function == NULL) || - (GST_ELEMENT (GST_PAD_PARENT (GST_PAD (GST_RPAD_PEER (pad))))->manager != GST_ELEMENT(bin))) { + (GST_RPAD_PEER(pad) && + (GST_ELEMENT (GST_PAD_PARENT (GST_PAD (GST_RPAD_PEER (pad))))->sched != chain->sched)) + ) { // set the chain proxies if (GST_RPAD_DIRECTION(pad) == GST_PAD_SINK) { - GST_DEBUG (0,"copying chain function into push proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + GST_DEBUG (GST_CAT_SCHEDULING,"copying chain function into push proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad)); GST_RPAD_PUSHFUNC(pad) = GST_RPAD_CHAINFUNC(pad); } else { - GST_DEBUG (0,"copying get function into pull proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + GST_DEBUG (GST_CAT_SCHEDULING,"copying get function into pull proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad)); GST_RPAD_PULLFUNC(pad) = GST_RPAD_GETFUNC(pad); GST_RPAD_PULLREGIONFUNC(pad) = GST_RPAD_GETREGIONFUNC(pad); } @@ -264,12 +290,12 @@ gst_schedule_cothreaded_chain (GstBin *bin, _GstBinChain *chain) { // otherwise we really are a cothread } else { if (gst_pad_get_direction (pad) == GST_PAD_SINK) { - GST_DEBUG (0,"setting cothreaded push proxy for sinkpad %s:%s\n",GST_DEBUG_PAD_NAME(pad)); - GST_RPAD_PUSHFUNC(pad) = GST_DEBUG_FUNCPTR(gst_bin_pushfunc_proxy); + GST_DEBUG (GST_CAT_SCHEDULING,"setting cothreaded push proxy for sinkpad %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + GST_RPAD_PUSHFUNC(pad) = GST_DEBUG_FUNCPTR(gst_schedule_pushfunc_proxy); } else { - GST_DEBUG (0,"setting cothreaded pull proxy for srcpad %s:%s\n",GST_DEBUG_PAD_NAME(pad)); - GST_RPAD_PULLFUNC(pad) = GST_DEBUG_FUNCPTR(gst_bin_pullfunc_proxy); - GST_RPAD_PULLREGIONFUNC(pad) = GST_DEBUG_FUNCPTR(gst_bin_pullregionfunc_proxy); + GST_DEBUG (GST_CAT_SCHEDULING,"setting cothreaded pull proxy for srcpad %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + GST_RPAD_PULLFUNC(pad) = GST_DEBUG_FUNCPTR(gst_schedule_pullfunc_proxy); + GST_RPAD_PULLREGIONFUNC(pad) = GST_DEBUG_FUNCPTR(gst_schedule_pullregionfunc_proxy); } } } @@ -279,10 +305,10 @@ gst_schedule_cothreaded_chain (GstBin *bin, _GstBinChain *chain) { if (element->threadstate == NULL) { // FIXME handle cothread_create returning NULL element->threadstate = cothread_create (bin->threadcontext); - GST_DEBUG (0,"created cothread %p for '%s'\n",element->threadstate,GST_ELEMENT_NAME(element)); + GST_DEBUG (GST_CAT_SCHEDULING,"created cothread %p for '%s'\n",element->threadstate,GST_ELEMENT_NAME(element)); } cothread_setfunc (element->threadstate, wrapper_function, 0, (char **)element); - GST_DEBUG (0,"set wrapper function for '%s' to &%s\n",GST_ELEMENT_NAME(element), + GST_DEBUG (GST_CAT_SCHEDULING,"set wrapper function for '%s' to &%s\n",GST_ELEMENT_NAME(element), GST_DEBUG_FUNCPTR_NAME(wrapper_function)); } } @@ -295,7 +321,7 @@ gst_schedule_chained_chain (GstBin *bin, _GstBinChain *chain) { GList *pads; GstPad *pad; - GST_DEBUG (0,"chain entered\n"); + GST_DEBUG (GST_CAT_SCHEDULING,"chain entered\n"); // walk through all the elements elements = chain->elements; while (elements) { @@ -310,10 +336,10 @@ gst_schedule_chained_chain (GstBin *bin, _GstBinChain *chain) { if (!GST_IS_REAL_PAD(pad)) continue; if (GST_RPAD_DIRECTION(pad) == GST_PAD_SINK) { - GST_DEBUG (0,"copying chain function into push proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + GST_DEBUG (GST_CAT_SCHEDULING,"copying chain function into push proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad)); GST_RPAD_PUSHFUNC(pad) = GST_RPAD_CHAINFUNC(pad); } else { - GST_DEBUG (0,"copying get function into pull proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + GST_DEBUG (GST_CAT_SCHEDULING,"copying get function into pull proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad)); GST_RPAD_PULLFUNC(pad) = GST_RPAD_GETFUNC(pad); GST_RPAD_PULLREGIONFUNC(pad) = GST_RPAD_GETREGIONFUNC(pad); } @@ -321,6 +347,7 @@ gst_schedule_chained_chain (GstBin *bin, _GstBinChain *chain) { } } +/* depracated!! */ static void gst_bin_schedule_cleanup (GstBin *bin) { @@ -332,6 +359,7 @@ gst_bin_schedule_cleanup (GstBin *bin) chain = (_GstBinChain *)(chains->data); chains = g_list_next(chains); +// g_list_free(chain->disabled); g_list_free(chain->elements); g_list_free(chain->entries); @@ -345,10 +373,11 @@ gst_bin_schedule_cleanup (GstBin *bin) static void gst_scheduler_handle_eos (GstElement *element, _GstBinChain *chain) { - GST_DEBUG (0,"chain removed from scheduler, EOS from element \"%s\"\n", GST_ELEMENT_NAME (element)); + GST_DEBUG (GST_CAT_SCHEDULING,"chain removed from scheduler, EOS from element \"%s\"\n", GST_ELEMENT_NAME (element)); chain->need_scheduling = FALSE; } +/* void gst_bin_schedule_func(GstBin *bin) { GList *elements; GstElement *element; @@ -357,14 +386,14 @@ void gst_bin_schedule_func(GstBin *bin) { GstPad *pad; GstElement *peerparent; GList *chains; - _GstBinChain *chain; + GstScheduleChain *chain; GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME (GST_ELEMENT (bin))); gst_bin_schedule_cleanup(bin); // next we have to find all the separate scheduling chains - GST_DEBUG (0,"\nattempting to find scheduling chains...\n"); + GST_DEBUG (GST_CAT_SCHEDULING,"attempting to find scheduling chains...\n"); // first make a copy of the managed_elements we can mess with elements = g_list_copy (bin->managed_elements); // we have to repeat until the list is empty to get all chains @@ -374,12 +403,12 @@ void gst_bin_schedule_func(GstBin *bin) { // if this is a DECOUPLED element if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { // skip this element entirely - GST_DEBUG (0,"skipping '%s' because it's decoupled\n",GST_ELEMENT_NAME(element)); + GST_DEBUG (GST_CAT_SCHEDULING,"skipping '%s' because it's decoupled\n",GST_ELEMENT_NAME(element)); elements = g_list_next (elements); continue; } - GST_DEBUG (0,"starting with element '%s'\n",GST_ELEMENT_NAME(element)); + GST_DEBUG (GST_CAT_SCHEDULING,"starting with element '%s'\n",GST_ELEMENT_NAME(element)); // prime the pending list with the first element off the top pending = g_slist_prepend (NULL, element); @@ -397,7 +426,7 @@ void gst_bin_schedule_func(GstBin *bin) { pending = g_slist_remove (pending, element); // add ourselves to the chain's list of elements - GST_DEBUG (0,"adding '%s' to chain\n",GST_ELEMENT_NAME(element)); + GST_DEBUG (GST_CAT_SCHEDULING,"adding '%s' to chain\n",GST_ELEMENT_NAME(element)); chain->elements = g_list_prepend (chain->elements, element); chain->num_elements++; gtk_signal_connect (GTK_OBJECT (element), "eos", gst_scheduler_handle_eos, chain); @@ -412,13 +441,13 @@ void gst_bin_schedule_func(GstBin *bin) { if ((element->manager == GST_ELEMENT(bin)) && !GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { // remove ourselves from the outer list of all managed elements -// GST_DEBUG (0,"removing '%s' from list of possible elements\n",GST_ELEMENT_NAME(element)); +// GST_DEBUG (GST_CAT_SCHEDULING,"removing '%s' from list of possible elements\n",GST_ELEMENT_NAME(element)); elements = g_list_remove (elements, element); // if this element is a source, add it as an entry if (element->numsinkpads == 0) { chain->entries = g_list_prepend (chain->entries, element); - GST_DEBUG (0,"added '%s' as SRC entry into the chain\n",GST_ELEMENT_NAME(element)); + GST_DEBUG (GST_CAT_SCHEDULING,"added '%s' as SRC entry into the chain\n",GST_ELEMENT_NAME(element)); } // now we have to walk the pads to find peers @@ -427,21 +456,22 @@ void gst_bin_schedule_func(GstBin *bin) { pad = GST_PAD (pads->data); pads = g_list_next (pads); if (!GST_IS_REAL_PAD(pad)) continue; - GST_DEBUG (0,"have pad %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + GST_DEBUG (GST_CAT_SCHEDULING,"have pad %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + if (GST_RPAD_PEER(pad) == NULL) continue; if (GST_RPAD_PEER(pad) == NULL) GST_ERROR(pad,"peer is null!"); g_assert(GST_RPAD_PEER(pad) != NULL); g_assert(GST_PAD_PARENT (GST_PAD(GST_RPAD_PEER(pad))) != NULL); peerparent = GST_ELEMENT(GST_PAD_PARENT (GST_PAD(GST_RPAD_PEER(pad)))); - GST_DEBUG (0,"peer pad %p\n", GST_RPAD_PEER(pad)); + GST_DEBUG (GST_CAT_SCHEDULING,"peer pad %p\n", GST_RPAD_PEER(pad)); // only bother with if the pad's peer's parent is this bin or it's DECOUPLED // only add it if it's in the list of un-visited elements still if ((g_list_find (elements, peerparent) != NULL) || GST_FLAG_IS_SET (peerparent, GST_ELEMENT_DECOUPLED)) { // add the peer element to the pending list - GST_DEBUG (0,"adding '%s' to list of pending elements\n", + GST_DEBUG (GST_CAT_SCHEDULING,"adding '%s' to list of pending elements\n", GST_ELEMENT_NAME(peerparent)); pending = g_slist_prepend (pending, peerparent); @@ -450,36 +480,36 @@ void gst_bin_schedule_func(GstBin *bin) { (GST_FLAG_IS_SET (peerparent, GST_ELEMENT_DECOUPLED))) { chain->entries = g_list_prepend (chain->entries, peerparent); gtk_signal_connect (GTK_OBJECT (peerparent), "eos", gst_scheduler_handle_eos, chain); - GST_DEBUG (0,"added '%s' as DECOUPLED entry into the chain\n",GST_ELEMENT_NAME(peerparent)); + GST_DEBUG (GST_CAT_SCHEDULING,"added '%s' as DECOUPLED entry into the chain\n",GST_ELEMENT_NAME(peerparent)); } } else - GST_DEBUG (0,"element '%s' has already been dealt with\n",GST_ELEMENT_NAME(peerparent)); + GST_DEBUG (GST_CAT_SCHEDULING,"element '%s' has already been dealt with\n",GST_ELEMENT_NAME(peerparent)); } } } while (pending); // add the chain to the bin - GST_DEBUG (0,"have chain with %d elements: ",chain->num_elements); + GST_DEBUG (GST_CAT_SCHEDULING,"have chain with %d elements: ",chain->num_elements); { GList *elements = chain->elements; while (elements) { element = GST_ELEMENT (elements->data); elements = g_list_next(elements); - GST_DEBUG_NOPREFIX(0,"%s, ",GST_ELEMENT_NAME(element)); + GST_DEBUG_NOPREFIX(GST_CAT_SCHEDULING,"%s, ",GST_ELEMENT_NAME(element)); } } - GST_DEBUG_NOPREFIX(0,"\n"); + GST_DEBUG_NOPREFIX(GST_CAT_DATAFLOW,"\n"); bin->chains = g_list_prepend (bin->chains, chain); bin->num_chains++; } // free up the list in case it's full of DECOUPLED elements g_list_free (elements); - GST_DEBUG (0,"\nwe have %d chains to schedule\n",bin->num_chains); + GST_DEBUG (GST_CAT_SCHEDULING,"\nwe have %d chains to schedule\n",bin->num_chains); // now we have to go through all the chains and schedule them chains = bin->chains; while (chains) { - chain = (_GstBinChain *)(chains->data); + chain = (GstScheduleChain *)(chains->data); chains = g_list_next (chains); // schedule as appropriate @@ -492,6 +522,7 @@ void gst_bin_schedule_func(GstBin *bin) { GST_DEBUG_LEAVE("(\"%s\")",GST_ELEMENT_NAME(GST_ELEMENT(bin))); } +*/ /* @@ -744,4 +775,691 @@ void gst_bin_schedule_func(GstBin *bin) { } */ +static void +gst_schedule_lock_element (GstSchedule *sched,GstElement *element) +{ + cothread_lock(element->threadstate); +} + +static void +gst_schedule_unlock_element (GstSchedule *sched,GstElement *element) +{ + cothread_unlock(element->threadstate); +} + + +/*************** INCREMENTAL SCHEDULING CODE STARTS HERE ***************/ + + +static void gst_schedule_class_init (GstScheduleClass *klass); +static void gst_schedule_init (GstSchedule *schedule); + +static GstObjectClass *parent_class = NULL; + +GtkType gst_schedule_get_type(void) { + static GtkType schedule_type = 0; + + if (!schedule_type) { + static const GtkTypeInfo schedule_info = { + "GstSchedule", + sizeof(GstSchedule), + sizeof(GstScheduleClass), + (GtkClassInitFunc)gst_schedule_class_init, + (GtkObjectInitFunc)gst_schedule_init, + (GtkArgSetFunc)NULL, + (GtkArgGetFunc)NULL, + (GtkClassInitFunc)NULL, + }; + schedule_type = gtk_type_unique(GST_TYPE_OBJECT,&schedule_info); + } + return schedule_type; +} + +static void +gst_schedule_class_init (GstScheduleClass *klass) +{ + parent_class = gtk_type_class(GST_TYPE_OBJECT); +} + +static void +gst_schedule_init (GstSchedule *schedule) +{ + schedule->add_element = GST_DEBUG_FUNCPTR(gst_schedule_add_element); + schedule->remove_element = GST_DEBUG_FUNCPTR(gst_schedule_remove_element); + schedule->enable_element = GST_DEBUG_FUNCPTR(gst_schedule_enable_element); + schedule->disable_element = GST_DEBUG_FUNCPTR(gst_schedule_disable_element); + schedule->lock_element = GST_DEBUG_FUNCPTR(gst_schedule_lock_element); + schedule->unlock_element = GST_DEBUG_FUNCPTR(gst_schedule_unlock_element); + schedule->pad_connect = GST_DEBUG_FUNCPTR(gst_schedule_pad_connect); + schedule->pad_disconnect = GST_DEBUG_FUNCPTR(gst_schedule_pad_disconnect); + schedule->iterate = GST_DEBUG_FUNCPTR(gst_schedule_iterate); +} + +GstSchedule* +gst_schedule_new(GstElement *parent) +{ + GstSchedule *sched = GST_SCHEDULE (gtk_type_new (GST_TYPE_SCHEDULE)); + + sched->parent = parent; + + return sched; +} + + +/* this function will look at a pad and determine if the peer parent is + * a possible candidate for connecting up in the same chain. */ +/* DEPRACATED !!!! +GstElement *gst_schedule_check_pad (GstSchedule *sched, GstPad *pad) { + GstRealPad *peer; + GstElement *element, *peerelement; + + GST_INFO (GST_CAT_SCHEDULING, "checking pad %s:%s for peer in scheduler", + GST_DEBUG_PAD_NAME(pad)); + + element = GST_ELEMENT(GST_PAD_PARENT(peer)); + GST_DEBUG(GST_CAT_SCHEDULING, "element is \"%s\"\n",GST_ELEMENT_NAME(element)); + + peer = GST_PAD_PEER (pad); + if (peer == NULL) return NULL; + peerelement = GST_ELEMENT(GST_PAD_PARENT (peer)); + if (peerelement == NULL) return NULL; + GST_DEBUG(GST_CAT_SCHEDULING, "peer element is \"%s\"\n",GST_ELEMENT_NAME(peerelement)); + + // now check to see if it's in the same schedule + if (GST_ELEMENT_SCHED(element) == GST_ELEMENT_SCHED(peerelement)) { + GST_DEBUG(GST_CAT_SCHEDULING, "peer is in same schedule\n"); + return peerelement; + } + + // otherwise it's not a candidate + return NULL; +} +*/ + +GstScheduleChain * +gst_schedule_chain_new (GstSchedule *sched) +{ + GstScheduleChain *chain = g_new (GstScheduleChain, 1); + + // initialize the chain with sane values + chain->sched = sched; + chain->disabled = NULL; + chain->elements = NULL; + chain->num_elements = 0; + chain->entry = NULL; + chain->cothreaded_elements = 0; + chain->schedule = FALSE; + + // add the chain to the schedules' list of chains + sched->chains = g_list_prepend (sched->chains, chain); + sched->num_chains++; + + GST_INFO (GST_CAT_SCHEDULING, "created new chain %p, now are %d chains in sched %p", + chain,sched->num_chains,sched); + + return chain; +} + +void +gst_schedule_chain_destroy (GstScheduleChain *chain) +{ + GstSchedule *sched = chain->sched; + + // remove the chain from the schedules' list of chains + chain->sched->chains = g_list_remove (chain->sched->chains, chain); + chain->sched->num_chains--; + + // destroy the chain + g_list_free (chain->disabled); // should be empty... + g_list_free (chain->elements); // ditto + g_free (chain); + + GST_INFO (GST_CAT_SCHEDULING, "destroyed chain %p, now are %d chains in sched %p",chain,sched->num_chains,sched); +} + +void +gst_schedule_chain_add_element (GstScheduleChain *chain, GstElement *element) +{ + GST_INFO (GST_CAT_SCHEDULING, "adding element \"%s\" to chain %p", GST_ELEMENT_NAME (element),chain); + + // set the sched pointer for the element + element->sched = chain->sched; + + // add the element to the list of 'disabled' elements + chain->disabled = g_list_prepend (chain->disabled, element); + chain->num_elements++; +} + +void +gst_schedule_chain_enable_element (GstScheduleChain *chain, GstElement *element) +{ + GST_INFO (GST_CAT_SCHEDULING, "enabling element \"%s\" in chain %p", GST_ELEMENT_NAME (element),chain); + + // remove from disabled list + chain->disabled = g_list_remove (chain->disabled, element); + + // add to elements list + chain->elements = g_list_prepend (chain->elements, element); + + // reschedule the chain + gst_schedule_cothreaded_chain(GST_BIN(chain->sched->parent),chain); +} + +void +gst_schedule_chain_disable_element (GstScheduleChain *chain, GstElement *element) +{ + GST_INFO (GST_CAT_SCHEDULING, "disabling element \"%s\" in chain %p", GST_ELEMENT_NAME (element),chain); + + // remove from elements list + chain->elements = g_list_remove (chain->elements, element); + + // add to disabled list + chain->disabled = g_list_prepend (chain->disabled, element); + + // reschedule the chain +// FIXME this should be done only if manager state != NULL +// gst_schedule_cothreaded_chain(GST_BIN(chain->sched->parent),chain); +} + +void +gst_schedule_chain_remove_element (GstScheduleChain *chain, GstElement *element) +{ + GST_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from chain %p", GST_ELEMENT_NAME (element),chain); + + // if it's active, deactivate it + if (g_list_find (chain->elements, element)) { + gst_schedule_chain_disable_element (chain, element); + } + + // remove the element from the list of elements + chain->disabled = g_list_remove (chain->disabled, element); + chain->num_elements--; + + // if there are no more elements in the chain, destroy the chain + if (chain->num_elements == 0) + gst_schedule_chain_destroy(chain); + + // unset the sched pointer for the element + element->sched = NULL; +} + +void +gst_schedule_chain_elements (GstSchedule *sched, GstElement *element1, GstElement *element2) +{ + GList *chains; + GstScheduleChain *chain; + GstScheduleChain *chain1 = NULL, *chain2 = NULL; + GstElement *element; + + // first find the chains that hold the two + chains = sched->chains; + while (chains) { + chain = (GstScheduleChain *)(chains->data); + chains = g_list_next(chains); + + if (g_list_find (chain->disabled,element1)) + chain1 = chain; + else if (g_list_find (chain->elements,element1)) + chain1 = chain; + + if (g_list_find (chain->disabled,element2)) + chain2 = chain; + else if (g_list_find (chain->elements,element2)) + chain2 = chain; + } + + // first check to see if they're in the same chain, we're done if that's the case + if ((chain1 != NULL) && (chain1 == chain2)) { + GST_INFO (GST_CAT_SCHEDULING, "elements are already in the same chain"); + return; + } + + // now, if neither element has a chain, create one + if ((chain1 == NULL) && (chain2 == NULL)) { + GST_INFO (GST_CAT_SCHEDULING, "creating new chain to hold two new elements"); + chain = gst_schedule_chain_new (sched); + gst_schedule_chain_add_element (chain, element1); + gst_schedule_chain_add_element (chain, element2); + // FIXME chain changed here +// gst_schedule_cothreaded_chain(chain->sched->parent,chain); + + // otherwise if both have chains already, join them + } else if ((chain1 != NULL) && (chain2 != NULL)) { + GST_INFO (GST_CAT_SCHEDULING, "merging chain %p into chain %p",chain2,chain1); + // take the contents of chain2 and merge them into chain1 + chain1->disabled = g_list_concat (chain1->disabled, g_list_copy(chain2->disabled)); + chain1->elements = g_list_concat (chain1->elements, g_list_copy(chain2->elements)); + chain1->num_elements += chain2->num_elements; + // FIXME chain changed here +// gst_schedule_cothreaded_chain(chain->sched->parent,chain); + + gst_schedule_chain_destroy(chain2); + + // otherwise one has a chain already, the other doesn't + } else { + // pick out which one has the chain, and which doesn't + if (chain1 != NULL) chain = chain1, element = element2; + else chain = chain2, element = element1; + + GST_INFO (GST_CAT_SCHEDULING, "adding element to existing chain"); + gst_schedule_chain_add_element (chain, element); + // FIXME chain changed here +// gst_schedule_cothreaded_chain(chain->sched->parent,chain); + } +} + +void +gst_schedule_pad_connect (GstSchedule *sched, GstPad *srcpad, GstPad *sinkpad) +{ + GstElement *srcelement,*sinkelement; + + srcelement = GST_PAD_PARENT(srcpad); + g_return_if_fail(srcelement != NULL); + sinkelement = GST_PAD_PARENT(sinkpad); + g_return_if_fail(sinkelement != NULL); + + GST_INFO (GST_CAT_SCHEDULING, "have pad connected callback on %s:%s to %s:%s",GST_DEBUG_PAD_NAME(srcpad),GST_DEBUG_PAD_NAME(sinkpad)); + GST_DEBUG(GST_CAT_SCHEDULING, "srcpad sched is %p, sinkpad sched is %p\n", +GST_ELEMENT_SCHED(srcelement),GST_ELEMENT_SCHED(sinkelement)); + + if (GST_ELEMENT_SCHED(srcelement) == GST_ELEMENT_SCHED(sinkelement)) { + GST_INFO (GST_CAT_SCHEDULING, "peer %s:%s is in same schedule, chaining together",GST_DEBUG_PAD_NAME(sinkpad)); + gst_schedule_chain_elements (sched, srcelement, sinkelement); + } +} + +// find the chain within the schedule that holds the element, if any +GstScheduleChain * +gst_schedule_find_chain (GstSchedule *sched, GstElement *element) +{ + GList *chains; + GstScheduleChain *chain; + + GST_INFO (GST_CAT_SCHEDULING, "searching for element \"%s\" in chains",GST_ELEMENT_NAME(element)); + + chains = sched->chains; + while (chains) { + chain = (GstScheduleChain *)(chains->data); + chains = g_list_next (chains); + + if (g_list_find (chain->elements, element)) + return chain; + if (g_list_find (chain->disabled, element)) + return chain; + } + return NULL; +} + +void +gst_schedule_chain_recursive_add (GstScheduleChain *chain, GstElement *element) +{ + GList *pads; + GstPad *pad; + GstElement *peerelement; + + // add the element to the chain + gst_schedule_chain_add_element (chain, element); + + GST_DEBUG(GST_CAT_SCHEDULING, "recursing on element \"%s\"\n",GST_ELEMENT_NAME(element)); + // now go through all the pads and see which peers can be added + pads = element->pads; + while (pads) { + pad = GST_PAD(pads->data); + pads = g_list_next (pads); + + GST_DEBUG(GST_CAT_SCHEDULING, "have pad %s:%s, checking for valid peer\n",GST_DEBUG_PAD_NAME(pad)); + // if the peer exists and could be in the same chain + if (GST_PAD_PEER(pad)) { + GST_DEBUG(GST_CAT_SCHEDULING, "has peer %s:%s\n",GST_DEBUG_PAD_NAME(GST_PAD_PEER(pad))); + peerelement = GST_PAD_PARENT(GST_PAD_PEER(pad)); + if (GST_ELEMENT_SCHED(GST_PAD_PARENT(pad)) == GST_ELEMENT_SCHED(peerelement)) { + GST_DEBUG(GST_CAT_SCHEDULING, "peer \"%s\" is valid for same chain\n",GST_ELEMENT_NAME(peerelement)); + // if it's not already in a chain, add it to this one + if (gst_schedule_find_chain (chain->sched, peerelement) == NULL) { + gst_schedule_chain_recursive_add (chain, peerelement); + } + } + } + } +} + +void +gst_schedule_pad_disconnect (GstSchedule *sched, GstPad *srcpad, GstPad *sinkpad) +{ + GstScheduleChain *chain; + GstElement *element1, *element2; + GstScheduleChain *chain1, *chain2; + + GST_INFO (GST_CAT_SCHEDULING, "disconnecting pads %s:%s and %s:%s", + GST_DEBUG_PAD_NAME(srcpad), GST_DEBUG_PAD_NAME(sinkpad)); + + // we need to have the parent elements of each pad + element1 = GST_ELEMENT(GST_PAD_PARENT(srcpad)); + element2 = GST_ELEMENT(GST_PAD_PARENT(sinkpad)); + + // first task is to remove the old chain they belonged to. + // this can be accomplished by taking either of the elements, + // since they are guaranteed to be in the same chain + // FIXME is it potentially better to make an attempt at splitting cleaner?? + chain = gst_schedule_find_chain (sched, element1); + if (chain) { + GST_INFO (GST_CAT_SCHEDULING, "destroying chain"); + gst_schedule_chain_destroy (chain); + } + + // now create a new chain to hold element1 and build it from scratch + chain1 = gst_schedule_chain_new (sched); + gst_schedule_chain_recursive_add (chain1, element1); + + // check the other element to see if it landed in the newly created chain + if (gst_schedule_find_chain (sched, element2) == NULL) { + // if not in chain, create chain and build from scratch + chain2 = gst_schedule_chain_new (sched); + gst_schedule_chain_recursive_add (chain2, element2); + } +} + + +void +gst_schedule_add_element (GstSchedule *sched, GstElement *element) +{ + GList *pads; + GstPad *pad; + GstElement *peerelement; + GstScheduleChain *chain; + + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_ELEMENT(element)); + + // if it's already in this schedule, don't bother doing anything + if (GST_ELEMENT_SCHED(element) == sched) return; + + GST_INFO (GST_CAT_SCHEDULING, "adding element \"%s\" to schedule", + GST_ELEMENT_NAME(element)); + + // if the element already has a different scheduler, remove the element from it + if (GST_ELEMENT_SCHED(element)) { + gst_schedule_remove_element(GST_ELEMENT_SCHED(element),element); + } + + // set the sched pointer in the element itself + GST_ELEMENT_SCHED(element) = sched; + + // only deal with elements after this point, not bins + // exception is made for Bin's that are schedulable, like the autoplugger + if (GST_IS_BIN (element) && !GST_FLAG_IS_SET(element, GST_BIN_SELF_SCHEDULABLE)) return; + + // first add it to the list of elements that are to be scheduled + sched->elements = g_list_prepend (sched->elements, element); + sched->num_elements++; + + // create a chain to hold it, and add + chain = gst_schedule_chain_new (sched); + gst_schedule_chain_add_element (chain, element); + + // set the sched pointer in all the pads + pads = element->pads; + while (pads) { + pad = GST_PAD(pads->data); + pads = g_list_next(pads); + + // we only operate on real pads + if (!GST_IS_REAL_PAD(pad)) continue; + + // set the pad's sched pointer + gst_pad_set_sched (pad, sched); + + // if the peer element exists and is a candidate + if (GST_PAD_PEER(pad)) { + peerelement = GST_PAD_PARENT( GST_PAD_PEER (pad) ); + if (GST_ELEMENT_SCHED(element) == GST_ELEMENT_SCHED(peerelement)) { + GST_INFO (GST_CAT_SCHEDULING, "peer is in same schedule, chaining together"); + // make sure that the two elements are in the same chain + gst_schedule_chain_elements (sched,element,peerelement); + } + } + } +} + +void +gst_schedule_enable_element (GstSchedule *sched, GstElement *element) +{ + GstScheduleChain *chain; + + // find the chain the element's in + chain = gst_schedule_find_chain (sched, element); + + if (chain) + gst_schedule_chain_enable_element (chain, element); + else + GST_INFO (GST_CAT_SCHEDULING, "element not found in any chain, not enabling"); +} + +void +gst_schedule_disable_element (GstSchedule *sched, GstElement *element) +{ + GstScheduleChain *chain; + + // find the chain the element is in + chain = gst_schedule_find_chain (sched, element); + + // remove it from the chain + if (chain) { + gst_schedule_chain_disable_element(chain,element); + } +} + +void +gst_schedule_remove_element (GstSchedule *sched, GstElement *element) +{ + GstScheduleChain *chain; + + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_ELEMENT(element)); + + if (g_list_find (sched->elements, element)) { + GST_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from schedule", + GST_ELEMENT_NAME(element)); + + // find what chain the element is in + chain = gst_schedule_find_chain(sched, element); + + // remove it from its chain + gst_schedule_chain_remove_element (chain, element); + + // remove it from the list of elements + sched->elements = g_list_remove (sched->elements, element); + sched->num_elements--; + + // unset the scheduler pointer in the element + GST_ELEMENT_SCHED(element) = NULL; + } +} + +gboolean +gst_schedule_iterate (GstSchedule *sched) +{ + GstBin *bin = GST_BIN(sched->parent); + GList *chains; + GstScheduleChain *chain; + GstElement *entry; + gint num_scheduled = 0; + gboolean eos = FALSE; + GList *elements; + + GST_DEBUG_ENTER("(\"%s\")", GST_ELEMENT_NAME (bin)); + + g_return_val_if_fail (bin != NULL, TRUE); + g_return_val_if_fail (GST_IS_BIN (bin), TRUE); +// g_return_val_if_fail (GST_STATE (bin) == GST_STATE_PLAYING, TRUE); + + // step through all the chains + chains = sched->chains; +// if (chains == NULL) return FALSE; +g_return_val_if_fail (chains != NULL, FALSE); + while (chains) { + chain = (GstScheduleChain *)(chains->data); + chains = g_list_next (chains); + +// if (!chain->need_scheduling) continue; + +// if (chain->need_cothreads) { + // all we really have to do is switch to the first child + // FIXME this should be lots more intelligent about where to start + GST_DEBUG (GST_CAT_DATAFLOW,"starting iteration via cothreads\n"); + + if (chain->elements) { + entry = NULL; //MattH ADDED? +GST_DEBUG(GST_CAT_SCHEDULING,"there are %d elements in this chain\n",chain->num_elements); + elements = chain->elements; + while (elements) { + entry = GST_ELEMENT(elements->data); + elements = g_list_next(elements); + if (GST_FLAG_IS_SET(entry,GST_ELEMENT_DECOUPLED)) { + GST_DEBUG(GST_CAT_SCHEDULING,"entry \"%s\" is DECOUPLED, skipping\n",GST_ELEMENT_NAME(entry)); + entry = NULL; + } else if (GST_FLAG_IS_SET(entry,GST_ELEMENT_NO_ENTRY)) { + GST_DEBUG(GST_CAT_SCHEDULING,"entry \"%s\" is not valid, skipping\n",GST_ELEMENT_NAME(entry)); + entry = NULL; + } else + break; + } + if (entry) { + GST_FLAG_SET (entry, GST_ELEMENT_COTHREAD_STOPPING); + GST_DEBUG (GST_CAT_DATAFLOW,"set COTHREAD_STOPPING flag on \"%s\"(@%p)\n", + GST_ELEMENT_NAME (entry),entry); + cothread_switch (entry->threadstate); + + // following is a check to see if the chain was interrupted due to a + // top-half state_change(). (i.e., if there's a pending state.) + // + // if it was, return to gstthread.c::gst_thread_main_loop() to + // execute the state change. + GST_DEBUG (GST_CAT_DATAFLOW,"cothread switch ended or interrupted\n"); + if (GST_STATE_PENDING(GST_SCHEDULE(sched)->parent) != GST_STATE_NONE_PENDING) + { + GST_DEBUG (GST_CAT_DATAFLOW,"handle pending state %d\n", + GST_STATE_PENDING(GST_SCHEDULE(sched)->parent)); + return 0; + } + + } else { + GST_INFO (GST_CAT_DATAFLOW,"NO ENTRY INTO CHAIN!"); + } + } else { + GST_INFO (GST_CAT_DATAFLOW,"NO ENABLED ELEMENTS IN CHAIN!!"); + } + +/* + } else { + GST_DEBUG (GST_CAT_DATAFLOW,"starting iteration via chain-functions\n"); + + entries = chain->entries; + + g_assert (entries != NULL); + + while (entries) { + entry = GST_ELEMENT (entries->data); + entries = g_list_next (entries); + + GST_DEBUG (GST_CAT_DATAFLOW,"have entry \"%s\"\n",GST_ELEMENT_NAME (entry)); + + if (GST_IS_BIN (entry)) { + gst_bin_iterate (GST_BIN (entry)); + } else { + pads = entry->pads; + while (pads) { + pad = GST_PAD (pads->data); + if (GST_RPAD_DIRECTION(pad) == GST_PAD_SRC) { + GST_DEBUG (GST_CAT_DATAFLOW,"calling getfunc of %s:%s\n",GST_DEBUG_PAD_NAME(pad)); + if (GST_REAL_PAD(pad)->getfunc == NULL) + fprintf(stderr, "error, no getfunc in \"%s\"\n", GST_ELEMENT_NAME (entry)); + else + buf = (GST_REAL_PAD(pad)->getfunc)(pad); + if (buf) gst_pad_push(pad,buf); + } + pads = g_list_next (pads); + } + } + } + }*/ + num_scheduled++; + } + +/* + // check if nothing was scheduled that was ours.. + if (!num_scheduled) { + // are there any other elements that are still busy? + if (bin->num_eos_providers) { + GST_LOCK (bin); + GST_DEBUG (GST_CATA_DATAFLOW,"waiting for eos providers\n"); + g_cond_wait (bin->eoscond, GST_OBJECT(bin)->lock); + GST_DEBUG (GST_CAT_DATAFLOW,"num eos providers %d\n", bin->num_eos_providers); + GST_UNLOCK (bin); + } + else { + gst_element_signal_eos (GST_ELEMENT (bin)); + eos = TRUE; + } + } +*/ + + GST_DEBUG (GST_CAT_DATAFLOW, "leaving (%s)\n", GST_ELEMENT_NAME (bin)); + return !eos; +} + + + +void +gst_schedule_show (GstSchedule *sched) +{ + GList *chains, *elements; + GstElement *element; + GstScheduleChain *chain; + + if (sched == NULL) { + g_print("schedule doesn't exist for this element\n"); + return; + } + + g_return_if_fail(GST_IS_SCHEDULE(sched)); + + g_print("SCHEDULE DUMP FOR MANAGING BIN \"%s\"\n",GST_ELEMENT_NAME(sched->parent)); + + g_print("schedule has %d elements in it: ",sched->num_elements); + elements = sched->elements; + while (elements) { + element = GST_ELEMENT(elements->data); + elements = g_list_next(elements); + + g_print("%s, ",GST_ELEMENT_NAME(element)); + } + g_print("\n"); + + g_print("schedule has %d chains in it\n",sched->num_chains); + chains = sched->chains; + while (chains) { + chain = (GstScheduleChain *)(chains->data); + chains = g_list_next(chains); + + g_print("%p: ",chain); + + elements = chain->disabled; + while (elements) { + element = GST_ELEMENT(elements->data); + elements = g_list_next(elements); + + g_print("!%s, ",GST_ELEMENT_NAME(element)); + } + + elements = chain->elements; + while (elements) { + element = GST_ELEMENT(elements->data); + elements = g_list_next(elements); + + g_print("%s, ",GST_ELEMENT_NAME(element)); + } + g_print("\n"); + } +} diff --git a/gst/gstscheduler.h b/gst/gstscheduler.h index 5cfbfff80..cdc905221 100644 --- a/gst/gstscheduler.h +++ b/gst/gstscheduler.h @@ -24,6 +24,7 @@ #ifndef __GST_SCHEDULER_H__ #define __GST_SCHEDULER_H__ +#include #include @@ -32,8 +33,106 @@ extern "C" { #endif /* __cplusplus */ +#define GST_TYPE_SCHEDULE \ + (gst_schedule_get_type()) +#define GST_SCHEDULE(obj) \ + (GTK_CHECK_CAST((obj),GST_TYPE_SCHEDULE,GstSchedule)) +#define GST_SCHEDULE_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_SCHEDULE,GstScheduleClass)) +#define GST_IS_SCHEDULE(obj) \ + (GTK_CHECK_TYPE((obj),GST_TYPE_SCHEDULE)) +#define GST_IS_SCHEDULE_CLASS(klass) \ + (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_SCHEDULE)) + + +#define GST_SCHED_PARENT(sched) ((sched)->parent) + +//typedef struct _GstSchedule GstSchedule; +//typedef struct _GstScheduleClass GstScheduleClass; +typedef struct _GstScheduleChain GstScheduleChain; + +struct _GstSchedule { + GstObject object; + + GstElement *parent; + + GList *elements; + gint num_elements; + + GList *chains; + gint num_chains; + + void (*add_element) (GstSchedule *sched, GstElement *element); + void (*remove_element) (GstSchedule *sched, GstElement *element); + void (*enable_element) (GstSchedule *sched, GstElement *element); + void (*disable_element) (GstSchedule *sched, GstElement *element); + void (*lock_element) (GstSchedule *sched, GstElement *element); + void (*unlock_element) (GstSchedule *sched, GstElement *element); + void (*pad_connect) (GstSchedule *sched, GstPad *srcpad, GstPad *sinkpad); + void (*pad_disconnect) (GstSchedule *sched, GstPad *srcpad, GstPad *sinkpad); + gboolean (*iterate) (GstSchedule *sched); +}; + +struct _GstScheduleClass { + GstObjectClass parent_class; +}; + +//#define GST_SCHEDULE_SAFETY if (sched) +#define GST_SCHEDULE_SAFETY + +#define GST_SCHEDULE_ADD_ELEMENT(sched,element) \ + GST_SCHEDULE_SAFETY ((sched)->add_element((sched),(element))) +#define GST_SCHEDULE_REMOVE_ELEMENT(sched,element) \ + GST_SCHEDULE_SAFETY ((sched)->remove_element((sched),(element))) +#define GST_SCHEDULE_ENABLE_ELEMENT(sched,element) \ + GST_SCHEDULE_SAFETY ((sched)->enable_element((sched),(element))) +#define GST_SCHEDULE_DISABLE_ELEMENT(sched,element) \ + GST_SCHEDULE_SAFETY ((sched)->disable_element((sched),(element))) +#define GST_SCHEDULE_LOCK_ELEMENT(sched,element) \ + GST_SCHEDULE_SAFETY if ((sched)->lock_element != NULL) \ + ((sched)->lock_element((sched),(element))) +#define GST_SCHEDULE_UNLOCK_ELEMENT(sched,element) \ + GST_SCHEDULE_SAFETY if ((sched)->unlock_element != NULL) \ + ((sched)->unlock_element((sched),(element))) +#define GST_SCHEDULE_PAD_CONNECT(sched,srcpad,sinkpad) \ + GST_SCHEDULE_SAFETY ((sched)->pad_connect((sched),(srcpad),(sinkpad))) +#define GST_SCHEDULE_PAD_DISCONNECT(sched,srcpad,sinkpad) \ + GST_SCHEDULE_SAFETY ((sched)->pad_disconnect((sched),(srcpad),(sinkpad))) +#define GST_SCHEDULE_ITERATE(sched) \ + ((sched)->iterate((sched))) + + + +struct _GstScheduleChain { + GstSchedule *sched; + + GList *disabled; + + GList *elements; + gint num_elements; + + GstElement *entry; + + gint cothreaded_elements; + gboolean schedule; +}; + + void gst_bin_schedule_func(GstBin *bin); +GtkType gst_schedule_get_type (void); +GstSchedule * gst_schedule_new (GstElement *parent); + +void gst_schedule_add_element (GstSchedule *sched, GstElement *element); +void gst_schedule_remove_element (GstSchedule *sched, GstElement *element); +void gst_schedule_enable_element (GstSchedule *sched, GstElement *element); +void gst_schedule_disable_element (GstSchedule *sched, GstElement *element); +void gst_schedule_pad_connect (GstSchedule *sched, GstPad *srcpad, GstPad *sinkpad); +void gst_schedule_pad_disconnect (GstSchedule *sched, GstPad *srcpad, GstPad *sinkpad); +gboolean gst_schedule_iterate (GstSchedule *sched); + +void gst_schedule_show (GstSchedule *sched); + #ifdef __cplusplus } diff --git a/gst/gstthread.c b/gst/gstthread.c index 1bdea99d0..8f91ff32d 100644 --- a/gst/gstthread.c +++ b/gst/gstthread.c @@ -26,7 +26,8 @@ #include "gst_private.h" #include "gstthread.h" - +#include "gstscheduler.h" +#include "gstqueue.h" GstElementDetails gst_thread_details = { "Threaded container", @@ -44,15 +45,24 @@ enum { LAST_SIGNAL }; +enum { + SPINUP=0, + STATECHANGE, + STARTUP +}; + enum { ARG_0, ARG_CREATE_THREAD, }; + static void gst_thread_class_init (GstThreadClass *klass); static void gst_thread_init (GstThread *thread); +static void gst_thread_real_destroy (GtkObject *gtk_object); + static void gst_thread_set_arg (GtkObject *object, GtkArg *arg, guint id); static void gst_thread_get_arg (GtkObject *object, GtkArg *arg, guint id); @@ -61,9 +71,7 @@ static GstElementStateReturn gst_thread_change_state (GstElement *element); static xmlNodePtr gst_thread_save_thyself (GstObject *object, xmlNodePtr parent); static void gst_thread_restore_thyself (GstObject *object, xmlNodePtr self); -static void gst_thread_signal_thread (GstThread *thread); -static void gst_thread_wait_thread (GstThread *thread); -static void gst_thread_schedule_dummy (GstBin *bin); +static void gst_thread_signal_thread (GstThread *thread, gboolean spinning); static void* gst_thread_main_loop (void *arg); @@ -108,12 +116,14 @@ gst_thread_class_init (GstThreadClass *klass) gtk_object_add_arg_type ("GstThread::create_thread", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_CREATE_THREAD); + gtkobject_class->destroy = gst_thread_real_destroy; + gstobject_class->save_thyself = gst_thread_save_thyself; gstobject_class->restore_thyself = gst_thread_restore_thyself; gstelement_class->change_state = gst_thread_change_state; - gstbin_class->schedule = gst_thread_schedule_dummy; +// gstbin_class->schedule = gst_thread_schedule_dummy; gtkobject_class->set_arg = gst_thread_set_arg; gtkobject_class->get_arg = gst_thread_get_arg; @@ -123,26 +133,41 @@ gst_thread_class_init (GstThreadClass *klass) static void gst_thread_init (GstThread *thread) { - GST_DEBUG (0,"initializing thread '%s'\n",GST_ELEMENT_NAME (thread)); + + GST_DEBUG (GST_CAT_THREAD,"initializing thread\n"); // we're a manager by default GST_FLAG_SET (thread, GST_BIN_FLAG_MANAGER); // default is to create a thread GST_FLAG_SET (thread, GST_THREAD_CREATE); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING); thread->lock = g_mutex_new(); thread->cond = g_cond_new(); + + GST_ELEMENT_SCHED(thread) = gst_schedule_new(GST_ELEMENT(thread)); + GST_DEBUG(GST_CAT_THREAD, "thread's scheduler is %p\n",GST_ELEMENT_SCHED(thread)); + + thread->ppid = getpid(); + +// gst_element_set_manager(GST_ELEMENT(thread),GST_ELEMENT(thread)); } static void -gst_thread_schedule_dummy (GstBin *bin) +gst_thread_real_destroy (GtkObject *gtk_object) { - g_return_if_fail (GST_IS_THREAD (bin)); + GstThread *thread = GST_THREAD (gtk_object); + + GST_DEBUG (GST_CAT_REFCOUNTING,"destroy()\n"); + + g_mutex_free (thread->lock); + g_cond_free (thread->cond); - if (!GST_FLAG_IS_SET (GST_THREAD (bin), GST_THREAD_STATE_SPINNING)) - GST_INFO (GST_CAT_THREAD,"gstthread: scheduling delayed until thread starts"); + if (GTK_OBJECT_CLASS (parent_class)->destroy) + GTK_OBJECT_CLASS (parent_class)->destroy (gtk_object); + + gst_object_destroy (GST_OBJECT (GST_ELEMENT_SCHED (thread))); + gst_object_unref (GST_OBJECT (GST_ELEMENT_SCHED (thread))); } static void @@ -156,13 +181,13 @@ gst_thread_set_arg (GtkObject *object, switch(id) { case ARG_CREATE_THREAD: if (GTK_VALUE_BOOL (*arg)) { - GST_INFO (GST_CAT_THREAD,"gstthread: turning ON the creation of the thread"); + GST_INFO (GST_CAT_THREAD,"turning ON the creation of the thread"); GST_FLAG_SET (object, GST_THREAD_CREATE); - GST_DEBUG (0,"gstthread: flags are 0x%08x\n", GST_FLAGS (object)); +// GST_DEBUG (GST_CAT_THREAD,"flags are 0x%08x\n", GST_FLAGS (object)); } else { GST_INFO (GST_CAT_THREAD,"gstthread: turning OFF the creation of the thread"); GST_FLAG_UNSET (object, GST_THREAD_CREATE); - GST_DEBUG (0,"gstthread: flags are 0x%08x\n", GST_FLAGS (object)); +// GST_DEBUG (GST_CAT_THREAD,"gstthread: flags are 0x%08x\n", GST_FLAGS (object)); } break; default: @@ -197,95 +222,237 @@ gst_thread_get_arg (GtkObject *object, * Returns: The new thread */ GstElement* -gst_thread_new (guchar *name) +gst_thread_new (const guchar *name) { return gst_elementfactory_make ("thread", name); } +#define THR_INFO(format,args...) \ + GST_INFO_ELEMENT(GST_CAT_THREAD, thread, "sync(" GST_DEBUG_THREAD_FORMAT "): " format , \ + GST_DEBUG_THREAD_ARGS(thread->pid) , ## args ) +#define THR_DEBUG(format,args...) \ + GST_DEBUG_ELEMENT(GST_CAT_THREAD, thread, "sync(" GST_DEBUG_THREAD_FORMAT "): " format , \ + GST_DEBUG_THREAD_ARGS(thread->pid) , ## args ) + +#define THR_INFO_MAIN(format,args...) \ + GST_INFO_ELEMENT(GST_CAT_THREAD, thread, "sync-main(" GST_DEBUG_THREAD_FORMAT "): " format , \ + GST_DEBUG_THREAD_ARGS(thread->ppid) , ## args ) +#define THR_DEBUG_MAIN(format,args...) \ + GST_DEBUG_ELEMENT(GST_CAT_THREAD, thread, "sync-main(" GST_DEBUG_THREAD_FORMAT "): " format , \ + GST_DEBUG_THREAD_ARGS(thread->ppid) , ## args ) + static GstElementStateReturn gst_thread_change_state (GstElement *element) { GstThread *thread; gboolean stateset = GST_STATE_SUCCESS; - gint pending, transition; + gint transition; + pthread_t self = pthread_self(); + GstElement *peerelement; g_return_val_if_fail (GST_IS_THREAD(element), FALSE); - GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME(element)); +// GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME(element)); thread = GST_THREAD (element); +// GST_DEBUG (GST_CAT_THREAD, "**** THREAD %ld changing THREAD %ld ****\n",self,thread->thread_id); +// GST_DEBUG (GST_CAT_THREAD, "**** current pid=%d\n",getpid()); - GST_INFO (GST_CAT_THREAD,"gstthread: thread \"%s\" change state %d", - GST_ELEMENT_NAME (GST_ELEMENT (element)), - GST_STATE_PENDING (element)); - - pending = GST_STATE_PENDING (element); transition = GST_STATE_TRANSITION (element); -// if (pending == GST_STATE (element)) return GST_STATE_SUCCESS; + THR_INFO("changing state from %s to %s", + gst_element_statename(GST_STATE (element)), + gst_element_statename(GST_STATE_PENDING (element))); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - - if (GST_ELEMENT_CLASS (parent_class)->change_state) - stateset = GST_ELEMENT_CLASS (parent_class)->change_state (element); - - GST_INFO (GST_CAT_THREAD, "gstthread: stateset %d %d %d %02x", GST_STATE (element), stateset, - GST_STATE_PENDING (element), GST_STATE_TRANSITION (element)); + //GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); switch (transition) { case GST_STATE_NULL_TO_READY: -// if (!stateset) return FALSE; - // we want to prepare our internal state for doing the iterations - GST_INFO (GST_CAT_THREAD, "gstthread: preparing thread \"%s\" for iterations:", - GST_ELEMENT_NAME (GST_ELEMENT (element))); - // set the state to idle GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - // create the thread if that's what we're supposed to do - GST_INFO (GST_CAT_THREAD, "gstthread: flags are 0x%08x", GST_FLAGS (thread)); + GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING); + // create the thread if that's what we're supposed to do if (GST_FLAG_IS_SET (thread, GST_THREAD_CREATE)) { - GST_INFO (GST_CAT_THREAD, "gstthread: starting thread \"%s\"", - GST_ELEMENT_NAME (GST_ELEMENT (element))); + THR_DEBUG ("creating thread \"%s\"\n", + GST_ELEMENT_NAME (element)); g_mutex_lock (thread->lock); + // create the thread pthread_create (&thread->thread_id, NULL, gst_thread_main_loop, thread); // wait for it to 'spin up' - //gst_thread_wait_thread (thread); - g_cond_wait (thread->cond, thread->lock); - g_mutex_unlock (thread->lock); + THR_DEBUG("waiting for child thread spinup\n"); + g_cond_wait(thread->cond,thread->lock); + THR_DEBUG("thread claims to be up\n"); + g_mutex_unlock(thread->lock); } else { - GST_INFO (GST_CAT_THREAD, "gstthread: NOT starting thread \"%s\"", + GST_INFO (GST_CAT_THREAD, "NOT creating thread \"%s\"", GST_ELEMENT_NAME (GST_ELEMENT (element))); + + // punt and change state on all the children + if (GST_ELEMENT_CLASS (parent_class)->change_state) + stateset = GST_ELEMENT_CLASS (parent_class)->change_state (element); + } + break; + case GST_STATE_READY_TO_PAUSED: + THR_INFO("readying thread"); + + // check to see if the thread is somehow changing its own state. + // FIXME this is currently illegal, but must somehow be made legal at some point. + if (pthread_equal(self, thread->thread_id)) + { + //FIXME this should not happen + g_assert(!pthread_equal(self, thread->thread_id)); + GST_FLAG_SET(thread, GST_THREAD_STATE_SPINNING); + GST_DEBUG(GST_CAT_THREAD,"no sync(" GST_DEBUG_THREAD_FORMAT "): setting own thread's state to spinning\n", + GST_DEBUG_THREAD_ARGS(thread->pid)); + } + else + { + g_mutex_lock(thread->lock); + gst_thread_signal_thread(thread,FALSE); } break; case GST_STATE_PAUSED_TO_PLAYING: - case GST_STATE_READY_TO_PLAYING: - if (!stateset) return FALSE; - GST_INFO (GST_CAT_THREAD, "gstthread: starting thread \"%s\"", - GST_ELEMENT_NAME (GST_ELEMENT (element))); - - GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING); - gst_thread_signal_thread (thread); + THR_INFO("starting thread"); + + // check to see if the thread is somehow changing its own state. + // FIXME this is currently illegal, but must somehow be made legal at some point. + if (pthread_equal(self, thread->thread_id)) + { + //FIXME this should not happen + g_assert(!pthread_equal(self, thread->thread_id)); + GST_FLAG_SET(thread, GST_THREAD_STATE_SPINNING); + GST_DEBUG(GST_CAT_THREAD,"no sync(" GST_DEBUG_THREAD_FORMAT "): setting own thread's state to spinning\n", + GST_DEBUG_THREAD_ARGS(thread->pid)); + } + else + { + THR_DEBUG("telling thread to start spinning\n"); + g_mutex_lock(thread->lock); + gst_thread_signal_thread(thread,TRUE); + } break; case GST_STATE_PLAYING_TO_PAUSED: - GST_INFO (GST_CAT_THREAD,"gstthread: pausing thread \"%s\"", - GST_ELEMENT_NAME (GST_ELEMENT (element))); - - //GST_FLAG_UNSET(thread,GST_THREAD_STATE_SPINNING); - gst_thread_signal_thread (thread); + THR_INFO("pausing thread"); + + // check to see if the thread is somehow changing its own state. + // FIXME this is currently illegal, but must somehow be made legal at some point. + if (pthread_equal(self, thread->thread_id)) + { + //FIXME this should not happen + GST_DEBUG(GST_CAT_THREAD,"no sync(" GST_DEBUG_THREAD_FORMAT "): setting own thread's state to paused\n", + GST_DEBUG_THREAD_ARGS(thread->pid)); + GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); + g_assert(!pthread_equal(self, thread->thread_id)); + } + else + { + GList *elements = (element->sched)->elements; + + // the following code ensures that the bottom half of thread will run + // to perform each elements' change_state() (by calling gstbin.c:: + // change_state()). + // + the pending state was already set by gstelement.c::set_state() + // + find every queue we manage, and signal its empty and full conditions + + g_mutex_lock(thread->lock); + + GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); + + while (elements) + { + GstElement *e = GST_ELEMENT(elements->data); + g_assert(e); + THR_DEBUG(" element \"%s\"\n",GST_ELEMENT_NAME(e)); + elements = g_list_next(elements); + if (GST_IS_QUEUE(e)) + { + //FIXME make this more efficient by only waking queues that are asleep + //FIXME and only waking the appropriate condition (depending on if it's + //FIXME on up- or down-stream side) + // + //FIXME also make this more efficient by keeping list of managed queues + THR_DEBUG("waking queue \"%s\"\n",GST_ELEMENT_NAME(e)); + GST_LOCK(e); + g_cond_signal((GST_QUEUE(e)->emptycond)); + g_cond_signal((GST_QUEUE(e)->fullcond)); + GST_UNLOCK(e); + } + else + { + GList *pads = GST_ELEMENT_PADS(e); + while (pads) + { + GstPad *p = GST_PAD(pads->data); + pads = g_list_next(pads); + + peerelement = GST_PAD_PARENT(GST_PAD_PEER(p)); + if (!peerelement) continue; // deal with case where there's no peer + + if (!GST_FLAG_IS_SET(peerelement,GST_ELEMENT_DECOUPLED)) { + GST_DEBUG(GST_CAT_THREAD,"peer element isn't DECOUPLED\n"); + continue; + } + + // FIXME this needs to go away eventually + if (!GST_IS_QUEUE(peerelement)) { + GST_DEBUG(GST_CAT_THREAD,"peer element isn't a Queue\n"); + continue; + } + + if (GST_ELEMENT_SCHED(peerelement) != GST_ELEMENT_SCHED(thread)) + { + THR_DEBUG(" element \"%s\" has pad cross sched boundary\n",GST_ELEMENT_NAME(e)); + GST_LOCK(peerelement); + g_cond_signal(GST_QUEUE(peerelement)->emptycond); + g_cond_signal(GST_QUEUE(peerelement)->fullcond); + GST_UNLOCK(peerelement); + } + } + } + } + THR_DEBUG("waiting for thread to stop spinning\n"); + g_cond_wait (thread->cond, thread->lock); + THR_DEBUG("telling thread to pause\n"); + gst_thread_signal_thread(thread,FALSE); + } break; case GST_STATE_READY_TO_NULL: - GST_INFO (GST_CAT_THREAD,"gstthread: stopping thread \"%s\"", - GST_ELEMENT_NAME (GST_ELEMENT (element))); + THR_INFO("stopping thread"); GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING); - gst_thread_signal_thread (thread); - pthread_join(thread->thread_id,NULL); + + // check to see if the thread is somehow changing its own state. + // FIXME this is currently illegal, but must somehow be made legal at some point. + if (pthread_equal(self, thread->thread_id)) + { + //FIXME this should not happen + g_assert(!pthread_equal(self, thread->thread_id)); + THR_DEBUG("setting own thread's state to NULL (paused)\n"); + GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); + } + else + { + THR_DEBUG("telling thread to pause (null) - and joining\n"); + //MattH FIXME revisit +// g_mutex_lock(thread->lock); +// gst_thread_signal_thread(thread,FALSE); + pthread_join(thread->thread_id,NULL); + } + + GST_FLAG_UNSET(thread,GST_THREAD_STATE_REAPING); + GST_FLAG_UNSET(thread,GST_THREAD_STATE_STARTED); + GST_FLAG_UNSET(thread,GST_THREAD_STATE_SPINNING); + GST_FLAG_UNSET(thread,GST_THREAD_STATE_ELEMENT_CHANGED); + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + stateset = GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT(thread)); + break; default: break; @@ -294,6 +461,16 @@ gst_thread_change_state (GstElement *element) return stateset; } +static void gst_thread_update_state (GstThread *thread) +{ + // check for state change + if (GST_STATE_PENDING(thread) != GST_STATE_NONE_PENDING) { + // punt and change state on all the children + if (GST_ELEMENT_CLASS (parent_class)->change_state) + GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT(thread)); + } +} + /** * gst_thread_main_loop: * @arg: the thread to start @@ -305,53 +482,186 @@ static void * gst_thread_main_loop (void *arg) { GstThread *thread = GST_THREAD (arg); + gint stateset; + + thread->pid = getpid(); + THR_INFO_MAIN("thread is running"); - GST_INFO (GST_CAT_THREAD,"gstthread: thread \"%s\" is running with PID %d", - GST_ELEMENT_NAME (GST_ELEMENT (thread)), getpid ()); + // first we need to change the state of all the children + if (GST_ELEMENT_CLASS (parent_class)->change_state) + stateset = GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT(thread)); // construct the plan and signal back +/* DEPRACATED for INCSCHED1 + THR_DEBUG_MAIN("creating plan for thread\n"); if (GST_BIN_CLASS (parent_class)->schedule) GST_BIN_CLASS (parent_class)->schedule (GST_BIN (thread)); +*/ + +// THR_DEBUG_MAIN("indicating spinup\n"); + g_mutex_lock (thread->lock); + g_cond_signal (thread->cond); + // don't unlock the mutex because we hold it into the top of the while loop + THR_DEBUG_MAIN("thread has indicated spinup to parent process\n"); - gst_thread_signal_thread (thread); + /***** THREAD IS NOW IN READY STATE *****/ while (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING)) { - if (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)) { + // NOTE we hold the thread lock at this point + // what we do depends on what state we're in + switch (GST_STATE(thread)) { + // NOTE: cannot be in NULL, we're not running in that state at all + case GST_STATE_READY: + // wait to be set to either the NULL or PAUSED states + THR_DEBUG_MAIN("thread in %s state, waiting for either %s or %s\n", + gst_element_statename(GST_STATE_READY), + gst_element_statename(GST_STATE_NULL), + gst_element_statename(GST_STATE_PAUSED)); + g_cond_wait(thread->cond,thread->lock); + // been signaled, we need to state transition now and signal back + gst_thread_update_state(thread); + THR_DEBUG_MAIN("done with state transition, signaling back to parent process\n"); + g_cond_signal(thread->cond); +// g_mutex_unlock(thread->lock); + // now we decide what to do next (FIXME can be collapsed to a continue) + if (GST_STATE(thread) == GST_STATE_NULL) { + // REAPING must be set, we can simply break this iteration + continue; + } else { + // PAUSED is the next state, we can wait for that next + continue; + } + break; + case GST_STATE_PAUSED: + // wait to be set to either the READY or PLAYING states + THR_DEBUG_MAIN("thread in %s state, waiting for either %s or %s\n", + gst_element_statename(GST_STATE_PAUSED), + gst_element_statename(GST_STATE_READY), + gst_element_statename(GST_STATE_PLAYING)); + g_cond_wait(thread->cond,thread->lock); + // been signaled, we need to state transition now and signal back + gst_thread_update_state(thread); + g_cond_signal(thread->cond); +// g_mutex_unlock(thread->lock); + // now we decide what to do next + if (GST_STATE(thread) == GST_STATE_READY) { + // READY is the next state, we can wait for that next + continue; + } else { + g_mutex_unlock(thread->lock); + // PLAYING is coming up, so we can now start spinning + while (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)) { + if (!gst_bin_iterate (GST_BIN (thread))) { + GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); + THR_DEBUG_MAIN("removed spinning state due to failed iteration!\n"); + } + } + g_mutex_lock(thread->lock); + // once we're here, SPINNING has stopped, we should signal that we're done + THR_DEBUG_MAIN("SPINNING stopped, signaling back to parent process\n"); + g_cond_signal (thread->cond); + // now we can wait for PAUSED + continue; + } + break; + case GST_STATE_PLAYING: + // wait to be set to PAUSED + THR_DEBUG_MAIN("thread in %s state, waiting for %s\n", + gst_element_statename(GST_STATE_PLAYING), + gst_element_statename(GST_STATE_PAUSED)); + g_cond_wait(thread->cond,thread->lock); + // been signaled, we need to state transition now and signal back + gst_thread_update_state(thread); + g_cond_signal(thread->cond); +// g_mutex_unlock(thread->lock); + // now we decide what to do next + // there's only PAUSED, we we just wait for it + continue; + break; + } + + // need to grab the lock so we're ready for the top of the loop +// g_mutex_lock(thread->lock); + } + +/* + while (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING)) { + // start out by waiting for a state change into spinning + THR_DEBUG_MAIN("waiting for signal from parent process (at top of while())\n"); + g_cond_wait (thread->cond,thread->lock); + THR_DEBUG_MAIN("woken up with %s pending\n",gst_element_statename(GST_STATE(thread))); + // now is a good time to change the state of the children and the thread itself + gst_thread_update_state (thread); + THR_DEBUG_MAIN("done changing state, signaling back\n"); + g_cond_signal (thread->cond); + g_mutex_unlock (thread->lock); + THR_DEBUG_MAIN("finished sycnronizing with main process\n"); + + while (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)) { if (!gst_bin_iterate (GST_BIN (thread))) { GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); + THR_DEBUG_MAIN("removed spinning state due to failed iteration!\n"); } } - else { - GST_DEBUG (0, "thread \"%s\" waiting\n", GST_ELEMENT_NAME (GST_ELEMENT (thread))); - gst_thread_wait_thread (thread); + + g_mutex_lock (thread->lock); + + if (GST_STATE_PENDING(thread) == GST_STATE_PAUSED) { + // we've stopped spinning, because of PLAYING->PAUSED + THR_DEBUG_MAIN("SPINNING flag unset, signaling parent process we're stopped\n"); + // we need to signal back that we've stopped spinning + g_cond_signal (thread->cond); } + +// THR_DEBUG_MAIN("signaling that the thread is out of the SPINNING loop\n"); +// g_cond_signal (thread->cond); +// g_cond_wait (thread->cond, thread->lock); +// THR_DEBUG_MAIN("parent process has signaled at bottom of while\n"); +// // now change the children's and thread's state +// gst_thread_update_state (thread); +// THR_DEBUG_MAIN("done changing state, signaling back to parent process\n"); +// g_cond_signal (thread->cond); +// // don't release the mutex, we hold that into the top of the loop +// THR_DEBUG_MAIN("done syncing with parent process at bottom of while\n"); } +*/ - GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING); -// FIXME this should be removed (why's it here???) -// //pthread_join (thread->thread_id, 0); + // since we don't unlock at the end of the while loop, do it here + g_mutex_unlock (thread->lock); GST_INFO (GST_CAT_THREAD, "gstthread: thread \"%s\" is stopped", GST_ELEMENT_NAME (thread)); return NULL; } +// the set flag is to say whether it should set TRUE or FALSE +// +// WARNING: this has synchronization built in! if you remove or add any +// locks, waits, signals, or unlocks you need to be sure they match the +// code above (in gst_thread_main_loop()). basically, don't change anything. static void -gst_thread_signal_thread (GstThread *thread) +gst_thread_signal_thread (GstThread *thread, gboolean spinning) { - GST_DEBUG (0,"signaling thread\n"); - g_mutex_lock (thread->lock); + // set the spinning state + if (spinning) GST_FLAG_SET(thread,GST_THREAD_STATE_SPINNING); + else GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); + + THR_DEBUG("thread locked\n"); +// g_mutex_lock(thread->lock); + +// if (!spinning) { +// THR_DEBUG("waiting for spindown\n"); +// g_cond_wait (thread->cond, thread->lock); +// } + THR_DEBUG("signaling\n"); g_cond_signal (thread->cond); - g_mutex_unlock (thread->lock); -} + THR_DEBUG("waiting for ack\n"); + g_cond_wait (thread->cond,thread->lock); + THR_DEBUG("got ack\n"); -static void -gst_thread_wait_thread (GstThread *thread) -{ - GST_DEBUG (0,"waiting for thread\n"); - g_mutex_lock (thread->lock); - g_cond_wait (thread->cond, thread->lock); - g_mutex_unlock (thread->lock); + THR_DEBUG("unlocking\n"); + g_mutex_unlock(thread->lock); + THR_DEBUG("unlocked\n"); } @@ -359,7 +669,7 @@ static void gst_thread_restore_thyself (GstObject *object, xmlNodePtr self) { - GST_DEBUG (0,"gstthread: restore\n"); + GST_DEBUG (GST_CAT_THREAD,"gstthread: restore\n"); if (GST_OBJECT_CLASS (parent_class)->restore_thyself) GST_OBJECT_CLASS (parent_class)->restore_thyself (object, self); diff --git a/gst/gstthread.h b/gst/gstthread.h index 8c53a118a..be8b49cd8 100644 --- a/gst/gstthread.h +++ b/gst/gstthread.h @@ -24,6 +24,7 @@ #ifndef __GST_THREAD_H__ #define __GST_THREAD_H__ +#include #include #include @@ -38,8 +39,11 @@ extern GstElementDetails gst_thread_details; typedef enum { GST_THREAD_CREATE = GST_BIN_FLAG_LAST, + + GST_THREAD_STATE_STARTED, GST_THREAD_STATE_SPINNING, GST_THREAD_STATE_REAPING, + GST_THREAD_STATE_ELEMENT_CHANGED, /* padding */ GST_THREAD_FLAG_LAST = GST_BIN_FLAG_LAST + 4, @@ -64,8 +68,12 @@ struct _GstThread { GstBin bin; pthread_t thread_id; /* id of the thread, if any */ + gint pid; /* the pid of the thread */ + gint ppid; /* the pid of the thread's parent process */ GMutex *lock; /* thread lock/condititon pair... */ GCond *cond; /* used to control the thread */ + + gint transition; /* the current state transition */ }; struct _GstThreadClass { @@ -74,7 +82,7 @@ struct _GstThreadClass { GtkType gst_thread_get_type (void); -GstElement* gst_thread_new (guchar *name); +GstElement* gst_thread_new (const guchar *name); #ifdef __cplusplus } diff --git a/gst/gsttype.c b/gst/gsttype.c index 437da194d..744089885 100644 --- a/gst/gsttype.c +++ b/gst/gsttype.c @@ -80,7 +80,7 @@ gst_type_register (GstTypeFactory *factory) _gst_types = g_list_prepend (_gst_types, type); id = type->id; - GST_DEBUG (0,"gsttype: new mime type '%s', id %d\n", type->mime, type->id); + GST_DEBUG (GST_CAT_TYPES,"gsttype: new mime type '%s', id %d\n", type->mime, type->id); } else { type = gst_type_find_by_id (id); @@ -108,12 +108,12 @@ guint16 gst_type_find_by_mime_func (const gchar *mime) g_return_val_if_fail (mime != NULL, 0); walk = _gst_types; -// GST_DEBUG (0,"searching for '%s'\n",mime); +// GST_DEBUG (GST_CAT_TYPES,"searching for '%s'\n",mime); mimelen = strlen (mime); while (walk) { type = (GstType *)walk->data; search = type->mime; -// GST_DEBUG (0,"checking against '%s'\n",search); +// GST_DEBUG (GST_CAT_TYPES,"checking against '%s'\n",search); typelen = strlen (search); while ((search - type->mime) < typelen) { found = strstr (search, mime); @@ -232,7 +232,7 @@ gst_type_typefind_dummy (GstBuffer *buffer, gpointer priv) guint16 typeid; GSList *funcs; - GST_DEBUG (0,"gsttype: need to load typefind function for %s\n", type->mime); + GST_DEBUG (GST_CAT_TYPES,"gsttype: need to load typefind function for %s\n", type->mime); type->typefindfuncs = NULL; gst_plugin_load_typefactory (type->mime); diff --git a/gst/gsttypefind.c b/gst/gsttypefind.c index 78ac04c81..2e045e0b8 100644 --- a/gst/gsttypefind.c +++ b/gst/gsttypefind.c @@ -180,11 +180,25 @@ gst_typefind_chain (GstPad *pad, GstBuffer *buf) GST_DEBUG (0,"try type :%d \"%s\"\n", type->id, type->mime); if (typefindfunc && (caps = typefindfunc (buf, type))) { - GST_DEBUG (0,"found type :%d \"%s\"\n", caps->id, type->mime); + GST_DEBUG (0,"found type :%d \"%s\" \"%s\"\n", caps->id, type->mime, + gst_caps_get_name (caps)); typefind->caps = caps; + + gst_pad_set_caps (pad, caps); + +{ /* FIXME: this should all be in an _emit() wrapper eventually */ + int oldstate = GST_STATE(typefind); + gst_object_ref (GST_OBJECT (typefind)); gtk_signal_emit (GTK_OBJECT (typefind), gst_typefind_signals[HAVE_TYPE], typefind->caps); - gst_pad_set_caps (pad, caps); + if (GST_STATE(typefind) != oldstate) { + gst_object_unref (GST_OBJECT (typefind)); + GST_DEBUG(0, "state changed during signal, aborting\n"); + cothread_switch(cothread_current_main()); + } + gst_object_unref (GST_OBJECT (typefind)); +} + goto end; } funcs = g_slist_next (funcs); diff --git a/gst/gsttypes.h b/gst/gsttypes.h new file mode 100644 index 000000000..a7c07ed8f --- /dev/null +++ b/gst/gsttypes.h @@ -0,0 +1,17 @@ +#ifndef __GST_TYPES_H__ +#define __GST_TYPES_H__ + +typedef struct _GstObject GstObject; +typedef struct _GstObjectClass GstObjectClass; +typedef struct _GstPad GstPad; +typedef struct _GstPadClass GstPadClass; +typedef struct _GstPadTemplate GstPadTemplate; +typedef struct _GstPadTemplateClass GstPadTemplateClass; +typedef struct _GstElement GstElement; +typedef struct _GstElementClass GstElementClass; +typedef struct _GstBin GstBin; +typedef struct _GstBinClass GstBinClass; +typedef struct _GstSchedule GstSchedule; +typedef struct _GstScheduleClass GstScheduleClass; + +#endif diff --git a/gst/gstxml.c b/gst/gstxml.c index 146fda0a7..28f67c9f0 100644 --- a/gst/gstxml.c +++ b/gst/gstxml.c @@ -167,7 +167,7 @@ gst_xml_parse_doc (GstXML *xml, xmlDocPtr doc, const guchar *root) if (!strcmp(field->name, "element") && (field->ns == xml->ns)) { GstElement *element; - element = gst_element_load_thyself(field, NULL); + element = gst_element_restore_thyself(field, NULL); xml->topelements = g_list_prepend (xml->topelements, element); } diff --git a/gstplay/Makefile.am b/gstplay/Makefile.am index 7b6dc5477..552490962 100644 --- a/gstplay/Makefile.am +++ b/gstplay/Makefile.am @@ -37,6 +37,7 @@ noinst_HEADERS = \ gstplayprivate.h \ full-screen.h + libgstmediaplay_la_LDFLAGS = -rdynamic gstmediaplay_CFLAGS = $(LIBGLADE_GNOME_CFLAGS) diff --git a/gstplay/gstmediaplay.c b/gstplay/gstmediaplay.c index 84e2c0f89..e42cc3bfc 100644 --- a/gstplay/gstmediaplay.c +++ b/gstplay/gstmediaplay.c @@ -39,12 +39,11 @@ target_drag_data_received (GtkWidget *widget, guint time, GstMediaPlay *play) { - if (strstr (data->data, "file:")) { - g_print ("Got: %s\n", &data->data[5]); - gdk_threads_leave (); - gst_media_play_start_uri (play, g_strchomp (&data->data[5])); - gdk_threads_enter (); - } + g_print ("Got: %s\n", data->data); + gdk_threads_leave (); + gst_media_play_start_uri (play, g_strchomp (data->data)); + gdk_threads_enter (); + } static GtkTargetEntry target_table[] = { diff --git a/gstplay/gstmediaplay.glade b/gstplay/gstmediaplay.glade index 78567a3f2..bdf88d442 100644 --- a/gstplay/gstmediaplay.glade +++ b/gstplay/gstmediaplay.glade @@ -508,6 +508,7 @@ Arik Devens <arik@gnome.org> Sun, 06 Aug 2000 15:55:52 GMT GNOME_STOCK_BUTTON_OK + GTK_RELIEF_NORMAL @@ -523,6 +524,7 @@ Arik Devens <arik@gnome.org> Sun, 06 Aug 2000 15:53:48 GMT GNOME_STOCK_BUTTON_CANCEL + GTK_RELIEF_NORMAL diff --git a/gstplay/gstplay.c b/gstplay/gstplay.c index 1b8569f24..6cb247765 100644 --- a/gstplay/gstplay.c +++ b/gstplay/gstplay.c @@ -1,20 +1,22 @@ #include +#include + #include "gstplay.h" #include "gstplayprivate.h" #include "full-screen.h" -static void gst_play_class_init (GstPlayClass *klass); -static void gst_play_init (GstPlay *play); +static void gst_play_class_init (GstPlayClass *klass); +static void gst_play_init (GstPlay *play); -static void gst_play_set_arg (GtkObject *object, GtkArg *arg, guint id); -static void gst_play_get_arg (GtkObject *object, GtkArg *arg, guint id); +static void gst_play_set_arg (GtkObject *object, GtkArg *arg, guint id); +static void gst_play_get_arg (GtkObject *object, GtkArg *arg, guint id); -static void gst_play_realize (GtkWidget *play); +static void gst_play_realize (GtkWidget *play); -static void gst_play_frame_displayed (GstElement *element, GstPlay *play); -static void gst_play_have_size (GstElement *element, guint width, guint height, GstPlay *play); -static void gst_play_audio_handoff (GstElement *element, GstPlay *play); +static void gst_play_frame_displayed (GstElement *element, GstPlay *play); +static void gst_play_have_size (GstElement *element, guint width, guint height, GstPlay *play); +static void gst_play_audio_handoff (GstElement *element, GstPlay *play); /* signals and args */ enum { @@ -118,10 +120,8 @@ gst_play_init (GstPlay *play) play->priv = priv; /* create a new bin to hold the elements */ - priv->thread = gst_thread_new ("main_thread"); - g_assert (priv->thread != NULL); - priv->bin = gst_bin_new ("main_bin"); - g_assert (priv->bin != NULL); + priv->pipeline = gst_pipeline_new ("main_pipeline"); + g_assert (priv->pipeline != NULL); priv->audio_element = gst_elementfactory_make ("osssink", "play_audio"); g_return_if_fail (priv->audio_element != NULL); @@ -165,6 +165,12 @@ gst_play_new () return GST_PLAY (gtk_type_new (GST_TYPE_PLAY)); } +static gboolean +gst_play_idle_func (gpointer data) +{ + return gst_bin_iterate (GST_BIN (data)); +} + static void gst_play_eos (GstElement *element, GstPlay *play) @@ -273,53 +279,68 @@ gst_play_object_added (GstAutoplug* autoplug, GstObject *object, GstPlay *play) } static void -gst_play_have_type (GstElement *sink, GstElement *sink2, gpointer data) +gst_play_cache_empty (GstElement *element, GstPlay *play) { - GST_DEBUG (0,"GstPipeline: play have type %p\n", (gboolean *)data); + GstPlayPrivate *priv; + GstElement *new_element; + + priv = (GstPlayPrivate *)play->priv; + + gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); + + new_element = gst_bin_get_by_name (GST_BIN (priv->pipeline), "new_element"); + + gst_element_disconnect (priv->src, "src", priv->cache, "sink"); + gst_element_disconnect (priv->cache, "src", new_element, "sink"); + gst_bin_remove (GST_BIN (priv->pipeline), priv->cache); + gst_element_connect (priv->src, "src", new_element, "sink"); - *(gboolean *)data = TRUE; + gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); } -static GstCaps* -gst_play_typefind (GstBin *bin, GstElement *element) +static void +gst_play_have_type (GstElement *sink, GstCaps *caps, GstPlay *play) { - gboolean found = FALSE; - GstElement *typefind; - GstCaps *caps = NULL; + GstPlayPrivate *priv; + GstElement *new_element; + GstAutoplug *autoplug; - GST_DEBUG (0, "GstPipeline: typefind for element \"%s\" %p\n", - GST_ELEMENT_NAME (element), &found); + GST_DEBUG (0,"GstPipeline: play have type\n"); - typefind = gst_elementfactory_make ("typefind", "typefind"); - g_return_val_if_fail (typefind != NULL, FALSE); + priv = (GstPlayPrivate *)play->priv; - gtk_signal_connect (GTK_OBJECT (typefind), "have_type", - GTK_SIGNAL_FUNC (gst_play_have_type), &found); + gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); - gst_pad_connect (gst_element_get_pad (element, "src"), - gst_element_get_pad (typefind, "sink")); + gst_element_disconnect (priv->cache, "src", priv->typefind, "sink"); + gst_bin_remove (GST_BIN (priv->pipeline), priv->typefind); - gst_bin_add (bin, typefind); + autoplug = gst_autoplugfactory_make ("staticrender"); + g_assert (autoplug != NULL); + + gtk_signal_connect (GTK_OBJECT (autoplug), "new_object", gst_play_object_added, play); + + new_element = gst_autoplug_to_renderers (autoplug, + caps, + priv->video_element, + priv->audio_element, + NULL); - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); + if (!new_element) { + // FIXME, signal a suitable error + return; + } - // push a buffer... the have_type signal handler will set the found flag - gst_bin_iterate (bin); + gst_element_set_name (new_element, "new_element"); - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL); + gst_bin_add (GST_BIN (priv->pipeline), new_element); - if (found) { - caps = gst_util_get_pointer_arg (GTK_OBJECT (typefind), "caps"); + gtk_object_set (GTK_OBJECT (priv->cache), "reset", TRUE, NULL); - gst_pad_set_caps (gst_element_get_pad (element, "src"), caps); - } + gst_element_connect (priv->cache, "src", new_element, "sink"); - gst_pad_disconnect (gst_element_get_pad (element, "src"), - gst_element_get_pad (typefind, "sink")); - gst_bin_remove (bin, typefind); - gst_object_unref (GST_OBJECT (typefind)); + gtk_signal_connect (GTK_OBJECT (priv->pipeline), "eos", GTK_SIGNAL_FUNC (gst_play_eos), play); - return caps; + gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); } static gboolean @@ -349,9 +370,7 @@ GstPlayReturn gst_play_set_uri (GstPlay *play, const guchar *uri) { GstPlayPrivate *priv; - GstCaps *src_caps; - GstElement *new_element; - GstAutoplug *autoplug; + gchar* uriloc; g_return_val_if_fail (play != NULL, GST_PLAY_ERROR); g_return_val_if_fail (GST_IS_PLAY (play), GST_PLAY_ERROR); @@ -362,47 +381,51 @@ gst_play_set_uri (GstPlay *play, const guchar *uri) if (priv->uri) g_free (priv->uri); - priv->uri = g_strdup (uri); - priv->src = gst_elementfactory_make ("disksrc", "disk_src"); - //priv->src = gst_elementfactory_make ("dvdsrc", "disk_src"); - priv->offset_element = priv->src; + /* see if it looks like a ARI */ + if ((uriloc = strstr (uri, ":/"))) { + priv->src = gst_elementfactory_make ("gnomevfssrc", "srcelement"); - g_return_val_if_fail (priv->src != NULL, -1); - gtk_object_set (GTK_OBJECT (priv->src), "location", uri, NULL); + if (!priv->src) { + if (strstr (uri, "file:/")) { + uri += strlen ("file:/"); + } + else + return GST_PLAY_CANNOT_PLAY; + } + } - gst_bin_add (GST_BIN (priv->bin), priv->src); + if (priv->src == NULL) { + priv->src = gst_elementfactory_make ("disksrc", "srcelement"); + } - src_caps = gst_play_typefind (GST_BIN (priv->bin), priv->src); + priv->uri = g_strdup (uri); - if (!src_caps) { - return GST_PLAY_UNKNOWN_MEDIA; - } + //priv->src = gst_elementfactory_make ("dvdsrc", "disk_src"); + priv->offset_element = priv->src; + g_return_val_if_fail (priv->src != NULL, GST_PLAY_CANNOT_PLAY); - autoplug = gst_autoplugfactory_make ("staticrender"); - g_assert (autoplug != NULL); + gtk_object_set (GTK_OBJECT (priv->src), "location", priv->uri, NULL); - gtk_signal_connect (GTK_OBJECT (autoplug), "new_object", gst_play_object_added, play); - new_element = gst_autoplug_to_renderers (autoplug, - gst_pad_get_caps (gst_element_get_pad (priv->src, "src")), - priv->video_element, - priv->audio_element, - NULL); + priv->cache = gst_elementfactory_make ("autoplugcache", "cache"); + g_return_val_if_fail (priv->cache != NULL, GST_PLAY_CANNOT_PLAY); - if (!new_element) { - return GST_PLAY_CANNOT_PLAY; - } + gtk_signal_connect (GTK_OBJECT (priv->cache), "cache_empty", + GTK_SIGNAL_FUNC (gst_play_cache_empty), play); - gst_bin_remove (GST_BIN (priv->bin), priv->src); - gst_bin_add (GST_BIN (priv->thread), priv->src); + priv->typefind = gst_elementfactory_make ("typefind", "typefind"); + g_return_val_if_fail (priv->typefind != NULL, GST_PLAY_CANNOT_PLAY); + gtk_signal_connect (GTK_OBJECT (priv->typefind), "have_type", + GTK_SIGNAL_FUNC (gst_play_have_type), play); - gst_bin_add (GST_BIN (priv->bin), new_element); - gst_element_connect (priv->src, "src", new_element, "sink"); + gst_bin_add (GST_BIN (priv->pipeline), priv->src); + gst_bin_add (GST_BIN (priv->pipeline), priv->cache); + gst_bin_add (GST_BIN (priv->pipeline), priv->typefind); - gst_bin_add (GST_BIN (priv->thread), priv->bin); - gtk_signal_connect (GTK_OBJECT (priv->thread), "eos", GTK_SIGNAL_FUNC (gst_play_eos), play); + gst_element_connect (priv->src, "src", priv->cache, "sink"); + gst_element_connect (priv->cache, "src", priv->typefind, "sink"); return GST_PLAY_OK; } @@ -449,10 +472,11 @@ gst_play_play (GstPlay *play) if (play->state == GST_PLAY_PLAYING) return; if (play->state == GST_PLAY_STOPPED) - gst_element_set_state (GST_ELEMENT (priv->thread),GST_STATE_READY); - gst_element_set_state (GST_ELEMENT (priv->thread),GST_STATE_PLAYING); + gst_element_set_state (GST_ELEMENT (priv->pipeline),GST_STATE_READY); + gst_element_set_state (GST_ELEMENT (priv->pipeline),GST_STATE_PLAYING); play->state = GST_PLAY_PLAYING; + gtk_idle_add (gst_play_idle_func, priv->pipeline); gtk_signal_emit (GTK_OBJECT (play), gst_play_signals[SIGNAL_STATE_CHANGED], play->state); @@ -470,9 +494,10 @@ gst_play_pause (GstPlay *play) if (play->state != GST_PLAY_PLAYING) return; - gst_element_set_state (GST_ELEMENT (priv->thread),GST_STATE_PAUSED); + gst_element_set_state (GST_ELEMENT (priv->pipeline),GST_STATE_PAUSED); play->state = GST_PLAY_PAUSED; + g_idle_remove_by_data (priv->pipeline); gtk_signal_emit (GTK_OBJECT (play), gst_play_signals[SIGNAL_STATE_CHANGED], play->state); @@ -491,11 +516,12 @@ gst_play_stop (GstPlay *play) priv = (GstPlayPrivate *)play->priv; // FIXME until state changes are handled properly - gst_element_set_state (GST_ELEMENT (priv->thread),GST_STATE_READY); + gst_element_set_state (GST_ELEMENT (priv->pipeline),GST_STATE_READY); gtk_object_set (GTK_OBJECT (priv->src),"offset",0,NULL); - //gst_element_set_state (GST_ELEMENT (priv->thread),GST_STATE_NULL); + //gst_element_set_state (GST_ELEMENT (priv->pipeline),GST_STATE_NULL); play->state = GST_PLAY_STOPPED; + g_idle_remove_by_data (priv->pipeline); gtk_signal_emit (GTK_OBJECT (play), gst_play_signals[SIGNAL_STATE_CHANGED], play->state); @@ -653,7 +679,7 @@ gst_play_get_pipeline (GstPlay *play) priv = (GstPlayPrivate *)play->priv; - return GST_ELEMENT (priv->bin); + return GST_ELEMENT (priv->pipeline); } static void diff --git a/gstplay/gstplay.h b/gstplay/gstplay.h index beda5f16d..8356dd1ee 100644 --- a/gstplay/gstplay.h +++ b/gstplay/gstplay.h @@ -19,10 +19,10 @@ typedef enum { } GstPlayState; typedef enum { - GST_PLAY_OK, - GST_PLAY_UNKNOWN_MEDIA, - GST_PLAY_CANNOT_PLAY, - GST_PLAY_ERROR, + GST_PLAY_OK, + GST_PLAY_UNKNOWN_MEDIA, + GST_PLAY_CANNOT_PLAY, + GST_PLAY_ERROR, } GstPlayReturn; typedef enum { diff --git a/gstplay/gstplayprivate.h b/gstplay/gstplayprivate.h index 763f80af1..2b80f179a 100644 --- a/gstplay/gstplayprivate.h +++ b/gstplay/gstplayprivate.h @@ -6,12 +6,13 @@ typedef struct _GstPlayPrivate GstPlayPrivate; struct _GstPlayPrivate { - GstElement *thread; - GstElement *bin; + GstElement *pipeline; GstElement *video_element, *audio_element; GstElement *video_show; GtkWidget *video_widget; GstElement *src; + GstElement *cache; + GstElement *typefind; guchar *uri; gboolean muted; diff --git a/gstreamer-uninstalled.pc.in b/gstreamer-uninstalled.pc.in new file mode 100644 index 000000000..0cc88fd90 --- /dev/null +++ b/gstreamer-uninstalled.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: GStreamer Uninstalled +Description: Streaming-media framework, Not Installed +Version: @VERSION@ +Requires: glib gtk+ +Libs: ${pcbuilddir}/${pcfiledir}/gst/libgst.la +Cflags: -I${pcbuilddir}/${pcfiledir} diff --git a/gstreamer.pc.in b/gstreamer.pc.in new file mode 100644 index 000000000..47ed44b0f --- /dev/null +++ b/gstreamer.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: GStreamer +Description: Streaming-media framework +Requires: +Version: @VERSION@ +Libs: -L${libdir} -lgst +Cflags: diff --git a/gstreamer.spec.in b/gstreamer.spec.in index 28456b138..6ccff3ea7 100644 --- a/gstreamer.spec.in +++ b/gstreamer.spec.in @@ -84,3 +84,4 @@ make prefix=$RPM_BUILD_ROOT%{prefix} install %{prefix}/include/* %{prefix}/lib/lib*.a %{prefix}/lib/lib*.so +%{prefix}/lib/pkgconfig/* diff --git a/libs/idct/gstidct.c b/libs/idct/gstidct.c index 39b3cfcf9..ae180e363 100644 --- a/libs/idct/gstidct.c +++ b/libs/idct/gstidct.c @@ -53,38 +53,38 @@ GstIDCT *gst_idct_new(GstIDCTMethod method) switch (method) { case GST_IDCT_FAST_INT: - GST_INFO (GST_CAT_PLUGIN_INFO, "GstIDCT: using fast_int_idct\n"); + GST_INFO (GST_CAT_PLUGIN_INFO, "using fast_int_idct"); gst_idct_init_fast_int_idct(); new->convert = gst_idct_fast_int_idct; break; case GST_IDCT_INT: - GST_INFO (GST_CAT_PLUGIN_INFO, "GstIDCT: using int_idct\n"); + GST_INFO (GST_CAT_PLUGIN_INFO, "using int_idct"); new->convert = gst_idct_int_idct; break; case GST_IDCT_FLOAT: - GST_INFO (GST_CAT_PLUGIN_INFO, "GstIDCT: using float_idct\n"); + GST_INFO (GST_CAT_PLUGIN_INFO, "using float_idct"); gst_idct_init_float_idct(); new->convert = gst_idct_float_idct; break; #ifdef HAVE_LIBMMX case GST_IDCT_MMX: - GST_INFO (GST_CAT_PLUGIN_INFO, "GstIDCT: using MMX_idct\n"); + GST_INFO (GST_CAT_PLUGIN_INFO, "using MMX_idct"); new->convert = gst_idct_mmx_idct; new->need_transpose = TRUE; break; case GST_IDCT_MMX32: - GST_INFO (GST_CAT_PLUGIN_INFO, "GstIDCT: using MMX32_idct\n"); + GST_INFO (GST_CAT_PLUGIN_INFO, "using MMX32_idct"); new->convert = gst_idct_mmx32_idct; new->need_transpose = TRUE; break; case GST_IDCT_SSE: - GST_INFO (GST_CAT_PLUGIN_INFO, "GstIDCT: using SSE_idct\n"); + GST_INFO (GST_CAT_PLUGIN_INFO, "using SSE_idct"); new->convert = gst_idct_sse_idct; new->need_transpose = TRUE; break; #endif /* HAVE_LIBMMX */ default: - GST_INFO (GST_CAT_PLUGIN_INFO, "GstIDCT: method not supported\n"); + GST_INFO (GST_CAT_PLUGIN_INFO, "method not supported"); g_free(new); return NULL; } diff --git a/plugins/elements/gstfakesrc.c b/plugins/elements/gstfakesrc.c index 5fa6af217..97570bbb8 100644 --- a/plugins/elements/gstfakesrc.c +++ b/plugins/elements/gstfakesrc.c @@ -49,6 +49,7 @@ enum { ARG_OUTPUT, ARG_PATTERN, ARG_NUM_BUFFERS, + ARG_EOS, ARG_SILENT }; @@ -125,6 +126,8 @@ gst_fakesrc_class_init (GstFakeSrcClass *klass) GTK_ARG_READWRITE, ARG_PATTERN); gtk_object_add_arg_type ("GstFakeSrc::num_buffers", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_NUM_BUFFERS); + gtk_object_add_arg_type ("GstFakeSrc::eos", GTK_TYPE_BOOL, + GTK_ARG_READWRITE, ARG_EOS); gtk_object_add_arg_type ("GstFakeSrc::silent", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_SILENT); @@ -222,6 +225,10 @@ gst_fakesrc_set_arg (GtkObject *object, GtkArg *arg, guint id) case ARG_NUM_BUFFERS: src->num_buffers = GTK_VALUE_INT (*arg); break; + case ARG_EOS: + src->eos = GTK_VALUE_BOOL (*arg); +GST_INFO (0, "will EOS on next buffer"); + break; case ARG_SILENT: src->silent = GTK_VALUE_BOOL (*arg); break; @@ -256,6 +263,8 @@ gst_fakesrc_get_arg (GtkObject *object, GtkArg *arg, guint id) case ARG_NUM_BUFFERS: GTK_VALUE_INT (*arg) = src->num_buffers; break; + case ARG_EOS: + GTK_VALUE_BOOL (*arg) = src->eos; case ARG_SILENT: GTK_VALUE_BOOL (*arg) = src->silent; break; @@ -295,6 +304,12 @@ gst_fakesrc_get(GstPad *pad) src->num_buffers--; } + if (src->eos) { + GST_INFO (0, "fakesrc is setting eos on pad"); + gst_pad_set_eos (pad); + return NULL; + } + if (!src->silent) g_print("fakesrc: ******* (%s:%s)> \n",GST_DEBUG_PAD_NAME(pad)); buf = gst_buffer_new(); @@ -336,7 +351,13 @@ gst_fakesrc_loop(GstElement *element) } else { if (src->num_buffers > 0) - src->num_buffers--; + src->num_buffers--; + } + + if (src->eos) { + GST_INFO (0, "fakesrc is setting eos on pad"); + gst_pad_set_eos (pad); + return; } buf = gst_buffer_new(); diff --git a/plugins/elements/gstfakesrc.h b/plugins/elements/gstfakesrc.h index a795c1932..ba92f631b 100644 --- a/plugins/elements/gstfakesrc.h +++ b/plugins/elements/gstfakesrc.h @@ -65,6 +65,7 @@ struct _GstFakeSrc { GstElement element; gboolean loop_based; + gboolean eos; gint numsrcpads; GSList *srcpads; GstFakeSrcOutputType output; diff --git a/plugins/elements/gstqueue.c b/plugins/elements/gstqueue.c index 37a4f7f3c..755c8f8c2 100644 --- a/plugins/elements/gstqueue.c +++ b/plugins/elements/gstqueue.c @@ -23,7 +23,7 @@ //#define DEBUG_ENABLED //#define STATUS_ENABLED #ifdef STATUS_ENABLED -#define STATUS(A) GST_DEBUG(0,A, gst_element_get_name(GST_ELEMENT(queue))) +#define STATUS(A) GST_DEBUG(GST_CAT_DATAFLOW, A, GST_ELEMENT_NAME(queue)) #else #define STATUS(A) #endif @@ -34,6 +34,7 @@ #include "gst_private.h" #include "gstqueue.h" +#include "gstscheduler.h" GstElementDetails gst_queue_details = { "Queue", @@ -47,15 +48,22 @@ GstElementDetails gst_queue_details = { /* Queue signals and args */ enum { - /* FILL ME */ + LOW_WATERMARK, + HIGH_WATERMARK, LAST_SIGNAL }; enum { ARG_0, + ARG_LEVEL_BUFFERS, + ARG_LEVEL_BYTES, + ARG_LEVEL_TIME, + ARG_SIZE_BUFFERS, + ARG_SIZE_BYTES, + ARG_SIZE_TIME, + ARG_LEAKY, ARG_LEVEL, ARG_MAX_LEVEL, - ARG_BLOCK, }; @@ -76,6 +84,23 @@ static void gst_queue_flush (GstQueue *queue); static GstElementStateReturn gst_queue_change_state (GstElement *element); + +static GtkType +queue_leaky_get_type(void) { + static GtkType queue_leaky_type = 0; + static GtkEnumValue queue_leaky[] = { + { GST_QUEUE_NO_LEAK, "0", "Not Leaky" }, + { GST_QUEUE_LEAK_UPSTREAM, "1", "Leaky on Upstream" }, + { GST_QUEUE_LEAK_DOWNSTREAM, "2", "Leaky on Downstream" }, + { 0, NULL, NULL }, + }; + if (!queue_leaky_type) { + queue_leaky_type = gtk_type_register_enum("GstQueueLeaky", queue_leaky); + } + return queue_leaky_type; +} +#define GST_TYPE_QUEUE_LEAKY (queue_leaky_get_type()) + static GstElementClass *parent_class = NULL; //static guint gst_queue_signals[LAST_SIGNAL] = { 0 }; @@ -111,12 +136,12 @@ gst_queue_class_init (GstQueueClass *klass) parent_class = gtk_type_class (GST_TYPE_ELEMENT); + gtk_object_add_arg_type ("GstQueue::leaky", GST_TYPE_QUEUE_LEAKY, + GTK_ARG_READWRITE, ARG_LEAKY); gtk_object_add_arg_type ("GstQueue::level", GTK_TYPE_INT, GTK_ARG_READABLE, ARG_LEVEL); gtk_object_add_arg_type ("GstQueue::max_level", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_MAX_LEVEL); - gtk_object_add_arg_type ("GstQueue::block", GTK_TYPE_BOOL, - GTK_ARG_READWRITE, ARG_BLOCK); gtkobject_class->set_arg = gst_queue_set_arg; gtkobject_class->get_arg = gst_queue_get_arg; @@ -144,14 +169,15 @@ gst_queue_init (GstQueue *queue) queue->queue = NULL; queue->level_buffers = 0; - queue->max_buffers = 100; - queue->block = TRUE; queue->level_bytes = 0; - queue->size_buffers = 0; - queue->size_bytes = 0; + queue->level_time = 0LL; + queue->size_buffers = 100; // 100 buffers + queue->size_bytes = 100 * 1024; // 100KB + queue->size_time = 1000000000LL; // 1sec queue->emptycond = g_cond_new (); queue->fullcond = g_cond_new (); + GST_DEBUG(GST_CAT_THREAD, "initialized queue's emptycond and fullcond\n"); } static GstBufferPool* @@ -206,10 +232,10 @@ gst_queue_handle_eos (GstPad *pad) queue = GST_QUEUE (GST_OBJECT_PARENT (pad)); - GST_DEBUG (0,"queue: %s received eos\n", GST_ELEMENT_NAME (queue)); + GST_DEBUG (GST_CAT_DATAFLOW,"%s received eos\n", GST_ELEMENT_NAME (queue)); GST_LOCK (queue); - GST_DEBUG (0,"queue: %s has %d buffers left\n", GST_ELEMENT_NAME (queue), + GST_DEBUG (GST_CAT_DATAFLOW,"%s has %d buffers left\n", GST_ELEMENT_NAME (queue), queue->level_buffers); GST_FLAG_SET (pad, GST_PAD_EOS); @@ -224,7 +250,7 @@ gst_queue_handle_eos (GstPad *pad) static void gst_queue_cleanup_buffers (gpointer data, const gpointer user_data) { - GST_DEBUG (0,"queue: %s cleaning buffer %p\n", (gchar *)user_data, data); + GST_DEBUG (GST_CAT_DATAFLOW,"%s cleaning buffer %p\n", (gchar *)user_data, data); gst_buffer_unref (GST_BUFFER (data)); } @@ -257,45 +283,79 @@ gst_queue_chain (GstPad *pad, GstBuffer *buf) /* we have to lock the queue since we span threads */ - GST_DEBUG (0,"queue: try have queue lock\n"); +// GST_DEBUG (GST_CAT_DATAFLOW,"trying to get lock on queue \"%s\"\n",name); GST_LOCK (queue); - GST_DEBUG (0,"queue: %s adding buffer %p %ld\n", name, buf, pthread_self ()); - GST_DEBUG (0,"queue: have queue lock\n"); if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLUSH)) { + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "buffer has FLUSH bit set, flushing queue\n"); gst_queue_flush (queue); } - GST_DEBUG (0,"queue: %s: chain %d %p\n", name, queue->level_buffers, buf); + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "adding buffer %p of size %d\n",buf,GST_BUFFER_SIZE(buf)); + + if (queue->level_buffers >= queue->size_buffers) { + // if this is a leaky queue... + if (queue->leaky) { + // if we leak on the upstream side, drop the current buffer + if (queue->leaky == GST_QUEUE_LEAK_UPSTREAM) { + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "queue is full, leaking buffer on upstream end\n"); + gst_buffer_unref(buf); + // now we have to clean up and exit right away + GST_UNLOCK (queue); + return; + } + // otherwise we have to push a buffer off the other end + else { + GSList *front; + GstBuffer *leakbuf; + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "queue is full, leaking buffer on downstream end\n"); + front = queue->queue; + leakbuf = (GstBuffer *)(front->data); + queue->level_buffers--; + queue->level_bytes -= GST_BUFFER_SIZE(leakbuf); + gst_buffer_unref(leakbuf); + queue->queue = g_slist_remove_link (queue->queue, front); + g_slist_free (front); + } + } - while (queue->level_buffers >= queue->max_buffers) { - GST_DEBUG (0,"queue: %s waiting %d\n", name, queue->level_buffers); - STATUS("%s: O\n"); - //g_cond_timed_wait (queue->fullcond, queue->fulllock, queue->timeval); - //FIXME need to signal other thread in case signals got lost? - g_cond_signal (queue->emptycond); - g_cond_wait (queue->fullcond, GST_OBJECT(queue)->lock); - STATUS("%s: O+\n"); - GST_DEBUG (0,"queue: %s waiting done %d\n", name, queue->level_buffers); + while (queue->level_buffers >= queue->size_buffers) { + // if there's a pending state change for this queue or its manager, switch + // back to iterator so bottom half of state change executes + if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING || +// GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING) +GST_STATE_PENDING(GST_SCHED_PARENT(GST_ELEMENT_SCHED(GST_PAD_PARENT(GST_PAD_PEER(queue->sinkpad))))) != +GST_STATE_NONE_PENDING) + { + GST_DEBUG(GST_CAT_DATAFLOW,"interrupted!!\n"); + if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING) + GST_DEBUG(GST_CAT_DATAFLOW,"GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING)\n"); + if (GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING) + GST_DEBUG(GST_CAT_DATAFLOW,"GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING\n"); + GST_UNLOCK(queue); + cothread_switch(cothread_current_main()); + } + + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "waiting for space, level is %d\n", queue->level_buffers); + g_cond_signal (queue->emptycond); + g_cond_wait (queue->fullcond, GST_OBJECT(queue)->lock); + GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, queue, "done waiting, level is now %d\n", queue->level_buffers); + } } /* put the buffer on the tail of the list */ queue->queue = g_slist_append (queue->queue, buf); -// STATUS("%s: +\n"); - GST_DEBUG (0,"(%s:%s)+ ",GST_DEBUG_PAD_NAME(pad)); - - /* if we were empty, but aren't any more, signal a condition */ - tosignal = (queue->level_buffers >= 0); queue->level_buffers++; + queue->level_bytes += GST_BUFFER_SIZE(buf); +// GST_DEBUG (GST_CAT_DATAFLOW, "(%s:%s)+\n",GST_DEBUG_PAD_NAME(pad)); - /* we can unlock now */ - GST_DEBUG (0,"queue: %s chain %d end signal(%d,%p)\n", name, queue->level_buffers, tosignal, queue->emptycond); - - if (tosignal) { -// STATUS("%s: >\n"); + /* if we were empty, but aren't any more, signal a condition */ + if (queue->level_buffers == 1) + { + GST_DEBUG (GST_CAT_DATAFLOW,"%s signalling emptycond\n", name); g_cond_signal (queue->emptycond); -// STATUS("%s: >>\n"); } + GST_UNLOCK (queue); } @@ -307,6 +367,8 @@ gst_queue_get (GstPad *pad) GSList *front; const guchar *name; + g_assert(pad != NULL); + g_assert(GST_IS_PAD(pad)); g_return_val_if_fail (pad != NULL, NULL); g_return_val_if_fail (GST_IS_PAD (pad), NULL); @@ -314,63 +376,74 @@ gst_queue_get (GstPad *pad) name = GST_ELEMENT_NAME (queue); /* have to lock for thread-safety */ - GST_DEBUG (0,"queue: %s try have queue lock\n", name); + GST_DEBUG (GST_CAT_DATAFLOW,"%s try have queue lock\n", name); GST_LOCK (queue); - GST_DEBUG (0,"queue: %s push %d %ld %p\n", name, queue->level_buffers, pthread_self (), queue->emptycond); - GST_DEBUG (0,"queue: %s have queue lock\n", name); - - // we bail if there's nothing there - if (!queue->level_buffers && !queue->block) { - GST_UNLOCK(queue); - return NULL; - } + GST_DEBUG (GST_CAT_DATAFLOW,"%s push %d %ld %p\n", name, queue->level_buffers, pthread_self (), queue->emptycond); + GST_DEBUG (GST_CAT_DATAFLOW,"%s have queue lock\n", name); while (!queue->level_buffers) { - STATUS("queue: %s U released lock\n"); - //g_cond_timed_wait (queue->emptycond, queue->emptylock, queue->timeval); if (GST_FLAG_IS_SET (queue->sinkpad, GST_PAD_EOS)) { + GST_DEBUG (GST_CAT_DATAFLOW, "%s U released lock\n", name); + GST_UNLOCK(queue); gst_pad_set_eos (queue->srcpad); + // this return NULL shouldn't hurt anything... return NULL; } - //FIXME need to signal other thread in case signals got lost? + + // if there's a pending state change for this queue or its manager, switch + // back to iterator so bottom half of state change executes + if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING || +// GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING) +GST_STATE_PENDING(GST_SCHED_PARENT(GST_ELEMENT_SCHED(GST_PAD_PARENT(GST_PAD_PEER(queue->srcpad))))) != +GST_STATE_NONE_PENDING) + { + GST_DEBUG(GST_CAT_DATAFLOW,"interrupted!!\n"); + if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING) + GST_DEBUG(GST_CAT_DATAFLOW,"GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING)\n"); + if (GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING) + GST_DEBUG(GST_CAT_DATAFLOW,"GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING\n"); + GST_UNLOCK(queue); + cothread_switch(cothread_current_main()); + } + g_cond_signal (queue->fullcond); g_cond_wait (queue->emptycond, GST_OBJECT(queue)->lock); -// STATUS("queue: %s U- getting lock\n"); } front = queue->queue; buf = (GstBuffer *)(front->data); - GST_DEBUG (0,"retrieved buffer %p from queue\n",buf); + GST_DEBUG (GST_CAT_DATAFLOW,"retrieved buffer %p from queue\n",buf); queue->queue = g_slist_remove_link (queue->queue, front); g_slist_free (front); - queue->level_buffers--; -// STATUS("%s: -\n"); - GST_DEBUG (0,"(%s:%s)- ",GST_DEBUG_PAD_NAME(pad)); - - if (queue->level_buffers < queue->max_buffers) { -// STATUS("%s: < \n"); +// if (queue->level_buffers < queue->size_buffers) + if (queue->level_buffers == queue->size_buffers) + { + GST_DEBUG (GST_CAT_DATAFLOW,"%s signalling fullcond\n", name); g_cond_signal (queue->fullcond); -// STATUS("%s: << \n"); } - GST_UNLOCK(queue); -// GST_DEBUG (0,"queue: %s pushing %d %p \n", name, queue->level_buffers, buf); -// gst_pad_push (queue->srcpad, buf); -// GST_DEBUG (0,"queue: %s pushing %d done \n", name, queue->level_buffers); + queue->level_buffers--; + queue->level_bytes -= GST_BUFFER_SIZE(buf); + GST_DEBUG (GST_CAT_DATAFLOW,"(%s:%s)- ",GST_DEBUG_PAD_NAME(pad)); + + GST_UNLOCK(queue); return buf; - /* unlock now */ } static GstElementStateReturn gst_queue_change_state (GstElement *element) { GstQueue *queue; + GstElementStateReturn ret; g_return_val_if_fail (GST_IS_QUEUE (element), GST_STATE_FAILURE); queue = GST_QUEUE (element); - GST_DEBUG (0,"gstqueue: state pending %d\n", GST_STATE_PENDING (element)); + + // lock the queue so another thread (not in sync with this thread's state) + // can't call this queue's _get (or whatever) + GST_LOCK (queue); /* if going down into NULL state, clear out buffers*/ if (GST_STATE_PENDING (element) == GST_STATE_READY) { @@ -380,9 +453,41 @@ gst_queue_change_state (GstElement *element) /* if we haven't failed already, give the parent class a chance to ;-) */ if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); + { + gboolean valid_handler = FALSE; + guint state_change_id = gtk_signal_lookup("state_change", GTK_OBJECT_TYPE(element)); + + // determine whether we need to block the parent (element) class' + // STATE_CHANGE signal so we can UNLOCK before returning. we block + // it if we could find the state_change signal AND there's a signal + // handler attached to it. + // + // note: this assumes that change_state() *only* emits state_change signal. + // if element change_state() emits other signals, they need to be blocked + // as well. + if (state_change_id && + gtk_signal_handler_pending(GTK_OBJECT(element), state_change_id, FALSE)) + valid_handler = TRUE; + if (valid_handler) + gtk_signal_handler_block(GTK_OBJECT(element), state_change_id); + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + if (valid_handler) + gtk_signal_handler_unblock(GTK_OBJECT(element), state_change_id); + + // UNLOCK, *then* emit signal (if there's one there) + GST_UNLOCK(queue); + if (valid_handler) + gtk_signal_emit(GTK_OBJECT (element), state_change_id, GST_STATE(element)); + } + else + { + ret = GST_STATE_SUCCESS; + GST_UNLOCK(queue); + } - return GST_STATE_SUCCESS; + return ret; } @@ -397,11 +502,11 @@ gst_queue_set_arg (GtkObject *object, GtkArg *arg, guint id) queue = GST_QUEUE (object); switch(id) { - case ARG_MAX_LEVEL: - queue->max_buffers = GTK_VALUE_INT (*arg); + case ARG_LEAKY: + queue->leaky = GTK_VALUE_INT (*arg); break; - case ARG_BLOCK: - queue->block = GTK_VALUE_BOOL (*arg); + case ARG_MAX_LEVEL: + queue->size_buffers = GTK_VALUE_INT (*arg); break; default: break; @@ -419,14 +524,14 @@ gst_queue_get_arg (GtkObject *object, GtkArg *arg, guint id) queue = GST_QUEUE (object); switch (id) { + case ARG_LEAKY: + GTK_VALUE_INT (*arg) = queue->leaky; + break; case ARG_LEVEL: GTK_VALUE_INT (*arg) = queue->level_buffers; break; case ARG_MAX_LEVEL: - GTK_VALUE_INT (*arg) = queue->max_buffers; - break; - case ARG_BLOCK: - GTK_VALUE_BOOL (*arg) = queue->block; + GTK_VALUE_INT (*arg) = queue->size_buffers; break; default: arg->type = GTK_TYPE_INVALID; diff --git a/plugins/elements/gstqueue.h b/plugins/elements/gstqueue.h index 606346735..085d5ac12 100644 --- a/plugins/elements/gstqueue.h +++ b/plugins/elements/gstqueue.h @@ -47,6 +47,12 @@ GstElementDetails gst_queue_details; #define GST_IS_QUEUE_CLASS(obj) \ (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_QUEUE)) +enum { + GST_QUEUE_NO_LEAK = 0, + GST_QUEUE_LEAK_UPSTREAM = 1, + GST_QUEUE_LEAK_DOWNSTREAM = 2 +}; + typedef struct _GstQueue GstQueue; typedef struct _GstQueueClass GstQueueClass; @@ -60,12 +66,16 @@ struct _GstQueue { GSList *queue; gint level_buffers; /* number of buffers queued here */ - gint max_buffers; /* maximum number of buffers queued here */ - gboolean block; /* if set to FALSE, _get returns NULL if queue empty */ gint level_bytes; /* number of bytes queued here */ + guint64 level_time; /* amount of time queued here */ + gint size_buffers; /* size of queue in buffers */ gint size_bytes; /* size of queue in bytes */ + guint64 size_time; /* size of queue in time */ + gint leaky; /* whether the queue is leaky, and if so at which end */ + +// GMutex *lock; (optimization?) GCond *emptycond; GCond *fullcond; @@ -74,6 +84,10 @@ struct _GstQueue { struct _GstQueueClass { GstElementClass parent_class; + + /* signal callbacks */ + void (*low_watermark) (GstQueue *queue, gint level); + void (*high_watermark) (GstQueue *queue, gint level); }; GtkType gst_queue_get_type (void); diff --git a/plugins/elements/gstsinesrc.c b/plugins/elements/gstsinesrc.c index 8cddd5d7b..6a00e1eef 100644 --- a/plugins/elements/gstsinesrc.c +++ b/plugins/elements/gstsinesrc.c @@ -407,4 +407,4 @@ gst_sinesrc_factory_init (GstElementFactory *factory) gst_elementfactory_add_padtemplate (factory, src_temp); return TRUE; -} \ No newline at end of file +} diff --git a/test/.gitignore b/test/.gitignore index 81021e48c..89626b4d5 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -29,6 +29,7 @@ mp3parse mpeg2parse mpeg2parse2 mpeg2parse3 +mpeg2parse4 mp3play ac3parse ac3play diff --git a/test/Makefile.am b/test/Makefile.am index ea89a6a5b..50f7a8fb2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -22,18 +22,6 @@ ac3play_SOURCES = ac3play.c mem.c noinst_HEADERS = mem.h -if HAVE_LIBXV -xvlibs=-lXv -else -xvlibs= -endif - -LDADD = ${xvlibs} -lXxf86vm $(GNOME_LIBS) $(GST_LIBS) - -#LDADD = $(GLIB_LIBS) $(GTK_LIBS) $(top_builddir)/gst/libgst.la \ -# $(top_builddir)/plugins/videosink/libvideosink.la -L/usr/X11/lib -lXxf86dga -#LDADD = $(GLIB_LIBS) $(GTK_LIBS) $(top_builddir)/gst/libgst.la - LIBS += $(GNOME_LIBS) $(GST_LIBS) CFLAGS += $(GNOME_CFLAGS) $(GST_CFLAGS) diff --git a/test/avi2mpg.c b/test/avi2mpg.c index 866162982..1d64a9ab9 100644 --- a/test/avi2mpg.c +++ b/test/avi2mpg.c @@ -141,7 +141,7 @@ int main(int argc,char *argv[]) { g_return_val_if_fail(src != NULL, -1); gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL); g_print("should be using file '%s'\n",argv[1]); - parse = gst_elementfactory_make("parseavi","parse"); + parse = gst_elementfactory_make("avidecoder","parse"); g_return_val_if_fail(parse != NULL, -1); mux = gst_elementfactory_make("system_encode","mux"); diff --git a/test/cobin.c b/test/cobin.c index 7f9630959..3f45ca45c 100644 --- a/test/cobin.c +++ b/test/cobin.c @@ -32,8 +32,6 @@ int main(int argc,char *argv[]) { gst_pad_connect(gst_element_get_pad(identity,"src"), gst_element_get_pad(sink,"sink")); - g_print("--- creating a plan\n"); - gst_bin_create_plan(GST_BIN(bin)); g_print("--- starting up\n"); gst_bin_iterate(GST_BIN(bin)); diff --git a/test/dvshow.c b/test/dvshow.c index 45fc498fc..b6b462aec 100644 --- a/test/dvshow.c +++ b/test/dvshow.c @@ -29,7 +29,7 @@ main (int argc,char *argv[]) gnome_init("Videotest","0.0.1",argc,argv); - bin = gst_bin_new("bin"); + bin = gst_pipeline_new("pipeline"); if (argc == 1) { src = gst_elementfactory_make ("dv1394src", "src"); diff --git a/test/fake.c b/test/fake.c index d24efa9de..2910d94b3 100644 --- a/test/fake.c +++ b/test/fake.c @@ -10,7 +10,7 @@ int main(int argc,char *argv[]) { // _gst_plugin_spew = TRUE; gst_init(&argc,&argv); - bin = gst_bin_new("bin"); + bin = gst_pipeline_new("pipeline"); g_return_if_fail(bin != NULL); g_print("--- creating src and sink elements\n"); @@ -35,8 +35,6 @@ int main(int argc,char *argv[]) { g_print("--- setting up\n"); gst_element_set_state(GST_ELEMENT(bin),GST_STATE_READY); - g_print("--- creating plan\n"); - gst_bin_create_plan(bin); g_print("--- iterating\n"); gst_bin_iterate(bin); } diff --git a/test/mpeg2parse2.c b/test/mpeg2parse2.c index 176eed35d..75a04c42f 100644 --- a/test/mpeg2parse2.c +++ b/test/mpeg2parse2.c @@ -86,6 +86,7 @@ int main(int argc,char *argv[]) { gtk_socket = gtk_socket_new (); gtk_widget_show (gtk_socket); + gtk_widget_set_usize(gtk_socket,320,240); gnome_app_set_contents(GNOME_APP(appwindow), GTK_WIDGET(gtk_socket)); diff --git a/test/mpeg2parse3.c b/test/mpeg2parse3.c index ee2e3d8b4..c74aacfe5 100644 --- a/test/mpeg2parse3.c +++ b/test/mpeg2parse3.c @@ -1,8 +1,10 @@ #include #include -GstElement *parse2, *v_queue, *a_queue; +GstPipeline *pipeline; +GstElement *v_queue, *a_queue, *v_thread, *a_thread; GtkWidget *appwindow; +GtkWidget *gtk_socket; void eof(GstElement *src) { g_print("have eos, quitting\n"); @@ -17,38 +19,37 @@ gboolean idle_func(gpointer data) { void mpeg2parse_newpad(GstElement *parser,GstPad *pad, GstElement *pipeline) { g_print("***** a new pad %s was created\n", gst_pad_get_name(pad)); - gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PAUSED); if (strncmp(gst_pad_get_name(pad), "video_", 6) == 0) { gst_pad_connect(pad, gst_element_get_pad(v_queue,"sink")); + gst_bin_add(GST_BIN(pipeline),v_thread); + gst_element_set_state(v_thread,GST_STATE_PLAYING); } else if (strcmp(gst_pad_get_name(pad), "private_stream_1.0") == 0) { gst_pad_connect(pad, gst_element_get_pad(a_queue,"sink")); + gst_bin_add(GST_BIN(pipeline),a_thread); + gst_element_set_state(a_thread,GST_STATE_PLAYING); } - gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING); +} + +void mpeg2parse_have_size(GstElement *videosink,gint width,gint height) { + gtk_widget_set_usize(gtk_socket,width,height); + gtk_widget_show_all(appwindow); } int main(int argc,char *argv[]) { - GstPipeline *pipeline; GstElement *src, *parse; - GstElement *v_thread, *v_decode, *show, *color; - GstElement *a_thread, *a_decode, *osssink; - GtkWidget *gtk_socket; + GstElement *v_decode, *show, *color; + GstElement *a_decode, *osssink; g_print("have %d args\n",argc); gst_init(&argc,&argv); gnome_init("MPEG2 Video player","0.0.1",argc,argv); - //gst_plugin_load("mpeg1parse"); + // ***** construct the main pipeline ***** pipeline = GST_PIPELINE(gst_pipeline_new("pipeline")); g_return_val_if_fail(pipeline != NULL, -1); - v_thread = GST_ELEMENT(gst_thread_new("v_thread")); - g_return_val_if_fail(v_thread != NULL, -1); - - a_thread = GST_ELEMENT(gst_thread_new("a_thread")); - g_return_val_if_fail(a_thread != NULL, -1); - if (strstr(argv[1],"video_ts")) { src = gst_elementfactory_make("dvdsrc","src"); g_print("using DVD source\n"); @@ -68,20 +69,19 @@ int main(int argc,char *argv[]) { //parse = gst_elementfactory_make("mpeg1parse","parse"); g_return_val_if_fail(parse != NULL, -1); + gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src)); + gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(parse)); + + gst_element_connect(src,"src",parse,"sink"); + + + // ***** pre-construct the video thread ***** + v_thread = GST_ELEMENT(gst_thread_new("v_thread")); + g_return_val_if_fail(v_thread != NULL, -1); + v_queue = gst_elementfactory_make("queue","v_queue"); g_return_val_if_fail(v_queue != NULL, -1); - - a_queue = gst_elementfactory_make("queue","a_queue"); - g_return_val_if_fail(a_queue != NULL, -1); - - /**** - * you can substitute mpeg2play with you own player here - * optionally you can remove the parse2 element. make - * sure to remove the pad connections too and don't add the - * mp2videoparse element to the bin. - **/ - //parse2 = gst_elementfactory_make("mp2videoparse","parse"); - //g_return_val_if_fail(parse2 != NULL, -1); + v_decode = gst_elementfactory_make("mpeg2dec","decode_video"); g_return_val_if_fail(v_decode != NULL, -1); @@ -89,16 +89,40 @@ int main(int argc,char *argv[]) { g_return_val_if_fail(color != NULL, -1); show = gst_elementfactory_make("xvideosink","show"); - //show = gst_elementfactory_make("aasink","show"); - //gtk_object_set(GTK_OBJECT(show),"xv_enabled",FALSE,NULL); g_return_val_if_fail(show != NULL, -1); + gst_bin_add(GST_BIN(v_thread),GST_ELEMENT(v_queue)); + gst_bin_add(GST_BIN(v_thread),GST_ELEMENT(v_decode)); + gst_bin_add(GST_BIN(v_thread),GST_ELEMENT(color)); + gst_bin_add(GST_BIN(v_thread),GST_ELEMENT(show)); + + gst_element_connect(v_queue,"src",v_decode,"sink"); + gst_element_connect(v_decode,"src",color,"sink"); + gst_element_connect(color,"src",show,"sink"); + + + // ***** pre-construct the audio thread ***** + a_thread = GST_ELEMENT(gst_thread_new("a_thread")); + g_return_val_if_fail(a_thread != NULL, -1); + + a_queue = gst_elementfactory_make("queue","a_queue"); + g_return_val_if_fail(a_queue != NULL, -1); + a_decode = gst_elementfactory_make("ac3dec","decode_audio"); g_return_val_if_fail(a_decode != NULL, -1); osssink = gst_elementfactory_make("osssink","osssink"); g_return_val_if_fail(osssink != NULL, -1); + gst_bin_add(GST_BIN(a_thread),GST_ELEMENT(a_queue)); + gst_bin_add(GST_BIN(a_thread),GST_ELEMENT(a_decode)); + gst_bin_add(GST_BIN(a_thread),GST_ELEMENT(osssink)); + + gst_element_connect(a_queue,"src",a_decode,"sink"); + gst_element_connect(a_decode,"src",osssink,"sink"); + + + // ***** construct the GUI ***** appwindow = gnome_app_new("MPEG player","MPEG player"); gtk_socket = gtk_socket_new (); @@ -111,45 +135,9 @@ int main(int argc,char *argv[]) { gtk_socket_steal (GTK_SOCKET (gtk_socket), gst_util_get_int_arg (GTK_OBJECT(show), "xid")); - gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src)); - gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(parse)); - gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(v_queue)); - - //gst_bin_add(GST_BIN(v_thread),GST_ELEMENT(parse2)); - gst_bin_add(GST_BIN(v_thread),GST_ELEMENT(v_decode)); - gst_bin_add(GST_BIN(v_thread),GST_ELEMENT(color)); - gst_bin_add(GST_BIN(v_thread),GST_ELEMENT(show)); - - gst_bin_add(GST_BIN(a_thread),GST_ELEMENT(a_decode)); - gst_bin_add(GST_BIN(a_thread),GST_ELEMENT(osssink)); - - gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(v_thread)); - gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(a_thread)); - gtk_signal_connect(GTK_OBJECT(parse),"new_pad",mpeg2parse_newpad, pipeline); - gtk_signal_connect(GTK_OBJECT(src),"eos",GTK_SIGNAL_FUNC(eof),NULL); - - gst_pad_connect(gst_element_get_pad(src,"src"), - gst_element_get_pad(parse,"sink")); - - // video - gst_pad_connect(gst_element_get_pad(v_queue,"src"), - // gst_element_get_pad(parse2,"sink")); - //gst_pad_connect(gst_element_get_pad(parse2,"src"), - gst_element_get_pad(v_decode,"sink")); - gst_pad_connect(gst_element_get_pad(v_decode,"src"), - gst_element_get_pad(color,"sink")); - gst_pad_connect(gst_element_get_pad(color,"src"), - gst_element_get_pad(show,"sink")); - - // audio - gst_pad_connect(gst_element_get_pad(a_queue,"src"), - gst_element_get_pad(a_decode,"sink")); - gst_pad_connect(gst_element_get_pad(a_decode,"src"), - gst_element_get_pad(osssink,"sink")); - - gtk_widget_show_all(appwindow); + gtk_signal_connect(GTK_OBJECT(show),"have_size",mpeg2parse_have_size, pipeline); g_print("setting to PLAYING state\n"); gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING); diff --git a/test/mpeg2parse4.c b/test/mpeg2parse4.c new file mode 100644 index 000000000..f918a11ac --- /dev/null +++ b/test/mpeg2parse4.c @@ -0,0 +1,224 @@ +#include +#include + +GstElement *pipeline, *src, *parse; +GstElement *v_decode_thread, *v_decode_queue, *v_decode, *v_color; +GstElement *v_show_thread, *v_show_queue, *v_show; +GstElement *a_decode_thread, *a_decode_queue, *a_decode; +GstElement *a_sink_thread, *a_sink_queue, *a_sink; +GtkWidget *appwindow; +GtkWidget *gtk_socket; + +void eof(GstElement *src) { + fprintf(stderr,"have eos, quitting\n"); + exit(0); +} + +gboolean idle_func(gpointer data) { + gst_bin_iterate(GST_BIN(data)); + return TRUE; +} + +int mpeg2parse_newpad(GstElement *parser,GstPad *pad, GstElement *pipeline) { + + fprintf(stderr,"***** a new pad %s was created\n", gst_pad_get_name(pad)); + + if (strncmp(gst_pad_get_name(pad), "video_", 6) == 0) { + + // build the decoder thread + v_decode_thread = GST_ELEMENT(gst_thread_new("v_decode_thread")); + g_return_val_if_fail(v_decode_thread != NULL, -1); + + v_decode_queue = gst_elementfactory_make("queue","v_decode_queue"); + g_return_val_if_fail(v_decode_queue != NULL, -1); + + v_decode = gst_elementfactory_make("mpeg2dec","v_decode"); + g_return_val_if_fail(v_decode != NULL, -1); + + v_color = gst_elementfactory_make("colorspace","v_color"); + g_return_val_if_fail(v_color != NULL, -1); + + gst_bin_add(GST_BIN(v_decode_thread),GST_ELEMENT(v_decode_queue)); + gst_bin_add(GST_BIN(v_decode_thread),GST_ELEMENT(v_decode)); + gst_bin_add(GST_BIN(v_decode_thread),GST_ELEMENT(v_color)); + + gst_element_connect(v_decode_queue,"src",v_decode,"sink"); + gst_element_connect(v_decode,"src",v_color,"sink"); + + gst_schedule_show(GST_ELEMENT_SCHED(v_decode_thread)); + + + // build the show thread + v_show_thread = GST_ELEMENT(gst_thread_new("v_show_thread")); + g_return_val_if_fail(v_show_thread != NULL, -1); + + v_show_queue = gst_elementfactory_make("queue","v_show_queue"); + g_return_val_if_fail(v_show_queue != NULL, -1); + + // v_show has ben created earlier + + gst_bin_add(GST_BIN(v_show_thread),GST_ELEMENT(v_show_queue)); + gst_bin_add(GST_BIN(v_show_thread),GST_ELEMENT(v_show)); + + gst_element_connect(v_show_queue,"src",v_show,"sink"); + + + // now assemble the decoder threads + gst_bin_add(GST_BIN(v_decode_thread),v_show_thread); + gst_element_connect(v_color,"src",v_show_queue,"sink"); + + gst_schedule_show(GST_ELEMENT_SCHED(v_decode_thread)); + gst_schedule_show(GST_ELEMENT_SCHED(v_show_thread)); + + // connect the whole thing to the main pipeline + gst_pad_connect(pad, gst_element_get_pad(v_decode_queue,"sink")); + gst_bin_add(GST_BIN(pipeline),v_decode_thread); + + gst_schedule_show(GST_ELEMENT_SCHED(v_decode_thread)); + gst_schedule_show(GST_ELEMENT_SCHED(v_show_thread)); + + // start it playing + gst_element_set_state(v_decode_thread,GST_STATE_PLAYING); + + } else if (strcmp(gst_pad_get_name(pad), "private_stream_1.0") == 0) { + // build the decoder thread + a_decode_thread = GST_ELEMENT(gst_thread_new("a_decode_thread")); + g_return_val_if_fail(a_decode_thread != NULL, -1); + + a_decode_queue = gst_elementfactory_make("queue","a_decode_queue"); + g_return_val_if_fail(a_decode_queue != NULL, -1); + + a_decode = gst_elementfactory_make("ac3dec","a_decode"); + g_return_val_if_fail(a_decode != NULL, -1); + + gst_bin_add(GST_BIN(a_decode_thread),GST_ELEMENT(a_decode_queue)); + gst_bin_add(GST_BIN(a_decode_thread),GST_ELEMENT(a_decode)); + + gst_element_connect(a_decode_queue,"src",a_decode,"sink"); + + gst_schedule_show(GST_ELEMENT_SCHED(a_decode_thread)); + + + // build the sink thread + a_sink_thread = GST_ELEMENT(gst_thread_new("a_sink_thread")); + g_return_val_if_fail(a_sink_thread != NULL, -1); + + a_sink_queue = gst_elementfactory_make("queue","a_sink_queue"); + g_return_val_if_fail(a_sink_queue != NULL, -1); + + a_sink = gst_elementfactory_make("esdsink","a_sink"); + g_return_val_if_fail(a_sink != NULL, -1); + + gst_bin_add(GST_BIN(a_sink_thread),GST_ELEMENT(a_sink_queue)); + gst_bin_add(GST_BIN(a_sink_thread),GST_ELEMENT(a_sink)); + + gst_element_connect(a_sink_queue,"src",a_sink,"sink"); + + + // now assemble the decoder threads + gst_bin_add(GST_BIN(a_decode_thread),a_sink_thread); + gst_element_connect(a_decode,"src",a_sink_queue,"sink"); + + gst_schedule_show(GST_ELEMENT_SCHED(a_decode_thread)); + gst_schedule_show(GST_ELEMENT_SCHED(a_sink_thread)); + + // connect the whole thing to the main pipeline + gst_pad_connect(pad, gst_element_get_pad(a_decode_queue,"sink")); + gst_bin_add(GST_BIN(pipeline),a_decode_thread); + + gst_schedule_show(GST_ELEMENT_SCHED(a_decode_thread)); + gst_schedule_show(GST_ELEMENT_SCHED(a_sink_thread)); + + // start it playing + gst_element_set_state(a_decode_thread,GST_STATE_PLAYING); + + } + + if (v_decode_thread && a_decode_thread) { + xmlSaveFile("mpeg2parse4.gst", gst_xml_write(GST_ELEMENT(pipeline))); +fprintf(stderr,"DUMP OF ALL SCHEDULES!!!:\n"); + gst_schedule_show(GST_ELEMENT_SCHED(pipeline)); + gst_schedule_show(GST_ELEMENT_SCHED(v_decode_thread)); + gst_schedule_show(GST_ELEMENT_SCHED(v_show_thread)); + gst_schedule_show(GST_ELEMENT_SCHED(a_decode_thread)); + gst_schedule_show(GST_ELEMENT_SCHED(a_sink_thread)); + } + + return 0; +} + +void mpeg2parse_have_size(GstElement *videosink,gint width,gint height) { + gtk_widget_set_usize(gtk_socket,width,height); + gtk_widget_show_all(appwindow); +} + +int main(int argc,char *argv[]) { + + g_print("have %d args\n",argc); + + gst_init(&argc,&argv); + gnome_init("MPEG2 Video player","0.0.1",argc,argv); + + // ***** construct the main pipeline ***** + pipeline = gst_pipeline_new("pipeline"); + g_return_val_if_fail(pipeline != NULL, -1); + + if (strstr(argv[1],"video_ts")) { + src = gst_elementfactory_make("dvdsrc","src"); + g_print("using DVD source\n"); + } else { + src = gst_elementfactory_make("disksrc","src"); + } + + g_return_val_if_fail(src != NULL, -1); + gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL); + if (argc >= 3) { + gtk_object_set(GTK_OBJECT(src),"bytesperread",atoi(argv[2]),NULL); + g_print("block size is %d\n",atoi(argv[2])); + } + g_print("should be using file '%s'\n",argv[1]); + + parse = gst_elementfactory_make("mpeg2parse","parse"); + //parse = gst_elementfactory_make("mpeg1parse","parse"); + g_return_val_if_fail(parse != NULL, -1); + + gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src)); + gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(parse)); + + gst_element_connect(src,"src",parse,"sink"); + + + // create v_show early so we can get and connect stuff + v_show = gst_elementfactory_make("xvideosink","v_show"); + g_return_val_if_fail(v_show != NULL, -1); + + + + // ***** construct the GUI ***** + appwindow = gnome_app_new("MPEG player","MPEG player"); + + gtk_socket = gtk_socket_new (); + gtk_widget_show (gtk_socket); + + gnome_app_set_contents(GNOME_APP(appwindow), + GTK_WIDGET(gtk_socket)); + + gtk_widget_realize (gtk_socket); + gtk_socket_steal (GTK_SOCKET (gtk_socket), + gst_util_get_int_arg (GTK_OBJECT(v_show), "xid")); + + gtk_signal_connect(GTK_OBJECT(parse),"new_pad",mpeg2parse_newpad, pipeline); + gtk_signal_connect(GTK_OBJECT(src),"eos",GTK_SIGNAL_FUNC(eof),NULL); + gtk_signal_connect(GTK_OBJECT(v_show),"have_size",mpeg2parse_have_size, pipeline); + + fprintf(stderr,"setting to PLAYING state\n"); + gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING); + + gtk_idle_add(idle_func,pipeline); + + gdk_threads_enter(); + gtk_main(); + gdk_threads_leave(); + + return 0; +} diff --git a/test/video2mp1.c b/test/video2mp1.c index 79b3a50c9..786f76535 100644 --- a/test/video2mp1.c +++ b/test/video2mp1.c @@ -54,7 +54,6 @@ gst_play_typefind (GstBin *bin, GstElement *element) gst_pad_disconnect (gst_element_get_pad (element, "src"), gst_element_get_pad (typefind, "sink")); gst_bin_remove (bin, typefind); - gst_object_unref (GST_OBJECT (typefind)); return caps; } @@ -174,6 +173,7 @@ int main(int argc,char *argv[]) exit (-1); } + gst_object_ref (GST_OBJECT (disksrc)); gst_bin_remove (GST_BIN (bin), disksrc); gst_object_destroy (GST_OBJECT (bin)); diff --git a/test/videotest2.c b/test/videotest2.c index c830cc8ff..0f69f44d1 100644 --- a/test/videotest2.c +++ b/test/videotest2.c @@ -27,7 +27,7 @@ main (int argc,char *argv[]) gnome_init("Videotest","0.0.1",argc,argv); - bin = gst_bin_new("bin"); + bin = gst_pipeline_new("pipeline"); src = gst_elementfactory_make ("v4lsrc", "src"); gtk_object_set(GTK_OBJECT(src),"format",9,NULL); diff --git a/tests/Makefile.am b/tests/Makefile.am index c30852d59..11ce4da68 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,8 +1,8 @@ -SUBDIRS = sched eos +SUBDIRS = sched eos noinst_PROGRAMS = init loadall simplefake states caps queue registry \ paranoia rip mp3encode autoplug props case4 markup load tee autoplug2 autoplug3 \ -capsconnect padfactory autoplug4 +capsconnect padfactory autoplug4 incsched reaping threadlock mp1vid # we have nothing but apps here, we can do this safely LIBS += $(GST_LIBS) diff --git a/tests/incsched.c b/tests/incsched.c new file mode 100644 index 000000000..f73b6171d --- /dev/null +++ b/tests/incsched.c @@ -0,0 +1,135 @@ +#include +#include + +int main(int argc,char *argv[]) { + GstBin *thread, *bin; + GstElement *src, *identity, *sink, *identity2; + + gst_init(&argc,&argv); + gst_info_set_categories(-1); + gst_debug_set_categories(-1); + + g_print("\n\nConstructing stuff:\n"); + thread = gst_pipeline_new("thread"); + bin = gst_bin_new("bin"); + src = gst_elementfactory_make("fakesrc","src"); + identity = gst_elementfactory_make("identity","identity"); + sink = gst_elementfactory_make("fakesink","sink"); + identity2 = gst_elementfactory_make("identity","identity2"); + + g_print("\nAdding src to thread:\n"); + gst_bin_add(thread,src); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nAdding identity to thread:\n"); + gst_bin_add(thread,identity); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nRemoving identity from thread:\n"); + gst_bin_remove(thread, identity); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nAdding identity to thread:\n"); + gst_bin_add(thread,identity); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nConnecting src to identity:\n"); + gst_element_connect(src,"src",identity,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nDisconnecting src from identity:\n"); + gst_element_disconnect(src,"src",identity,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nConnecting src to identity:\n"); + gst_element_connect(src,"src",identity,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nAdding sink to bin:\n"); + gst_bin_add(bin,sink); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nAdding bin to thread:\n"); + gst_bin_add(thread, GST_ELEMENT(bin)); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nConnecting identity to sink:\n"); + gst_element_connect(identity,"src",sink,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nDisconnecting sink:\n"); + gst_element_disconnect(identity,"src",sink,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nAdding identity2 to bin:\n"); + gst_bin_add(bin, identity2); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nConnecting identity2 to sink\n"); + gst_element_connect(identity2,"src",sink,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nConnecting identity to identity2\n"); + gst_element_connect(identity,"src",identity2,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\n\nNow setting state from NULL to READY:\n"); + gst_element_set_state(GST_ELEMENT(thread),GST_STATE_READY); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\n\nNow setting state from READY to PLAYING:\n"); + gst_element_set_state(GST_ELEMENT(thread),GST_STATE_PLAYING); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\n\nIterating:\n"); + gst_bin_iterate(thread); + + g_print("\n\nNow setting state from PLAYING to READY:\n"); + gst_element_set_state(GST_ELEMENT(thread),GST_STATE_READY); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\n\nNow setting state from READY to PLAYING:\n"); + gst_element_set_state(GST_ELEMENT(thread),GST_STATE_PLAYING); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\n\nIterating:\n"); + gst_bin_iterate(thread); + + g_print("\n\nNow setting state from PLAYING to READY:\n"); + gst_element_set_state(GST_ELEMENT(thread),GST_STATE_READY); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nDisconnecting identity from identity2:\n"); + gst_element_disconnect(identity,"src",identity2,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nDisconnecting identity2 from sink:\n"); + gst_element_disconnect(identity2,"src",sink,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\nConnecting identity to sink:\n"); + gst_element_connect(identity,"src",sink,"sink"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\n\nNow setting identity2 to NULL:\n"); + gst_element_set_state(identity2,GST_STATE_NULL); + + g_print("\nRemoving identity2 from bin:\n"); + gst_bin_remove(bin, identity2); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\n\nNow setting state from READY to PLAYING:\n"); + gst_element_set_state(GST_ELEMENT(thread),GST_STATE_PLAYING); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + g_print("\n\nIterating:\n"); + gst_bin_iterate(thread); + +//return; + g_print("\n\nSetting EOS on fakesrc and iterating again:\n"); + gtk_object_set(GTK_OBJECT(src),"eos",TRUE,NULL); + gst_bin_iterate(thread); + + g_print("\n\nIterating:\n"); + gst_bin_iterate(thread); +} diff --git a/tests/mp1vid.c b/tests/mp1vid.c new file mode 100644 index 000000000..2ad61b420 --- /dev/null +++ b/tests/mp1vid.c @@ -0,0 +1,78 @@ +#include + +GstElement *audiothread; +GstElement *audioqueue; +GstElement *audiodecode; +GstElement *audiosink; + +void new_pad(GstElement *parse,GstPad *pad,GstElement *pipeline) { + + if (!strncmp(gst_pad_get_name(pad), "audio_", 6)) { + fprintf(stderr,"have audio pad\n"); + + fprintf(stderr,"creating thread\n"); + audiothread = gst_elementfactory_make("thread","audiothread"); + gst_bin_add(GST_BIN(pipeline),audiothread); + + fprintf(stderr,"creating queue\n"); + audioqueue = gst_elementfactory_make("queue","audioqueue"); + gst_bin_add(GST_BIN(audiothread),audioqueue); + gst_pad_connect(pad,gst_element_get_pad(audioqueue,"sink")); + + fprintf(stderr,"creating decoder\n"); + audiodecode = gst_elementfactory_make("mad","audiodecode"); + gst_bin_add(GST_BIN(audiothread),audiodecode); + gst_element_connect(audioqueue,"src",audiodecode,"sink"); + + fprintf(stderr,"creating esdsink\n"); + audiosink = gst_elementfactory_make("osssink","audiosink"); + gst_bin_add(GST_BIN(audiothread),audiosink); + gst_element_connect(audiodecode,"src",audiosink,"sink"); + + fprintf(stderr,"setting state to PLAYING\n"); + gst_element_set_state(audiothread,GST_STATE_PLAYING); + + fprintf(stderr,"done dealing with new audio pad\n"); + } +} + +int main(int argc,char *argv[]) { + GstElement *pipeline, *sourcethread, *src, *parse; + int i; + + gst_init(&argc,&argv); + + pipeline = gst_pipeline_new("pipeline"); + sourcethread = gst_elementfactory_make("thread","sourcethread"); + src = gst_elementfactory_make("disksrc","src"); + gtk_object_set(GTK_OBJECT(src),"location","/home/omega/media/AlienSong.mpg",NULL); + parse = gst_elementfactory_make("mpeg1parse","parse"); + + gtk_signal_connect(GTK_OBJECT(parse),"new_pad", + GTK_SIGNAL_FUNC(new_pad),pipeline); + + gst_bin_add(GST_BIN(sourcethread),src); + gst_bin_add(GST_BIN(sourcethread),parse); + + gst_element_connect(src,"src",parse,"sink"); + + gst_bin_add(GST_BIN(pipeline),sourcethread); + + gst_schedule_show(GST_ELEMENT_SCHED(pipeline)); + + gst_element_set_state(pipeline,GST_STATE_PLAYING); + sleep(1); + + while (1) { +// sleep(1); +fprintf(stderr,"setting to PAUSED\n"); + gst_element_set_state(pipeline,GST_STATE_PAUSED);fprintf(stderr,"paused... "); +// sleep(1); +fprintf(stderr,"setting to PLAYING\n"); + gst_element_set_state(pipeline,GST_STATE_PLAYING);fprintf(stderr,"playing.\n"); + } + +// for (i=0;i<10;i++) +// while (1) +// gst_bin_iterate(GST_BIN(pipeline)); +} diff --git a/tests/old/examples/Makefile.am b/tests/old/examples/Makefile.am index c251eabb6..a97088356 100644 --- a/tests/old/examples/Makefile.am +++ b/tests/old/examples/Makefile.am @@ -8,4 +8,4 @@ endif SUBDIRS = $(GNOME_SUBDS) \ helloworld helloworld2 \ queue queue2 queue3 queue4 \ - launch thread xml plugins typefind + launch thread xml plugins typefind mixer diff --git a/tests/old/examples/autoplug/autoplug.c b/tests/old/examples/autoplug/autoplug.c index e9135fb8d..70ac4b7a2 100644 --- a/tests/old/examples/autoplug/autoplug.c +++ b/tests/old/examples/autoplug/autoplug.c @@ -2,94 +2,36 @@ #include static void -gst_play_have_type (GstElement *sink, GstElement *sink2, gpointer data) +autoplug_have_size (GstElement *element, guint width, guint height, + GtkWidget *socket) { - GST_DEBUG (0,"GstPipeline: play have type %p\n", (gboolean *)data); - - *(gboolean *)data = TRUE; -} - -gboolean -idle_func (gpointer data) -{ - return gst_bin_iterate (GST_BIN (data)); -} - -static GstCaps* -gst_play_typefind (GstBin *bin, GstElement *element) -{ - gboolean found = FALSE; - GstElement *typefind; - GstCaps *caps = NULL; - - GST_DEBUG (0,"GstPipeline: typefind for element \"%s\" %p\n", - GST_ELEMENT_NAME(element), &found); - - typefind = gst_elementfactory_make ("typefind", "typefind"); - g_return_val_if_fail (typefind != NULL, FALSE); - - gtk_signal_connect (GTK_OBJECT (typefind), "have_type", - GTK_SIGNAL_FUNC (gst_play_have_type), &found); - - gst_pad_connect (gst_element_get_pad (element, "src"), - gst_element_get_pad (typefind, "sink")); - - gst_bin_add (bin, typefind); - - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); - - // push a buffer... the have_type signal handler will set the found flag - gst_bin_iterate (bin); - - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL); - - caps = gst_pad_get_caps (gst_element_get_pad (element, "src")); - - gst_pad_disconnect (gst_element_get_pad (element, "src"), - gst_element_get_pad (typefind, "sink")); - gst_bin_remove (bin, typefind); - gst_object_unref (GST_OBJECT (typefind)); - - return caps; + gtk_widget_set_usize(socket,width,height); } -int main(int argc,char *argv[]) +static void +gst_play_have_type (GstElement *typefind, GstCaps *caps, GstElement *pipeline) { - GstElement *disksrc, *osssink, *videosink; - GstElement *bin; + GstElement *osssink, *videosink; GtkWidget *appwindow; - GstCaps *srccaps; GstElement *new_element; GstAutoplug *autoplug; GtkWidget *socket; + GstElement *autobin; + GstElement *disksrc; + GstElement *cache; - g_thread_init(NULL); - gst_init(&argc,&argv); - gnome_init("autoplug","0.0.1", argc,argv); - - if (argc != 2) { - g_print("usage: %s \n", argv[0]); - exit(-1); - } - - /* create a new bin to hold the elements */ - bin = gst_pipeline_new("pipeline"); - g_assert(bin != NULL); - - /* create a disk reader */ - disksrc = gst_elementfactory_make("disksrc", "disk_source"); - g_assert(disksrc != NULL); - gtk_object_set(GTK_OBJECT(disksrc),"location", argv[1],NULL); + GST_DEBUG (0,"GstPipeline: play have type\n"); - gst_bin_add (GST_BIN (bin), disksrc); + gst_element_set_state (pipeline, GST_STATE_PAUSED); - srccaps = gst_play_typefind (GST_BIN (bin), disksrc); + disksrc = gst_bin_get_by_name (GST_BIN (pipeline), "disk_source"); + autobin = gst_bin_get_by_name (GST_BIN (pipeline), "autobin"); + cache = gst_bin_get_by_name (GST_BIN (autobin), "cache"); - if (!srccaps) { - g_print ("could not autoplug, unknown media type...\n"); - exit (-1); - } - + // disconnect the typefind from the pipeline and remove it + gst_element_disconnect (cache, "src", typefind, "sink"); + gst_bin_remove (GST_BIN (autobin), typefind); + /* and an audio sink */ osssink = gst_elementfactory_make("osssink", "play_audio"); g_assert(osssink != NULL); @@ -102,7 +44,7 @@ int main(int argc,char *argv[]) g_assert (autoplug != NULL); new_element = gst_autoplug_to_renderers (autoplug, - srccaps, + caps, videosink, osssink, NULL); @@ -112,42 +54,122 @@ int main(int argc,char *argv[]) exit (-1); } - gst_bin_remove (GST_BIN (bin), disksrc); - // FIXME hack, reparent the disksrc so the scheduler doesn't break - bin = gst_pipeline_new("pipeline"); + gst_element_set_name (new_element, "new_element"); - gst_bin_add (GST_BIN (bin), disksrc); - gst_bin_add (GST_BIN (bin), new_element); + gst_bin_add (GST_BIN (autobin), new_element); - gst_element_connect (disksrc, "src", new_element, "sink"); + gtk_object_set (GTK_OBJECT (cache), "reset", TRUE, NULL); - appwindow = gnome_app_new("autoplug demo","autoplug demo"); + gst_element_connect (cache, "src", new_element, "sink"); + + appwindow = gnome_app_new ("autoplug demo","autoplug demo"); socket = gtk_socket_new (); gtk_widget_show (socket); - gnome_app_set_contents(GNOME_APP(appwindow), + gnome_app_set_contents (GNOME_APP (appwindow), GTK_WIDGET (socket)); gtk_widget_realize (socket); gtk_socket_steal (GTK_SOCKET (socket), gst_util_get_int_arg (GTK_OBJECT (videosink), "xid")); + gtk_widget_set_usize(socket,320,240); - gtk_widget_show_all(appwindow); + gtk_widget_show_all (appwindow); - xmlSaveFile("xmlTest.gst", gst_xml_write(GST_ELEMENT(bin))); + gtk_signal_connect (GTK_OBJECT (videosink), "have_size", + GTK_SIGNAL_FUNC (autoplug_have_size), socket); - /* start playing */ - gst_element_set_state(GST_ELEMENT(bin), GST_STATE_PLAYING); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + xmlSaveFile("xmlTest.gst", gst_xml_write (GST_ELEMENT (pipeline))); +} + +gboolean +idle_func (gpointer data) +{ + return gst_bin_iterate (GST_BIN (data)); +} + +static void +gst_play_cache_empty (GstElement *element, GstElement *pipeline) +{ + GstElement *autobin; + GstElement *disksrc; + GstElement *cache; + GstElement *new_element; + + fprintf (stderr, "have cache empty\n"); + + gst_element_set_state (pipeline, GST_STATE_PAUSED); + + disksrc = gst_bin_get_by_name (GST_BIN (pipeline), "disk_source"); + autobin = gst_bin_get_by_name (GST_BIN (pipeline), "autobin"); + cache = gst_bin_get_by_name (GST_BIN (autobin), "cache"); + new_element = gst_bin_get_by_name (GST_BIN (autobin), "new_element"); + + gst_element_disconnect (disksrc, "src", cache, "sink"); + gst_element_disconnect (cache, "src", new_element, "sink"); + gst_bin_remove (GST_BIN (autobin), cache); + gst_element_connect (disksrc, "src", new_element, "sink"); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + fprintf (stderr, "done with cache_empty\n"); +} + +int main(int argc,char *argv[]) +{ + GstElement *disksrc; + GstElement *pipeline; + GstElement *autobin; + GstElement *typefind; + GstElement *cache; + + g_thread_init(NULL); + gst_init(&argc,&argv); + gnome_init("autoplug","0.0.1", argc,argv); + + if (argc != 2) { + g_print("usage: %s \n", argv[0]); + exit(-1); + } + + /* create a new pipeline to hold the elements */ + pipeline = gst_pipeline_new("pipeline"); + g_assert(pipeline != NULL); - gtk_idle_add(idle_func, bin); + /* create a disk reader */ + disksrc = gst_elementfactory_make("disksrc", "disk_source"); + g_assert(disksrc != NULL); + gtk_object_set(GTK_OBJECT(disksrc),"location", argv[1],NULL); + gst_bin_add (GST_BIN (pipeline), disksrc); + + autobin = gst_bin_new ("autobin"); + cache = gst_elementfactory_make ("autoplugcache", "cache"); + gtk_signal_connect (GTK_OBJECT (cache), "cache_empty", GTK_SIGNAL_FUNC (gst_play_cache_empty), pipeline); + + typefind = gst_elementfactory_make ("typefind", "typefind"); + gtk_signal_connect (GTK_OBJECT (typefind), "have_type", GTK_SIGNAL_FUNC (gst_play_have_type), pipeline); + gst_bin_add (GST_BIN (autobin), cache); + gst_bin_add (GST_BIN (autobin), typefind); + + gst_element_connect (cache, "src", typefind, "sink"); + gst_element_add_ghost_pad (autobin, gst_element_get_pad (cache, "sink"), "sink"); + + gst_bin_add (GST_BIN( pipeline), autobin); + gst_element_connect (disksrc, "src", autobin, "sink"); + + /* start playing */ + gst_element_set_state( GST_ELEMENT (pipeline), GST_STATE_PLAYING); - gst_main(); + gtk_idle_add (idle_func, pipeline); + gst_main (); - /* stop the bin */ - gst_element_set_state(GST_ELEMENT(bin), GST_STATE_NULL); + /* stop the pipeline */ + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); - gst_pipeline_destroy(bin); + gst_object_unref (GST_OBJECT (pipeline)); exit(0); } diff --git a/tests/old/examples/mixer/mixer.c b/tests/old/examples/mixer/mixer.c index 205d0738a..4461d4796 100644 --- a/tests/old/examples/mixer/mixer.c +++ b/tests/old/examples/mixer/mixer.c @@ -4,10 +4,13 @@ * demonstrates the adder plugin and the volume envelope plugin * work in progress but do try it out * - * Latest change : 16/04/2001 - * multiple input channels allowed - * volume envelope adapted - * Version : 0.3 + * Latest change : 28/04/2001 + * trying to adapt to incsched + * delayed start for channels > 1 + * now works by quickhacking the + * adder plugin to set + * GST_ELEMENT_COTHREAD_STOPPING + * Version : 0.5 */ #include @@ -15,8 +18,10 @@ #include "mixer.h" #include +//#define WITH_BUG +//#define WITH_BUG2 //#define DEBUG - +//#define AUTOPLUG /* define if you want autoplugging of input channels */ /* function prototypes */ input_channel_t* create_input_channel (int id, char* location); @@ -35,55 +40,50 @@ void eos(GstElement *element) // playing = FALSE; } -static void -gst_play_have_type (GstElement *sink, GstElement *sink2, gpointer data) -{ - GST_DEBUG (0,"GstPipeline: play have type %p\n", (gboolean *)data); - - *(gboolean *)data = TRUE; -} - static GstCaps* gst_play_typefind (GstBin *bin, GstElement *element) { - gboolean found = FALSE; GstElement *typefind; + GstElement *pipeline; GstCaps *caps = NULL; - GST_DEBUG (0,"GstPipeline: typefind for element \"%s\" %p\n", - GST_ELEMENT_NAME(element), &found); + GST_DEBUG (0,"GstPipeline: typefind for element \"%s\"\n", + GST_ELEMENT_NAME(element)); + + pipeline = gst_pipeline_new ("autoplug_pipeline"); typefind = gst_elementfactory_make ("typefind", "typefind"); g_return_val_if_fail (typefind != NULL, FALSE); - gtk_signal_connect (GTK_OBJECT (typefind), "have_type", - GTK_SIGNAL_FUNC (gst_play_have_type), &found); - gst_pad_connect (gst_element_get_pad (element, "src"), gst_element_get_pad (typefind, "sink")); gst_bin_add (bin, typefind); + gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (bin)); - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); + gst_element_set_state (pipeline, GST_STATE_PLAYING); // push a buffer... the have_type signal handler will set the found flag - gst_bin_iterate (bin); + gst_bin_iterate (GST_BIN (pipeline)); - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL); + gst_element_set_state (pipeline, GST_STATE_NULL); caps = gst_pad_get_caps (gst_element_get_pad (element, "src")); gst_pad_disconnect (gst_element_get_pad (element, "src"), gst_element_get_pad (typefind, "sink")); gst_bin_remove (bin, typefind); + gst_bin_remove (GST_BIN (pipeline), GST_ELEMENT (bin)); gst_object_unref (GST_OBJECT (typefind)); + gst_object_unref (GST_OBJECT (pipeline)); return caps; } int main(int argc,char *argv[]) { - int i; + int i, j; int num_channels; + gboolean done; char buffer[20]; @@ -108,38 +108,41 @@ int main(int argc,char *argv[]) /* set up output channel and main bin */ /* create adder */ - adder = gst_elementfactory_make("adder", "adderel"); + adder = gst_elementfactory_make ("adder", "adderel"); /* create an audio sink */ - audiosink = gst_elementfactory_make("esdsink", "play_audio"); + audiosink = gst_elementfactory_make ("esdsink", "play_audio"); /* create main bin */ - main_bin = gst_bin_new("bin"); + main_bin = gst_pipeline_new("bin"); /* connect adder and output to bin */ - - gst_bin_add(GST_BIN(main_bin), adder); - gst_bin_add(GST_BIN(main_bin), audiosink); + GST_INFO (0, "main: adding adder to bin"); + gst_bin_add (GST_BIN(main_bin), adder); + GST_INFO (0, "main: adding audiosink to bin"); + gst_bin_add (GST_BIN(main_bin), audiosink); /* connect adder and audiosink */ gst_pad_connect(gst_element_get_pad(adder,"src"), gst_element_get_pad(audiosink,"sink")); - /* create input channels, add to bin and connect */ - + /* start looping */ input_channels = NULL; for (i = 1; i < argc; ++i) { printf ("Opening channel %d from file %s...\n", i, argv[i]); channel_in = create_input_channel (i, argv[i]); - input_channels = g_list_append (input_channels, channel_in); - gst_bin_add(GST_BIN(main_bin), channel_in->pipe); + input_channels = g_list_append (input_channels, channel_in); + + if (i > 1) gst_element_set_state (main_bin, GST_STATE_PAUSED); + gst_bin_add (GST_BIN(main_bin), channel_in->pipe); /* request pads and connect to adder */ + GST_INFO (0, "requesting pad\n"); pad = gst_element_request_pad_by_name (adder, "sink%d"); - g_print ("\tGot new adder sink pad %s\n", gst_pad_get_name (pad)); + printf ("\tGot new adder sink pad %s\n", gst_pad_get_name (pad)); sprintf (buffer, "channel%d", i); gst_pad_connect (gst_element_get_pad (channel_in->pipe, buffer), pad); @@ -178,24 +181,32 @@ int main(int argc,char *argv[]) env_register_cp (channel_in->volenv, num_channels * 10.0 - 5.0, 0.0000001); /* start fade in */ } env_register_cp (channel_in->volenv, num_channels * 10.0 , 1.0 / num_channels); /* to end level */ - } - - /* sleep a few seconds doesn't seem to help anyway */ - printf ("Sleeping a few seconds ...\n"); - sleep (2); - printf ("Waking up ...\n"); + xmlSaveFile("mixer.xml", gst_xml_write(GST_ELEMENT(main_bin))); - - /* start playing */ - gst_element_set_state(main_bin, GST_STATE_PLAYING); + /* start playing */ + gst_element_set_state(main_bin, GST_STATE_PLAYING); - playing = TRUE; + // write out the schedule + gst_schedule_show(GST_ELEMENT_SCHED(main_bin)); + playing = TRUE; - while (playing) { + j = 0; + //printf ("main: start iterating from 0"); + while (playing && j < 100) + { +// printf ("main: iterating %d\n", j); + gst_bin_iterate(GST_BIN(main_bin)); + //fprintf(stderr,"after iterate()\n"); + ++j; + } + } + printf ("main: all the channels are open\n"); + while (playing) + { gst_bin_iterate(GST_BIN(main_bin)); + //fprintf(stderr,"after iterate()\n"); } - /* stop the bin */ gst_element_set_state(main_bin, GST_STATE_NULL); @@ -228,11 +239,10 @@ create_input_channel (int id, char* location) GstAutoplug *autoplug; GstCaps *srccaps; GstElement *new_element; + GstElement *decoder; -#ifdef DEBUG - printf ("DEBUG : c_i_p : creating channel with id %d for file %s\n", + GST_DEBUG (0, "c_i_p : creating channel with id %d for file %s\n", id, location); -#endif /* allocate channel */ @@ -245,23 +255,21 @@ create_input_channel (int id, char* location) /* create channel */ -#ifdef DEBUG - printf ("DEBUG : c_i_p : creating pipeline\n"); -#endif + GST_DEBUG (0, "c_i_p : creating pipeline\n"); - channel->pipe = gst_bin_new ("pipeline"); + sprintf (buffer, "pipeline%d", id); + channel->pipe = gst_bin_new (buffer); g_assert(channel->pipe != NULL); /* create elements */ -#ifdef DEBUG - printf ("DEBUG : c_i_p : creating disksrc\n"); -#endif + GST_DEBUG(0, "c_i_p : creating disksrc\n"); sprintf (buffer, "disksrc%d", id); channel->disksrc = gst_elementfactory_make ("disksrc", buffer); g_assert(channel->disksrc != NULL); - + + GST_DEBUG(0, "c_i_p : setting location\n"); gtk_object_set(GTK_OBJECT(channel->disksrc),"location", location, NULL); /* add disksrc to the bin before autoplug */ @@ -286,8 +294,24 @@ create_input_channel (int id, char* location) printf ("DEBUG : c_i_p : getting srccaps\n"); #endif +#ifdef WITH_BUG srccaps = gst_play_typefind (GST_BIN (channel->pipe), channel->disksrc); +#endif +#ifdef WITH_BUG2 + { + GstElement *pipeline; + pipeline = gst_pipeline_new ("autoplug_pipeline"); + + gst_bin_add (GST_BIN (pipeline), channel->pipe); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_bin_remove (GST_BIN (pipeline), channel->pipe); + + } +#endif + +#ifdef AUTOPLUG if (!srccaps) { g_print ("could not autoplug, unknown media type...\n"); exit (-1); @@ -311,7 +335,24 @@ create_input_channel (int id, char* location) g_print ("could not autoplug, no suitable codecs found...\n"); exit (-1); } + +#else + + new_element = gst_bin_new ("autoplug_bin"); + + /* static plug, use mad plugin and assume mp3 input */ + decoder = gst_elementfactory_make ("mad", "mpg123"); + + gst_bin_add (GST_BIN (new_element), decoder); + + gst_element_add_ghost_pad (new_element, + gst_element_get_pad (decoder, "sink"), "sink"); + gst_element_add_ghost_pad (new_element, + gst_element_get_pad (decoder, "src"), "src_00"); +#endif + xmlSaveFile ("mixer.gst", gst_xml_write (new_element)); + gst_bin_add (GST_BIN(channel->pipe), channel->volenv); gst_bin_add (GST_BIN (channel->pipe), new_element); diff --git a/tests/old/testsuite/refcounting/Makefile.am b/tests/old/testsuite/refcounting/Makefile.am new file mode 100644 index 000000000..51df4da64 --- /dev/null +++ b/tests/old/testsuite/refcounting/Makefile.am @@ -0,0 +1,18 @@ +SUBDIRS = + +testprogs = object element pad element_pad bin thread + +object_SOURCES = object.c mem.c +element_SOURCES = element.c mem.c +pad_SOURCES = pad.c mem.c +element_pad_SOURCES = element_pad.c mem.c +bin_SOURCES = bin.c mem.c +thread_SOURCES = thread.c mem.c + +TESTS = $(testprogs) + +check_PROGRAMS = $(testprogs) + +# we have nothing but apps here, we can do this safely +LIBS += $(GST_LIBS) +CFLAGS += $(GST_CFLAGS) diff --git a/tests/old/testsuite/refcounting/bin.c b/tests/old/testsuite/refcounting/bin.c new file mode 100644 index 000000000..16c7eb15d --- /dev/null +++ b/tests/old/testsuite/refcounting/bin.c @@ -0,0 +1,272 @@ +#include + +#define ITERS 100000 +#include +#include "mem.h" + +static GstElement* +create_bin (void) +{ + GstElement *bin; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + gst_bin_add (GST_BIN (bin), element); + element = gst_element_new (); + gst_element_set_name (element, "test2"); + gst_bin_add (GST_BIN (bin), element); + + return bin; +} + +static GstElement* +create_bin_ghostpads (void) +{ + GstElement *bin; + GstElement *element1, *element2; + + bin = gst_bin_new ("testbin"); + element1 = gst_element_new (); + gst_element_set_name (element1, "test1"); + gst_element_add_pad (element1, gst_pad_new ("src1", GST_PAD_SRC)); + gst_bin_add (GST_BIN (bin), element1); + element2 = gst_element_new (); + gst_element_set_name (element2, "test2"); + gst_element_add_pad (element2, gst_pad_new ("sink1", GST_PAD_SINK)); + gst_bin_add (GST_BIN (bin), element2); + gst_element_connect (element1, "src1", element2, "sink1"); + gst_element_add_ghost_pad (bin, gst_element_get_pad (element2, "sink1"), "ghost_sink"); + + return bin; +} + +static void +add_remove_test1 (void) +{ + GstElement *bin; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + gst_bin_remove (GST_BIN (bin), element); + + gst_object_unref (GST_OBJECT (bin)); +} + +static void +add_remove_test2 (void) +{ + GstElement *bin; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + gst_object_ref (GST_OBJECT (element)); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + gst_bin_remove (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + g_assert (!GST_OBJECT_DESTROYED (element)); + + gst_object_destroy (GST_OBJECT (element)); + g_assert (GST_OBJECT_DESTROYED (element)); + gst_object_unref (GST_OBJECT (element)); + + gst_object_unref (GST_OBJECT (bin)); +} + +static void +add_remove_test3 (void) +{ + GstElement *bin; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + + gst_object_destroy (GST_OBJECT (element)); + g_assert (gst_bin_get_by_name (GST_BIN (bin), "test1") == NULL); + + gst_object_unref (GST_OBJECT (bin)); +} + +static void +add_remove_test4 (void) +{ + GstElement *bin, *bin2; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + + bin2 = create_bin (); + g_assert (GST_OBJECT_FLOATING (bin2)); + gst_bin_add (GST_BIN (bin), bin2); + g_assert (!GST_OBJECT_FLOATING (bin2)); + + gst_object_destroy (GST_OBJECT (bin2)); + g_assert (gst_bin_get_by_name (GST_BIN (bin), "testbin") == NULL); + gst_object_destroy (GST_OBJECT (element)); + g_assert (gst_bin_get_by_name (GST_BIN (bin), "test1") == NULL); + + gst_object_unref (GST_OBJECT (bin)); +} + +int +main (int argc, gchar *argv[]) +{ + GstElement *bin; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting test\n"); + usage1 = vmsize(); + + bin = gst_bin_new ("somebin"); + gst_object_unref (GST_OBJECT (bin)); + g_print ("create/unref new bin %ld\n", vmsize()-usage1); + + for (i=0; i + +#define ITERS 100000 +#include +#include "mem.h" + +int +main (int argc, gchar *argv[]) +{ + GstElement *element; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting test\n"); + usage1 = vmsize(); + + element = gst_element_new (); + gst_object_unref (GST_OBJECT (element)); + g_print ("create/unref new element %ld\n", vmsize()-usage1); + + for (i=0; i + +#define ITERS 100000 +#include +#include "mem.h" + +static GstElement* +create_element (gchar *padname, GstPadDirection dir) +{ + GstElement *element; + GstPad *pad; + + element = gst_element_new (); + pad = gst_pad_new (padname, dir); + gst_element_add_pad (element, pad); + + return element; +} + +int +main (int argc, gchar *argv[]) +{ + GstElement *element; + GstElement *element2; + GstPad *pad; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + + g_print ("starting element with pad test with %d iterations\n", iters); + usage1 = vmsize(); + + element = create_element ("sink", GST_PAD_SINK); + pad = gst_element_get_pad (element, "sink"); + g_assert (GST_OBJECT_FLOATING (element)); + g_assert (!GST_OBJECT_FLOATING (pad)); + g_assert (gst_pad_get_parent (pad) == element); + gst_object_unref (GST_OBJECT (element)); + g_print ("create/addpad/unref new element %ld\n", vmsize()-usage1); + + for (i=0; i +#include +#include +#include + +int vmsize() { + int pid,fd,size,i,mem; + char filename[17], buf[256], *ptr, *end; + + pid = getpid(); + snprintf(filename,17,"/proc/%d/stat",pid); + fd = open(filename,O_RDONLY); + size = read(fd,buf,240); + ptr = buf; + for (i=0;i<22;i++) + ptr = (char *)strchr(ptr,' ') + 1; + end = (char *)strchr(ptr,' '); + *end = 0; + sscanf(ptr,"%d",&mem); + close(fd); + return mem; +} diff --git a/tests/old/testsuite/refcounting/mem.h b/tests/old/testsuite/refcounting/mem.h new file mode 100644 index 000000000..28999db2c --- /dev/null +++ b/tests/old/testsuite/refcounting/mem.h @@ -0,0 +1 @@ +int vmsize(); diff --git a/tests/old/testsuite/refcounting/object.c b/tests/old/testsuite/refcounting/object.c new file mode 100644 index 000000000..477ec9b5d --- /dev/null +++ b/tests/old/testsuite/refcounting/object.c @@ -0,0 +1,156 @@ +#include + +#define ITERS 100000 +#include +#include "mem.h" + +int +main (int argc, gchar *argv[]) +{ + GstObject *object, *object2; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting test with %d iterations\n", iters); + usage1 = vmsize(); + object = gst_object_new (); + gst_object_unref (object); + g_print ("create/unref new object %ld\n", vmsize()-usage1); + + for (i=0; i + +#define ITERS 100000 +#include +#include "mem.h" + +int +main (int argc, gchar *argv[]) +{ + GstPad *pad; + GstPad *pad2; + GstPadTemplate *padtempl; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting pad test\n"); + usage1 = vmsize(); + + pad = gst_pad_new ("padname", GST_PAD_SINK); + gst_object_unref (GST_OBJECT (pad)); + g_print ("create/unref new pad %ld\n", vmsize()-usage1); + + for (i=0; i + +#define ITERS 100000 +#include +#include "mem.h" + +static GstElement* +create_thread (void) +{ + GstElement *thread; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + gst_bin_add (GST_BIN (thread), element); + element = gst_element_new (); + gst_element_set_name (element, "test2"); + gst_bin_add (GST_BIN (thread), element); + + return thread; +} + +static GstElement* +create_thread_ghostpads (void) +{ + GstElement *thread; + GstElement *element1, *element2; + + thread = gst_thread_new ("testthread"); + element1 = gst_element_new (); + gst_element_set_name (element1, "test1"); + gst_element_add_pad (element1, gst_pad_new ("src1", GST_PAD_SRC)); + gst_bin_add (GST_BIN (thread), element1); + element2 = gst_element_new (); + gst_element_set_name (element2, "test2"); + gst_element_add_pad (element2, gst_pad_new ("sink1", GST_PAD_SINK)); + gst_bin_add (GST_BIN (thread), element2); + gst_element_connect (element1, "src1", element2, "sink1"); + gst_element_add_ghost_pad (thread, gst_element_get_pad (element2, "sink1"), "sink1"); + + return thread; +} + +static void +add_remove_test1 (void) +{ + GstElement *thread; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + gst_bin_remove (GST_BIN (thread), element); + + gst_object_unref (GST_OBJECT (thread)); +} + +static void +add_remove_test2 (void) +{ + GstElement *thread; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + gst_object_ref (GST_OBJECT (element)); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + gst_bin_remove (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + g_assert (!GST_OBJECT_DESTROYED (element)); + + gst_object_destroy (GST_OBJECT (element)); + g_assert (GST_OBJECT_DESTROYED (element)); + gst_object_unref (GST_OBJECT (element)); + + gst_object_unref (GST_OBJECT (thread)); +} + +static void +add_remove_test3 (void) +{ + GstElement *thread; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + + gst_object_destroy (GST_OBJECT (element)); + g_assert (gst_bin_get_by_name (GST_BIN (thread), "test1") == NULL); + + gst_object_unref (GST_OBJECT (thread)); +} + +static void +add_remove_test4 (void) +{ + GstElement *thread, *thread2; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + + thread2 = create_thread (); + g_assert (GST_OBJECT_FLOATING (thread2)); + gst_bin_add (GST_BIN (thread), thread2); + g_assert (!GST_OBJECT_FLOATING (thread2)); + + gst_object_destroy (GST_OBJECT (thread2)); + g_assert (gst_bin_get_by_name (GST_BIN (thread), "testthread") == NULL); + gst_object_destroy (GST_OBJECT (element)); + g_assert (gst_bin_get_by_name (GST_BIN (thread), "test1") == NULL); + + gst_object_unref (GST_OBJECT (thread)); +} + +int +main (int argc, gchar *argv[]) +{ + GstElement *thread, *element; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting test\n"); + usage1 = vmsize(); + + thread = gst_thread_new ("somethread"); + gst_object_unref (GST_OBJECT (thread)); + g_print ("create/unref new thread %ld\n", vmsize()-usage1); + + for (i=0; i +#include + +int main(int argc,char *argv[]) { + GstBin *pipeline, *thread; + GstElement *src, *queue1, *sink; + + gst_init(&argc,&argv); + gst_info_set_categories(-1); + gst_debug_set_categories(-1); + + pipeline = gst_pipeline_new("pipeline"); + thread = gst_thread_new("thread"); + src = gst_elementfactory_make("fakesrc","src"); + queue1 = gst_elementfactory_make("queue","queue"); + sink = gst_elementfactory_make("fakesink","sink"); + + gst_bin_add(pipeline,src); + gst_bin_add(pipeline,queue1); + gst_bin_add(pipeline,GST_ELEMENT(thread)); + gst_bin_add(thread,sink); + + gst_element_add_ghost_pad(GST_ELEMENT(thread),gst_element_get_pad(sink,"sink"),"sink"); + + gst_element_connect (src,"src",queue1,"sink"); + gst_element_connect (queue1, "src", thread, "sink"); + + + fprintf(stderr,"\n\n\n"); + gst_element_set_state (pipeline, GST_STATE_READY); + + + fprintf(stderr,"\n\n\n"); + gst_element_set_state (pipeline, GST_STATE_NULL); +} diff --git a/tests/states.c b/tests/states.c index 3d5b6accf..26dbc7491 100644 --- a/tests/states.c +++ b/tests/states.c @@ -2,7 +2,7 @@ gboolean state_change(GstElement *element,GstElementState state) { g_print(">STATES: element '%s' state set to %d(%s)\n", - gst_element_get_name(element),state,_gst_print_statename(state)); + gst_element_get_name(element),state,gst_element_statename(state)); g_print(">STATES: element state is actually %d\n",GST_STATE(element)); return TRUE; @@ -37,15 +37,15 @@ int main(int argc,char *argv[]) { GTK_SIGNAL_FUNC(state_change),NULL); g_print("STATES: element '%s' starts at state %d(%s)\n",gst_element_get_name(src), - GST_STATE(src),_gst_print_statename(GST_STATE(src))); + GST_STATE(src),gst_element_statename(GST_STATE(src))); g_print("STATES: element '%s' starts at state %d(%s)\n",gst_element_get_name(subbin), - GST_STATE(subbin),_gst_print_statename(GST_STATE(subbin))); + GST_STATE(subbin),gst_element_statename(GST_STATE(subbin))); g_print("STATES: element '%s' starts at state %d(%s)\n",gst_element_get_name(filter), - GST_STATE(filter),_gst_print_statename(GST_STATE(filter))); + GST_STATE(filter),gst_element_statename(GST_STATE(filter))); g_print("STATES: element '%s' starts at state %d(%s)\n",gst_element_get_name(sink), - GST_STATE(sink),_gst_print_statename(GST_STATE(sink))); + GST_STATE(sink),gst_element_statename(GST_STATE(sink))); g_print("STATES: element '%s' starts at state %d(%s)\n",gst_element_get_name(bin), - GST_STATE(bin),_gst_print_statename(GST_STATE(bin))); + GST_STATE(bin),gst_element_statename(GST_STATE(bin))); gst_bin_add(GST_BIN(subbin),filter); gst_element_add_ghost_pad(GST_ELEMENT(bin),gst_element_get_pad(filter,"sink"),"sink"); diff --git a/tests/threadlock.c b/tests/threadlock.c new file mode 100644 index 000000000..83b62aa6b --- /dev/null +++ b/tests/threadlock.c @@ -0,0 +1,51 @@ +#include +#include + +int main(int argc,char *argv[]) { + GstBin *pipeline, *thread; + GstElement *src, *queue1, *sink; + + gst_init(&argc,&argv); + gst_info_set_categories(-1); + gst_debug_set_categories(-1); + + pipeline = gst_pipeline_new("pipeline"); + thread = gst_thread_new("thread"); + src = gst_elementfactory_make("fakesrc","src"); + sink = gst_elementfactory_make("fakesink","sink"); + + fprintf(stderr,"ADDING src\n"); + gst_bin_add(thread,src); + fprintf(stderr,"ADDING sink\n"); + gst_bin_add(thread,sink); + fprintf(stderr,"ADDING thread\n"); + gst_bin_add(pipeline,GST_ELEMENT(thread)); + +// gst_element_add_ghost_pad(GST_ELEMENT(thread),gst_element_get_pad(sink,"sink"),"sink"); + + fprintf(stderr,"CONNECTING src to sink\n"); + gst_element_connect (src, "src", sink, "sink"); + + fprintf(stderr,"\nSWITCHING to READY:\n"); + gst_element_set_state (thread, GST_STATE_READY); + fprintf(stderr,"\nPIPELINE sched:\n"); + gst_schedule_show(GST_ELEMENT_SCHED(pipeline)); + fprintf(stderr,"\nTHREAD sched:\n"); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + + fprintf(stderr,"\nSWITCHING to PLAYING:\n"); + gst_element_set_state (thread, GST_STATE_PLAYING); + gst_schedule_show(GST_ELEMENT_SCHED(pipeline)); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + +fprintf(stderr,"sleeping...\n"); +sleep(1); +fprintf(stderr,"done sleeping...\n"); + + fprintf(stderr,"\nSWITCHING to READY:\n"); + gst_element_set_state (thread, GST_STATE_READY); + gst_schedule_show(GST_ELEMENT_SCHED(pipeline)); + gst_schedule_show(GST_ELEMENT_SCHED(thread)); + +sleep(1); +} diff --git a/testsuite/refcounting/Makefile.am b/testsuite/refcounting/Makefile.am new file mode 100644 index 000000000..51df4da64 --- /dev/null +++ b/testsuite/refcounting/Makefile.am @@ -0,0 +1,18 @@ +SUBDIRS = + +testprogs = object element pad element_pad bin thread + +object_SOURCES = object.c mem.c +element_SOURCES = element.c mem.c +pad_SOURCES = pad.c mem.c +element_pad_SOURCES = element_pad.c mem.c +bin_SOURCES = bin.c mem.c +thread_SOURCES = thread.c mem.c + +TESTS = $(testprogs) + +check_PROGRAMS = $(testprogs) + +# we have nothing but apps here, we can do this safely +LIBS += $(GST_LIBS) +CFLAGS += $(GST_CFLAGS) diff --git a/testsuite/refcounting/bin.c b/testsuite/refcounting/bin.c new file mode 100644 index 000000000..16c7eb15d --- /dev/null +++ b/testsuite/refcounting/bin.c @@ -0,0 +1,272 @@ +#include + +#define ITERS 100000 +#include +#include "mem.h" + +static GstElement* +create_bin (void) +{ + GstElement *bin; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + gst_bin_add (GST_BIN (bin), element); + element = gst_element_new (); + gst_element_set_name (element, "test2"); + gst_bin_add (GST_BIN (bin), element); + + return bin; +} + +static GstElement* +create_bin_ghostpads (void) +{ + GstElement *bin; + GstElement *element1, *element2; + + bin = gst_bin_new ("testbin"); + element1 = gst_element_new (); + gst_element_set_name (element1, "test1"); + gst_element_add_pad (element1, gst_pad_new ("src1", GST_PAD_SRC)); + gst_bin_add (GST_BIN (bin), element1); + element2 = gst_element_new (); + gst_element_set_name (element2, "test2"); + gst_element_add_pad (element2, gst_pad_new ("sink1", GST_PAD_SINK)); + gst_bin_add (GST_BIN (bin), element2); + gst_element_connect (element1, "src1", element2, "sink1"); + gst_element_add_ghost_pad (bin, gst_element_get_pad (element2, "sink1"), "ghost_sink"); + + return bin; +} + +static void +add_remove_test1 (void) +{ + GstElement *bin; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + gst_bin_remove (GST_BIN (bin), element); + + gst_object_unref (GST_OBJECT (bin)); +} + +static void +add_remove_test2 (void) +{ + GstElement *bin; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + gst_object_ref (GST_OBJECT (element)); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + gst_bin_remove (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + g_assert (!GST_OBJECT_DESTROYED (element)); + + gst_object_destroy (GST_OBJECT (element)); + g_assert (GST_OBJECT_DESTROYED (element)); + gst_object_unref (GST_OBJECT (element)); + + gst_object_unref (GST_OBJECT (bin)); +} + +static void +add_remove_test3 (void) +{ + GstElement *bin; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + + gst_object_destroy (GST_OBJECT (element)); + g_assert (gst_bin_get_by_name (GST_BIN (bin), "test1") == NULL); + + gst_object_unref (GST_OBJECT (bin)); +} + +static void +add_remove_test4 (void) +{ + GstElement *bin, *bin2; + GstElement *element; + + bin = gst_bin_new ("testbin"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (bin), element); + g_assert (!GST_OBJECT_FLOATING (element)); + + bin2 = create_bin (); + g_assert (GST_OBJECT_FLOATING (bin2)); + gst_bin_add (GST_BIN (bin), bin2); + g_assert (!GST_OBJECT_FLOATING (bin2)); + + gst_object_destroy (GST_OBJECT (bin2)); + g_assert (gst_bin_get_by_name (GST_BIN (bin), "testbin") == NULL); + gst_object_destroy (GST_OBJECT (element)); + g_assert (gst_bin_get_by_name (GST_BIN (bin), "test1") == NULL); + + gst_object_unref (GST_OBJECT (bin)); +} + +int +main (int argc, gchar *argv[]) +{ + GstElement *bin; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting test\n"); + usage1 = vmsize(); + + bin = gst_bin_new ("somebin"); + gst_object_unref (GST_OBJECT (bin)); + g_print ("create/unref new bin %ld\n", vmsize()-usage1); + + for (i=0; i + +#define ITERS 100000 +#include +#include "mem.h" + +int +main (int argc, gchar *argv[]) +{ + GstElement *element; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting test\n"); + usage1 = vmsize(); + + element = gst_element_new (); + gst_object_unref (GST_OBJECT (element)); + g_print ("create/unref new element %ld\n", vmsize()-usage1); + + for (i=0; i + +#define ITERS 100000 +#include +#include "mem.h" + +static GstElement* +create_element (gchar *padname, GstPadDirection dir) +{ + GstElement *element; + GstPad *pad; + + element = gst_element_new (); + pad = gst_pad_new (padname, dir); + gst_element_add_pad (element, pad); + + return element; +} + +int +main (int argc, gchar *argv[]) +{ + GstElement *element; + GstElement *element2; + GstPad *pad; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + + g_print ("starting element with pad test with %d iterations\n", iters); + usage1 = vmsize(); + + element = create_element ("sink", GST_PAD_SINK); + pad = gst_element_get_pad (element, "sink"); + g_assert (GST_OBJECT_FLOATING (element)); + g_assert (!GST_OBJECT_FLOATING (pad)); + g_assert (gst_pad_get_parent (pad) == element); + gst_object_unref (GST_OBJECT (element)); + g_print ("create/addpad/unref new element %ld\n", vmsize()-usage1); + + for (i=0; i +#include +#include +#include + +int vmsize() { + int pid,fd,size,i,mem; + char filename[17], buf[256], *ptr, *end; + + pid = getpid(); + snprintf(filename,17,"/proc/%d/stat",pid); + fd = open(filename,O_RDONLY); + size = read(fd,buf,240); + ptr = buf; + for (i=0;i<22;i++) + ptr = (char *)strchr(ptr,' ') + 1; + end = (char *)strchr(ptr,' '); + *end = 0; + sscanf(ptr,"%d",&mem); + close(fd); + return mem; +} diff --git a/testsuite/refcounting/mem.h b/testsuite/refcounting/mem.h new file mode 100644 index 000000000..28999db2c --- /dev/null +++ b/testsuite/refcounting/mem.h @@ -0,0 +1 @@ +int vmsize(); diff --git a/testsuite/refcounting/object.c b/testsuite/refcounting/object.c new file mode 100644 index 000000000..477ec9b5d --- /dev/null +++ b/testsuite/refcounting/object.c @@ -0,0 +1,156 @@ +#include + +#define ITERS 100000 +#include +#include "mem.h" + +int +main (int argc, gchar *argv[]) +{ + GstObject *object, *object2; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting test with %d iterations\n", iters); + usage1 = vmsize(); + object = gst_object_new (); + gst_object_unref (object); + g_print ("create/unref new object %ld\n", vmsize()-usage1); + + for (i=0; i + +#define ITERS 100000 +#include +#include "mem.h" + +int +main (int argc, gchar *argv[]) +{ + GstPad *pad; + GstPad *pad2; + GstPadTemplate *padtempl; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting pad test\n"); + usage1 = vmsize(); + + pad = gst_pad_new ("padname", GST_PAD_SINK); + gst_object_unref (GST_OBJECT (pad)); + g_print ("create/unref new pad %ld\n", vmsize()-usage1); + + for (i=0; i + +#define ITERS 100000 +#include +#include "mem.h" + +static GstElement* +create_thread (void) +{ + GstElement *thread; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + gst_bin_add (GST_BIN (thread), element); + element = gst_element_new (); + gst_element_set_name (element, "test2"); + gst_bin_add (GST_BIN (thread), element); + + return thread; +} + +static GstElement* +create_thread_ghostpads (void) +{ + GstElement *thread; + GstElement *element1, *element2; + + thread = gst_thread_new ("testthread"); + element1 = gst_element_new (); + gst_element_set_name (element1, "test1"); + gst_element_add_pad (element1, gst_pad_new ("src1", GST_PAD_SRC)); + gst_bin_add (GST_BIN (thread), element1); + element2 = gst_element_new (); + gst_element_set_name (element2, "test2"); + gst_element_add_pad (element2, gst_pad_new ("sink1", GST_PAD_SINK)); + gst_bin_add (GST_BIN (thread), element2); + gst_element_connect (element1, "src1", element2, "sink1"); + gst_element_add_ghost_pad (thread, gst_element_get_pad (element2, "sink1"), "sink1"); + + return thread; +} + +static void +add_remove_test1 (void) +{ + GstElement *thread; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + gst_bin_remove (GST_BIN (thread), element); + + gst_object_unref (GST_OBJECT (thread)); +} + +static void +add_remove_test2 (void) +{ + GstElement *thread; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + gst_object_ref (GST_OBJECT (element)); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + gst_bin_remove (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + g_assert (!GST_OBJECT_DESTROYED (element)); + + gst_object_destroy (GST_OBJECT (element)); + g_assert (GST_OBJECT_DESTROYED (element)); + gst_object_unref (GST_OBJECT (element)); + + gst_object_unref (GST_OBJECT (thread)); +} + +static void +add_remove_test3 (void) +{ + GstElement *thread; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + + gst_object_destroy (GST_OBJECT (element)); + g_assert (gst_bin_get_by_name (GST_BIN (thread), "test1") == NULL); + + gst_object_unref (GST_OBJECT (thread)); +} + +static void +add_remove_test4 (void) +{ + GstElement *thread, *thread2; + GstElement *element; + + thread = gst_thread_new ("testthread"); + element = gst_element_new (); + gst_element_set_name (element, "test1"); + g_assert (GST_OBJECT_FLOATING (element)); + gst_bin_add (GST_BIN (thread), element); + g_assert (!GST_OBJECT_FLOATING (element)); + + thread2 = create_thread (); + g_assert (GST_OBJECT_FLOATING (thread2)); + gst_bin_add (GST_BIN (thread), thread2); + g_assert (!GST_OBJECT_FLOATING (thread2)); + + gst_object_destroy (GST_OBJECT (thread2)); + g_assert (gst_bin_get_by_name (GST_BIN (thread), "testthread") == NULL); + gst_object_destroy (GST_OBJECT (element)); + g_assert (gst_bin_get_by_name (GST_BIN (thread), "test1") == NULL); + + gst_object_unref (GST_OBJECT (thread)); +} + +int +main (int argc, gchar *argv[]) +{ + GstElement *thread, *element; + long usage1; + gint i, iters; + + gst_init (&argc, &argv); + + if (argc == 2) + iters = atoi (argv[1]); + else + iters = ITERS; + + g_print ("starting test\n"); + usage1 = vmsize(); + + thread = gst_thread_new ("somethread"); + gst_object_unref (GST_OBJECT (thread)); + g_print ("create/unref new thread %ld\n", vmsize()-usage1); + + for (i=0; i +#include +#include +#include + +#include "config.h" + +typedef struct { + gchar *name; + GSList *srcpads; + GSList *sinkpads; + GSList *srcpadtemplates; + GSList *sinkpadtemplates; + GSList *arguments; +} comp_element; + +enum { + ARG_INT, + ARG_FILENAME, + ARG_ENUM +}; + +typedef struct { + gchar *name; + int type; + GSList *enums; +} comp_argument; + +typedef struct { + gint value; + gchar *nick; +} enum_value; + + +void print_match_list (gchar *prefix, int len, GSList *wordlist) { + GSList *words = wordlist; + + while (words) { + if (!len || !strncmp((gchar *)(words->data), prefix, len)) + printf("%s\n",(gchar *)(words->data)); + words = g_slist_next (words); + } +} + +int match_element (comp_element *element, gchar *name) { + return strcmp(element->name,name); +} + +int main(int argc,char *argv[]) { + xmlDocPtr doc; + xmlNodePtr rootnode, elementnode, propnode, argnode; + GList *element_list = NULL; + comp_element *element; + GSList *element_names = NULL; + comp_argument *argument; + enum_value *option; + + gchar *prev_word = argv[3]; + gchar *partial_word = argv[2]; + int partial_len = strlen(partial_word); + GList *elements; + GSList *pads; + int num_pads; + GSList *args; + gchar *word; + GSList *words = NULL; + + /***** Loading the completion information from the registry *****/ + + doc = xmlParseFile (GST_CONFIG_DIR "/compreg.xml"); + rootnode = doc->xmlRootNode; + + elementnode = rootnode->xmlChildrenNode; + while (elementnode) { + if (!strcmp(elementnode->name, "element")) { + element = g_new0(comp_element,1); + propnode = elementnode->xmlChildrenNode; + while (propnode) { + + if (!strcmp(propnode->name, "name")) { + element->name = xmlNodeGetContent(propnode); +//fprintf(stderr,element->name); + } else if (!strcmp(propnode->name, "srcpad")) { + element->srcpads = g_slist_prepend(element->srcpads, xmlNodeGetContent(propnode)); +//fprintf(stderr,"."); + } else if (!strcmp(propnode->name, "sinkpad")) { + element->sinkpads = g_slist_prepend(element->sinkpads, xmlNodeGetContent(propnode)); + } else if (!strcmp(propnode->name, "srcpadtemplate")) { + element->srcpadtemplates = g_slist_prepend(element->srcpadtemplates, xmlNodeGetContent(propnode)); +//fprintf(stderr,"."); + } else if (!strcmp(propnode->name, "sinkpad")) { + element->sinkpadtemplates = g_slist_prepend(element->sinkpadtemplates, xmlNodeGetContent(propnode)); + } else if (!strcmp(propnode->name, "argument")) { + argument = g_new0(comp_argument,1); + argument->name = xmlNodeGetContent(propnode); + argument->type = ARG_INT; + + // walk through the values data + argnode = propnode->xmlChildrenNode; + while (argnode) { + if (!strcmp(argnode->name, "filename")) { + argument->type = ARG_FILENAME; + } else if (!strcmp(argnode->name,"option")) { + argument->type = ARG_ENUM; + option = g_new0(enum_value,1); + sscanf(xmlNodeGetContent(argnode),"%d",&option->value); + argument->enums = g_slist_prepend (argument->enums, option); + } + argnode = argnode->next; + } + + element->arguments = g_slist_prepend(element->arguments, argument); + } + + propnode = propnode->next; + } + element_list = g_list_prepend(element_list, element); + element_names = g_slist_prepend(element_names, element->name); + } + elementnode = elementnode->next; + } + + + + /***** Completion *****/ + + /* The bulk of the work is in deciding exactly which words are an option. */ + + // if we're right at the beginning, with -launch in the first word + if (strstr(prev_word,"-launch")) { + // print out only elements with no sink pad or padtemplate + elements = element_list; + while (elements) { + element = (comp_element *)(elements->data); + if (!element->sinkpads && !element->sinkpadtemplates) + words = g_slist_prepend (words, element->name); + elements = g_list_next(elements); + } + } + + // if the previous word is a connection + if (strchr(prev_word, '!')) { + // print out oly elements with a sink pad or template + elements = element_list; + while (elements) { + element = (comp_element *)(elements->data); + if (element->sinkpads || element->sinkpadtemplates) + words = g_slist_prepend (words, element->name); + elements = g_list_next (elements); + } + } + + // if the partial word is an argument, and it's an enum + if (strchr(prev_word,'=')) { + fprintf(stderr,"it's an arg, but dunno what element yet\n"); + } + + // if the previous word is an element, we need to list both pads and arguments + if ((elements = g_list_find_custom(element_list, prev_word, (GCompareFunc)match_element))) { + element = elements->data; + // zero the numpads list so we can count them + num_pads = 0; + + // pads + pads = element->srcpads; + while (pads) { + num_pads++; + words = g_slist_prepend (words, g_strdup_printf("%s!",(gchar *)(pads->data))); + pads = g_slist_next (pads); + } + + // padtemplates + pads = element->srcpadtemplates; + while (pads) { + num_pads++; + word = g_strdup_printf("%s!",(gchar *)(pads->data)); + if (!g_slist_find_custom(words,word,(GCompareFunc)strcmp)) + words = g_slist_prepend (words, word); + pads = g_slist_next (pads); + } + + // if there is only one pad, add '!' to the list of completions + if (num_pads == 1) { + words = g_slist_prepend (words, "!"); + } + + // arguments + args = element->arguments; + while (args) { + argument = (comp_argument *)(args->data); + word = strstr(argument->name,"::")+2; + words = g_slist_prepend (words, g_strdup_printf("%s=",word)); + words = g_slist_prepend (words, g_strdup_printf("%s=...",word)); + args = g_slist_next (args); + } + } + + + /* The easy part is ouptuting the correct list of possibilities. */ + print_match_list (partial_word, partial_len, words); + + return 0; +} diff --git a/tools/gstreamer-compprep.c b/tools/gstreamer-compprep.c new file mode 100644 index 000000000..c0c3e3afd --- /dev/null +++ b/tools/gstreamer-compprep.c @@ -0,0 +1,91 @@ +#include +#include "config.h" + +int main(int argc,char *argv[]) { + xmlDocPtr doc; + xmlNodePtr factorynode, padnode, argnode, optionnode; + GList *plugins, *factories, *padtemplates, *pads; + GstPlugin *plugin; + GstElementFactory *factory; + GstElement *element; + GstPad *pad; + GstPadTemplate *padtemplate; + GtkArg *args; + guint32 *flags; + gint num_args,i; + + gst_debug_set_categories(0); + gst_info_set_categories(0); + gst_init(&argc,&argv); + + doc = xmlNewDoc("1.0"); + doc->xmlRootNode = xmlNewDocNode(doc, NULL, "GST-CompletionRegistry", NULL); + + plugins = gst_plugin_get_list(); + while (plugins) { + plugin = (GstPlugin *)(plugins->data); + plugins = g_list_next (plugins); + + factories = gst_plugin_get_factory_list(plugin); + while (factories) { + factory = (GstElementFactory *)(factories->data); + factories = g_list_next (factories); + + factorynode = xmlNewChild (doc->xmlRootNode, NULL, "element", NULL); + xmlNewChild (factorynode, NULL, "name", factory->name); + + element = gst_elementfactory_create(factory,"element"); + if (element == NULL) { + fprintf(stderr,"couldn't construct element from factory %s\n",factory->name); + return 1; + } + + // write out the padtemplates + padtemplates = factory->padtemplates; + while (padtemplates) { + padtemplate = (GstPadTemplate *)(padtemplates->data); + padtemplates = g_list_next (padtemplates); + + if (padtemplate->direction == GST_PAD_SRC) + padnode = xmlNewChild (factorynode, NULL, "srcpadtemplate", padtemplate->name_template); + else if (padtemplate->direction == GST_PAD_SINK) + padnode = xmlNewChild (factorynode, NULL, "sinkpadtemplate", padtemplate->name_template); + } + + pads = gst_element_get_pad_list (element); + while (pads) { + pad = (GstPad *)(pads->data); + pads = g_list_next (pads); + + if (GST_PAD_DIRECTION(pad) == GST_PAD_SRC) + padnode = xmlNewChild (factorynode, NULL, "srcpad", GST_PAD_NAME(pad)); + else if (GST_PAD_DIRECTION(pad) == GST_PAD_SINK) + padnode = xmlNewChild (factorynode, NULL, "sinkpad", GST_PAD_NAME(pad)); + } + + // write out the args + args = gtk_object_query_args(GTK_OBJECT_TYPE(element), &flags, &num_args); + for (i=0;iloopfunc) - printf(" loopfunc()-based element\n"); + printf(" loopfunc()-based element: %s\n",GST_DEBUG_FUNCPTR_NAME(element->loopfunc)); else printf(" No loopfunc(), must be chain-based or not configured yet\n"); - if (gstelement_class->change_state) - printf(" Has change_state() function\n"); - else - printf(" No change_state() class function\n"); - if (gstobject_class->save_thyself) - printf(" Has custom save_thyself() class function\n"); - if (gstobject_class->restore_thyself) - printf(" Has custom restore_thyself() class function\n"); - printf("\n"); + printf(" Has change_state() function: %s\n", + GST_DEBUG_FUNCPTR_NAME(gstelement_class->change_state)); + printf(" Has custom save_thyself() function: %s\n", + GST_DEBUG_FUNCPTR_NAME(gstobject_class->save_thyself)); + printf(" Has custom restore_thyself() function: %s\n", + GST_DEBUG_FUNCPTR_NAME(gstobject_class->restore_thyself)); - printf("Pads:\n"); + + + printf("\nPads:\n"); if (element->numpads) { pads = gst_element_get_pad_list(element); while (pads) { pad = GST_PAD(pads->data); pads = g_list_next(pads); - realpad = GST_REAL_PAD(pad); + realpad = GST_PAD_REALIZE(pad); if (gst_pad_get_direction(pad) == GST_PAD_SRC) - printf(" SRC: '%s'\n",gst_pad_get_name(pad)); + printf(" SRC: '%s'",gst_pad_get_name(pad)); else if (gst_pad_get_direction(pad) == GST_PAD_SINK) - printf(" SINK: '%s'\n",gst_pad_get_name(pad)); + printf(" SINK: '%s'",gst_pad_get_name(pad)); else printf(" UNKNOWN!!!: '%s'\n",gst_pad_get_name(pad)); + if (GST_IS_GHOST_PAD(pad)) + printf(", ghost of real pad %s:%s\n",GST_DEBUG_PAD_NAME(realpad)); + else + printf("\n"); + printf(" Implementation:\n"); if (realpad->chainfunc) printf(" Has chainfunc(): %s\n",GST_DEBUG_FUNCPTR_NAME(realpad->chainfunc)); @@ -216,7 +223,7 @@ print_element_info (GstElementFactory *factory) printf(" Has getregionfunc(): %s\n",GST_DEBUG_FUNCPTR_NAME(realpad->getregionfunc)); if (realpad->qosfunc) printf(" Has qosfunc(): %s\n",GST_DEBUG_FUNCPTR_NAME(realpad->qosfunc)); - if (realpad->eosfunc) { + if (realpad->eosfunc != gst_pad_eos_func) { printf(" Has eosfunc(): %s\n",GST_DEBUG_FUNCPTR_NAME(realpad->eosfunc)); } @@ -243,13 +250,13 @@ print_element_info (GstElementFactory *factory) caps = caps->next; } } - - printf("\n"); } } else - printf(" none\n\n"); + printf(" none\n"); + + - printf("Element Arguments:\n"); + printf("\nElement Arguments:\n"); args = gtk_object_query_args(GTK_OBJECT_TYPE(element), &flags, &num_args); for (i=0;idata); + children = g_list_next (children); + + g_print(" %s\n",GST_ELEMENT_NAME(child)); + } + } + return 0; } -- cgit v1.2.3