initial checkin
authorErik Walthinsen <omega@temple-baptist.org>
Sun, 30 Jan 2000 10:44:33 +0000 (10:44 +0000)
committerErik Walthinsen <omega@temple-baptist.org>
Sun, 30 Jan 2000 10:44:33 +0000 (10:44 +0000)
Original commit message from CVS:
initial checkin

99 files changed:
docs/Makefile.am [new file with mode: 0644]
docs/random/arch [new file with mode: 0644]
docs/random/buffers [new file with mode: 0644]
docs/random/coroutines [new file with mode: 0644]
docs/random/design [new file with mode: 0644]
docs/random/example [new file with mode: 0644]
docs/random/factoryinfo [new file with mode: 0644]
docs/random/gboolean [new file with mode: 0644]
docs/random/intro [new file with mode: 0644]
docs/random/metadata [new file with mode: 0644]
docs/random/mutability [new file with mode: 0644]
docs/random/padarch [new file with mode: 0644]
docs/random/sequence [new file with mode: 0644]
docs/random/state-transitions [new file with mode: 0644]
docs/random/states [new file with mode: 0644]
docs/random/states.old [new file with mode: 0644]
docs/random/types [new file with mode: 0644]
docs/random/vis-transform [new file with mode: 0644]
docs/random/walkthrough [new file with mode: 0644]
docs/slides/README [new file with mode: 0644]
docs/slides/abstract [new file with mode: 0644]
docs/slides/abstract.save [new file with mode: 0644]
docs/slides/outline [new file with mode: 0644]
docs/slides/slides [new file with mode: 0644]
editor/Makefile.am [new file with mode: 0644]
editor/editor.c [new file with mode: 0644]
editor/editor.glade [new file with mode: 0644]
editor/editorelement.glade [new file with mode: 0644]
editor/gsteditor.c [new file with mode: 0644]
editor/gsteditor.h [new file with mode: 0644]
editor/gsteditorbin.c [new file with mode: 0644]
editor/gsteditorcanvas.c [new file with mode: 0644]
editor/gsteditorconnection.c [new file with mode: 0644]
editor/gsteditorconnection.h [new file with mode: 0644]
editor/gsteditorcreate.c [new file with mode: 0644]
editor/gsteditorcreate.h [new file with mode: 0644]
editor/gsteditorelement.c [new file with mode: 0644]
editor/gsteditorpad.c [new file with mode: 0644]
editor/gsteditorpad.h [new file with mode: 0644]
editor/gstelementselect.c [new file with mode: 0644]
editor/gstelementselect.h [new file with mode: 0644]
gst/xml/Makefile.am [new file with mode: 0644]
gst/xml/notes [new file with mode: 0644]
gst/xml/save.c [new file with mode: 0644]
test/Makefile.am [new file with mode: 0644]
test/a.c [new file with mode: 0644]
test/ac3parse.c [new file with mode: 0644]
test/ac3play.c [new file with mode: 0644]
test/ac3sync.c [new file with mode: 0644]
test/args.c [new file with mode: 0644]
test/basic.c [new file with mode: 0644]
test/bindings/Makefile.am [new file with mode: 0644]
test/bindings/dummy.c [new file with mode: 0644]
test/bindings/dummy.h [new file with mode: 0644]
test/bindings/test.c [new file with mode: 0644]
test/buffer.c [new file with mode: 0644]
test/cobin.c [new file with mode: 0644]
test/cothreads/Makefile.am [new file with mode: 0644]
test/cothreads/cothreads.c [new file with mode: 0644]
test/cothreads/cothreads.h [new file with mode: 0644]
test/cothreads/looper.c [new file with mode: 0644]
test/cothreads/looper.h [new file with mode: 0644]
test/cothreads/object.c [new file with mode: 0644]
test/cothreads/object.h [new file with mode: 0644]
test/cothreads/simple.c [new file with mode: 0644]
test/cothreads/test.c [new file with mode: 0644]
test/dvdcat.c [new file with mode: 0644]
test/fake.c [new file with mode: 0644]
test/gsttracedump.c [new file with mode: 0644]
test/jitter.c [new file with mode: 0644]
test/m.c [new file with mode: 0644]
test/mcut.c [new file with mode: 0644]
test/mem.c [new file with mode: 0644]
test/mem.h [new file with mode: 0644]
test/mp3.c [new file with mode: 0644]
test/mp3parse.c [new file with mode: 0644]
test/mp3play.c [new file with mode: 0644]
test/mpeg2parse.c [new file with mode: 0644]
test/mpg123.c [new file with mode: 0644]
test/p.c [new file with mode: 0644]
test/plugin.c [new file with mode: 0644]
test/push.c [new file with mode: 0644]
test/qtest.c [new file with mode: 0644]
test/r.c [new file with mode: 0644]
test/record.c [new file with mode: 0644]
test/s.c [new file with mode: 0644]
test/spectrum.c [new file with mode: 0644]
test/states.c [new file with mode: 0644]
test/teardown.c [new file with mode: 0644]
test/typefind.c [new file with mode: 0644]
test/types.c [new file with mode: 0644]
test/w.c [new file with mode: 0644]
test/wave.c [new file with mode: 0644]
test/xml/Makefile.am [new file with mode: 0644]
test/xml/createreg.c [new file with mode: 0644]
test/xml/readreg.c [new file with mode: 0644]
tools/Makefile.am [new file with mode: 0644]
tools/README [new file with mode: 0644]
tools/launch.c [new file with mode: 0644]

diff --git a/docs/Makefile.am b/docs/Makefile.am
new file mode 100644 (file)
index 0000000..e9a72e9
--- /dev/null
@@ -0,0 +1 @@
+#EXTRA_DIST = random slides
diff --git a/docs/random/arch b/docs/random/arch
new file mode 100644 (file)
index 0000000..ba7e88a
--- /dev/null
@@ -0,0 +1,78 @@
+GstElementFactory:
+
+Base class for all elementfactories.  Is a single-instance (per program)
+object that creates objects of the associated GstElement derivative.
+
+
+GstPlugin:
+
+Defines a given plugin.  Records plugin name, function pointers, details,
+etc.  Includes a list of GstElementFactories that are associated with
+this plugin.
+
+
+
+GstBuffer
+GstPad
+GstObject
+       GstSrc
+               GstDiskSrc
+               GstHTTPSrc
+               *
+       GstAsyncSrc
+               GstAsyncDiskSrc
+               *
+       GstFilter
+               GstVideoFilter
+                       GstVideoMPEG
+                       *
+               GstAudioFilter
+                       GstAudioMPEG
+                       *
+               *
+       GstSink
+               GstAudioSink
+                       GstAudioOSSSink
+                       GstAudioALSASink
+                       GstAudioESDSink
+                       *
+               GstVideoSink
+                       GstVideoGDKRGBSink
+                       GstVideoXvSink
+                       *
+               *
+       *
+       GstBin
+               GstPipeline
+               GstThread
+GstConnection
+       GstQueue
+       GstNetwork
+       GstSHM
+
+
+GstObject:
+
+Base class for all streamer objects (duh), defines some basic stuff like a
+pointer to the master pipeline for the object.
+
+
+GstBin:
+
+Contains a bunch of GstObjects.
+
+
+GstPipeline:
+
+A bin that can be used raw in an application.  The object that gets
+embedded into applications.  Can contain any set of GstObjects.  Nothing
+but a convenience object for the moment, will eventually be *the* object
+dealt with externally.
+
+
+GstThread:
+
+A bin that will become a thread if possible when the pipeline is started
+up.  Can contain any set of GstObjects except another GstThread.  All
+starting points and/or clocked events must be registered with this object,
+to be dealt with in the separate thread.
diff --git a/docs/random/buffers b/docs/random/buffers
new file mode 100644 (file)
index 0000000..dd657d9
--- /dev/null
@@ -0,0 +1,15 @@
+Buffer mutability properties are the most important part of gst, and
+similarly are the most complex.
+
+The simple case is that a buffer is created, memory allocated, data put
+in it, and passed to the next filter.  That filter reads the data, does
+something (like creating a new buffer and decoding into it), and
+unreferences the buffer.  This causes the data to be freed and the buffer
+to be destroyed.
+
+A more complex case is when the filter modifies the data in place.  It
+does so and simply passes on the buffer to the next element.  This is just
+as easy to deal with.
+
+If the second filter adds metadata to the buffer, it just has to add the
+pointer to the list.  The next element simply traverses the list and 
diff --git a/docs/random/coroutines b/docs/random/coroutines
new file mode 100644 (file)
index 0000000..d4e677b
--- /dev/null
@@ -0,0 +1,5 @@
+In a cothread-based container, all elements are run as cothreads.
+Chain-function based elements are wrapped by a generic element-class
+function that just calls the chain function repeatedly after pulling a
+buffer for it.  (in reality, chain functions are only usable in the
+single-input case)
diff --git a/docs/random/design b/docs/random/design
new file mode 100644 (file)
index 0000000..24b1d5f
--- /dev/null
@@ -0,0 +1,12 @@
+The fundamental component of GStreamer is the "element", an object that
+sources and/or sinks data.  Elements are connected to each other via
+"pads", which are extremely light-weight generic connections.  Elements
+can be contained inside "bins", which themselves are elements.
+
+A pipeline consists of any number of elements, connected to each other one
+after another.  A source would connect to one end of a decoder, which in
+turn would be connected (on the other end) to a sink, such as a sound
+card.  Other elements can be located anywhere in the pipeline, including
+tees and transcoders.
+
+
diff --git a/docs/random/example b/docs/random/example
new file mode 100644 (file)
index 0000000..0bb166b
--- /dev/null
@@ -0,0 +1,104 @@
+Here's a pipeline that does audio/video MPEG streams with a queue on
+either side of each decompressor, for a total of 5 threads (read/split,
+decode audio, decode video, play audio, play video):
+
+NOTES: mpegsplit is the intelligence in this pipeline, providing an IDL
+that allows one to connect things to a GUI.  
+
+Pipeline(mpegplay)
+       Thread(reader)
+               Element(:disk_async_src) [StreamerAsyncSrc]
+                       OutPad(disk1)
+               Element(:mpegsplit)
+                       InPad(disk1)
+                       OutPad(audio1)
+                       OutPad(video1)
+
+       Queue(audioQ1)
+               InPad(audio1)
+               OutPad(audio2)
+       Thread(audiodecode)
+               Element(:mpeg_audio_decode) [StreamerVideoFilter]
+                       InPad(audio2)
+                       OutPad(audio3)
+       Queue(audioQ2)
+               InPad(audio3)
+               OutPad(audio4)
+       Thread(audioplay)
+               Element(:audio_play) [StreamerAudioSink]
+                       InPad(audio4)
+
+       Queue(videoQ1)
+               InPad(video1)
+               OutPad(video2)
+       Thread(videodecode)
+               Element(:mpeg_video_decode) [StreamerVideoFilter]
+                       InPad(video2)
+                       OutPad(video3)
+       Queue(videoQ2)
+               InPad(video3)
+               OutPad(video4)
+       Thread(videoplay)
+               Element(:video_play) [StreamerVideoSink]
+                       InPad(video4)
+
+
+A simpler pipeline that just does MPEG videos:
+
+Pipeline(mpegplay)
+       Thread(reader)
+               Element(:disk_async_src) [GstAsyncSrc]
+                       OutPad(disk1)
+               Element(:mpeg_control)
+                       InPad(disk1)
+                       OutPad(video1)
+               Element(:mpeg_video_decode) [GstVideoFilter]
+                       InPad(video1)
+                       InPad(video2)
+       Queue(queue)
+               InPad(video2)
+               OutPad(video3)
+       Thread(play)
+               Element(:video_play) [GstVideoSink]
+                       InPad(video3)
+
+The code for the above looks roughly like:
+
+/* all the objects we're worried about */
+GstPipeline *mpegplay;
+GstThread *reader;
+GstSrc *disk_src;
+GstControl *mpeg_control;
+GstFilter *mpeg_video_decode;
+GstQueue *queue;
+GstThread *play;
+GstSink *video_play;
+
+/*** first we create all of the objects ***/
+
+mpegplay = gst_pipeline_new();
+reader = gst_thread_new();
+disk_src = gst_async_disk_src_new("filename.mpg");
+mpeg_control = gst_mpeg_control_new();
+mpeg_video_decode = gst_mpeg_video_decode_new();
+queue = gst_queue_new();
+play = gst_thread_new();
+video_play = gst_video_sink_new();
+
+
+/*** now we start to create the pipeline ***/
+
+/* first set up the reader thread */
+gst_bin_add(reader,disk_src);
+gst_object_connect(disk_src,"out",mpeg_control,"in");
+gst_object_connect(mpeg_control,"out",mpeg_audio_decode,"in");
+gst_bin_ghost_pad(reader,mpeg_audio_decode,"out");
+
+/* then set up the player thread */
+gst_bin_add(play,audio_play);
+gst_bin_ghost_pad(play,audio_play,"in");
+
+/* now plug them all into the main pipeline */
+gst_bin_add(mp3play,reader);
+gst_object_connect(reader,"out",queue,"in");
+gst_object_connect(queue,"out",play,"in");
diff --git a/docs/random/factoryinfo b/docs/random/factoryinfo
new file mode 100644 (file)
index 0000000..9a58888
--- /dev/null
@@ -0,0 +1,7 @@
+short name
+class
+long name
+descripion
+author
+version
+copyright
diff --git a/docs/random/gboolean b/docs/random/gboolean
new file mode 100644 (file)
index 0000000..866cb6f
--- /dev/null
@@ -0,0 +1,4 @@
+NOTE TO SELF:
+
+there are a lot of routines here that should be returning gboolean's for
+status, rather than just plain failing.
diff --git a/docs/random/intro b/docs/random/intro
new file mode 100644 (file)
index 0000000..d305d53
--- /dev/null
@@ -0,0 +1,17 @@
+GNOME Streamer is a pipeline-based media streaming framework.  It is built
+on top of the Gtk+ object model, and while it currently sits on top of
+gtk, it can be divorced from it at any point in the future.
+
+A pipeline consists of at one or more each of sources, sinks, and filters.
+These elements may be combined inside container elements, which may have
+their own specific properties, and act as any other element.  Each element 
+has one or more pads, which are connection points.  These pads are
+connected to chain from one element to the next, providing a path along
+which buffers are passed.
+
+Operation of the pipeline is fully automatic once a buffer is put in the
+front of the pipeline.  As each element finishes its processing, it pushes
+the buffer to the next element through an indirect function call, by way
+of the two connected pads.
+
+
diff --git a/docs/random/metadata b/docs/random/metadata
new file mode 100644 (file)
index 0000000..afe0a75
--- /dev/null
@@ -0,0 +1,77 @@
+The point of the metadata is to provide some context for each buffer.  In
+the case of audio data, for instance, it would provide the samplerate, bit
+depth, and channel count.
+
+The trick is that there may be multiple types of metadata ganged onto a
+single buffer.  This is why they're going to be a GList.  This does mean
+extra overhead in all cases, but I think it's minimal.  The GList type
+uses a chunk allocater so we're not wasting too much memory or time when
+adding to the list.
+
+The trick is dealing with these structs as they pass through a pipeline,
+since they have potentially different mutability properties.  For
+instance, if you've got a mp3 decoder connected to a tee, which sends the
+buffers off to both the decoder and a spectrum analyzer (and then a
+visualization element).  The preferred setup would be where every time a
+audio/raw metadata comes down the pipe (indicating a potential change in
+audio format), the audiosink and spectrum would just save off pointers.
+
+So when exactly does this metadata go away (deallocated)?  Well, that
+means metadata has to be refcounted.  But that gets rather hairy.  OK, in
+the simple case you create a metadata struct, it comes with refcount set
+to 1.  You pass it through, it stays one, eventually someone drops the
+last reference on the buffer it's tied to, you free the metadata too.
+Easy.  What if you tee?  You could go through and for every metadata in
+the buffer, increment the refcount by the same as the buffer.  So in the
+above case (tee'd), the audiosink and spectrum would get the buffer with a
+refcount of 2, and it'd have a metadata with refcount 2.  Do they ref it
+each themselves, then unref the buffer?  Or do they remove the metadata?
+Removing the metadata would require a buffer CoW, which would suck, so
+yes, they'd just ref the metadata.
+
+But....  what if they're all in different threads?  Then we're off into
+the magical world of mutexes.  Everything with a refcount in a threaded
+world must be mutexed, else you can do atomic increment and atomic
+dec&test.  Can this be done from C easily?  Perhaps it needs to be found
+from kernel includes via autoconf?
+
+
+
+
+The goal in designing the way metadata will be defined and used is to keep
+it as simple as possible.  The basis for accomplishing this is the fact
+that in order to actually use (rather than just pass) the metadata, you
+have to know what the fields are, which means you have to have compiled in
+support for that metadata at build time.  Therefore, if you're using
+metadata, you must have build-time access to the necessary include file
+that defines it.
+
+So, given that you've got an include file, it would be nice if the whole
+thing could be contained there.  This would limit the need to be linked
+against something, or have load-time requirements as to that has to be
+loaded before you are.
+
+Given that really all metadata is is a region of memory of a given size
+with a certain signature, this isn't all that hard.  First you lay out the
+struct that defines the metadata.  Then you set up #defines that expand to
+the size of the struct in question, as well as the four-cc code that
+defines the type.
+
+The work is done by a few #defines, a la the #defines used in all Gtk
+objects.  The first is a NEW() method that allocates the memory for the
+metadata and fills in all the normal fields (type, size, utility
+functions).  Because of the way it's defined (as a #define, no less),
+you'll have to invoke it as META_NEW(meta), since it can't return()
+anything.
+
+Another #define will check to make sure a meta is indeed that type by
+verifying the type code and size.  Theoretically, meta types can overlap
+with the same fourcc code, as long as they have different sizes.  But I
+probably ought to have a global public registry so people writing things
+don't conflict.  MSFT got that right, at least.
+
+So, a hairy problem is what to do when there are utility functions
+associated with one of these things.  One option is to not bother with
+them.  This is very likely a possible solution, since metadata is supposed
+to be flat memory of a given size.  Not much to do to either free or copy
+it, is there?
diff --git a/docs/random/mutability b/docs/random/mutability
new file mode 100644 (file)
index 0000000..74a0944
--- /dev/null
@@ -0,0 +1,30 @@
+Mutability is the property of an object that defines whether or not you
+are allowed to modify it.  In the context of GST, that means that if you
+want to mutilate a buffer, say to do an audio effect, you may have to do
+this on a copy of the buffer, if someone else has a reference on it.
+
+The simplest sequence of events in a decoder pipeline is as follows:
+
+1) create buffer
+2) allocate and fill data region, attach to buffer
+3) pass to next element
+4) decode the data into new buffer, free original buffer
+5) pass to next element
+6) buffer gets copied to output device (sound, video, whatever)
+
+Both of these buffers are created from malloc()'d memory, are referenced
+by one and only one element at a time, and are never modified in place.
+They have no special flags, and when ref==0, they're simply free()'d.
+
+An optimization in the case of the sound card or video double buffering,
+where the output buffer actually comes from the output device.  In that
+case the element will be aware of such things.
+
+A more complex example is where the data is teed after being decoded, sent
+to an effects or visualization object.
+
+1) create buffer, fill from source
+2) hand to decoder
+3) create new buffer, decode into it, free old buffer
+4) hand to tee
+5) ref++, hand off to 
diff --git a/docs/random/padarch b/docs/random/padarch
new file mode 100644 (file)
index 0000000..3ceed6d
--- /dev/null
@@ -0,0 +1,14 @@
+--src----------------------------      --sink----------------------------
+                               |       |
+               --srcpad--------|       |--sinkpad-------
+               | pad_push()    .       .               |
+push()         |  ->chain      .       .  ->chain      | ->    chain()
+               |               .       .               |
+               ----------------|       |----------------
+                               |       |
+
+chain() is the function provided by sink element
+sinkpad->chain is a pointer to that function
+srcpad->chain should be a copy of that pointer
+pad_push() calls the function pointer srcpad->chain
+push() is the function provided by the src element
diff --git a/docs/random/sequence b/docs/random/sequence
new file mode 100644 (file)
index 0000000..51810d1
--- /dev/null
@@ -0,0 +1,15 @@
+Here's a possible (huge, large, complete?) sequence of execution for an
+invocation of [GIST] playing a media stream.  I'll start with a mp3 audio
+stream, but eventually this should be a mpeg a/v stream off the net with
+rolling capture (think ReplayTV/Tivo), pausing, rewinding, etc.  This
+could easily be hundreds of lines by the time I'm done...
+
+This may look a lot like C in places, simply because that's the most
+efficient way of representing a given action.
+
+
+gst_init();
+pipeline = gst_pipeline_new();
+       state = 
+src = gst_disksrc_new("src","song.mp3");
+
diff --git a/docs/random/state-transitions b/docs/random/state-transitions
new file mode 100644 (file)
index 0000000..4752afe
--- /dev/null
@@ -0,0 +1,45 @@
+So, the method of having a _start() and _stop() function for each element
+just doesn't scale.  In the case of pipeline/thread model with a PLAYING
+state bit, I have no way of passing a state change all the way down the
+graph, i.e. a thread sitting inside a supplied bin.
+
+Proposal is to have a single state-change class function, which gets
+passed a single argument (no more 'state' going along with RUNNING).  This
+function can be overridden by each element as necessary, but must chain to
+the parent_class version of it.  It does its work, just like [st]et_arg,
+in a case.  It checks for STATE and ~STATE, and does the appropriate
+steps. All the _start() and _stop() functions get moved into this one, in
+the GST_STATE_RUNNING and ~GST_STATE_RUNNING cases.
+
+This allows bins to and derivations thereof to simply pass through any
+state they don't understand.  Meta-elements will also pass through.
+
+There may need to be some mechanism that provides for setting state on
+only a certain type.  This can be provided as an alternate path supplied
+by bins.  The bin is the one that would do the work in any case.  Simply
+provide class function for bins that does the selective stuff, and a main
+_bin_ function that calls this class function.  The supplied class
+function for each bin would simply check against the given GtkType before
+setting its state.  Derivations of GstBin would always get it.
+
+Success chaining (gbooleans) starts to get a little hairier...
+
+
+Functions:
+
+gst_element_set_state(element,state) is called by the application to set
+the state for an element (or bin).
+
+elementclass->change_state() is the class function that actually does the
+setting of the state for this element.  Any subclass implementation will
+chain to the parent_class's version.
+
+gst_element_change_state(element,state) is the Element class's
+implementation of the change_state() function.  It simply sets the state.
+
+
+gst_bin_set_state_type(element,state,type) is a specialized function for
+bins only that sets the type only on elements of that type.
+
+binclass->change_state_type() is the class function that does the
+selective 
diff --git a/docs/random/states b/docs/random/states
new file mode 100644 (file)
index 0000000..ef05903
--- /dev/null
@@ -0,0 +1,71 @@
+GST State Bits and Transition Rules (graph to follow)
+-----------------------------------
+
+These are the 'new' state bits and what they mean.
+
+What the state bits are:
+GST_STATE_COMPLETE: if the element has enough data, but is not in any kind
+       of running or explicitely stopped state.  ready to be used.
+GST_STATE_RUNNING: this is the normal state of the pipeline, where data
+       goes all the way through the pipeline normally.
+GST_STATE_DISCOVERY: anything the element does in this state must be reset
+       after discovery.  any data read from sync source must be cached.
+GST_STATE_PREROLL: not a lot different from PLAYING, except sinks don't
+       render what they're getting.  useful for elements that require
+       data to get in sync, such as an MPEG video decoder that needs
+       IBBPBB before starting at the next P.
+
+
+
+Basic transition rules:
+
+Completeness is based on the element having enough information to actually
+do something.  GST_STATE_COMPLETE is required for any other state to be
+valid, though the only invariant is that you can't be RUNNING unless
+you're COMPLETE.  In fact, AFAICT, that's the *only* invariant.
+
+The element is entirely in control of this bit at all times.  There is no
+way to externally change this bit except by changing the state of the
+element in such a way as to effect a change.
+
+|= GST_STATE_COMPLETE
+       setting whatever the last bit of info the element was looking for
+       (gst_object_set)
+
+&= ~GST_STATE_COMPLETE
+       changing anything that invalidates the complete state of the element
+
+
+Whether the element is running or not, on the other hand, is almost
+entirely out of the hands of the individual element.  This is generally
+turned on by way of gst_element_run() as called by the parent (ultimately
+by the Pipeline), which happens to optionally call a function private to
+the element to prepare it.  As per docs/random/gboolean, very likely this
+function should return a TRUE/FALSE.
+
+Generally, I think if there is no such function, the generic element code
+should go ahead and set the state, and trigger the state_changed signal,
+returning TRUE.  If there is a function, call it.  If it returns TRUE,
+fire off the signal (since the signal is actually an Element signal
+anyway, why eat another function call?).  Return the result regardless.
+
+|= GST_STATE_RUNNING
+       starting up the pipeline with gst_pipeline_start
+
+~= ~GST_STATE_RUNNING
+       stopping the pipeline with gst_pipeline_stop, or some error state
+
+gst_pipeline_start() simply calls the gst_element_start() function on each
+of the elements in it.  This sets the RUNNING bit of each element, and for
+GstBin's it loops through that list.  gst_pipeline_start() is just a
+special case version of gst_bin_start().  All start() functions are
+GstElementClass functions, meaning you can start any element the same way.
+
+The pipeline can be stopped the same way, but more likely the pipeline
+will be stopped due to some stoppage condition, such as EOF on the source
+file, or the parser being told to stop the stream.  In the EOF case, it
+would turn its RUNNING bit off, then call the stop() class function on its
+parent.  This would trigger an up-hill, breath-first traversal of the
+whole graph.  Alternately, if each element lists its uber-parent (the
+Pipeline) it can simply inform the pipeline directly, causing a
+depth-first traversal just like the start() case.
diff --git a/docs/random/states.old b/docs/random/states.old
new file mode 100644 (file)
index 0000000..74d7de1
--- /dev/null
@@ -0,0 +1,80 @@
+GST States and Transition Rules (graph to follow)
+-------------------------------
+
+This should be a map of possible state transitions and what triggers them.
+
+What the states are:
+GST_STATE_NEW: a new element has this, if it has nothing set except name.
+GST_STATE_INCOMPLETE: any element that has some, but not enough
+       information to function is in this state.
+GST_STATE_COMPLETE: if the element has enough data, but is not in any kind
+       of running or explicitely stopped state.  ready to be used.
+GST_STATE_DISCOVERY: anything the element does in this state must be reset
+       after discovery.  any data read from sync source must be cached.
+GST_STATE_PREROLL: not a lot different from PLAYING, except sinks don't
+       render what they're getting.  useful for elements that require
+       data to get in sync, such as an MPEG video decoder that needs
+       IBBPBB before starting at the next P.
+GST_STATE_RUNNING: this is the normal state of the pipeline, where data
+       goes all the way through the pipeline normally.
+GST_STATE_STOPPED: an explicit stop state, different from COMPLETE in that
+       the state doesn't get reset.
+
+
+NULL -> GST_STATE_NEW
+       creating an element (gst_*_new*)
+
+GST_STATE_NEW -> GST_STATE_INCOMPLETE
+       setting anything in the element that isn't sufficient to bring it
+       to a useful state (gst_object_set)
+
+GST_STATE_INCOMPLETE -> GST_STATE_COMPLETE
+       setting whatever the last bit of info the element was looking for
+       (gst_object_set)
+
+GST_STATE_COMPLETE -> GST_STATE_INCOMPLETE
+       changing anything that invalidates the complete state of the element
+
+GST_STATE_COMPLETE -> GST_STATE_DISCOVERY
+       setting the state to DISCOVERY
+       [ used for autoplug ]
+
+GST_STATE_DISCOVERY -> GST_STATE_COMPLETE
+       setting the state !DISCOVERY
+       [ used when autoplug is over ]
+
+GST_STATE_DISCOVERY -> GST_STATE_PREROLL
+       setting the state to PREROLL
+       [ you can go straight to preroll from discovery if you want ]
+
+GST_STATE_DISCOVERY -> GST_STATE_RUNNING
+       setting the state to RUNNING
+       [ you can even go straight to running from preroll ]
+
+GST_STATE_DISCOVERY -> GST_STATE_STOPPED
+       setting the state to STOPPED
+       [ normally you'd go from discovery to stopped when you load a src ]
+
+GST_STATE_PREROLL -> GST_STATE_RUNNING
+       setting the state to RUNNING
+       [ preroll generally leads straight to running, as in above ]
+
+GST_STATE_PREROLL -> GST_STATE_STOPPED
+       setting the state to STOPPED
+       [ it is possible to go to stopped, i.e load file@time ]
+
+GST_STATE_RUNNING -> GST_STATE_PREROLL
+       setting the state to PREROLL
+       [ unsure if you'd need this state, you'd go to stopped first ]
+
+GST_STATE_RUNNING -> GST_STATE_STOPPED
+       setting the state to STOPPED
+       [ pause. ]
+
+GST_STATE_STOPPED -> GST_STATE_PREROLL
+       setting the state to PREROLL
+       [ if you seek to intermediate time while stopped, you'd preroll to
+         prepare to start running again immediately ]
+
+GST_STATE_STOPPED -> GST_STATE_RUNNING
+       setting the state to RUNNING
diff --git a/docs/random/types b/docs/random/types
new file mode 100644 (file)
index 0000000..0a14f8f
--- /dev/null
@@ -0,0 +1,33 @@
+GstTypes exist to try to make sure data eveyrone is talking about the
+right kind of data.  They aid quite a bit in autoplugging, in fact make it
+possible.  Each well-formed type includes a function (typefind) that will
+take one or more buffers and determine whether or not it is indeed a
+stream of that type, and possible a metadata to go with it.  It may
+provide related metadata structure IDs (and must if it provides metadata
+from the typefind function).
+
+Because multiple elements and plugins are very likely to be using the same
+types, the process of creating/finding types is designed to be done with a
+single function call.  All operations on GstTypes occur via their guint16
+ID numbers, with the GstType structure "private" to the GST library.  A
+plugin wishing to use a give type would contain a static struct of type
+GstTypeFactory, which lists the MIME type, possible extensions (which may
+overlap the mime-types file), a typefind function, and any other cruft I
+decide to add.
+
+A plugin init function would take this typefactory and hand it to the
+gst_type_new() (FIXME: badly named) function, which would first search for
+that same MIME type in the current list.  If it found one, it would
+compare the two to see if the new one is "better".  Better is defined as
+having more extentions (to be merged) or a typefind function verses none.
+
+The point of returning an existing MIME type is a result of the goal of
+unifying types enough to guarantee that, for instance, all MP3 decoders
+will work interchangably.  If MP3 decoder A says "MIME type 'audio/mpeg'
+with extensions 'mpeg3'" and decoder B says "MIME type 'audio/mpeg' with
+extensions 'mp3'", we don't want to have two types defined, possibly with
+two typefind functions. If we did, it's not obvious which of the two would
+be tried first (luck) and if both would really identify streams as mp3
+correctly in all cases.  And whichever wins, we're stuck using the
+associated decoder to play that stream.  We lose the choice between any
+valid mp3 decoder, and thus the whole point of the type system.
diff --git a/docs/random/vis-transform b/docs/random/vis-transform
new file mode 100644 (file)
index 0000000..a83e7c0
--- /dev/null
@@ -0,0 +1,78 @@
+Duh, an 'easy' way to replicate Giess's behavior:
+
+For each frame, you have to mutate it by a transform matrix.  This is
+easy, thought not cheap.  First you precalculate the transform matrix how
+you want it, based on whatever rotations or whatever you want.
+
+The data stored in each spot on the matrix tells you how to transform a
+single pixel.  The simple case is dx,dy, where both are relatively small.
+The probably ought to be a byte in any case, so you can scale the
+transform matrix on slow machines.  A more complex case is some trick
+whereby a single pixel ends up splattered in several places.  Idea below.
+
+The matrix consists of some number of 8bit arrays of the same size as the
+image.  They'd probably be line-interleaved or better to help with cache
+effects (which are VERY serious here).  Each channel represents some
+aspect of the transform. The first two would likely be dx and dy, the
+third might be a multiplier if that wasn't done statically.
+
+The idea: any number of transform sets could be applied, given available
+processing power.  Just set the static scalar or the multiplier matrices
+so you don't completely swamp the output pixels.
+
+Note that this is fastest in 8-bit, but theoretically could be applied to
+32 bit.  15 and 16 are hard, since you can't easily apply the multipliers
+unless they're 1/2^n, and even then it's significantly heavier (you'd have
+to mask the top n bits of each color out).
+
+This SCREAMS for MMX, in case you haven't figured it out yet.
+Unfortunatley, MMX is only directly useful for the scalar matrix, unless
+you do a trick where all the pixels in that fit in 64 bits (8 8bit, 4
+16bit, or 2 32bit) are always moved in a group.  This is very possible,
+and might be a significant perf increase by being able to use MMX all the
+way through.  Otherwise you have to place each pixel by extracting the MMX
+stuff back into normal registers, and that just plain sucks.
+
+A pseudo-C implementation:
+
+----- BEGIN -----
+gint x,y;                              /* image x and y size */
+guchar old_image[x][y];                        /* original image */
+guchar new_image[x][y];                        /* new image */
+gchar x_xform[x][y];                   /* dx matrix */
+gchar y_xform[x][y];                   /* dy matrix */
+guchar s_xform[x][y];                  /* intensity scalar matrix */
+guchar scalar;                         /* global scalar */
+
+gint i,j;                              /* indixes */
+gulong p;                              /* pixel value in question */
+guchar u,v,w;                          /* modifier variables */
+
+/* clear the new image, we don't want anything getting in the way */
+/* NOT NECESSARILY A GOOD THING, THOUGH */
+memset(new_image,0,x*y);
+
+/* loop through all the lines in the image */
+for (j=0;j<y;j++) {
+  /* loop through all the pixels in the line */
+  for (i=0;i<x;i++) {
+    p = old_image[i][j];
+    u = x_xform[i][j];
+    v = y_xform[i][j];
+    w = s_xform[i][j];
+    new_image[i+u][j+v] = (guchar)((p<<14) / (w * scalar));
+  }
+}
+----- END -----
+
+Note that the above really, *REALLY* sucks performance-wise.  Throw it a
+80x60 image and it'll swamp my poor laptop.  Also note that I simply set
+the pixel value, not merge it.  That means you'd better be sure your
+transform matrix doesn't have overlapping destinations.
+
+Other notes about the above code: x_xform and y_xform are signed chars,
+which means pixels can move in all directions.  The intensity matrix is
+unsigned, with a range from 0 to 255, so is the global scalar.  Note the
+shift of 14bits (2 * 7bits), then divide by each.  That means identity for
+both scalars is at 128.  The FP range of each is thus 0.0 to 2.0.  Very
+handy.
diff --git a/docs/random/walkthrough b/docs/random/walkthrough
new file mode 100644 (file)
index 0000000..f6b21fa
--- /dev/null
@@ -0,0 +1,39 @@
+You might start by creating a source element and put it into a pipeline.
+At this point both element's state would be GST_STATE_NEW, since they
+don't have enough state to actually run yet.  At this point you can set
+the filename of the source, and possibly bytesperread and other things.
+
+Then you'd want to discover the data type of the file you're sourcing.
+This will typically be handled by the pipeline itself by calling
+gst_pipeline_autoplug(), or gst_pipeline_find_pad_type(), or somesuch. The
+pipeline would first set its state to GST_STATE_DISCOVERY.  A gstfindtype
+sink would be added to the pipeline and connected to the source.  Its
+HAVE_TYPE signal would be connected to a private pipeline function.
+
+The pipeline would then set the the src state to GST_STATE_DISCOVERY, and
+call the src's push() function until a the type is set by the function
+connected to the gstfindtype element's signal.  At this point the pipeline
+would disconnect the gstfindtype element from the src, set the type of the
+pad to the type returned by the gstfindtype element.  At disconnection of
+the find element, the src's state automatically reverts to NEW.
+
+(The trick with the sources when they do DISCOVERY is that synchronous
+sources can't go back and read data again.  So perhaps I should set up a
+wrapper function for the push() function that uses either a sync or
+async function as provided by the src instance to provide DISCOVERY and
+normal operations.  It would use a [GstBufferCache] to read ahead into
+memory if necessary, creating baby buffers as necessary to answer the
+needs of each DISCOVERY sequence.)
+
+If you called find_pad_type(), it would return right about now, with the
+ID of the type it found.  At the same time, if you have connected a signal
+to the pad's SET_TYPE signal, it would fire right as the type is set by
+the find_pad_type() function.  This would allow your application to do its
+own selection of filters to connect to the pad.
+
+If you called autoplug(), the pipeline would make a selection of element
+to connect.  The element would be created (state=NEW), added to the
+pipeline, and the appropriate sink pad connected to the src in question.
+(Note that multi-sink elements won't be supported unless there's a really
+good and obvious way to do so)  The whole process would repeat until the
+recently added element no longer has a src pad.
diff --git a/docs/slides/README b/docs/slides/README
new file mode 100644 (file)
index 0000000..923e6dc
--- /dev/null
@@ -0,0 +1,4 @@
+These are notes for slides to be presented at the OGI DISC Cookie Talk,
+Oct 22, 1999.  Outline will be text, probably written up in PowerPoint for
+ease of presentation, and converted to .ps, .pdf, and .html once the talk
+has been given.
diff --git a/docs/slides/abstract b/docs/slides/abstract
new file mode 100644 (file)
index 0000000..b4bc016
--- /dev/null
@@ -0,0 +1,9 @@
+Intro to GStreamer
+
+GStreamer is a media-streaming architecture that I've been developing at
+home for about 4 months now. It's designed to overcome some of the
+problems I've seen in both the OGI Pipeline and in reading through
+DirectShow docs.  After an overview of the existing OGI Pipeline, I'll
+cover GStreamer's existing and planned architecture, and list what remains
+to be done.  GStreamer is mostly frozen right now, as it is to be released
+as 0.1.0 on Oct 31.  Major thaw will occur in a few weeks.
diff --git a/docs/slides/abstract.save b/docs/slides/abstract.save
new file mode 100644 (file)
index 0000000..b317c5a
--- /dev/null
@@ -0,0 +1,10 @@
+Intro to GStreamer
+
+GStreamer is a media-streaming architecture that I've been developing at
+home for about 4 months now. It's designed to overcome some of the
+problems I've seen in both the OGI Pipeline and in reading through
+DirectShow docs.  After an overview of the existing OGI Pipeline, I'll
+cover GStreamer's existing and planned architecture, and list what remains
+to be done.  GStreamer is mostly frozen right now, as it is to be released
+as 0.1.0 on Oct 31.  Major thaw will occur in a few weeks.
+
diff --git a/docs/slides/outline b/docs/slides/outline
new file mode 100644 (file)
index 0000000..5808dbb
--- /dev/null
@@ -0,0 +1,144 @@
+Introduction (1)
+       (sorry, no cool logo/graphic yet, ideas?)
+       GStreamer is a library and set of tools to build arbitrary,
+        reconfigurable filter graphs.  It derives from the OGI Pipeline
+        and DirectShow (docs, no experience), and is in its second
+        generation (first was completed/abandonded *in* Atlanta on the
+        way to the Linux Expo).
+       0.1.0 release is scheduled for Oct 31, 1999.
+       Will cover Background, Goals, Design, and Futures
+Why do we need this?
+       launch reads the command line to create the graph, from .so's
+       Connections (queues) are made by launcher, lots of switchout code
+       Argument handling is messy, at start-time only
+       ...thus there is basically only one state: running
+       There is no real master process capable of seeing the whole
+        pipeline as a graph, so wiring A->B->C with some backchannel
+        (parameter, not data stream) from C to A is hard
+       Channels try to do IPC, file, and network I/O, excess abstraction
+Goals (1)
+       Provide a clean way to both build graphs and write new elements
+       Make things easier by providing auto-connect, stock sub-graphs
+       Include tools sorely lacking in OGI pipeline, like editor, saves
+       Enable Linux to catch up with M$'s world, allowing commercial
+        plugins to a stable API (as of 1.0) so we don't end up with N
+        wheels from N-M different people (multiple projects)
+Overview (1)
+       Object hierarchy capable of run-time discovery, based on GtkObject
+       Deeply nested parent-child relationships enable threads, blackboxes
+       Buffers can point to anything, are typed, and can carry metadata
+       Plugins can be loaded at any point, and registry reduces loads
+       Symbiotic editor lets you design/run/save graphs visually
+What are filter graphs? (1)
+       Filters take data in and spit data out, doing something to it
+       Filters have N>=0 inputs and M>=0 outputs
+       Filter graphs are many filters connected together, like a circuit
+       The goal is typically to move data from 'left' to 'right', towards
+        some kind of user-visible conclusion, i.e. audio or video
+Architecture (3?)
+  - Graphs of Elements
+       (here lies screen-grab from editor)
+       Element is core Object, Bins hold (and are) Elements
+       Pads are fundamental to an Element, are cross-wired with pointers
+       Since Bins hold Elements, and Bins are Elements, Bins hold Bins
+       'Ghostpads' provide interfaces for Bins without native interfaces
+#      Threads are type of Bin that actually run in separate threads
+
+  - States
+       (table of states, invariants, and descriptions)
+           COMPLETE    Element has all needed information
+           RUNNING     Element has acquired resources, ready to go
+           DISCOVERY   ... (unimplemented)
+           PREROLL     ... (unimplemented)
+           PLAYING     Element is actively trading data
+           PAUSED      Special state where things just don't run (?..)
+       States are used to keep elements in check
+
+  - Buffers
+       Buffers designed to be versatile, with arbitrary typing/metadata
+       Has pointer to data, length, so can point to someone else's data
+       Type system (crude so far) ensures buffers don't go stray
+       Metadata can be attached, such as the audio parameters
+       Ref-counting and copy-on-write avoids most copies, not complete
+       Sub-buffers can be created from bigger buffer, limitting copies
+Gtk+ Object System (2)
+  - Pros
+       C-language object system, well tested (Gtk+, Gnome...)
+       Arguments of any fundamental type, read/write, built-in hooks
+       Signals used for hooks into object events, overridable
+       Run-time discovery of args, signals (quarks)
+  - Cons
+       No multiple-inheritance, though I haven't *needed* it
+       There are some holes (can't attach quarks to *eveything*)
+
+  - Design
+       Classes, instances are structs; 1st item is parent class
+       Type system allows clean casting, ^^^^^^
+       Arguments are set/get by string, use functions to do the work,
+        thus setting an arg might trigger a redraw of the GUI
+       Signals are strings, use marshallers, various firing methods
+Basic GStreamer objects (1)
+  - Elements
+       (show class,instance structs)
+       Very simple, just provides a means to handle pads, state
+  - Bins
+       (show class,instance structs)
+       Supports children, handles group state transitions
+Pads (1)
+       Pad list type, direction, and chaining function ptr
+       When creating a sink pad (!src) you set the chaining function
+       gst_pad_connect() sets the peers, and copies chain function to src
+       Passing buffer to a src pad transparently calls the chain function
+       (graph goes here...)
+Sources (1)
+       Source provides functions to push data
+       Regular push() function just takes next N bytes and sends them
+       Async push_region() grabs N bytes at offset O and sends them
+       EOF signal [will] reset the state from PLAYING down to !RUNNING
+       "location" argument is global by convention, for filenames...URIs
+Connections (1)
+       Special type of Filter that 
+Threads (1)
+       Special case of Bin that actually creates a thread transparently
+       When RUNNING, thread exists, mutex/cond used to go [!]PLAYING
+       Automatically determines how to start sub-graph
+               Looks for both Sources and Elements wired to Connection
+       Will cooperate with Pipelines when threading is not available
+Typing and Metadata (1)
+  - Types
+       Based on MIME types, set up as quarks, and dealt with as int
+       Usable entirely at run-time, since they're registerable by plugins
+  - Metadata
+       Also registered as an int, but must be compile time due to structs
+       Have refcounts and CoW semantics, since they travel with buffers
+Plugins (1)
+       Plugin architecture designed around class system
+       Arguments and signals provide interface over standard base class
+       Each Element defined by ElementFactory, which is queried by name
+       At plugin load, any number of ElementFactories and Types registered
+       Element registers against Type as source or sink
+Editor (2+?)
+       (show filter graph snapshot, a different one, more complex)
+       Built as a parallel object hierarchy on top of GNOME Canvas
+       Every object in filter graph has equivalent in editor, plus some
+       Canvas is designed with groups and signal-propagation, so...
+       Why not build the whole thing as subclasses of CanvasGroup?
+
+       ...because updates get messy/recursive (the way I *was* doing it)
+       Solution is to modify objects so they own Group rather than being
+       Relatively trivial modification, but requires lots of repointering
+       Still a genealogical mess of parents and children...
+XML
+       The goal is to use XML heavily, with an eye towards DOM
+       Used for both saving and providing pre-build components
+       Both graph and editor will have namespace, they'll interleave
+       A generic save function will exist for Elements, with hooks
+       Saving an EditorElement will also save Element
+       Also used for a plugin registry, to avoid loading all plugins
+
+
+
+
+leaky bucket is trivial
+applications - generic conferencing tool (repluggable codecs), mixing
+ environment (data flow graphs)
diff --git a/docs/slides/slides b/docs/slides/slides
new file mode 100644 (file)
index 0000000..b434811
--- /dev/null
@@ -0,0 +1,17 @@
+Introduction
+Why do we need this?
+Goals
+Overview
+What are filter graphs?
+Gtk+ Object System
+Architecture - Elements
+Pads
+Architecture - Buffers
+Typing and Metadata
+Sources
+Threads and Connections
+Architecture - States
+Plugins
+Editor
+XML
+Futures
diff --git a/editor/Makefile.am b/editor/Makefile.am
new file mode 100644 (file)
index 0000000..394cf83
--- /dev/null
@@ -0,0 +1,32 @@
+LDFLAGS = $(GLIB_LIBS) $(GTK_LIBS) $(top_srcdir)/gst/libgst.la \
+       $(shell gnome-config --libs gnomeui)
+INCLUDES = $(GLIB_CFLAGS) $(GTK_CFLAGS) -I$(top_srcdir)/gst \
+           $(shell gnome-config --cflags gnomeui)
+
+
+lib_LTLIBRARIES = libgsteditor.la
+
+libgsteditor_la_SOURCES =      \
+       gsteditor.c             \
+       gsteditorelement.c      \
+       gsteditorbin.c          \
+       gsteditorcanvas.c       \
+       gsteditorpad.c          \
+       gsteditorconnection.c   \
+       gstelementselect.c      \
+       gsteditorcreate.c
+
+libgsteditorincludedir = $(includedir)/gst
+libgsteditorinclude_HEADERS =  \
+       gsteditor.h
+
+
+bin_PROGRAMS = editor
+editor_LDFLAGS = libgsteditor.la
+
+
+noinst_HEADERS =               \
+       gstelementselect.h      \
+       gsteditorcreate.h
+
+EXTRA_DIST = editor.glade editorelement.glade
diff --git a/editor/editor.c b/editor/editor.c
new file mode 100644 (file)
index 0000000..a16a0f2
--- /dev/null
@@ -0,0 +1,49 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gtk/gtk.h>
+#include <gnome.h>
+#include <libgnomeui/gnome-canvas.h>
+
+#include <gst/gst.h>
+
+#include "gsteditor.h"
+
+extern gboolean _gst_plugin_spew;
+
+int main(int argc,char *argv[]) {
+  GtkWidget *appwindow;
+  GstEditor *editor;
+
+  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+  gnome_init("GST Graph Editor",VERSION,argc,argv);
+
+  appwindow = gnome_app_new("gst-editor","GST Graph Editor");
+  editor = gst_editor_new("pipeline");
+  gtk_widget_set_usize(GTK_WIDGET(editor),250,250);
+  gnome_app_set_contents(GNOME_APP(appwindow),GTK_WIDGET(editor));
+  gtk_widget_show_all(appwindow);
+
+  gtk_main();
+
+  return(0);
+}
diff --git a/editor/editor.glade b/editor/editor.glade
new file mode 100644 (file)
index 0000000..2f4c60a
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+  <name>project1</name>
+  <directory></directory>
+  <source_directory></source_directory>
+  <pixmaps_directory>pixmaps</pixmaps_directory>
+  <language>C</language>
+  <gnome_support>True</gnome_support>
+  <gettext_support>True</gettext_support>
+  <use_widget_names>False</use_widget_names>
+  <main_source_file>gladesrc.c</main_source_file>
+  <main_header_file>gladesrc.h</main_header_file>
+  <handler_source_file>gladesig.c</handler_source_file>
+  <handler_header_file>gladesig.h</handler_header_file>
+</project>
+
+<widget>
+  <class>GtkWindow</class>
+  <name>editor_window</name>
+  <title>GST Editor</title>
+  <type>GTK_WINDOW_TOPLEVEL</type>
+  <position>GTK_WIN_POS_NONE</position>
+  <allow_shrink>False</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>False</auto_shrink>
+
+  <widget>
+    <class>GtkVBox</class>
+    <name>vbox3</name>
+    <homogeneous>False</homogeneous>
+    <spacing>0</spacing>
+
+    <widget>
+      <class>Custom</class>
+      <name>canvas_custom</name>
+      <child>
+       <padding>0</padding>
+       <expand>True</expand>
+       <fill>True</fill>
+      </child>
+      <creation_function>create_canvas</creation_function>
+      <int1>0</int1>
+      <int2>0</int2>
+    </widget>
+
+    <widget>
+      <class>GtkHBox</class>
+      <name>hbox3</name>
+      <child>
+       <padding>0</padding>
+       <expand>True</expand>
+       <fill>True</fill>
+      </child>
+      <homogeneous>False</homogeneous>
+      <spacing>0</spacing>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>create_button</name>
+       <child>
+         <padding>0</padding>
+         <expand>False</expand>
+         <fill>False</fill>
+       </child>
+       <can_focus>True</can_focus>
+       <label>Add Element</label>
+      </widget>
+    </widget>
+  </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/editor/editorelement.glade b/editor/editorelement.glade
new file mode 100644 (file)
index 0000000..5711e01
--- /dev/null
@@ -0,0 +1,143 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+  <name>project1</name>
+  <directory></directory>
+  <source_directory></source_directory>
+  <pixmaps_directory>pixmaps</pixmaps_directory>
+  <language>C</language>
+  <gnome_support>True</gnome_support>
+  <gettext_support>True</gettext_support>
+  <use_widget_names>False</use_widget_names>
+  <main_source_file>gladesrc.c</main_source_file>
+  <main_header_file>gladesrc.h</main_header_file>
+  <handler_source_file>gladesig.c</handler_source_file>
+  <handler_header_file>gladesig.h</handler_header_file>
+</project>
+
+<widget>
+  <class>GtkWindow</class>
+  <name>window1</name>
+  <title>window1</title>
+  <type>GTK_WINDOW_TOPLEVEL</type>
+  <position>GTK_WIN_POS_NONE</position>
+  <allow_shrink>False</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>False</auto_shrink>
+
+  <widget>
+    <class>GtkFrame</class>
+    <name>element</name>
+    <border_width>25</border_width>
+    <label>mpegparse</label>
+    <label_xalign>0</label_xalign>
+    <shadow_type>GTK_SHADOW_ETCHED_IN</shadow_type>
+
+    <widget>
+      <class>GtkHBox</class>
+      <name>hbox1</name>
+      <homogeneous>False</homogeneous>
+      <spacing>0</spacing>
+
+      <widget>
+       <class>GtkVBox</class>
+       <name>vbox1</name>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+       <homogeneous>False</homogeneous>
+       <spacing>0</spacing>
+
+       <widget>
+         <class>GtkFrame</class>
+         <name>frame5</name>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+         <label_xalign>0</label_xalign>
+         <shadow_type>GTK_SHADOW_ETCHED_IN</shadow_type>
+
+         <widget>
+           <class>GtkLabel</class>
+           <name>src</name>
+           <label>src</label>
+           <justify>GTK_JUSTIFY_CENTER</justify>
+           <xalign>0.5</xalign>
+           <yalign>0.5</yalign>
+           <xpad>0</xpad>
+           <ypad>0</ypad>
+         </widget>
+       </widget>
+      </widget>
+
+      <widget>
+       <class>Placeholder</class>
+      </widget>
+
+      <widget>
+       <class>GtkVBox</class>
+       <name>vbox3</name>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+       <homogeneous>False</homogeneous>
+       <spacing>0</spacing>
+
+       <widget>
+         <class>GtkFrame</class>
+         <name>frame3</name>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+         <label_xalign>0</label_xalign>
+         <shadow_type>GTK_SHADOW_ETCHED_IN</shadow_type>
+
+         <widget>
+           <class>GtkLabel</class>
+           <name>audiosrc</name>
+           <label>audiosrc</label>
+           <justify>GTK_JUSTIFY_CENTER</justify>
+           <xalign>0.5</xalign>
+           <yalign>0.5</yalign>
+           <xpad>0</xpad>
+           <ypad>0</ypad>
+         </widget>
+       </widget>
+
+       <widget>
+         <class>GtkFrame</class>
+         <name>frame4</name>
+         <child>
+           <padding>0</padding>
+           <expand>True</expand>
+           <fill>True</fill>
+         </child>
+         <label_xalign>0</label_xalign>
+         <shadow_type>GTK_SHADOW_ETCHED_IN</shadow_type>
+
+         <widget>
+           <class>GtkLabel</class>
+           <name>videosrc</name>
+           <label></label>
+           <justify>GTK_JUSTIFY_CENTER</justify>
+           <xalign>0.5</xalign>
+           <yalign>0.5</yalign>
+           <xpad>0</xpad>
+           <ypad>0</ypad>
+         </widget>
+       </widget>
+      </widget>
+    </widget>
+  </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/editor/gsteditor.c b/editor/gsteditor.c
new file mode 100644 (file)
index 0000000..62f4eee
--- /dev/null
@@ -0,0 +1,144 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gtk/gtk.h>
+#include <gnome.h>
+#include <gst/gst.h>
+
+#include "gsteditor.h"
+
+/* signals and args */
+enum {
+  LAST_SIGNAL
+};
+
+enum {
+  ARG_0,
+  ARG_NAME,
+};
+
+static void gst_editor_class_init(GstEditorClass *klass);
+static void gst_editor_init(GstEditor *editor);
+
+static void gst_editor_set_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_get_arg(GtkObject *object,GtkArg *arg,guint id);
+
+static GtkFrame *parent_class = NULL;
+
+GtkType gst_editor_get_type(void) {
+  static GtkType editor_type = 0;
+
+  if (!editor_type) {
+    static const GtkTypeInfo editor_info = {
+      "GstEditor",
+      sizeof(GstEditor),
+      sizeof(GstEditorClass),
+      (GtkClassInitFunc)gst_editor_class_init,
+      (GtkObjectInitFunc)gst_editor_init,
+      NULL,
+      NULL,
+      (GtkClassInitFunc)NULL,
+    };
+    editor_type = gtk_type_unique(gtk_frame_get_type(),&editor_info);
+  }
+  return editor_type;
+}
+
+static void gst_editor_class_init(GstEditorClass *klass) {
+  GtkObjectClass *object_class;
+
+  object_class = (GtkObjectClass*)klass;
+
+  parent_class = gtk_type_class(gtk_frame_get_type());
+
+  gtk_object_add_arg_type("GstEditor::name",GTK_TYPE_STRING,
+                          GTK_ARG_READWRITE,ARG_NAME);
+
+  object_class->set_arg = gst_editor_set_arg;
+  object_class->get_arg = gst_editor_get_arg;
+}
+
+static void gst_editor_init(GstEditor *editor) {
+  /* create the pipeline */
+  editor->pipeline = gst_pipeline_new("pipeline");
+  g_return_if_fail(editor->pipeline != NULL);
+
+  /* create the editor canvas */
+  editor->canvas = gst_editor_canvas_new(GST_BIN(editor->pipeline),NULL);
+
+  /* create the scrolled window */
+  editor->scrollwindow = gtk_scrolled_window_new(NULL,NULL);
+
+  /* get the canvas widget */
+  editor->canvaswidget = gst_editor_canvas_get_canvas(editor->canvas);
+
+  /* add the canvas to the scrolled window */
+  gtk_container_add(GTK_CONTAINER(editor->scrollwindow),
+                    editor->canvaswidget);
+
+  /* add the scrolled window to the canvas */
+  gtk_container_add(GTK_CONTAINER(editor),editor->scrollwindow);
+}
+
+/**
+ * gst_editor_new:
+ * name: name of editor frame
+ *
+ * Creates a new GstEditor composite widget with the given name.
+ *
+ * Returns: Freshly created GstEditor widget.
+ */
+GstEditor *gst_editor_new(gchar *name) {
+  GstEditor *editor;
+
+  editor = gtk_type_new(gst_editor_get_type());
+  gtk_object_set(GTK_OBJECT(editor),"name",name,NULL);
+
+  return editor;
+}
+
+static void gst_editor_set_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditor *editor = GST_EDITOR(object);
+
+  switch (id) {
+    case ARG_NAME:
+      gtk_object_set(GTK_OBJECT(editor),"label",GTK_VALUE_STRING(*arg),NULL);
+      gst_element_set_name(GST_OBJECT(editor->pipeline),
+                           GTK_VALUE_STRING(*arg));
+      break;
+    default:
+      g_warning("gsteditor: unknown arg!\n");
+      break;
+  }
+}
+
+static void gst_editor_get_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditor *editor = GST_EDITOR(object);
+
+  switch (id) {
+    case ARG_NAME:
+      GTK_VALUE_STRING(*arg) =
+        gst_element_get_name(GST_OBJECT(editor->pipeline));
+      break;
+    default:
+      arg->type = GTK_TYPE_INVALID;
+      break;
+  }
+}
diff --git a/editor/gsteditor.h b/editor/gsteditor.h
new file mode 100644 (file)
index 0000000..4bc04e1
--- /dev/null
@@ -0,0 +1,360 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_EDITOR_H__
+#define __GST_EDITOR_H__
+
+#include <gnome.h>
+#include <gst/gst.h>
+
+
+typedef struct _GstEditor GstEditor;
+typedef struct _GstEditorClass GstEditorClass;
+typedef struct _GstEditorElement GstEditorElement;
+typedef struct _GstEditorElementClass GstEditorElementClass;
+typedef struct _GstEditorBin GstEditorBin;
+typedef struct _GstEditorBinClass GstEditorBinClass;
+typedef struct _GstEditorCanvas GstEditorCanvas;
+typedef struct _GstEditorCanvasClass GstEditorCanvasClass;
+typedef struct _GstEditorPad GstEditorPad;
+typedef struct _GstEditorPadClass GstEditorPadClass;
+typedef struct _GstEditorConnection GstEditorConnection;
+typedef struct _GstEditorConnectionClass GstEditorConnectionClass;
+
+
+
+#define GST_TYPE_EDITOR \
+  (gst_editor_get_type())
+#define GST_EDITOR(obj) \
+  (GTK_CHECK_CAST((obj),GST_TYPE_EDITOR,GstEditor))
+#define GST_EDITOR_CLASS(klass) \
+  (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_EDITOR,GstEditorClass))
+#define GST_IS_EDITOR(obj) \
+  (GTK_CHECK_TYPE((obj),GST_TYPE_EDITOR))
+#define GST_IS_EDITOR_CLASS(obj) \
+  (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_EDITOR)))
+
+
+struct _GstEditor {
+  GtkFrame frame;
+
+  /* the actual pipeline to be associated with this thing */
+  GstPipeline *pipeline;
+
+  /* the editor canvas */
+  GstEditorCanvas *canvas;
+
+  /* the canvas and scrollwindow */
+  GtkWidget *canvaswidget;
+  GtkWidget *scrollwindow;
+};
+
+struct _GstEditorClass {
+  GnomeCanvasClass parent_class;
+};
+
+
+GtkType gst_editor_get_type();
+GstEditor *gst_editor_new(gchar *name);
+
+
+
+#define GST_EDITOR_SET_OBJECT(item,object) \
+  (gtk_object_set_data(GTK_OBJECT(item),"gsteditorobject",(object)))
+#define GST_EDTIOR_GET_OBJECT(item) \
+  (gtk_object_get_data(GTK_OBJECT(item),"gsteditorobject"))
+
+
+
+#define GST_TYPE_EDITOR_ELEMENT \
+  (gst_editor_element_get_type())
+#define GST_EDITOR_ELEMENT(obj) \
+  (GTK_CHECK_CAST((obj),GST_TYPE_EDITOR_ELEMENT,GstEditorElement))
+#define GST_EDITOR_ELEMENT_CLASS(klass) \
+  (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_EDITOR_ELEMENT,GstEditorElementClass))
+#define GST_IS_EDITOR_ELEMENT(obj) \
+  (GTK_CHECK_TYPE((obj),GST_TYPE_EDITOR_ELEMENT))
+#define GST_IS_EDITOR_ELEMENT_CLASS(obj) \
+  (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_EDITOR_ELEMENT)))
+
+#define GST_EDITOR_ELEMENT_PARENT(obj) (GST_EDITOR_ELEMENT(obj)->parent)
+#define GST_EDITOR_ELEMENT_GROUP(obj) (GST_EDITOR_ELEMENT(obj)->group)
+#define GST_EDITOR_ELEMENT_CANVAS(obj) (GST_EDITOR_ELEMENT(obj)->canvas)
+
+struct _GstEditorElement {
+  GstObject object;
+
+  /* parent object (NULL if I am the parent) */
+  GstEditorBin *parent;
+
+  /* toplevel canvas (myself if I am the toplevel) */
+  GstEditorCanvas *canvas;
+
+  /* the element we're associated with */
+  GstElement *element;
+
+  /* whether we've been realized or not */
+  gboolean realized;
+
+  /* toplevel group, must be !NULL */
+  GnomeCanvasGroup *group;                     // parent group
+
+  /* visual stuff */
+  gdouble x,y;                                 // center
+  gdouble width,height;                                // size
+  GnomeCanvasItem *border,*title,*resizebox;   // easy ones
+  GnomeCanvasItem *statebox[4],*statetext[4];  // GST_STATE_*
+  GnomeCanvasItem *playbox,*playtext;          // playstate
+  gboolean states[5];                          // visual states
+
+  gdouble insidewidth,insideheight;            // minimum space inside
+  gdouble minwidth,minheight;                  // minimum size
+  gdouble titlewidth,titleheight;              // size of title
+  gdouble statewidth,stateheight;              // size of state boxes
+  gdouble sinkwidth,sinkheight;                        // size of sink pads
+  gdouble srcwidth,srcheight;                  // size of src pads
+  gint sinks,srcs;                             // how many pads?
+
+  GnomeCanvasGroup *insidegroup;               // contents if any
+
+  gboolean resize;                             // does it need resizing?
+
+  /* list of pads */
+  GList *srcpads,*sinkpads;
+  gboolean padlistchange;
+
+  /* interaction state */
+  gboolean dragging,resizing,moved,hesitating;
+  gdouble offx,offy,dragx,dragy;
+};
+
+struct _GstEditorElementClass {
+  GnomeCanvasGroupClass parent_class;
+
+  void (*realize) (GstEditorElement *element);
+  gint (*event) (GnomeCanvasItem *item,GdkEvent *event,
+                GstEditorElement *element);
+  gint (*button_event) (GnomeCanvasItem *item,GdkEvent *event,
+                        GstEditorElement *element);
+};
+
+
+GtkType gst_editor_element_get_type();
+GstEditorElement *gst_editor_element_new(GstEditorBin *parent,
+                                         GstElement *element,
+                                         const gchar *first_arg_name,...);
+void gst_editor_element_construct(GstEditorElement *element,
+                                  GstEditorBin *parent,
+                                  const gchar *first_arg_name,
+                                  va_list args);
+void gst_editor_element_repack(GstEditorElement *element);
+GstEditorPad *gst_editor_element_add_pad(GstEditorElement *element,
+                                         GstPad *pad);  
+
+
+#define GST_TYPE_EDITOR_BIN \
+  (gst_editor_bin_get_type())
+#define GST_EDITOR_BIN(obj) \
+  (GTK_CHECK_CAST((obj),GST_TYPE_EDITOR_BIN,GstEditorBin))
+#define GST_EDITOR_BIN_CLASS(klass) \
+  (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_EDITOR_BIN,GstEditorBin))
+#define GST_IS_EDITOR_BIN(obj) \
+  (GTK_CHECK_TYPE((obj),GST_TYPE_EDITOR_BIN))
+#define GST_IS_EDITOR_BIN_CLASS(obj) \
+  (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_EDITOR_BIN)))
+  
+struct _GstEditorBin {
+  GstEditorElement element;
+
+  /* lists of GUI elements and connections */ 
+  GList *elements, *connections;
+
+  /* connection state */
+  GstEditorPad *frompad;               // where the drawing started from
+  gboolean fromsrc;                    // are we connecting *from* a source?
+  gboolean connecting;                 // if we're trying to connect right now
+  GstEditorConnection *connection;     // the connection we're operating on
+  GstEditorPad *ghostpad;              // potential ghost pad
+  gboolean inpadregion;                        // is cursor in pad region
+};
+
+struct _GstEditorBinClass {
+  GstEditorElementClass parent_class;
+};
+
+
+
+GtkType gst_editor_bin_get_type();
+GstEditorBin *gst_editor_bin_new(GstEditorBin *parent,GstBin *bin,
+                                 const gchar *first_arg_name,...);
+
+
+
+#define GST_TYPE_EDITOR_CANVAS \
+  (gst_editor_canvas_get_type())
+#define GST_EDITOR_CANVAS(obj) \
+  (GTK_CHECK_CAST((obj),GST_TYPE_EDITOR_CANVAS,GstEditorCanvas))
+#define GST_EDITOR_CANVAS_CLASS(klass) \
+  (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_EDITOR_CANVAS,GstEditorCanvasClass))
+#define GST_IS_EDITOR_CANVAS(obj) \
+  (GTK_CHECK_TYPE((obj),GST_TYPE_EDITOR_CANVAS))
+#define GST_IS_EDITOR_CANVAS_CLASS(obj) \
+  (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_EDITOR_CANVAS)))
+
+
+struct _GstEditorCanvas {
+  GstEditorBin bin;
+
+  gboolean inchild;
+
+  GnomeCanvas *canvas;
+};
+
+struct _GstEditorCanvasClass {
+  GnomeCanvasClass parent_class;
+};
+
+
+GtkType gst_editor_canvas_get_type();
+GstEditorCanvas *gst_editor_canvas_new(GstBin *bin,
+                                       const gchar *first_arg_name,...);
+GtkWidget *gst_editor_canvas_get_canvas(GstEditorCanvas *canvas);
+void gst_editor_bin_add(GstEditorBin *parent,GstEditorElement *element);
+
+
+#define GST_TYPE_EDITOR_PAD \
+  (gst_editor_pad_get_type())
+#define GST_EDITOR_PAD(obj) \
+  (GTK_CHECK_CAST((obj),GST_TYPE_EDITOR_PAD,GstEditorPad))
+#define GST_EDITOR_PAD_CLASS(klass) \
+  (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_EDITOR_PAD,GstEditorPadClass))
+#define GST_IS_EDITOR_PAD(obj) \
+  (GTK_CHECK_TYPE((obj),GST_TYPE_EDITOR_PAD))
+#define GST_IS_EDITOR_PAD_CLASS(obj) \
+  (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_EDITOR_PAD)))
+
+struct _GstEditorPad {
+  GtkObject object; 
+
+  /* parent element */
+  GstEditorElement *parent;
+
+  /* toplevel canvas */
+  GstEditorCanvas *canvas;
+
+  /* the pad we're associated with */
+  GstPad *pad;
+  /* if this is a sink (convenience) */
+  gboolean issrc;
+
+  /* whether we've been realized or not */
+  gboolean realized;
+
+  /* connections */
+  GstEditorConnection *connection;
+  GstEditorConnection *ghostconnection;
+
+  /* visual stuff */
+  GnomeCanvasGroup *group;
+  GnomeCanvasItem *border,*title,*padbox;
+  gboolean sinkpad;                            // is this a sink pad?
+  gdouble x,y;                                 // location
+  gdouble width,height;                                // actual size
+  gdouble boxwidth,boxheight;                  // size of pad box
+  gboolean resize;                             // does it need resizing?
+  
+  /* interaction state */
+  gboolean dragging,resizing,moved;
+  gdouble dragx,dragy;
+
+  /* connection */
+//  GnomeCanvasItem *connection;               // can't use
+//GstEditorConnection
+};
+  
+struct _GstEditorPadClass {
+  GtkObjectClass parent_class;
+
+  void (*realize) (GstEditorPad *pad);
+};
+  
+GtkType gst_editor_pad_get_type();
+GstEditorPad *gst_editor_pad_new(GstEditorElement *parent,GstPad *pad,
+                                 const gchar *first_arg_name, ...);
+void gst_editor_pad_construct(GstEditorPad *element,
+                              GstEditorElement *parent,
+                              const gchar *first_arg_name,va_list args);
+void gst_editor_pad_repack(GstEditorPad *pad);
+
+
+
+#define GST_TYPE_EDITOR_CONNECTION \
+  (gst_editor_connection_get_type())
+#define GST_EDITOR_CONNECTION(obj) \
+  (GTK_CHECK_CAST((obj),GST_TYPE_EDITOR_CONNECTION,GstEditorConnection))
+#define GST_EDITOR_CONNECTION_CLASS(klass) \
+  (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_EDITOR_CONNECTION,GstEditorConnectionClass))
+#define GST_IS_EDITOR_CONNECTION(obj) \
+  (GTK_CHECK_TYPE((obj),GST_TYPE_EDITOR_CONNECTION))
+#define GST_IS_EDITOR_CONNECTION_CLASS(obj) \
+  (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_EDITOR_CONNECTION)))
+
+struct _GstEditorConnection {
+  GtkObject object;
+
+  /* our parent */
+  GstEditorElement *parent;
+
+  /* the two pads we're connecting */
+  GstEditorPad *frompad, *topad;
+  /* is this starting at a source (convenience) */
+  gboolean fromsrc;
+
+  /* toplevel canvas */
+  GstEditorCanvas *canvas;
+
+  /* whether we've been realized or not */
+  gboolean realized;
+
+  /* are we a ghosted connection? */
+  gboolean ghost;
+
+  /* visual stuff */
+  GnomeCanvasItem *line;
+  GnomeCanvasPoints *points;
+  gdouble x,y;                                  // terminating point
+  gboolean resize;                             // does it need resizing?
+};
+
+struct _GstEditorConnectionClass {
+  GtkObjectClass parent_class;
+  void (*realize) (GstEditorConnection *connection);
+};
+
+GtkType gst_editor_connection_get_type();
+void gst_editor_connection_resize(GstEditorConnection *connection);
+void gst_editor_connection_set_endpoint(GstEditorConnection *connection,
+                                        gdouble x,gdouble y);
+void gst_editor_connection_set_endpad(GstEditorConnection *connection,
+                                      GstEditorPad *pad);
+void gst_editor_connection_connect(GstEditorConnection *connection);
+
+
+#endif /* __GST_EDITOR_H__ */
diff --git a/editor/gsteditorbin.c b/editor/gsteditorbin.c
new file mode 100644 (file)
index 0000000..44ebeab
--- /dev/null
@@ -0,0 +1,266 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gnome.h>
+#include <gst/gst.h>
+
+#include "gsteditor.h"
+
+/* signals and args */
+enum {
+  LAST_SIGNAL
+};
+
+enum {
+  ARG_0,
+};
+
+static void gst_editor_bin_class_init(GstEditorBinClass *klass);
+static void gst_editor_bin_init(GstEditorBin *bin);
+static void gst_editor_bin_set_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_bin_get_arg(GtkObject *object,GtkArg *arg,guint id);
+
+static gint gst_editor_bin_event(GnomeCanvasItem *item,
+                                 GdkEvent *event,
+                                 GstEditorElement *element);
+static gint gst_editor_bin_button_event(GnomeCanvasItem *item,
+                                        GdkEvent *event,
+                                        GstEditorElement *element);
+
+static GstEditorElementClass *parent_class = NULL;
+
+GtkType gst_editor_bin_get_type(void) {
+  static GtkType bin_type = 0;
+
+  if (!bin_type) {
+    static const GtkTypeInfo bin_info = {
+      "GstEditorBin",
+      sizeof(GstEditorBin),
+      sizeof(GstEditorBinClass),
+      (GtkClassInitFunc)gst_editor_bin_class_init,
+      (GtkObjectInitFunc)gst_editor_bin_init,
+      NULL,
+      NULL,
+      (GtkClassInitFunc)NULL,
+    };
+    bin_type = gtk_type_unique(gst_editor_element_get_type(),&bin_info);
+  }
+  return bin_type;
+}
+
+static void gst_editor_bin_class_init(GstEditorBinClass *klass) {
+  GstEditorElementClass *element_class;
+
+  element_class = (GstEditorElementClass*)klass;
+
+  parent_class = gtk_type_class(gst_editor_element_get_type());
+
+  element_class->event = gst_editor_bin_event;
+  element_class->button_event = gst_editor_bin_button_event;
+}
+
+static void gst_editor_bin_init(GstEditorBin *bin) {
+  GstEditorElement *element = GST_EDITOR_ELEMENT(bin);
+
+  element->insidewidth = 200;
+  element->insideheight = 100;
+}
+
+GstEditorBin *gst_editor_bin_new(GstEditorBin *parent,GstBin *bin,
+                                 const gchar *first_arg_name,...) {
+  GstEditorBin *editorbin;
+  va_list args;
+
+  g_return_if_fail(parent != NULL);
+  g_return_if_fail(GST_IS_EDITOR_BIN(parent));
+  g_return_if_fail(bin != NULL);
+  g_return_if_fail(GST_IS_BIN(bin));
+
+  editorbin = GST_EDITOR_BIN(gtk_type_new(GST_TYPE_EDITOR_BIN));
+  GST_EDITOR_ELEMENT(editorbin)->element = GST_ELEMENT(bin);
+
+  va_start(args,first_arg_name);
+  gst_editor_element_construct(GST_EDITOR_ELEMENT(editorbin),parent,
+                               first_arg_name,args);
+  va_end(args);
+
+  return editorbin;
+}
+
+
+static gint gst_editor_bin_event(GnomeCanvasItem *item,
+                                 GdkEvent *event,
+                                 GstEditorElement *element) {
+  GstEditorBin *bin = GST_EDITOR_BIN(element);
+
+//  g_print("bin got %d event at %.2fx%.2f\n",event->type,
+//          event->button.x,event->button.y);
+
+  switch (event->type) {
+    case GDK_BUTTON_RELEASE:
+      if (bin->connecting) {
+//        g_print("bin got release event during drag\n");
+        gnome_canvas_item_ungrab(
+          GNOME_CANVAS_ITEM(element->group),
+          event->button.time);
+        if (bin->connection->topad)
+          gst_editor_connection_connect(bin->connection);
+        else {
+          bin->connection->frompad->connection = NULL;
+          g_list_remove(bin->connections,bin->connection);
+          gtk_object_destroy(GTK_OBJECT(bin->connection));
+          bin->connection = NULL;
+        }
+        bin->connecting = FALSE;
+//g_print("in bin, setting inchild for button release\n");
+        element->canvas->inchild = TRUE;
+        return TRUE;
+      }
+      break;
+    case GDK_MOTION_NOTIFY:
+      if (bin->connecting) {
+        gdouble x,y;
+        x = event->button.x;y = event->button.y;
+//        g_print("bin has motion during connection draw at %.2fx%.2f\n",
+//                x,y);
+        gst_editor_bin_connection_drag(bin,x,y);
+        return TRUE;
+      }
+      break;
+    default:
+      break;
+  }
+
+  if (GST_EDITOR_ELEMENT_CLASS(parent_class)->event)
+    return (*GST_EDITOR_ELEMENT_CLASS(parent_class)->event)(item,event,element);
+}
+
+
+static gint gst_editor_bin_button_event(GnomeCanvasItem *item,
+                                        GdkEvent *event,
+                                        GstEditorElement *element) {
+  GstEditorBin *bin = GST_EDITOR_BIN(element);
+  GstEditorElement *newelement;
+
+//  g_print("bin got button event\n");
+
+  if (event->type != GDK_BUTTON_RELEASE) return FALSE;
+
+  gnome_canvas_item_w2i(item,&event->button.x,&event->button.y);
+//  g_print("calling gst_editor_create_item(,%.2f,%.2f)\n",
+//          event->button.x,event->button.y);
+  newelement = gst_editor_create_item(bin,event->button.x,event->button.y);
+  if (newelement != NULL);
+    return TRUE;
+  return FALSE;
+}
+
+
+void gst_editor_bin_start_banding(GstEditorBin *bin,GstEditorPad *pad) {
+  GdkCursor *cursor;
+
+//  g_print("starting to band\n");
+
+  g_return_if_fail(GST_IS_EDITOR_PAD(pad));
+
+  bin->connection = gst_editor_connection_new(bin,pad);
+  bin->connections = g_list_prepend(bin->connections,bin->connection);
+  cursor = gdk_cursor_new(GDK_SB_RIGHT_ARROW);
+  gnome_canvas_item_grab(
+    GNOME_CANVAS_ITEM(GST_EDITOR_ELEMENT(bin)->group),
+    GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+    cursor,GDK_CURRENT_TIME);
+
+  bin->connecting = TRUE;
+}
+
+
+void gst_editor_bin_connection_drag(GstEditorBin *bin,
+                                    gdouble wx,gdouble wy) {
+  GstEditorElement *element;
+  gdouble bx,by;
+  GnomeCanvasItem *underitem, *under = NULL;
+  GstEditorPad *destpad;
+
+  element = GST_EDITOR_ELEMENT(bin);
+
+  bx = wx;by = wy;
+  gnome_canvas_item_w2i(GST_EDITOR_ELEMENT(bin)->group,&bx,&by);
+
+  // first see if we're on top of an interesting pad
+  underitem = gnome_canvas_get_item_at(
+    GST_EDITOR_ELEMENT(bin)->canvas->canvas,wx,wy);
+  if (underitem != NULL)
+    under = GST_EDTIOR_GET_OBJECT(underitem);
+  if ((under != NULL) && GST_IS_EDITOR_PAD(under)) {
+    destpad = GST_EDITOR_PAD(under);
+    if (destpad != bin->connection->frompad)
+      gst_editor_connection_set_endpad(bin->connection,destpad);
+  } else {
+    gst_editor_connection_set_endpoint(bin->connection,bx,by);
+  }
+
+/* This code is a nightmare, it'll be fixed in the next minor version
+  if (
+      ((bx < element->sinkwidth) ||
+       (bx > (element->width - element->srcwidth))) &&
+      ((by > element->titleheight) &&
+       (by < (element->height - element->stateheight)))
+     ) {
+    if (!bin->inpadregion) {
+      GstEditorPad *ghostpad;
+      g_print("I'd be creating a ghost pad right about now...\n");
+      gst_element_add_ghost_pad(
+        GST_EDITOR_ELEMENT(bin)->element,
+        bin->connection->frompad->pad);
+      ghostpad = gst_editor_element_add_pad(GST_EDITOR_ELEMENT(bin),
+        bin->connection->frompad->pad);
+      gst_editor_connection_set_endpad(bin->connection,ghostpad);
+      gtk_object_set(GTK_OBJECT(bin->connection),"ghost",TRUE,NULL);
+      bin->inpadregion = TRUE;
+    } else {
+      g_print("I'd be moving the ghost pad around now...\n");
+    }
+  } else {
+    if (bin->inpadregion) {
+      g_print("I'd be removing the ghost pad now...\n");
+      bin->inpadregion = FALSE;
+    }
+  }
+*/
+}
+
+
+void gst_editor_bin_add(GstEditorBin *bin,GstEditorElement *element) {
+  /* set the element's parent */
+  element->parent = bin;
+
+  /* set the canvas */
+  if (GST_IS_EDITOR_CANVAS(bin))
+    element->canvas = GST_EDITOR_CANVAS(bin);
+  else
+    element->canvas = GST_EDITOR_ELEMENT(bin)->canvas;
+
+  /* add element to list of bin's children */
+  bin->elements = g_list_prepend(bin->elements,element);
+
+  /* add the real element to the real bin */
+  gst_bin_add(GST_BIN(GST_EDITOR_ELEMENT(bin)->element),element->element);
+}
diff --git a/editor/gsteditorcanvas.c b/editor/gsteditorcanvas.c
new file mode 100644 (file)
index 0000000..76b4600
--- /dev/null
@@ -0,0 +1,251 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gnome.h>
+#include <gst/gst.h>
+
+#include "gsteditor.h"
+
+/* signals and args */
+enum {
+  LAST_SIGNAL
+};
+
+enum {
+  ARG_0,
+  ARG_CANVAS,
+};
+
+static void gst_editor_canvas_class_init(GstEditorCanvasClass *klass);
+static void gst_editor_canvas_init(GstEditorCanvas *editorcanvas);
+static void gst_editor_canvas_set_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_canvas_get_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_canvas_realize(GstEditorElement *element);
+
+
+static gint gst_editor_canvas_button_release(GtkWidget *widget,
+                                             GdkEvent *event,
+                                             GstEditorCanvas *canvas);
+static gint gst_editor_canvas_event(GnomeCanvasItem *item,
+                                    GdkEvent *event,
+                                    GstEditorElement *element);
+
+//gint gst_editor_canvas_verbose_event(GtkWidget *widget,GdkEvent *event);
+
+
+static GstEditorBinClass *parent_class = NULL;
+
+GtkType gst_editor_canvas_get_type(void) {
+  static GtkType editor_canvas_type = 0;
+
+  if (!editor_canvas_type) {
+    static const GtkTypeInfo editor_canvas_info = {
+      "GstEditorCanvas",
+      sizeof(GstEditorCanvas),
+      sizeof(GstEditorCanvasClass),
+      (GtkClassInitFunc)gst_editor_canvas_class_init,
+      (GtkObjectInitFunc)gst_editor_canvas_init,
+      NULL,
+      NULL,
+      (GtkClassInitFunc)NULL,
+    };
+    editor_canvas_type = gtk_type_unique(gst_editor_bin_get_type(),&editor_canvas_info);
+  }
+  return editor_canvas_type;
+}
+
+static void gst_editor_canvas_class_init(GstEditorCanvasClass *klass) {
+  GstEditorElementClass *element_class;
+
+  element_class = (GstEditorElementClass*)klass;
+
+  parent_class = gtk_type_class(gst_editor_bin_get_type());
+
+  gtk_object_add_arg_type("GstEditorCanvas::canvas",GTK_TYPE_POINTER,
+                          GTK_ARG_READABLE,ARG_CANVAS);
+
+  element_class->realize = gst_editor_canvas_realize;
+}
+
+static void gst_editor_canvas_init(GstEditorCanvas *editorcanvas) {
+}
+
+GstEditorCanvas *gst_editor_canvas_new(GstBin *bin,
+                                       const gchar *first_arg_name,...) {
+  GstEditorCanvas *editorcanvas;
+  va_list args;
+
+  g_return_if_fail(bin != NULL);
+  g_return_if_fail(GST_IS_BIN(bin));
+
+  editorcanvas = GST_EDITOR_CANVAS(gtk_type_new(GST_TYPE_EDITOR_CANVAS));
+  GST_EDITOR_ELEMENT(editorcanvas)->element = GST_ELEMENT(bin);
+
+  va_start(args,first_arg_name);
+  gst_editor_element_construct(GST_EDITOR_ELEMENT(editorcanvas),NULL,
+                               first_arg_name,args);
+  va_end(args);
+
+  return editorcanvas;
+}
+
+static void gst_editor_canvas_realize(GstEditorElement *element) {
+  GstEditorCanvas *canvas = GST_EDITOR_CANVAS(element);
+
+  canvas->canvas = GNOME_CANVAS(gnome_canvas_new());
+  element->canvas = canvas;
+  gtk_signal_connect(GTK_OBJECT(canvas->canvas),
+                     "event",
+                     GTK_SIGNAL_FUNC(gst_editor_canvas_event),
+                     canvas);
+  gtk_signal_connect_after(GTK_OBJECT(canvas->canvas),
+                     "button_release_event",
+                     GTK_SIGNAL_FUNC(gst_editor_canvas_button_release),
+                     canvas);
+  GST_EDITOR_SET_OBJECT(canvas->canvas,canvas);
+
+  element->group = gnome_canvas_root(canvas->canvas);
+
+  gnome_canvas_item_new(element->group,gnome_canvas_rect_get_type(),
+                        "width_units",1.0,"fill_color","white",
+                        "outline_color","black",
+                        "x1",-2.0,"y1",-2.0,"x2",2.0,"y2",2.0,NULL);
+}
+
+static void gst_editor_canvas_set_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditorCanvas *canvas;
+
+  canvas = GST_EDITOR_CANVAS(object);
+
+  switch (id) {
+    default:
+      g_warning("gsteditorcanvas: unknown arg!");
+      break;
+  }
+}
+
+static void gst_editor_canvas_get_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditorCanvas *canvas;
+
+  canvas = GST_EDITOR_CANVAS(object);
+
+  switch (id) {
+    case ARG_CANVAS:
+      GTK_VALUE_POINTER(*arg) = canvas->canvas;
+      break;
+    default:
+      arg->type = GTK_TYPE_INVALID;
+      break;
+  }
+}
+
+GtkWidget *gst_editor_canvas_get_canvas(GstEditorCanvas *canvas) {
+  return GTK_WIDGET(canvas->canvas);
+}
+
+
+static gint gst_editor_canvas_button_release(GtkWidget *widget,
+                                             GdkEvent *event,
+                                             GstEditorCanvas *canvas) {
+  GstEditorBin *bin = GST_EDITOR_BIN(canvas);
+  gdouble x,y;
+  GstEditorElement *element;
+
+//  g_print("canvas got button press at %.2fx%.2f\n",
+//          event->button.x,event->button.y);
+  if (event->type != GDK_BUTTON_RELEASE) return FALSE;
+
+  // if we're connecting a pair of objects in the canvas, fall through
+//  if (bin->connection) {
+//    g_print("we're in a connection, not handling\n");
+//    return FALSE;
+//  }
+
+  if (canvas->inchild) {
+//    g_print("inchild, not responding to button_release\n");
+    canvas->inchild = FALSE;
+    return FALSE;
+  }
+
+  gnome_canvas_window_to_world(GNOME_CANVAS(widget),
+                               event->button.x,event->button.y,&x,&y);
+//  g_print("calling gst_editor_create_item()\n");
+  if (element = gst_editor_create_item(GST_EDITOR_BIN(canvas),x,y))
+    return TRUE;
+  return FALSE;
+}
+
+
+/* FIXME: guerilla prototype... */
+void gst_editor_bin_connection_drag(GstEditorBin *bin,
+                                    gdouble wx,gdouble wy);
+
+static gint gst_editor_canvas_event(GnomeCanvasItem *item,
+                                    GdkEvent *event,
+                                    GstEditorElement *element) {
+//  if (GST_EDITOR_ELEMENT_CLASS(parent_class)->event)
+//    return (*GST_EDITOR_ELEMENT_CLASS(parent_class)->event)(
+//      element->group,event,element);
+
+  GstEditorBin *bin = GST_EDITOR_BIN(element);
+  GstEditorCanvas *canvas = GST_EDITOR_CANVAS(element);
+
+//  g_print("canvas got event %d at %.2fx%.2f\n",event->type,
+//          event->button.x,event->button.y);
+
+  switch (event->type) {
+    case GDK_BUTTON_RELEASE:
+      if (bin->connecting) {
+//        g_print("canvas got button release during drag\n");
+        gnome_canvas_item_ungrab(
+          GNOME_CANVAS_ITEM(element->group),
+          event->button.time);
+        if (bin->connection->topad)
+          gst_editor_connection_connect(bin->connection);
+        else
+          gtk_object_destroy(GTK_OBJECT(bin->connection));
+        bin->connecting = FALSE;
+//g_print("finished dragging connection on canvas, setting inchild\n");
+        element->canvas->inchild = TRUE;
+        return TRUE;
+      } else {
+//        g_print("got release, calling button_release()\n");
+//        gst_editor_canvas_button_release(canvas->canvas,event,canvas);
+        return FALSE;
+      }
+      break;
+    case GDK_MOTION_NOTIFY:
+      if (bin->connecting) {
+        gdouble x,y;
+        x = event->button.x;y = event->button.y;
+        gnome_canvas_window_to_world(canvas->canvas,
+                               event->button.x,event->button.y,&x,&y);
+//        g_print("canvas has motion during connection draw at
+//%.2fx%.2f\n",
+//                x,y);
+        gst_editor_bin_connection_drag(bin,x,y);
+        return TRUE;
+      }
+      break;
+    default:
+      break;
+  }
+  return FALSE;
+}
diff --git a/editor/gsteditorconnection.c b/editor/gsteditorconnection.c
new file mode 100644 (file)
index 0000000..dbe4147
--- /dev/null
@@ -0,0 +1,289 @@
+#include <gnome.h>
+
+#include <gst/gst.h>
+#include <gst/gstutils.h>
+
+#include "gsteditor.h"
+
+/* class functions */
+static void gst_editor_connection_class_init(GstEditorConnectionClass *klass);
+static void gst_editor_connection_init(GstEditorConnection *connection);
+static void gst_editor_connection_set_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_connection_get_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_connection_destroy(GtkObject *object);
+static void gst_editor_connection_realize(GstEditorConnection *connection);
+
+/* events fired by items within self */
+static gint gst_editor_connection_line_event(GnomeCanvasItem *item,
+                                             GdkEvent *event,
+                                             GstEditorConnection *connection);
+
+/* utility functions */
+
+
+enum {
+  ARG_0,
+  ARG_X,
+  ARG_Y,
+  ARG_FROMPAD,
+  ARG_TOPAD,
+  ARG_GHOST,
+};
+
+enum {
+  LAST_SIGNAL
+};
+
+static GtkObjectClass *parent_class;
+static guint gst_editor_connection_signals[LAST_SIGNAL] = { 0 };
+
+GtkType gst_editor_connection_get_type() {
+  static GtkType connection_type = 0;
+
+  if (!connection_type) {
+    static const GtkTypeInfo connection_info = {
+      "GstEditorConnection",
+      sizeof(GstEditorConnection),
+      sizeof(GstEditorConnectionClass),
+      (GtkClassInitFunc)gst_editor_connection_class_init,
+      (GtkObjectInitFunc)gst_editor_connection_init,
+      NULL,
+      NULL,
+      (GtkClassInitFunc)NULL,
+    };
+    connection_type = gtk_type_unique(gtk_object_get_type(),&connection_info);
+  }
+  return connection_type;
+}
+
+static void gst_editor_connection_class_init(GstEditorConnectionClass *klass) {
+  GtkObjectClass *object_class;
+
+  object_class = (GtkObjectClass*)klass;
+
+  parent_class = gtk_type_class(gnome_canvas_line_get_type());
+
+  gtk_object_add_arg_type("GstEditorConnection::x",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_X);
+  gtk_object_add_arg_type("GstEditorConnection::y",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_Y);
+  gtk_object_add_arg_type("GstEditorConnection::frompad",GTK_TYPE_POINTER,
+                          GTK_ARG_READWRITE,ARG_FROMPAD);
+  gtk_object_add_arg_type("GstEditorConnection::topad",GTK_TYPE_POINTER,
+                          GTK_ARG_READWRITE,ARG_TOPAD);
+  gtk_object_add_arg_type("GstEditorConnection::ghost",GTK_TYPE_BOOL,
+                          GTK_ARG_READWRITE,ARG_GHOST);
+
+  klass->realize = gst_editor_connection_realize;
+
+  object_class->set_arg = gst_editor_connection_set_arg;
+  object_class->get_arg = gst_editor_connection_get_arg;
+  object_class->destroy = gst_editor_connection_destroy;
+}
+
+static void gst_editor_connection_init(GstEditorConnection *connection) {
+  connection->points = gnome_canvas_points_new(2);
+}
+
+GstEditorConnection *gst_editor_connection_new(GstEditorBin *parent,
+                                               GstEditorPad *frompad) {
+  GstEditorConnection *connection;
+
+  g_return_if_fail(parent != NULL);
+  g_return_if_fail(GST_IS_EDITOR_BIN(parent));
+  g_return_if_fail(frompad != NULL);
+  g_return_if_fail(GST_IS_EDITOR_PAD(frompad));
+
+  connection = GST_EDITOR_CONNECTION(gtk_type_new(GST_TYPE_EDITOR_CONNECTION));
+  connection->frompad = frompad;
+  connection->frompad->connection = connection;
+  connection->fromsrc = connection->frompad->issrc;
+  connection->parent = parent;
+
+  gst_editor_connection_realize(connection);
+
+  return connection;
+}
+
+
+static void gst_editor_connection_set_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditorConnection *connection;
+
+  /* get the major types of this object */
+  connection = GST_EDITOR_CONNECTION(object);
+
+  switch (id) {
+    case ARG_X:
+      connection->x = GTK_VALUE_DOUBLE(*arg);
+      connection->resize = TRUE;
+      break;
+    case ARG_Y:
+      connection->y = GTK_VALUE_DOUBLE(*arg);
+      connection->resize = TRUE;
+      break;
+    case ARG_TOPAD:
+      if (connection->topad) {
+        if (connection->ghost)
+          connection->topad->ghostconnection = NULL;
+        else
+          connection->topad->connection = NULL;
+      }
+      connection->topad = GTK_VALUE_POINTER(*arg);
+      /* if this is the same type, refuse */
+      if (connection->topad &&
+          (connection->frompad->issrc == connection->topad->issrc))
+        connection->topad = NULL;
+      if (connection->topad) {
+        if (connection->ghost)
+          connection->topad->ghostconnection = connection;
+        else
+          connection->topad->connection = connection;
+      }
+      connection->resize = TRUE;
+      break;
+    case ARG_GHOST:
+      connection->ghost = GTK_VALUE_BOOL(*arg);
+      break;
+    default:
+      g_warning("gsteditorconnection: unknown arg!");
+      break;
+  }
+  gst_editor_connection_resize(connection);
+}
+
+static void gst_editor_connection_get_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditorConnection *connection;
+
+  /* get the major types of this object */
+  connection = GST_EDITOR_CONNECTION(object);
+
+  switch (id) {
+    case ARG_X:
+      GTK_VALUE_INT(*arg) = connection->x;
+      break;
+    case ARG_Y:
+      GTK_VALUE_INT(*arg) = connection->y;
+      break;
+    default:
+      arg->type = GTK_TYPE_INVALID;
+      break;
+  }
+}
+
+
+static void gst_editor_connection_realize(GstEditorConnection *connection) {
+  connection->points->coords[0] = 0.0;
+  connection->points->coords[1] = 0.0;
+  connection->points->coords[2] = 0.0;
+  connection->points->coords[3] = 0.0;
+  connection->line = gnome_canvas_item_new(
+    GST_EDITOR_ELEMENT(connection->parent)->group,
+    gnome_canvas_line_get_type(),
+    "points",connection->points,NULL);
+}
+
+static void gst_editor_connection_destroy(GtkObject *object) {
+  GstEditorConnection *connection = GST_EDITOR_CONNECTION(object);
+
+  gtk_object_destroy(GTK_OBJECT(connection->line));
+}
+
+void gst_editor_connection_resize(GstEditorConnection *connection) {
+  gdouble x1,y1,x2,y2;
+
+  if (connection->resize != TRUE) return;
+  connection->resize = FALSE;
+
+//  g_print("resizing connection, frompad is %p, topad is %p\n",
+//          connection->frompad,connection->topad);
+
+  /* calculate the new endpoints */
+  if (connection->topad == NULL) {
+    /* our base point is the source pad */
+    if (connection->fromsrc)
+      x1 = connection->frompad->x + connection->frompad->width;
+    else
+      x1 = connection->frompad->x;
+    y1 = connection->frompad->y + (connection->frompad->height / 2);
+    x2 = connection->x;
+    y2 = connection->y;
+    /* NOTE: coords are in the following state:
+       x1,y1: item coords relative to the element's group
+       x2,y2: item coords relative to the bin's group
+       This means translating the x1,y1 coords into world, then into bin.
+    */
+    gnome_canvas_item_i2w(connection->frompad->parent->group,&x1,&y1);
+    gnome_canvas_item_w2i(GST_EDITOR_ELEMENT_GROUP(connection->parent),
+                          &x1,&y1);
+  } else {
+    if (connection->fromsrc) {
+      x1 = connection->frompad->x + connection->frompad->width;
+      x2 = connection->topad->x;
+    } else {
+      x1 = connection->frompad->x;
+      x2 = connection->topad->x + connection->topad->width;
+    }
+    y1 = connection->frompad->y + (connection->frompad->height / 2);
+    y2 = connection->topad->y + (connection->topad->height / 2);
+    gnome_canvas_item_i2w(connection->frompad->parent->group,&x1,&y1);
+    gnome_canvas_item_w2i(GST_EDITOR_ELEMENT_GROUP(connection->parent),
+                          &x1,&y1);
+    gnome_canvas_item_i2w(connection->topad->parent->group,&x2,&y2);
+    gnome_canvas_item_w2i(GST_EDITOR_ELEMENT_GROUP(connection->parent),
+                          &x2,&y2);
+  }
+
+  connection->points->coords[0] = x1;
+  connection->points->coords[1] = y1;
+  connection->points->coords[2] = x2;
+  connection->points->coords[3] = y2;
+  gnome_canvas_item_set(connection->line,
+                        "points",connection->points,NULL);
+}
+
+void gst_editor_connection_set_endpoint(GstEditorConnection *connection,
+                                        gdouble x,gdouble y) {
+  connection->x = x;
+  connection->y = y;
+  if (connection->topad) {
+    if (connection->ghost)
+      connection->topad->ghostconnection = NULL;
+    else
+      connection->topad->connection = NULL;
+    connection->topad = NULL;
+  }
+  connection->resize = TRUE;
+  gst_editor_connection_resize(connection);
+}
+
+void gst_editor_connection_set_endpad(GstEditorConnection *connection,
+                                      GstEditorPad *pad) {
+  // first check for the trivial case
+  if (connection->topad == pad) return;
+
+  // now clean up if we've changed pads
+  if (connection->topad) {
+    if (connection->ghost)
+      connection->topad->ghostconnection = NULL;
+    else
+      connection->topad->connection = NULL;
+  }
+  connection->topad = pad;
+  if (connection->ghost)
+    connection->topad->ghostconnection = connection;
+  else
+    connection->topad->connection = connection;
+  connection->resize = TRUE;
+  gst_editor_connection_resize(connection);
+}
+
+void gst_editor_connection_connect(GstEditorConnection *connection) {
+  if (connection->ghost) {
+    g_print("uhhh.... Boo!\n");
+  } else {
+    if (connection->fromsrc)
+      gst_pad_connect(connection->frompad->pad,connection->topad->pad);
+    else
+      gst_pad_connect(connection->topad->pad,connection->frompad->pad);
+  }
+}
diff --git a/editor/gsteditorconnection.h b/editor/gsteditorconnection.h
new file mode 100644 (file)
index 0000000..011edd3
--- /dev/null
@@ -0,0 +1,61 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_EDITOR_CONNECTION_H__
+#define __GST_EDITOR_CONNECTION_H__
+
+#include <gnome.h>
+#include <gst.h>
+
+#define GST_TYPE_EDITOR_CONNECTION \
+  (gst_editor_connection_get_type())
+#define GST_EDITOR_CONNECTION(obj) \
+  (GTK_CHECK_CAST((obj),GST_TYPE_EDITOR_CONNECTION,GstEditorConnection))
+#define GST_EDITOR_CONNECTION_CLASS(klass) \
+  (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_EDITOR_CONNECTION,GstEditorConnection))
+#define GST_IS_EDITOR_CONNECTION(obj) \
+  (GTK_CHECK_TYPE((obj),GST_TYPE_EDITOR_CONNECTION))
+#define GST_IS_EDITOR_CONNECTION_CLASS(obj) \
+  (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_EDITOR_CONNECTION)))
+
+typedef struct _GstEditorConnection GstEditorConnection;
+typedef struct _GstEditorConnectionClass GstEditorConnectionClass;
+
+struct _GstEditorConnection {
+  GnomeCanvasLine line;
+
+  /* the two pads we're connecting */
+  GstEditorPad *pad1, *pad2;
+  gdouble fromsrc;
+
+  /* visual stuff */
+  gdouble x,y;                                 // terminating point
+  GnomeCanvasPoints *points;
+  gboolean created;                            // has it been created?
+  gboolean resized;                            // does it need resizing?
+};
+
+struct _GstEditorConnectionClass {
+  GnomeCanvasGroupClass parent_class;
+};
+
+GtkType gst_editor_connection_get_type();
+
+#endif /* __GST_EDITOR_CONNECTION_H__ */
diff --git a/editor/gsteditorcreate.c b/editor/gsteditorcreate.c
new file mode 100644 (file)
index 0000000..bfadd83
--- /dev/null
@@ -0,0 +1,57 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gnome.h>
+
+#include <gst/gst.h>
+
+#include "gsteditor.h"
+#include "gstelementselect.h"
+
+GstEditorElement *gst_editor_create_item(GstEditorBin *bin,
+                                         gdouble x,gdouble y) {
+  GstElementFactory *factory;
+  GstElement *element;
+  GstEditorElement *editorelement;
+  GtkType itemtype;
+
+  factory = element_select_dialog();
+  if (factory) {
+//    g_print("got factory \"%s\"\n",factory->name);
+    element = gst_elementfactory_create(factory,factory->name);
+    if (element) {
+      if (GST_IS_BIN(element)) {
+//        g_print("factory is a bin\n");
+        editorelement = GST_EDITOR_ELEMENT(gst_editor_bin_new(
+          GST_EDITOR_BIN(bin),GST_BIN(element),
+          "x",x,"y",y,"width",50.0,"height",20.0,NULL));
+      } else {
+//        g_print("factory is an element\n");
+        editorelement = gst_editor_element_new(bin,element,
+          "x",x,"y",y,"width",50.0,"height",20.0,NULL);
+      }
+//      g_print("created element \"%s\" at %.2fx%.2f\n",
+//              gst_object_get_name(GST_OBJECT(element)),
+//              x,y);
+      return editorelement;
+    }
+  }
+  return NULL;
+}
diff --git a/editor/gsteditorcreate.h b/editor/gsteditorcreate.h
new file mode 100644 (file)
index 0000000..448de91
--- /dev/null
@@ -0,0 +1,24 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 "gsteditor.h"
+
+GstEditorElement *gst_editor_create_item(GstEditorBin *bin,
+                                         gdouble x,gdouble y);
diff --git a/editor/gsteditorelement.c b/editor/gsteditorelement.c
new file mode 100644 (file)
index 0000000..d13c40f
--- /dev/null
@@ -0,0 +1,927 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gnome.h>
+
+#include <gst/gst.h>
+#include <gst/gstutils.h>
+
+#include "gsteditor.h"
+
+/* class functions */
+static void gst_editor_element_class_init(GstEditorElementClass *klass);
+static void gst_editor_element_init(GstEditorElement *element);
+static void gst_editor_element_set_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_element_get_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_element_realize(GstEditorElement *element);
+static gint gst_editor_element_event(GnomeCanvasItem *item,
+                                     GdkEvent *event,
+                                     GstEditorElement *element);
+
+/* events fired by items within self */
+static gint gst_editor_element_resizebox_event(GnomeCanvasItem *item,
+                                               GdkEvent *event,
+                                               GstEditorElement *element);
+static gint gst_editor_element_group_event(GnomeCanvasItem *item,
+                                           GdkEvent *event,
+                                           GstEditorElement *element);
+static gint gst_editor_element_state_event(GnomeCanvasItem *item,
+                                           GdkEvent *event,
+                                           gpointer data);
+
+/* external events (from GstElement) */
+static void gst_editor_element_state_change(GstElement *element,
+                                            gint state,
+                                            GstEditorElement *editorelement);
+
+/* utility functions */
+static void gst_editor_element_resize(GstEditorElement *element);
+static void gst_editor_element_set_state(GstEditorElement *element,
+                                         gint id,gboolean set);
+static void gst_editor_element_sync_state(GstEditorElement *element);
+static void gst_editor_element_move(GstEditorElement *element,
+                                    gdouble dx,gdouble dy);
+
+
+static gchar *_gst_editor_element_states[] = { "C","R","D","P" };
+
+
+enum {
+  ARG_0,
+  ARG_X,
+  ARG_Y,
+  ARG_WIDTH,  
+  ARG_HEIGHT,
+  ARG_X1,
+  ARG_Y1,
+  ARG_X2,
+  ARG_Y2,
+  ARG_ELEMENT,
+};
+
+enum {
+  LAST_SIGNAL
+};
+
+static GtkObjectClass *parent_class;
+static guint gst_editor_element_signals[LAST_SIGNAL] = { 0 };
+
+GtkType gst_editor_element_get_type() {
+  static GtkType element_type = 0;
+
+  if (!element_type) {
+    static const GtkTypeInfo element_info = {
+      "GstEditorElement",
+      sizeof(GstEditorElement),
+      sizeof(GstEditorElementClass),
+      (GtkClassInitFunc)gst_editor_element_class_init,
+      (GtkObjectInitFunc)gst_editor_element_init,
+      NULL,
+      NULL,
+      (GtkClassInitFunc)NULL,
+    };
+    element_type = gtk_type_unique(gtk_object_get_type(),&element_info);
+  }
+  return element_type;
+}
+
+static void gst_editor_element_class_init(GstEditorElementClass *klass) {
+  GtkObjectClass *object_class;
+
+  object_class = (GtkObjectClass*)klass;
+
+  parent_class = gtk_type_class(gtk_object_get_type());
+
+  gtk_object_add_arg_type("GstEditorElement::x",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE|GTK_ARG_CONSTRUCT_ONLY,
+                          ARG_X);
+  gtk_object_add_arg_type("GstEditorElement::y",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE|GTK_ARG_CONSTRUCT_ONLY,
+                          ARG_Y);
+  gtk_object_add_arg_type("GstEditorElement::width",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE|GTK_ARG_CONSTRUCT_ONLY,
+                          ARG_WIDTH);
+  gtk_object_add_arg_type("GstEditorElement::height",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE|GTK_ARG_CONSTRUCT_ONLY,
+                          ARG_HEIGHT);
+  gtk_object_add_arg_type("GstEditorElement::x1",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_X1);
+  gtk_object_add_arg_type("GstEditorElement::y1",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_Y1);
+  gtk_object_add_arg_type("GstEditorElement::x2",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_X2);
+  gtk_object_add_arg_type("GstEditorElement::y2",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_Y2);
+  gtk_object_add_arg_type("GstEditorElement::element",GTK_TYPE_POINTER,
+                          GTK_ARG_READABLE,ARG_ELEMENT);
+
+  klass->realize = gst_editor_element_realize;
+  klass->event = gst_editor_element_event;
+
+  object_class->set_arg = gst_editor_element_set_arg;
+  object_class->get_arg = gst_editor_element_get_arg;
+}
+
+static void gst_editor_element_init(GstEditorElement *element) {
+}
+
+GstEditorElement *gst_editor_element_new(GstEditorBin *parent,
+                                         GstElement *element,
+                                         const gchar *first_arg_name, ...) {
+  GstEditorElement *editorelement;
+  va_list args;
+
+  g_return_if_fail(parent != NULL);
+  g_return_if_fail(GST_IS_EDITOR_BIN(parent));
+  g_return_if_fail(element != NULL);
+  g_return_if_fail(GST_IS_ELEMENT(element));
+
+  editorelement = GST_EDITOR_ELEMENT(gtk_type_new(GST_TYPE_EDITOR_ELEMENT));
+  editorelement->element = element;
+
+  va_start(args,first_arg_name);
+  gst_editor_element_construct(editorelement,parent,first_arg_name,args);
+  va_end(args);
+
+  return editorelement;
+}
+
+void gst_editor_element_construct(GstEditorElement *element,
+                                  GstEditorBin *parent,
+                                  const gchar *first_arg_name,
+                                  va_list args) {
+  GtkObject *obj = GTK_OBJECT(element);
+  GSList *arg_list = NULL, *info_list = NULL;
+  gchar *error;
+  GstEditorElementClass *elementclass;
+
+//  g_print("in gst_editor_element_construct()\n");
+
+  error = gtk_object_args_collect(GTK_OBJECT_TYPE(obj),&arg_list,
+                                  &info_list,first_arg_name,args);
+  if (error) {
+    g_warning("gst_editor_element_construct(): %s",error);
+    g_free(error);
+  } else {
+    GSList *arg,*info;
+//    g_print("setting all the arguments on the element\n");
+    for (arg=arg_list,info=info_list;arg;arg=arg->next,info=info->next)
+      gtk_object_arg_set(obj,arg->data,info->data);
+    gtk_args_collect_cleanup(arg_list,info_list);
+  }
+
+  if (parent)
+    gst_editor_bin_add(parent,element);
+  else if (!GST_IS_EDITOR_BIN(element))
+    g_warning("floating element...\n");
+
+  elementclass = GST_EDITOR_ELEMENT_CLASS(GTK_OBJECT(element)->klass);
+  if (elementclass->realize)
+    (elementclass->realize)(element);
+}
+
+static void gst_editor_element_set_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditorElement *element;
+  gdouble dx,dy,newwidth,newheight;
+
+  /* get the major types of this object */
+  element = GST_EDITOR_ELEMENT(object);
+
+  switch (id) {
+    case ARG_X:
+      element->x = GTK_VALUE_DOUBLE(*arg);
+      break;
+    case ARG_Y:
+      element->y = GTK_VALUE_DOUBLE(*arg);
+      break;
+    case ARG_WIDTH:
+      element->width = GTK_VALUE_DOUBLE(*arg);
+      element->resize = TRUE;
+      break;
+    case ARG_HEIGHT:
+      element->height = GTK_VALUE_DOUBLE(*arg);
+      element->resize = TRUE;
+      break;
+    case ARG_X1:
+      element->x = GTK_VALUE_DOUBLE(*arg);
+      element->resize = TRUE;
+      break;
+    case ARG_Y1:
+      element->y = GTK_VALUE_DOUBLE(*arg);
+      element->resize = TRUE;
+      break;
+    case ARG_X2:
+      // make sure it's big enough, grow if not
+      element->width = MAX(GTK_VALUE_DOUBLE(*arg),element->minwidth);
+      element->resize = TRUE;
+      break;
+    case ARG_Y2:
+      // make sure it's big enough, grow if not
+      element->height = MAX(GTK_VALUE_DOUBLE(*arg),element->minheight);
+      element->resize = TRUE;
+      break;
+    default:
+      g_warning("gsteditorelement: unknown arg!");
+      break;
+  }
+}
+
+static void gst_editor_element_get_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditorElement *element;
+
+  /* get the major types of this object */
+  element = GST_EDITOR_ELEMENT(object);
+
+  switch (id) {
+    case ARG_X:
+      GTK_VALUE_INT(*arg) = element->x + (element->width / 2.0);
+      break;
+    case ARG_Y:
+      GTK_VALUE_INT(*arg) = element->y + (element->height / 2.0);
+      break;
+    case ARG_WIDTH:
+      GTK_VALUE_INT(*arg) = element->width;
+      break;
+    case ARG_HEIGHT:
+      GTK_VALUE_INT(*arg) = element->height;
+      break;
+    case ARG_X1:
+      GTK_VALUE_INT(*arg) = element->x;
+      break;
+    case ARG_Y1:
+      GTK_VALUE_INT(*arg) = element->y;
+      break;
+    case ARG_X2:
+      GTK_VALUE_INT(*arg) = element->x + element->width;
+      break;
+    case ARG_Y2:
+      GTK_VALUE_INT(*arg) = element->y + element->height;
+      break;
+    case ARG_ELEMENT:
+      GTK_VALUE_POINTER(*arg) = element->element;
+      break;
+    default:
+      arg->type = GTK_TYPE_INVALID;
+      break;
+  }
+}
+
+static void gst_editor_element_realize(GstEditorElement *element) {
+  GnomeCanvasGroup *parentgroup;
+  gint i;
+  gdouble x1,y1,x2,y2;
+  GList *pads;
+  GstPad *pad;
+
+//  g_print("realizing editor element %p\n",element);
+
+  /* we have to have a parent by this point */
+  g_return_if_fail(element->parent != NULL);
+
+  // set the state signal of the actual element
+  gtk_signal_connect(GTK_OBJECT(element->element),"state_change",
+                     GTK_SIGNAL_FUNC(gst_editor_element_state_change),
+                     element);
+
+  // create the bounds if we haven't had them set
+//  g_print("centering element at %.2fx%.2f (%.2fx%.2f)\n",
+//          element->x,element->y,element->width,element->height);
+
+  /* create the group holding all the stuff for this element */
+  parentgroup = GST_EDITOR_ELEMENT(element->parent)->group;
+  element->group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(parentgroup,
+    gnome_canvas_group_get_type(),
+    "x",element->x - (element->width / 2.0),
+    "y",element->y - (element->height / 2.0),NULL));
+//  g_print("origin of group is %.2fx%.2f\n",
+//          element->x - (element->width / 2.0),
+//          element->y - (element->height / 2.0));
+  g_return_if_fail(element->group != NULL);
+  GST_EDITOR_SET_OBJECT(element->group,element);
+  gtk_signal_connect(GTK_OBJECT(element->group),"event",
+    GTK_SIGNAL_FUNC(gst_editor_element_group_event),element);
+
+  // calculate the inter-group coords (x1,y1,x2,y2 are convenience vars)
+  x1 = 0.0;y1 = 0.0;
+  x2 = element->width;y2 = element->height;
+
+  /* create bordering box */
+  element->border = gnome_canvas_item_new(element->group,
+    gnome_canvas_rect_get_type(),
+    "width_units",2.0,"fill_color","white","outline_color","black", 
+    "x1",x1,"y1",y1,"x2",x2,"y2",y2,NULL);
+  g_return_if_fail(element->border != NULL);
+  GST_EDITOR_SET_OBJECT(element->border,element);
+
+  /* create resizing box */
+  element->resizebox = gnome_canvas_item_new(element->group,
+    gnome_canvas_rect_get_type(),
+    "width_units",1.0,"fill_color","white","outline_color","black",
+    "x1",x2-4.0,"y1",y2-4.0,"x2",x2,"y2",y2,NULL);
+  g_return_if_fail(element->resizebox != NULL);
+  GST_EDITOR_SET_OBJECT(element->resizebox,element);
+  gtk_signal_connect(GTK_OBJECT(element->resizebox),"event",
+    GTK_SIGNAL_FUNC(gst_editor_element_resizebox_event),element);
+
+  /* create the title */
+  element->title = gnome_canvas_item_new(element->group,
+    gnome_canvas_text_get_type(),
+    "text",gst_element_get_name(GST_OBJECT(element->element)),
+    "x",x1+1.0,"y",y1+1.0,"anchor",GTK_ANCHOR_NORTH_WEST,
+    "font_gdk",gtk_widget_get_default_style()->font,
+    NULL);
+  g_return_if_fail(element->title != NULL);
+  GST_EDITOR_SET_OBJECT(element->title,element);
+
+  /* create the state boxen */
+  for (i=0;i<4;i++) {
+    element->statebox[i] = gnome_canvas_item_new(element->group,
+      gnome_canvas_rect_get_type(),
+      "width_units",1.0,"fill_color","white","outline_color","black",
+      "x1",0.0,"y1",0.0,"x2",0.0,"y2",0.0,
+      NULL);
+    g_return_if_fail(element->statebox[i] != NULL);
+    GST_EDITOR_SET_OBJECT(element->statebox[i],element);
+    gtk_signal_connect(GTK_OBJECT(element->statebox[i]),"event",
+                       GTK_SIGNAL_FUNC(gst_editor_element_state_event),
+                       GINT_TO_POINTER(i));
+    element->statetext[i] = gnome_canvas_item_new(element->group,
+      gnome_canvas_text_get_type(),
+      "text",_gst_editor_element_states[i],
+      "x",0.0,"y",0.0,"anchor",GTK_ANCHOR_NORTH_WEST,
+      "font","-*-*-*-*-*-*-6-*-*-*-*-*-*-*",
+      NULL);
+    g_return_if_fail(element->statetext[i] != NULL);
+    GST_EDITOR_SET_OBJECT(element->statetext[i],element);
+    gtk_signal_connect(GTK_OBJECT(element->statetext[i]),"event",
+                       GTK_SIGNAL_FUNC(gst_editor_element_state_event),
+                       GINT_TO_POINTER(i));
+  }
+  /* and the play box (FIXME: should be icons, not text */
+  element->playbox = gnome_canvas_item_new(element->group,
+    gnome_canvas_rect_get_type(),
+    "width_units",1.0,"fill_color","white","outline_color","black",
+    "x1",0.0,"y1",0.0,"x2",0.0,"y2",0.0,
+    NULL);
+  g_return_if_fail(element->playbox != NULL);
+  GST_EDITOR_SET_OBJECT(element->playbox,element);
+  gtk_signal_connect(GTK_OBJECT(element->playbox),"event",
+                     GTK_SIGNAL_FUNC(gst_editor_element_state_event),
+                     GINT_TO_POINTER(4));
+  element->playtext = gnome_canvas_item_new(element->group,
+    gnome_canvas_text_get_type(),
+    "text","P",
+    "x",0.0,"y",0.0,"anchor",GTK_ANCHOR_NORTH_WEST,
+    "font","-*-*-*-*-*-*-6-*-*-*-*-*-*-*",
+    NULL);
+  g_return_if_fail(element->playtext != NULL);
+  GST_EDITOR_SET_OBJECT(element->playtext,element);
+  gtk_signal_connect(GTK_OBJECT(element->playtext),"event",
+                     GTK_SIGNAL_FUNC(gst_editor_element_state_event),
+                     GINT_TO_POINTER(4));
+
+  // get all the pads
+  pads = gst_element_get_pad_list(element->element);
+  while (pads) {
+    pad = GST_PAD(pads->data);
+    gst_editor_element_add_pad(element,pad);
+    pads = g_list_next(pads);
+  }
+
+  element->realized = TRUE;
+
+  // force a resize
+  element->resize = TRUE;
+  gst_editor_element_resize(element);
+
+  // recenter things on the supposed center
+//  g_print("recentering element at %.2fx%.2f (%.2fx%.2f)\n",
+//          element->x,element->y,element->width,element->height);
+  element->x -= (element->width / 2.0);
+  element->y -= (element->height / 2.0);
+  gnome_canvas_item_set(GNOME_CANVAS_ITEM(element->group),
+    "x",element->x,"y",element->y,NULL);
+//  g_print("origin of group is %.2fx%.2f\n",element->x,element->y);
+
+  gst_editor_element_repack(element);
+}
+
+
+static void gst_editor_element_resize(GstEditorElement *element) {
+  gdouble itemwidth,itemheight;
+  gdouble groupwidth,groupheight;
+  GList *pads;
+  GstEditorPad *editorpad;
+  gint i;
+
+  if (element->resize != TRUE) return;
+  element->resize = FALSE;
+
+//  g_print("resizing element\n");
+
+  element->minwidth = element->insidewidth;
+  element->minheight = element->insideheight;
+
+  // get the text size and add it into minsize
+  g_return_if_fail(element->title != NULL);
+  itemwidth = gst_util_get_double_arg(GTK_OBJECT(element->title),
+                                      "text_width") + 2.0;
+  itemheight = gst_util_get_double_arg(GTK_OBJECT(element->title),
+                                       "text_height") + 2.0;
+  element->titlewidth = itemwidth;
+  element->titleheight = itemheight;
+  element->minwidth = MAX(element->minwidth,itemwidth);
+  element->minheight += itemheight;
+
+  // now do the bottom bar
+  // find the biggest of the state chars
+  element->statewidth = 0.0;element->stateheight = 0.0;
+  for (i=0;i<4;i++) {
+    g_return_if_fail(element->statetext[i] != NULL);
+    itemwidth = gst_util_get_double_arg(GTK_OBJECT(element->statetext[i]),
+                                        "text_width") - 2.0;
+    itemwidth = gst_util_get_double_arg(GTK_OBJECT(element->statetext[i]),
+                                        "text_height");
+    element->statewidth = MAX(element->statewidth,itemwidth);
+    element->stateheight = MAX(element->stateheight,itemheight);
+  }
+  // calculate the size of the primary group
+  groupwidth = element->statewidth * 5;        // 4 states plus playstate
+  groupheight = element->stateheight;
+  // add in the resize box
+  groupwidth += 7.0;           // 2.0 for buffer, 5.0 for actual size
+  groupheight = MAX(groupheight,5.0);
+  // update the minsize
+  element->minwidth = MAX(element->minwidth,groupwidth);
+  element->minheight += groupheight;
+
+  // now go and try to calculate necessary space for the pads
+  element->sinkwidth = 10.0;element->sinkheight = 0.0;element->sinks = 0;
+  pads = element->sinkpads;
+  while (pads) {
+    editorpad = GST_EDITOR_PAD(pads->data);
+    element->sinkwidth = MAX(element->sinkwidth,editorpad->width);
+    element->sinkheight = MAX(element->sinkheight,editorpad->height);
+    element->sinks++;
+    pads = g_list_next(pads);
+  }
+  element->srcwidth = 10.0;element->srcheight = 0.0;element->srcs = 0;
+  pads = element->srcpads;
+  while (pads) {
+    editorpad = GST_EDITOR_PAD(pads->data);
+    element->srcwidth = MAX(element->srcwidth,editorpad->width);
+    element->srcheight = MAX(element->srcheight,editorpad->height);
+    element->srcs++;
+    pads = g_list_next(pads);
+  }
+  // add in the needed space
+  element->minheight += MAX((element->sinkheight*element->sinks),
+                            (element->srcheight*element->srcs)) + 4.0;
+  element->minwidth = MAX(element->minwidth,
+                          ((element->sinkwidth*element->sinks) +
+                           (element->srcwidth*element->srcs) + 4.0));
+//  g_print("have %d sinks (%.2fx%.2f) and %d srcs (%.2fx%.2f)\n",
+//          element->sinks,element->sinkwidth,element->sinkheight,
+//          element->srcs,element->srcwidth,element->srcheight);
+
+  // grow the element to hold all the stuff
+//  g_print("minsize is %.2fx%.2f,
+//",element->minwidth,element->minheight);
+//  g_print("size was %.2fx%.2f, ",element->width,element->height);
+  element->width = MAX(element->width,element->minwidth);
+  element->height = MAX(element->height,element->minheight);
+//  g_print("is now %.2fx%.2f\n",element->width,element->height);
+}
+
+void gst_editor_element_repack(GstEditorElement *element) {
+  GList *pads;
+  GstPad *pad;
+  GstEditorPad *editorpad;
+  gdouble sinkwidth,sinkheight;
+  gint sinks;
+  gdouble srcwidth,srcheight;
+  gint srcs;
+  gdouble x1,y1,x2,y2;
+  gint i;
+
+  if (!element->realized) return;
+
+  gst_editor_element_resize(element);
+
+  // still use x1,y1,x2,y2 so we can change around later
+  x1 = 0.0;y1 = 0.0;
+  x2 = element->width;y2 = element->height;
+
+//  g_print("repacking element at %.2fx%.2f + %.2fx%.2f\n",
+//          element->x,element->y,x2,y2);
+
+  // move the element group to match
+  gnome_canvas_item_set(GNOME_CANVAS_ITEM(element->group),
+                        "x",element->x,"y",element->y,NULL);
+
+  // start by resizing the bordering box
+  g_return_if_fail(element->border != NULL);
+  gtk_object_set(GTK_OBJECT(element->border),
+                 "x1",x1,"y1",y1,"x2",x2,"y2",y2,NULL);
+
+  // then move the text to the new top left
+  g_return_if_fail(element->title != NULL);
+  gtk_object_set(GTK_OBJECT(element->title),
+                 "x",x1+1.0,"y",y1+1.0,
+                 "anchor",GTK_ANCHOR_NORTH_WEST,
+                 NULL);
+
+  // and move the resize box
+  g_return_if_fail(element->resizebox != NULL);
+  gtk_object_set(GTK_OBJECT(element->resizebox),
+                 "x1",x2-5.0,"y1",y2-5.0,"x2",x2,"y2",y2,NULL);
+
+  // now place the state boxes
+  for (i=0;i<4;i++) {
+    g_return_if_fail(element->statebox[i] != NULL);
+    gtk_object_set(GTK_OBJECT(element->statebox[i]),
+                   "x1",x1+(element->statewidth*i),
+                   "y1",y2-element->stateheight,
+                   "x2",x1+(element->statewidth*(i+1)),"y2",y2,NULL);
+  g_return_if_fail(element->statetext[i] != NULL);
+    gtk_object_set(GTK_OBJECT(element->statetext[i]),
+                   "x",x1+(element->statewidth*i)+2.0,
+                   "y",y2-element->stateheight+1.0,
+                   "anchor",GTK_ANCHOR_NORTH_WEST,NULL);
+  }
+  // and the playstate box
+  g_return_if_fail(element->playbox != NULL);
+  gtk_object_set(GTK_OBJECT(element->playbox),
+                 "x1",x1+(element->statewidth*4),
+                 "y1",y2-element->stateheight,
+                 "x2",x1+(element->statewidth*5),"y2",y2,NULL);
+  g_return_if_fail(element->playtext != NULL);
+  gtk_object_set(GTK_OBJECT(element->playtext),
+                 "x",x1+(element->statewidth*4)+2.0,
+                 "y",y2-element->stateheight+1.0,
+                 "anchor",GTK_ANCHOR_NORTH_WEST,NULL);
+
+  // now we try to place all the pads
+  sinks = element->sinks;
+  pads = element->sinkpads;
+  while (pads) {
+    editorpad = GST_EDITOR_PAD(pads->data);
+    gtk_object_set(GTK_OBJECT(editorpad),
+                   "x",x1,
+                   "y",y2 - 2.0 - element->stateheight - 
+                       (element->sinkheight * sinks),
+                   NULL);
+    gst_editor_pad_repack(editorpad);
+    sinks--;
+    pads = g_list_next(pads);
+  }
+
+  srcs = element->srcs;
+  pads = element->srcpads;
+  while (pads) {
+    editorpad = GST_EDITOR_PAD(pads->data);
+    gtk_object_set(GTK_OBJECT(editorpad),
+                   "x",x2 - element->srcwidth,
+                   "y",y2 - 2.0 - element->stateheight -
+                       (element->srcheight * srcs),
+                   NULL);
+    gst_editor_pad_repack(editorpad);
+    srcs--;
+    pads = g_list_next(pads);
+  }
+
+//  g_print("done resizing element\n");
+}
+
+
+GstEditorPad *gst_editor_element_add_pad(GstEditorElement *element,
+                                         GstPad *pad) {
+  GstEditorPad *editorpad;
+
+  editorpad = gst_editor_pad_new(element,pad,NULL);
+  if (pad->direction == GST_PAD_SINK) {
+    element->sinkpads = g_list_prepend(element->sinkpads,editorpad);
+    element->sinks++;
+//    g_print("added 'new' pad to sink list\n");
+  } else if (pad->direction == GST_PAD_SRC) {
+    element->srcpads = g_list_prepend(element->srcpads,editorpad);
+    element->srcs++;
+//    g_print("added 'new' pad to src list\n");
+  } else
+    g_print("HUH?!?  Don't know which direction this pad is...\n");
+
+  element->padlistchange = TRUE;
+  gst_editor_element_repack(element);
+  return editorpad;
+}
+
+
+static gint gst_editor_element_group_event(GnomeCanvasItem *item,
+                                           GdkEvent *event,
+                                           GstEditorElement *element) {
+//  g_print("in group_event, type %d\n",event->type);
+  if (GST_EDITOR_ELEMENT_CLASS(GTK_OBJECT(element)->klass)->event)
+    return (GST_EDITOR_ELEMENT_CLASS(GTK_OBJECT(element)->klass)->event)(
+      item,event,element);
+  return FALSE;
+}
+
+
+static gint gst_editor_element_event(GnomeCanvasItem *item,GdkEvent *event,
+                                     GstEditorElement *element) {
+  gdouble item_x,item_y,dx,dy;
+  GdkCursor *fleur;
+
+//  g_print("element in event, type %d\n",event->type);
+
+  switch(event->type) {
+    case GDK_ENTER_NOTIFY:
+      break;
+    case GDK_LEAVE_NOTIFY:
+      break;
+    case GDK_BUTTON_PRESS:
+      // dragxy coords are world coords of button press
+      element->dragx = event->button.x;
+      element->dragy = event->button.y;
+      // set some flags
+      element->dragging = TRUE;
+      element->moved = FALSE;
+      fleur = gdk_cursor_new(GDK_FLEUR);
+      gnome_canvas_item_grab(item,
+                             GDK_POINTER_MOTION_MASK |
+//                             GDK_ENTER_NOTIFY_MASK |
+//                             GDK_LEAVE_NOTIFY_MASK |
+                             GDK_BUTTON_RELEASE_MASK,
+                             fleur,event->button.time);
+      return TRUE;
+      break;
+    case GDK_MOTION_NOTIFY:
+      if (element->dragging) {
+        dx = event->button.x - element->dragx;
+        dy = event->button.y - element->dragy;
+        gst_editor_element_move(element,dx,dy);
+        element->dragx = event->button.x;
+        element->dragy = event->button.y;
+        element->moved = TRUE;
+      }
+      return TRUE;
+      break;
+    case GDK_BUTTON_RELEASE:
+      if (element->dragging) {
+        element->dragging = FALSE;
+        gnome_canvas_item_ungrab(item,event->button.time);
+      }
+      if (!element->moved) {
+        GstEditorElementClass *elementclass;
+        elementclass = GST_EDITOR_ELEMENT_CLASS(GTK_OBJECT(element)->klass);
+        if (elementclass->button_event)
+          (elementclass->button_event)(item,event,element);
+      }
+//g_print("in element group_event, setting inchild");
+      element->canvas->inchild = TRUE;
+      return TRUE;
+      break;
+
+    default:
+      break;
+  }
+  return FALSE;
+}
+
+
+static gint gst_editor_element_resizebox_event(GnomeCanvasItem *item,
+                                               GdkEvent *event,
+                                               GstEditorElement *element) {
+  GdkCursor *bottomright;
+  gdouble item_x,item_y;
+
+//  g_print("in resizebox_event...\n");
+
+  // calculate coords relative to the group, not the box
+  item_x = event->button.x;
+  item_y = event->button.y;
+  gnome_canvas_item_w2i(item->parent,&item_x,&item_y);
+
+  switch(event->type) {
+    case GDK_ENTER_NOTIFY:
+      break;
+    case GDK_LEAVE_NOTIFY:
+      element->hesitating = FALSE;
+      break;
+    case GDK_BUTTON_PRESS:
+      element->dragx = event->button.x;
+      element->dragy = event->button.y;
+      element->resizing = TRUE;
+      element->hesitating = TRUE;
+      bottomright = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER);
+      gnome_canvas_item_grab(item,
+                             GDK_POINTER_MOTION_MASK |
+                             GDK_ENTER_NOTIFY_MASK |
+                             GDK_LEAVE_NOTIFY_MASK |
+                             GDK_BUTTON_RELEASE_MASK,
+                             bottomright,event->button.time);
+      return TRUE;
+      break;
+    case GDK_MOTION_NOTIFY:
+      if (element->resizing) {
+        // doing a set because the code is in the arg set code
+//        g_print("resizing to x2,y2 of %.2f,%.2f\n",item_x,item_y);
+        gtk_object_set(GTK_OBJECT(element),"x2",item_x,"y2",item_y,NULL);
+        element->resize = TRUE;
+        gst_editor_element_repack(element);
+        return TRUE;
+      }
+      break;
+    case GDK_BUTTON_RELEASE:
+      if (element->resizing) {
+        element->resizing = FALSE;
+        gnome_canvas_item_ungrab(item,event->button.time);
+//g_print("in element resizebox_event, setting inchild");
+        element->canvas->inchild = TRUE;
+        return TRUE;
+      }
+      break;
+    default:
+      break;
+  }
+  return FALSE;
+}
+
+
+static gint gst_editor_element_state_event(GnomeCanvasItem *item,
+                                           GdkEvent *event,
+                                           gpointer data) {
+  GstEditorElement *element;
+  gint id = GPOINTER_TO_INT(data);
+  GdkCursor *uparrow;
+
+  element = GST_EDTIOR_GET_OBJECT(item);
+
+  switch (event->type) {
+    case GDK_ENTER_NOTIFY:
+      uparrow = gdk_cursor_new(GDK_SB_UP_ARROW);
+      gnome_canvas_item_grab(item,
+                             GDK_POINTER_MOTION_MASK |
+                             GDK_BUTTON_RELEASE_MASK |
+                             GDK_LEAVE_NOTIFY_MASK,
+                             uparrow,event->button.time);
+      /* NOTE: when grabbing canvas item, always get pointer_motion,
+         this will allow you to actually get all the other synth events */
+      break;
+    case GDK_LEAVE_NOTIFY:
+      gnome_canvas_item_ungrab(item,event->button.time);
+      break;
+    case GDK_BUTTON_PRESS:
+      return TRUE;
+      break;
+    case GDK_BUTTON_RELEASE:
+      if (id < 5) {
+        element->states[id] = !element->states[id];
+        gst_editor_element_set_state(element,id,TRUE);
+      } else
+        g_warning("Uh, shouldn't have gotten here, unknown state\n");
+//g_print("in element statebox_event, setting inchild");
+      element->canvas->inchild = TRUE;
+      return TRUE;
+      break;
+    default:
+      break;
+  }
+  return FALSE;
+}
+
+
+static void gst_editor_element_set_state(GstEditorElement *element,
+                                         gint id,gboolean set) {
+  gboolean stateset = TRUE;    /* if we have no element, set anyway */
+  if (element->states[id]) {
+    /* set the object state */
+    if (set && element->element)
+      stateset = gst_element_set_state(element->element,(1 << id));
+    /* change the display */
+    if (stateset) {
+      if (id < 4) {
+        gtk_object_set(GTK_OBJECT(element->statebox[id]),
+                       "fill_color","black",NULL);
+        gtk_object_set(GTK_OBJECT(element->statetext[id]),
+                       "fill_color","white",NULL);
+      } else if (id == 4) {
+        gtk_object_set(GTK_OBJECT(element->playbox),
+                       "fill_color","black",NULL);
+        gtk_object_set(GTK_OBJECT(element->playtext),
+                       "fill_color","white",NULL);
+      }
+    } else {
+      g_print("error setting state %d\n",id);
+      element->states[id] = !element->states[id];
+    }
+  } else {
+    if (set && element->element)
+      stateset = gst_element_set_state(element->element,~(1 << id));
+    if (stateset) {
+      if (id < 4) {
+        gtk_object_set(GTK_OBJECT(element->statebox[id]),
+                       "fill_color","white",NULL);
+        gtk_object_set(GTK_OBJECT(element->statetext[id]),
+                       "fill_color","black",NULL);
+      } else if (id == 4) {
+        gtk_object_set(GTK_OBJECT(element->playbox),
+                       "fill_color","white",NULL);
+        gtk_object_set(GTK_OBJECT(element->playtext),
+                       "fill_color","black",NULL);
+      }
+    } else {
+      g_print("error unsetting state %d\n",id);
+      element->states[id] = !element->states[id];
+    }
+  }
+}
+
+
+static void gst_editor_element_state_change(GstElement *element,
+                                            gint state,
+                                            GstEditorElement *editorelement) {
+  gint id;
+
+  g_return_if_fail(editorelement != NULL);
+
+//  g_print("gst_editor_element_state_change got state 0x%08x\n",state);
+  // if it's an unset
+  if (state & GST_STATE_MAX) {
+    state = ~state;
+    for (id=0;id<(sizeof(state)*8)-1;id++) {
+      if (state & 1) {
+        editorelement->states[id] = FALSE;
+        break;
+      }
+      state /= 2;
+    }
+  } else {
+    for (id=0;id<(sizeof(state)*8)-1;id++) {
+      if (state & 1) {
+        editorelement->states[id] = TRUE;
+        break;
+      }
+      state /= 2;
+    }
+  }
+  gst_editor_element_set_state(editorelement,id,FALSE);
+}
+
+static void gst_editor_element_sync_state(GstEditorElement *element) {
+  gint id;
+
+//  g_print("syncronizing state\n");
+  for (id=0;id<5;id++) {
+    element->states[id] = GST_FLAG_IS_SET(element->element,1<<id);
+    gst_editor_element_set_state(element,id,FALSE);
+  }
+}
+
+static void gst_editor_element_move(GstEditorElement *element,
+                                    gdouble dx,gdouble dy) {
+  GList *pads;
+  GstEditorPad *pad;
+
+  // this is a 'little' trick to keep from repacking the whole thing...
+  element->x += dx;element->y += dy;
+  gnome_canvas_item_move(GNOME_CANVAS_ITEM(element->group),dx,dy);
+
+  pads = element->srcpads;
+  while (pads) {
+    pad = GST_EDITOR_PAD(pads->data);
+    if (pad->connection) {
+//      g_print("updating pad's connection\n");
+      pad->connection->resize = TRUE;
+      gst_editor_connection_resize(pad->connection);
+    }
+    pads = g_list_next(pads);
+  }
+  pads = element->sinkpads;
+  while (pads) {
+    pad = GST_EDITOR_PAD(pads->data);
+    if (pad->connection) {
+//      g_print("updating pad's connection\n");
+      pad->connection->resize = TRUE;
+      gst_editor_connection_resize(pad->connection);
+    }
+    pads = g_list_next(pads);
+  }
+}
diff --git a/editor/gsteditorpad.c b/editor/gsteditorpad.c
new file mode 100644 (file)
index 0000000..9aea1f6
--- /dev/null
@@ -0,0 +1,406 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gnome.h>
+#include <gst/gst.h>
+
+#include "gsteditor.h"
+
+/* class functions */
+static void gst_editor_pad_class_init(GstEditorPadClass *klass);
+static void gst_editor_pad_init(GstEditorPad *pad);
+static void gst_editor_pad_set_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_pad_get_arg(GtkObject *object,GtkArg *arg,guint id);
+static void gst_editor_pad_realize(GstEditorPad *pad);
+
+/* class implementation functions */
+static void gst_editor_pad_update(GnomeCanvasItem *item,double *affine,
+                                      ArtSVP *clip_path,int flags);
+static gint gst_editor_pad_event(GnomeCanvasItem *item,GdkEvent *event);
+
+/* events fired by items within self */
+static gint gst_editor_pad_padbox_event(GnomeCanvasItem *item,
+                                        GdkEvent *event,
+                                        GstEditorPad *pad);
+
+/* utility functions */
+static void gst_editor_pad_resize(GstEditorPad *pad);
+
+
+enum {
+  ARG_0,
+  ARG_X,
+  ARG_Y,
+  ARG_WIDTH,
+  ARG_HEIGHT,
+  ARG_PAD,
+};
+
+enum {
+  LAST_SIGNAL
+};
+
+static GtkObjectClass *parent_class;
+static guint gst_editor_pad_signals[LAST_SIGNAL] = { 0 };
+
+GtkType gst_editor_pad_get_type() {
+  static GtkType pad_type = 0;
+
+  if (!pad_type) {
+    static const GtkTypeInfo pad_info = {
+      "GstEditorPad",
+      sizeof(GstEditorPad),
+      sizeof(GstEditorPadClass),
+      (GtkClassInitFunc)gst_editor_pad_class_init,
+      (GtkObjectInitFunc)gst_editor_pad_init,
+      NULL,
+      NULL,
+      (GtkClassInitFunc)NULL,
+    };
+    pad_type = gtk_type_unique(gtk_object_get_type(),&pad_info);
+  }
+  return pad_type;
+}
+
+static void gst_editor_pad_class_init(GstEditorPadClass *klass) {
+  GtkObjectClass *object_class;
+
+  object_class = (GtkObjectClass*)klass;
+
+  parent_class = gtk_type_class(gtk_object_get_type());
+
+  gtk_object_add_arg_type("GstEditorPad::x",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_X);
+  gtk_object_add_arg_type("GstEditorPad::y",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_Y);
+  gtk_object_add_arg_type("GstEditorPad::width",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_WIDTH);
+  gtk_object_add_arg_type("GstEditorPad::height",GTK_TYPE_DOUBLE,
+                          GTK_ARG_READWRITE,ARG_HEIGHT);
+  gtk_object_add_arg_type("GstEditorPad::pad",GTK_TYPE_POINTER,
+                          GTK_ARG_READWRITE,ARG_PAD);
+
+  klass->realize = gst_editor_pad_realize;
+
+  object_class->set_arg = gst_editor_pad_set_arg;
+  object_class->get_arg = gst_editor_pad_get_arg;
+}
+
+static void gst_editor_pad_init(GstEditorPad *pad) {
+}
+
+GstEditorPad *gst_editor_pad_new(GstEditorElement *parent,GstPad *pad,
+                                 const gchar *first_arg_name, ...) {
+  GstEditorPad *editorpad;
+  va_list args;
+
+  g_return_if_fail(parent != NULL);
+  g_return_if_fail(GST_IS_EDITOR_ELEMENT(parent));
+  g_return_if_fail(pad != NULL);
+  g_return_if_fail(GST_IS_PAD(pad));
+
+  editorpad = GST_EDITOR_PAD(gtk_type_new(GST_TYPE_EDITOR_PAD));
+  editorpad->pad = pad;
+
+  va_start(args,first_arg_name);
+  gst_editor_pad_construct(editorpad,parent,first_arg_name,args);
+  va_end(args);
+
+  return editorpad;
+}
+
+void gst_editor_pad_construct(GstEditorPad *pad,
+                              GstEditorElement *parent,
+                              const gchar *first_arg_name,va_list args) {
+  GtkObject *obj = GTK_OBJECT(pad);
+  GSList *arg_list = NULL, *info_list = NULL;
+  gchar *error;
+  GstEditorPadClass *padclass;
+  
+//  g_print("in gst_editor_pad_construct()\n");
+      
+  error = gtk_object_args_collect(GTK_OBJECT_TYPE(obj),&arg_list,
+                                  &info_list,first_arg_name,args);
+  if (error) {
+    g_warning("gst_editor_pad_construct(): %s",error);
+    g_free(error);
+  } else {
+    GSList *arg,*info;
+//    g_print("setting all the arguments on the pad\n");
+    for (arg=arg_list,info=info_list;arg;arg=arg->next,info=info->next)
+      gtk_object_arg_set(obj,arg->data,info->data);
+    gtk_args_collect_cleanup(arg_list,info_list);
+  }
+
+  pad->parent = parent;
+
+  padclass = GST_EDITOR_PAD_CLASS(GTK_OBJECT(pad)->klass);
+  if (padclass)
+    (padclass->realize)(pad);
+}
+
+static void gst_editor_pad_set_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditorPad *pad;
+
+  /* get the major types of this object */
+  pad = GST_EDITOR_PAD(object);
+
+  switch (id) {
+    case ARG_X:
+      pad->x = GTK_VALUE_DOUBLE(*arg);
+      break;
+    case ARG_Y:
+      pad->y = GTK_VALUE_DOUBLE(*arg);
+      break;
+    case ARG_WIDTH:
+      pad->width = GTK_VALUE_DOUBLE(*arg);
+      pad->resize = TRUE;
+      break;
+    case ARG_HEIGHT:
+      pad->height = GTK_VALUE_DOUBLE(*arg);
+      pad->resize = TRUE;
+      break;
+    case ARG_PAD:
+      /* FIXME: this is very brute force */
+      pad->pad = GTK_VALUE_POINTER(*arg);
+      break;
+    default:
+      g_warning("gsteditorpad: unknown arg!");
+      break;
+  }
+}
+
+static void gst_editor_pad_get_arg(GtkObject *object,GtkArg *arg,guint id) {
+  GstEditorPad *pad;
+
+  /* get the major types of this object */
+  pad = GST_EDITOR_PAD(object);
+
+  switch (id) {
+    case ARG_X:
+      GTK_VALUE_INT(*arg) = pad->x;
+      break;
+    case ARG_Y:
+      GTK_VALUE_INT(*arg) = pad->y;
+      break;
+    case ARG_WIDTH:
+      GTK_VALUE_INT(*arg) = pad->width;
+      break;
+    case ARG_HEIGHT:
+      GTK_VALUE_INT(*arg) = pad->height;
+      break;
+    default:
+      arg->type = GTK_TYPE_INVALID;
+      break;
+  }
+}
+
+static void gst_editor_pad_realize(GstEditorPad *pad) {
+  gint i;
+
+//  g_print("realizing editor pad %p\n",pad);
+
+  /* we must be attached to an element */
+  g_return_if_fail(pad->parent != NULL);
+
+  /* create the group and bounding box */
+  pad->group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(pad->parent->group,
+    gnome_canvas_group_get_type(),"x",pad->x,"y",pad->y,NULL));
+  g_return_if_fail(pad->group != NULL);
+  GST_EDITOR_SET_OBJECT(pad->group,pad);
+
+  pad->border = gnome_canvas_item_new(pad->group,
+    gnome_canvas_rect_get_type(),
+    "width_units",1.0,"fill_color","white","outline_color","black", 
+    "x1",0.0,"y1",0.0,"x2",pad->width,"y2",pad->height,NULL);
+  g_return_if_fail(pad->border != NULL);
+  GST_EDITOR_SET_OBJECT(pad->border,pad);
+
+  /* create the pad box on the correct side */
+  pad->issrc = (pad->pad->direction == GST_PAD_SRC);
+  if (pad->issrc)
+    pad->padbox = gnome_canvas_item_new(pad->group,
+      gnome_canvas_rect_get_type(),
+      "width_units",1.0,"fill_color","white","outline_color","black",
+      "x1",pad->x-4.0,"y1",2.0,"x2",pad->x,"y2",pad->height-2.0,NULL);
+  else
+    pad->padbox = gnome_canvas_item_new(pad->group,
+      gnome_canvas_rect_get_type(),
+      "width_units",1.0,"fill_color","white","outline_color","black",
+      "x1",0.0,"y1",2.0,"x2",4.0,"y2",pad->height-2.0,NULL);
+  g_return_if_fail(pad->padbox != NULL);
+  GST_EDITOR_SET_OBJECT(pad->padbox,pad);
+  gtk_signal_connect(GTK_OBJECT(pad->padbox),"event",
+    GTK_SIGNAL_FUNC(gst_editor_pad_padbox_event),pad);
+
+  pad->title = gnome_canvas_item_new(pad->group,
+    gnome_canvas_text_get_type(),
+    "text",gst_pad_get_name(pad->pad),
+    "x",0.0,"y",0.0,"anchor",GTK_ANCHOR_NORTH_WEST,
+    "font_gdk",gtk_widget_get_default_style()->font,
+    NULL);
+  g_return_if_fail(pad->title != NULL);
+  GST_EDITOR_SET_OBJECT(pad->title,pad);
+
+  pad->realized = TRUE;
+  pad->resize = TRUE;
+  gst_editor_pad_repack(pad);
+}
+
+
+static void gst_editor_pad_resize(GstEditorPad *pad) {
+  gdouble minwidth,minheight;
+
+//  g_print("resizing pad\n");
+
+  minwidth = 0;minheight = 0;
+
+  /* get the text size and add it into minsize */
+  minwidth = gst_util_get_double_arg(GTK_OBJECT(pad->title),
+                                     "text_width") + 2.0;
+  minheight = gst_util_get_double_arg(GTK_OBJECT(pad->title),
+                                      "text_height");
+
+  /* calculate the size of the padbox */
+  pad->boxheight = minheight - 4.0;
+  pad->boxwidth = pad->boxheight / 2.0;
+  minwidth += pad->boxwidth;
+
+  /* force the thing to grow if necessary */
+  pad->width = MAX(pad->width,minwidth);
+  pad->height = MAX(pad->height,minheight);
+
+  /* update the connection if there is one */
+//  g_print("connection is %p\n",pad->connection);
+  if (pad->connection != NULL)
+    gst_editor_connection_resize(pad->connection);
+}
+
+void gst_editor_pad_repack(GstEditorPad *pad) {
+  gdouble x1,y1,x2,y2;
+
+  if (!pad->realized) return;
+
+  gst_editor_pad_resize(pad);
+
+  x1 = 0;y1 = 0;
+  x2 = x1 + pad->width;y2 = y1 + pad->height;
+//  g_print("repacking pad at %.2fx%.2f - %.2fx%.2f\n",x1,y1,x2,y2);
+
+  /* move the group */
+  gtk_object_set(GTK_OBJECT(pad->group),"x",pad->x,"y",pad->y,NULL);
+
+  /* start by resizing the bordering box */
+  gtk_object_set(GTK_OBJECT(pad->border),
+                 "x1",x1,"y1",y1,"x2",x2,"y2",y2,NULL);
+
+  /* if we're a left-jusified sink */
+  if (pad->issrc) {
+    /* and move the pad box */
+    gtk_object_set(GTK_OBJECT(pad->padbox),
+                   "x1",x2-pad->boxwidth,"y1",y1+2.0,
+                   "x2",x2,"y2",y2-2.0,NULL);
+    /* then move the text to the right place */
+    gtk_object_set(GTK_OBJECT(pad->title),
+                   "x",x2-pad->boxwidth-1.0,"y",y1,
+                   "anchor",GTK_ANCHOR_NORTH_EAST,
+                   NULL);
+  } else {
+    /* and move the pad box */
+    gtk_object_set(GTK_OBJECT(pad->padbox),
+                   "x1",x1,"y1",y1+2.0,
+                   "x2",x1+pad->boxwidth,"y2",y2-2.0,NULL);
+    /* then move the text to the right place */
+    gtk_object_set(GTK_OBJECT(pad->title),
+                   "x",x1+pad->boxwidth+1.0,"y",y1,
+                   "anchor",GTK_ANCHOR_NORTH_WEST,
+                   NULL);
+  }
+
+  if (pad->connection != NULL) {
+    pad->connection->resize = TRUE;
+    gst_editor_connection_resize(pad->connection);
+  }
+
+  pad->resize = FALSE;
+}
+
+
+/*
+static gint gst_editor_pad_event(GnomeCanvasItem *item,GdkEvent *event) {
+  GstEditorPad *pad = GST_EDITOR_PAD(item);
+  gdouble item_x,item_y;
+  GdkCursor *fleur;
+  gdouble tx,ty;
+
+  item_x = event->button.x;
+  item_y = event->button.y;
+  gnome_canvas_item_w2i(item->parent,&item_x,&item_y);
+
+  switch(event->type) {
+    case GDK_ENTER_NOTIFY:
+//      g_print("entered pad\n");
+      break;
+    case GDK_LEAVE_NOTIFY:
+//      g_print("left pad\n");
+      break;
+    default:
+      break;
+  }
+  return FALSE;
+}
+*/
+
+/* FIXME FIXME FIXME */
+static gint gst_editor_pad_padbox_event(GnomeCanvasItem *item,
+                                        GdkEvent *event,
+                                        GstEditorPad *pad) {
+  GstEditorElement *element;
+  GstEditorBin *bin;
+
+//  g_print("padbox has event %d\n",event->type);
+  g_return_if_fail(GST_IS_EDITOR_PAD(pad));
+
+  element = pad->parent;
+  bin = element->parent;
+
+  switch(event->type) {
+    case GDK_ENTER_NOTIFY:
+//      g_print("entered pad '%s'\n",
+//              gst_pad_get_name(pad->pad));
+      break;
+    case GDK_LEAVE_NOTIFY:
+//      g_print("left pad '%s'\n",
+//              gst_pad_get_name(pad->pad));
+      break;
+    case GDK_BUTTON_PRESS:
+//      g_print("have button press in pad '%s'\n",
+//              gst_pad_get_name(pad->pad));
+      gst_editor_bin_start_banding(bin,pad);
+      return TRUE;
+      break;
+    case GDK_MOTION_NOTIFY:
+//      g_print("have motion in pad\n");
+      break;
+    default:
+      break;
+  }
+  return FALSE;
+}
diff --git a/editor/gsteditorpad.h b/editor/gsteditorpad.h
new file mode 100644 (file)
index 0000000..ed3f8ef
--- /dev/null
@@ -0,0 +1,79 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_EDITOR_PAD_H__
+#define __GST_EDITOR_PAD_H__
+
+#include <gst/gst.h>
+
+#define GST_TYPE_EDITOR_PAD \
+  (gst_editor_pad_get_type())
+#define GST_EDITOR_PAD(obj) \
+  (GTK_CHECK_CAST((obj),GST_TYPE_EDITOR_PAD,GstEditorPad))
+#define GST_EDITOR_PAD_CLASS(klass) \
+  (GTK_CHECK_CLASS_CAST((klass),GST_TYPE_EDITOR_PAD,GstEditorPad))
+#define GST_IS_EDITOR_PAD(obj) \
+  (GTK_CHECK_TYPE((obj),GST_TYPE_EDITOR_PAD))
+#define GST_IS_EDITOR_PAD_CLASS(obj) \
+  (GTK_CHECK_CLASS_TYPE((klass),GST_TYPE_EDITOR_PAD)))
+
+typedef struct _GstEditorPad GstEditorPad;
+typedef struct _GstEditorPadClass GstEditorPadClass;
+
+struct _GstEditorPad {
+  GtkObject object;
+
+  /* parent object */
+  GtkObject *parent;           // FIXME!!!
+
+  /* the pad we're associated with */
+  GstPad *pad;
+  gboolean issink;
+
+  /* visual stuff */
+  GnomeCanvasGroup *group;
+  GnomeCanvasItem *border,*title,*padbox;
+  gboolean sinkpad;                            // is this a sink pad?
+  gdouble x,y;                                 // location
+  gdouble width,height;                                // actual size
+  gdouble boxwidth,boxheight;                  // size of pad box
+  gboolean resize;                             // does it need resizing?
+
+  /* interaction state */
+  gboolean dragging,resizing,moved;
+  gdouble dragx,dragy;
+
+  /* connection */
+//  GnomeCanvasItem *connection;               // can't use
+//GstEditorConnection
+};
+
+struct _GstEditorPadClass {
+  GtkObjectClass parent_class;
+};
+
+GtkType gst_editor_pad_get_type();
+GstEditorPad *gst_editor_pad_new(GstEditorElement *parent,GstPad *pad,
+                                 const gchar *first_arg_name, ...);
+void gst_editor_pad_construct(GstEditorPad *element,
+                              GstEditorElement *parent,
+                              const gchar *first_arg_name,va_list args);
+
+#endif /* __GST_EDITOR_PAD_H__ */
diff --git a/editor/gstelementselect.c b/editor/gstelementselect.c
new file mode 100644 (file)
index 0000000..e467b9c
--- /dev/null
@@ -0,0 +1,278 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gst/gst.h>
+#include <gnome.h>
+
+struct element_select_classlist {
+  gchar *name;
+  GSList *subclasses;
+  GSList *factories;
+};
+
+struct element_select_details {
+  GstElementFactory *factory;
+  GtkWidget *longname, *description, *version, *author, *copyright;
+};
+
+static gint compare_name(gconstpointer a,gconstpointer b) {
+  return (strcmp(((GstElementFactory *)a)->name,
+                 ((GstElementFactory *)b)->name));
+}
+
+gint str_compare(gconstpointer a,gconstpointer b) {
+  return (strcmp((gchar *)a,(gchar *)b));
+}
+
+/* this function creates a GtkCTreeNode with the contents of the classtree */
+static void make_ctree(GtkCTree *tree,GtkCTreeNode *parent,
+                       struct element_select_classlist *class) {
+  GSList *traverse;
+  GtkCTreeNode *classnode, *node = NULL;
+  gchar *data[2];
+
+  data[0] = g_strdup(class->name);
+  data[1] = NULL;
+  classnode = gtk_ctree_insert_node(tree,parent,NULL,data,0,
+                                    NULL,NULL,NULL,NULL,FALSE,TRUE);
+  gtk_ctree_node_set_selectable(tree,classnode,FALSE);
+
+  traverse = class->subclasses;
+  while (traverse) {
+    make_ctree(tree,classnode,
+               (struct element_select_classlist *)(traverse->data));
+    traverse = g_slist_next(traverse);
+  }
+
+  traverse = class->factories;
+  while (traverse) {
+    GstElementFactory *factory = (GstElementFactory *)(traverse->data);
+    data[0] = g_strdup(factory->name);
+    data[1] = g_strdup(factory->details->description);
+    node = gtk_ctree_insert_node(tree,classnode,NULL,data,0,
+                                 NULL,NULL,NULL,NULL,TRUE,FALSE);
+    gtk_ctree_node_set_row_data_full(tree,node,factory,NULL);
+    traverse = g_slist_next(traverse);
+  }
+}
+
+static void ctree_select(GtkWidget *widget,gint row,gint column,
+                         GdkEventButton *bevent,gpointer data) {
+  GtkCTree *tree = GTK_CTREE(widget);
+  GtkCTreeNode *node;
+  GstElementFactory *factory;
+  struct element_select_details *details;
+  node = gtk_ctree_node_nth(tree,row);
+  factory = (GstElementFactory *)gtk_ctree_node_get_row_data(tree,node);
+  if (!factory)
+    return;
+  details = (struct element_select_details *)data;
+  details->factory = factory;
+
+  gtk_entry_set_text(GTK_ENTRY(details->longname),
+                     factory->details->longname);
+  gtk_entry_set_text(GTK_ENTRY(details->description),
+                     factory->details->description);
+  gtk_entry_set_text(GTK_ENTRY(details->version),
+                     factory->details->version);
+  gtk_entry_set_text(GTK_ENTRY(details->author),
+                     factory->details->author);
+  gtk_entry_set_text(GTK_ENTRY(details->copyright),
+                     factory->details->copyright);
+
+  if (bevent && bevent->type == GDK_2BUTTON_PRESS)
+    gtk_main_quit();
+}
+
+
+GstElementFactory *element_select_dialog() {
+  GtkWidget *dialog;
+  gchar *titles[2];
+  GtkWidget *ctree;
+  GtkWidget *scroller;
+  GtkTable *table;
+  GtkWidget *detailslabel;
+  GtkWidget *detailshsep;
+  GtkWidget *longname, *description, *version, *author, *copyright;
+
+  GList *elements;
+  GstElementFactory *element;
+  gchar **classes, **class;
+  GSList *classlist;
+  GSList *classtree, *treewalk;
+  GSList **curlist;
+  struct element_select_classlist *branch;
+  struct element_select_details details;
+
+  /* first create the dialog and associated stuff */
+  dialog = gnome_dialog_new("Select Element",
+                            GNOME_STOCK_BUTTON_OK,
+                            GNOME_STOCK_BUTTON_CANCEL,
+                            NULL);
+  gnome_dialog_set_close(GNOME_DIALOG(dialog),TRUE);
+  gnome_dialog_close_hides(GNOME_DIALOG(dialog),TRUE);
+  gnome_dialog_set_default(GNOME_DIALOG(dialog),GNOME_OK);
+
+  titles[0] = "Element                               ";
+  titles[1] = "Description";
+  ctree = gtk_ctree_new_with_titles(2,0,titles);
+  gtk_widget_set_usize(ctree,400,350);
+
+  scroller = gtk_scrolled_window_new(NULL,NULL);
+  gtk_container_add(GTK_CONTAINER(scroller),ctree);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
+                                 GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+  gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox),scroller,
+                             TRUE,TRUE,0);
+
+  /* create the details table and put a title on it */
+  table = GTK_TABLE(gtk_table_new(2,7,FALSE));
+  detailslabel = gtk_label_new("Element Details:");
+  gtk_misc_set_alignment(GTK_MISC(detailslabel),0.0,0.5);
+  gtk_table_attach(table,detailslabel,0,2,0,1,GTK_FILL|GTK_EXPAND,0,0,0);
+
+  /* then a separator to keep the title separate */
+  detailshsep = gtk_hseparator_new();
+  gtk_table_attach(table,detailshsep,0,2,1,2,GTK_FILL|GTK_EXPAND,0,0,0);
+
+  /* the long name of the element */
+  longname = gtk_label_new("Name:");
+  gtk_misc_set_alignment(GTK_MISC(longname),1.0,0.5);
+  gtk_table_attach(table,longname,0,1,2,3,GTK_FILL,0,5,0);
+  details.longname = gtk_entry_new();
+  gtk_entry_set_editable(GTK_ENTRY(details.longname),FALSE);
+  gtk_table_attach(table,details.longname,1,2,2,3,GTK_FILL|GTK_EXPAND,0,0,0);
+
+  /* the description */
+  description = gtk_label_new("Description:");
+  gtk_misc_set_alignment(GTK_MISC(description),1.0,0.5);
+  gtk_table_attach(table,description,0,1,3,4,GTK_FILL,0,5,0);
+  details.description = gtk_entry_new();
+  gtk_entry_set_editable(GTK_ENTRY(details.description),FALSE);
+  gtk_table_attach(table,details.description,1,2,3,4,GTK_FILL|GTK_EXPAND,0,0,0);
+
+  /* the version */
+  version = gtk_label_new("Version:");
+  gtk_misc_set_alignment(GTK_MISC(version),1.0,0.5);
+  gtk_table_attach(table,version,0,1,4,5,GTK_FILL,0,5,0);
+  details.version = gtk_entry_new();
+  gtk_entry_set_editable(GTK_ENTRY(details.version),FALSE);
+  gtk_table_attach(table,details.version,1,2,4,5,GTK_FILL|GTK_EXPAND,0,0,0);
+
+  /* the author */
+  author = gtk_label_new("Author:");
+  gtk_misc_set_alignment(GTK_MISC(author),1.0,0.5);
+  gtk_table_attach(table,author,0,1,6,7,GTK_FILL,0,5,0);
+  details.author = gtk_entry_new();
+  gtk_entry_set_editable(GTK_ENTRY(details.author),FALSE);
+  gtk_table_attach(table,details.author,1,2,6,7,GTK_FILL|GTK_EXPAND,0,0,0);
+
+  /* the copyright */
+  copyright = gtk_label_new("Copyright:");
+  gtk_misc_set_alignment(GTK_MISC(copyright),1.0,0.5);
+  gtk_table_attach(table,copyright,0,1,7,8,GTK_FILL,0,5,0);
+  details.copyright = gtk_entry_new();
+  gtk_entry_set_editable(GTK_ENTRY(details.copyright),FALSE);
+  gtk_table_attach(table,details.copyright,1,2,7,8,GTK_FILL|GTK_EXPAND,0,0,0);
+
+  gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox),GTK_WIDGET(table),
+                             TRUE,TRUE,0);
+
+
+  /* first create a sorted (by class) tree of all the factories */
+  classtree = NULL;
+  elements = gst_elementfactory_get_list();
+  while (elements) {
+    element = (GstElementFactory *)(elements->data);
+    /* split up the factory's class */
+    classes = g_strsplit(element->details->class,"/",0);
+    class = classes;
+    curlist = &classtree;
+    /* walk down the class tree to find where to place this element */
+    /* the goal is for treewalk to point to the right class branch */
+    /* when we exit this thing, branch is pointing where we want */
+    while (*class) {
+      treewalk = *curlist;
+      /* walk the current level of class to see if we have the class */
+      while (treewalk) {
+        branch = (struct element_select_classlist *)(treewalk->data);
+        /* see if this class matches what we're looking for */
+        if (!strcmp(branch->name,*class)) {
+          /* if so, we progress down the list into this one's list */
+          curlist = &branch->subclasses;
+          break;
+        }
+        treewalk = g_slist_next(treewalk);
+      }
+      /* if treewalk == NULL, it wasn't in the list. add one */
+      if (treewalk == NULL) {
+        /* curlist is pointer to list */
+        branch = g_new0(struct element_select_classlist,1);
+        branch->name = g_strdup(*class);
+        *curlist = g_slist_insert_sorted(*curlist,branch,str_compare);
+        curlist = &branch->subclasses;
+      }
+      class++;
+    }
+    /* theoretically branch points where we want now */
+    branch->factories = g_slist_insert_sorted(branch->factories,element,
+                                              compare_name);
+    elements = g_list_next(elements);
+  }
+
+  /* now fill in the ... */
+  gtk_clist_freeze(GTK_CLIST(ctree));
+  treewalk = classtree;
+  while (treewalk) {
+    make_ctree(GTK_CTREE(ctree),NULL,
+               (struct element_select_classlist *)(treewalk->data));
+    treewalk = g_slist_next(treewalk);
+  }
+  gtk_clist_thaw(GTK_CLIST(ctree));
+
+  gtk_signal_connect(GTK_OBJECT(ctree),"select_row",
+                     GTK_SIGNAL_FUNC(ctree_select),&details);
+
+  gtk_widget_show_all(GTK_WIDGET(dialog));
+
+  details.factory = NULL;
+  if (gnome_dialog_run_and_close(GNOME_DIALOG(dialog)) == GNOME_CANCEL)
+    return NULL;
+  else
+    return details.factory;
+};
+
+
+/* this is available so we can do a quick test of this thing */
+#ifdef ELEMENTSELECT_MAIN
+int main(int argc,char *argv[]) {
+  GstElementFactory *chosen;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+  gnome_init("elementselect_test","0.0.0",argc,argv);
+  chosen = element_select_dialog();
+  if (chosen)
+    g_print("selected '%s'\n",chosen->name);
+  else
+    g_print("didn't choose any\n");
+  exit(0);
+}
+#endif /* ELEMENTSELECT_MAIN */
diff --git a/editor/gstelementselect.h b/editor/gstelementselect.h
new file mode 100644 (file)
index 0000000..65ad3ed
--- /dev/null
@@ -0,0 +1,23 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <gst/gst.h>
+
+GstElementFactory *element_select_dialog();
diff --git a/gst/xml/Makefile.am b/gst/xml/Makefile.am
new file mode 100644 (file)
index 0000000..bff3857
--- /dev/null
@@ -0,0 +1,5 @@
+bin_PROGRAMS = save
+
+INCLUDES = $(GLIB_CFLAGS) $(GTK_CFLAGS) $(XML_CFLAGS) -I$(top_srcdir)
+LDADD = $(GLIB_LIBS) $(GTK_LIBS) $(XML_LIBS) $(top_srcdir)/gst/libgst.la
+
diff --git a/gst/xml/notes b/gst/xml/notes
new file mode 100644 (file)
index 0000000..5ed0566
--- /dev/null
@@ -0,0 +1,6 @@
+The naming hiearchy you'll see bits of but never the whole thing is
+basically container:container:container:element.pad, which reflects
+parentage.  When naming connections between elements, those are the what
+will be used, possibly along with .. as container to indicate 'up one'.  I
+don't think this will ever by used, since ghost pads are supposed to make
+all connections between elements with the same parent.
diff --git a/gst/xml/save.c b/gst/xml/save.c
new file mode 100644 (file)
index 0000000..5e41244
--- /dev/null
@@ -0,0 +1,43 @@
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+GstPipeline *create_pipeline() {
+  GstPipeline *pipeline;
+  GstElement *src, *sink;
+  GstPad *srcpad, *sinkpad;
+
+  pipeline = gst_pipeline_new("fake_pipeline");
+  g_return_if_fail(pipeline != NULL);
+
+  src = gst_elementfactory_make("fakesrc","fakesrc");
+  g_return_if_fail(src != NULL);
+  sink = gst_elementfactory_make("fakesink","fakesink");
+  g_return_if_fail(sink != NULL);
+
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(sink));
+
+  srcpad = gst_element_get_pad(src,"src");
+  g_return_if_fail(srcpad != NULL);
+  sinkpad = gst_element_get_pad(sink,"sink");
+  g_return_if_fail(srcpad != NULL);
+
+  gst_pad_connect(srcpad,sinkpad);
+
+  return GST_PIPELINE(pipeline);
+}
+
+int main(int argc,char *argv[]) {
+  GstElement *pipeline;
+  xmlDocPtr doc;
+
+//  _gst_plugin_spew = TRUE;
+
+  gst_init(&argc,&argv);
+
+  pipeline = GST_ELEMENT(create_pipeline());
+
+  doc = gst_xml_write(pipeline);
+  xmlSaveFile("save.xml",doc);
+}
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644 (file)
index 0000000..50c6b26
--- /dev/null
@@ -0,0 +1,21 @@
+#bin_PROGRAMS = basic m types a r plugin w s args mpg123 mcut push qtest
+bin_PROGRAMS = qtest spectrum record wave mp3 teardown buffer mp3parse \
+               mpeg2parse mp3play ac3parse ac3play dvdcat fake cobin
+
+SUBDIRS = xml cothreads
+
+spectrum_CFLAGS = $(shell gnome-config --cflags gnomeui)
+spectrum_LDFLAGS = $(shell gnome-config --libs gnomeui)
+wave_CFLAGS = $(shell gnome-config --cflags gnomeui)
+wave_LDFLAGS = $(shell gnome-config --libs gnomeui)
+
+buffer_SOURCES = buffer.c mem.c
+teardown_SOURCES = teardown.c mem.c
+ac3play_SOURCES = ac3play.c mem.c
+
+LDADD = $(GLIB_LIBS) $(GTK_LIBS) $(top_builddir)/gst/libgst.la
+
+INCLUDES = $(GLIB_CFLAGS) $(GTK_CFLAGS) -I$(top_srcdir) \
+           $(shell gnome-config --cflags gnomeui)
+
+EXTRA_DIST = README
diff --git a/test/a.c b/test/a.c
new file mode 100644 (file)
index 0000000..59ad5d6
--- /dev/null
+++ b/test/a.c
@@ -0,0 +1,69 @@
+#include <gst/gst.h>
+
+void eof(GstSrc *src) {
+  g_print("have eof, quitting\n");
+  exit(0);
+}
+
+int main(int argc,char *argv[]) {
+  GstType *autype;
+  GList *factories;
+  GstElementFactory *parsefactory;
+  GstElement *bin, *disksrc, *parse, *audiosink;
+  GList *padlist;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  bin = gst_bin_new("bin");
+
+  disksrc = gst_disksrc_new("disksrc");
+  g_print("created disksrc\n");
+  if (argc == 2)
+    gst_disksrc_set_filename(disksrc,argv[1]);
+  else
+    gst_disksrc_set_filename(disksrc,"Thank_you_very_much.au");
+  g_print("loaded file '%s'\n",gst_disksrc_get_filename(disksrc));
+
+
+  /* now it's time to get the parser */
+  autype = gst_type_get_by_mime("audio/au");
+  factories = gst_type_get_sinks(autype);
+  if (factories != NULL)
+    parsefactory = GST_ELEMENTFACTORY(factories->data);
+  else {
+    g_print("sorry, can't find anyone registered to sink 'au'\n");
+    return 1;
+  }
+  parse = gst_elementfactory_create(parsefactory,"parser");
+  if (parse == NULL) {
+    g_print("sorry, couldn't create parser\n");
+    return 1;
+  }
+
+
+  audiosink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(disksrc),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(disksrc));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(parse));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(audiosink));
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(disksrc,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(audiosink,"sink"));
+
+  while(1)
+    gst_disksrc_push(GST_SRC(disksrc));
+
+  gst_object_destroy(GST_OBJECT(audiosink));
+  gst_object_destroy(GST_OBJECT(parse));
+  gst_object_destroy(GST_OBJECT(disksrc));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/ac3parse.c b/test/ac3parse.c
new file mode 100644 (file)
index 0000000..e018dc8
--- /dev/null
@@ -0,0 +1,55 @@
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+void ac3parse_info_chain(GstPad *pad,GstBuffer *buf) {
+  g_print("got buffer of size %d\n",GST_BUFFER_SIZE(buf));
+  gst_buffer_unref(buf);
+}
+
+int main(int argc,char *argv[]) {
+  GstPipeline *pipeline;
+  GstElementFactory *srcfactory, *parsefactory;
+  GstElement *src, *parse;
+  GstPad *infopad;
+
+  g_print("have %d args\n",argc);
+
+  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+// gst_plugin_load("ac3parse");
+  gst_plugin_load_all();
+
+  pipeline = gst_pipeline_new("pipeline");
+  g_return_if_fail(pipeline != NULL);
+
+  srcfactory = gst_elementfactory_find("disksrc");
+  g_return_if_fail(srcfactory != NULL);
+  parsefactory = gst_elementfactory_find("ac3parse");
+  g_return_if_fail(parsefactory != NULL);
+
+  src = gst_elementfactory_create(srcfactory,"src");
+  g_return_if_fail(src != NULL);
+  gtk_object_set(GTK_OBJECT(src),"location",argv[1],"bytesperread",4096,NULL);
+  g_print("should be using file '%s'\n",argv[1]);
+  parse = gst_elementfactory_create(parsefactory,"parse");
+  g_return_if_fail(parse != NULL);
+
+  infopad = gst_pad_new("sink",GST_PAD_SINK);
+  gst_pad_set_chain_function(infopad,ac3parse_info_chain);
+
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(parse));
+
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  infopad);
+
+  g_print("setting to RUNNING state\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
+
+  g_print("about to enter loop\n");
+  while (1)
+    gst_src_push(GST_SRC(src));
+}
diff --git a/test/ac3play.c b/test/ac3play.c
new file mode 100644 (file)
index 0000000..24e18e3
--- /dev/null
@@ -0,0 +1,84 @@
+#include <gst/gst.h>
+
+#include "mem.h"
+
+extern gboolean _gst_plugin_spew;
+
+int main(int argc,char *argv[]) {
+  GstElement *pipeline, *decodethread, *playthread;
+  GstElement *src, *parse, *decode, *play;
+  GstElement *queue;
+  GstPad *infopad;
+
+  g_print("have %d args\n",argc);
+
+  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+// gst_plugin_load("ac3parse");
+  gst_plugin_load_all();
+
+  pipeline = gst_elementfactory_make("pipeline","ac3player");
+  g_return_if_fail(pipeline != NULL);
+  decodethread = gst_elementfactory_make("thread","decodethread");
+  g_return_if_fail(decodethread != NULL);
+  playthread = gst_elementfactory_make("thread","playthread");
+  g_return_if_fail(playthread != NULL);
+  queue = gst_elementfactory_make("queue","queue");
+  g_return_if_fail(queue != NULL);
+
+  src = gst_elementfactory_make("disksrc","src");
+  g_return_if_fail(src != NULL);
+  gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL);
+  g_print("should be using file '%s'\n",argv[1]);
+  parse = gst_elementfactory_make("ac3parse","parse");
+  g_return_if_fail(parse != NULL);
+  decode = gst_elementfactory_make("ac3dec","decode");
+  g_return_if_fail(decode != NULL);
+  play = gst_elementfactory_make("audiosink","play");
+  g_return_if_fail(play != NULL);
+
+  // construct the decode thread
+  g_print("constructing the decode thread\n");
+  gst_bin_add(GST_BIN(decodethread),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(decodethread),GST_ELEMENT(parse));
+  gst_bin_add(GST_BIN(decodethread),GST_ELEMENT(decode));
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(decode,"sink"));
+  gst_element_add_ghost_pad(GST_ELEMENT(decodethread),
+                            gst_element_get_pad(decode,"src"));
+
+  // construct the play thread
+  g_print("constructing the play thread\n");
+  gst_bin_add(GST_BIN(playthread),GST_ELEMENT(play));
+  gst_element_add_ghost_pad(GST_ELEMENT(playthread),
+                            gst_element_get_pad(play,"sink"));
+
+  // construct the outer pipeline
+  g_print("constructing the main pipeline\n");
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(decodethread));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(queue));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(playthread));
+  gst_pad_connect(gst_element_get_pad(decodethread,"src"),
+                  gst_element_get_pad(queue,"sink"));
+  gst_pad_connect(gst_element_get_pad(queue,"src"),
+                  gst_element_get_pad(playthread,"sink"));
+
+  // set thread start state
+  gtk_object_set(GTK_OBJECT(decodethread),"create_thread",TRUE,NULL);
+  gtk_object_set(GTK_OBJECT(playthread),"create_thread",FALSE,NULL);
+
+  g_print("setting to RUNNING state\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
+
+//  sleep(1);
+  g_print("about to enter loop\n");
+  while (1) {
+    gst_thread_iterate(GST_THREAD(playthread));
+    g_print("using %d bytes\n",vmsize());
+  }
+
+  xmlSaveFile("ac3play.xml",gst_xml_write(GST_ELEMENT(pipeline)));
+}
diff --git a/test/ac3sync.c b/test/ac3sync.c
new file mode 100644 (file)
index 0000000..67e79dd
--- /dev/null
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <fcntl.h>
+
+int main(int argc,char *argv[]) {
+  int fd;
+  int offset = 0;
+  int got;
+  unsigned short buf[2048];
+  int i;
+  int prev = 0;
+
+  if (argc >= 2) fd = open(argv[1],O_RDONLY);
+  else fd = 0;
+
+  while (got = read(fd,buf,sizeof(buf))) {
+    for (i=0;i<(got/2);i++) {
+      if (buf[i] == 0x770b) {
+        printf("have sync at %d (+%d)\n",offset+(i*2),(offset+(i*2))-prev);
+        prev = offset+(i*2);
+      }
+    }
+    offset += got;
+  }
+}
diff --git a/test/args.c b/test/args.c
new file mode 100644 (file)
index 0000000..751a7af
--- /dev/null
@@ -0,0 +1,20 @@
+#include <glib.h>
+#include <gst/gst.h>
+
+int main(int argc,char *argv[]) {
+  GstElement *src;
+  GList *padlist;
+  GtkArg arg;
+
+  gst_init(&argc,&argv);
+
+  src = gst_disksrc_new("fakesrc");
+  gtk_object_set(GTK_OBJECT(src),"location","demo.mp3",NULL);
+
+  arg.name = "location";
+  gtk_object_getv(GTK_OBJECT(src),1,&arg);
+  g_print("location is %s\n",GTK_VALUE_STRING(arg));
+
+  gst_object_destroy(GST_OBJECT(src));
+}
+
diff --git a/test/basic.c b/test/basic.c
new file mode 100644 (file)
index 0000000..bb6d659
--- /dev/null
@@ -0,0 +1,109 @@
+#include <glib.h>
+#include <gst/gst.h>
+
+void added_child(GstObject *object,GstObject *child) {
+  g_print("added_child(): added child '%s' to '%s'\n",
+          gst_object_get_name(child),
+          gst_object_get_name(object));
+}
+
+void added_parent(GstObject *object,GstObject *parent) {
+  g_print("added_parent(): object '%s' has new parent '%s'\n",
+          gst_object_get_name(object),
+          gst_object_get_name(parent));
+}
+
+void list_pads(GstElement *element) {
+  GList *padlist;
+
+  padlist = gst_element_get_pad_list(element);
+  if (padlist == NULL)
+    g_print("%s has no pads...\n",gst_object_get_name(element));
+  else {
+    while (padlist) {
+      GstPad *pad = GST_PAD(padlist->data);
+      if (gst_pad_get_ghost_parent(pad) == GST_OBJECT(element))
+        g_print("'%s' had %s ghost pad '%s'\n",gst_object_get_name(element),
+                (gst_pad_get_direction(pad) == GST_PAD_SRC) ? "SRC" : "SINK",
+                gst_pad_get_name(pad));
+      else
+        g_print("'%s' had %s pad '%s'\n",gst_object_get_name(element),
+                (gst_pad_get_direction(pad) == GST_PAD_SRC) ? "SRC" : "SINK",
+                gst_pad_get_name(pad));
+      padlist = g_list_next(padlist);
+    }
+  }
+}
+
+int main(int argc,char *argv[]) {
+  GstElement *bin, *src, *binf, *filter1, *filter2, *sink;
+  GList *padlist;
+
+  gtk_init(&argc,&argv);
+
+  bin = gst_bin_new("bin");
+
+  src = gst_disksrc_new("fakesrc");
+  gst_disksrc_set_filename(src,"demo.mp3");
+  list_pads(src);
+
+  binf = gst_bin_new("binf");
+
+  filter1 = gst_fakefilter_new("filter1");
+  list_pads(filter1);
+
+  filter2 = gst_fakefilter_new("filter2");
+  list_pads(filter2);
+
+  sink = gst_fakesink_new("fakesink");
+  list_pads(sink);
+
+  gtk_signal_connect(GTK_OBJECT(bin),"object_added",
+                     GTK_SIGNAL_FUNC(added_child),NULL);
+  gtk_signal_connect(GTK_OBJECT(binf),"object_added",
+                     GTK_SIGNAL_FUNC(added_child),NULL);
+
+  gtk_signal_connect(GTK_OBJECT(binf),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+  gtk_signal_connect(GTK_OBJECT(src),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+  gtk_signal_connect(GTK_OBJECT(filter1),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+  gtk_signal_connect(GTK_OBJECT(filter2),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+  gtk_signal_connect(GTK_OBJECT(sink),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+
+  /* add filter1 to the subbin */
+  gst_bin_add(GST_BIN(binf),GST_ELEMENT(filter1));
+  gst_bin_add(GST_BIN(binf),GST_ELEMENT(filter2));
+  /* connect the two together */
+  gst_pad_connect(gst_element_get_pad(filter1,"src"),
+                  gst_element_get_pad(filter2,"sink"));
+  /* export the pads */
+  gst_element_add_ghost_pad(binf,gst_element_get_pad(filter1,"sink"));
+  gst_element_add_ghost_pad(binf,gst_element_get_pad(filter2,"src"));
+  list_pads(binf);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(src));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(binf));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(sink));
+
+  /* connect src to binf */
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(binf,"sink"));
+  /* connect binf to sink */
+  gst_pad_connect(gst_element_get_pad(binf,"src"),
+                  gst_element_get_pad(sink,"sink"));
+
+  gst_disksrc_push(GST_SRC(src));
+
+  gst_object_destroy(GST_OBJECT(src));
+  gst_object_destroy(GST_OBJECT(filter1));
+  gst_object_destroy(GST_OBJECT(filter2));
+  gst_object_destroy(GST_OBJECT(binf));
+  gst_object_destroy(GST_OBJECT(sink));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/bindings/Makefile.am b/test/bindings/Makefile.am
new file mode 100644 (file)
index 0000000..1f74861
--- /dev/null
@@ -0,0 +1,11 @@
+lib_LTLIBRARIES = libcrashtest.la
+
+libcrashtest_la_SOURCES = \
+       dummy.c
+
+include_HEADERS = \
+       dummy.h
+
+bin_PROGRAMS = test
+
+test_LDADD = libcrashtest.la
diff --git a/test/bindings/dummy.c b/test/bindings/dummy.c
new file mode 100644 (file)
index 0000000..0172eb4
--- /dev/null
@@ -0,0 +1,20 @@
+#include <dummy.h>
+
+Dummy *dummy_new() {
+  Dummy *dummy;
+
+  dummy = g_malloc(sizeof(Dummy));
+
+  dummy->flags = 0;
+  dummy->name = NULL;
+
+  return dummy;
+}
+
+Dummy *dummy_new_with_name(gchar *name) {
+  Dummy *dummy = dummy_new();
+
+  dummy->name = g_strdup(name);
+
+  return dummy;
+}
diff --git a/test/bindings/dummy.h b/test/bindings/dummy.h
new file mode 100644 (file)
index 0000000..7b8ad84
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __DUMMY_H__
+#define __DUMMY_H__
+
+#include <glib.h>
+
+#define DUMMY(dummy) ((Dummy *)(dummy))
+
+typedef struct _Dummy Dummy;
+
+struct _Dummy {
+  gint flags;
+
+  gchar *name;
+};
+
+Dummy *dummy_new();
+Dummy *dummy_new_with_name(gchar *name);
+
+#endif /* __DUMMY_H__ */
diff --git a/test/bindings/test.c b/test/bindings/test.c
new file mode 100644 (file)
index 0000000..75dc3f2
--- /dev/null
@@ -0,0 +1,11 @@
+#include <glib.h>
+#include <dummy.h>
+
+int main(int argc,char *argv[]) {
+  Dummy *driver,*passenger;
+
+  driver = dummy_new();
+  passenger = dummy_new_with_name("moron");
+
+  g_print("created a couple of dummies, %p and %p\n",driver,passenger);
+}
diff --git a/test/buffer.c b/test/buffer.c
new file mode 100644 (file)
index 0000000..baeaa34
--- /dev/null
@@ -0,0 +1,53 @@
+#include <stdlib.h>
+#include <glib.h>
+#include <gst/gst.h>
+
+#include "mem.h"
+
+int main(int argc,char *argv[]) {
+  GstBuffer *buf;
+  GstBuffer **buffers;
+  gpointer dummy;
+  int i,max;
+  long usage1,usage2;
+
+  gst_init(&argc,&argv);
+
+  max = atoi(argv[1]);
+
+  g_print("creating and destroying a buffer %d times...",max);
+  usage1 = vmsize();
+  for (i=0;i<max;i++) {
+    buf = gst_buffer_new();
+    gst_buffer_unref(buf);
+  }
+  usage2 = vmsize();
+  g_print(" used %d more bytes\n",usage2-usage1);
+
+//  g_print("pre-allocating space...");
+//  usage1 = vmsize();
+//  dummy = g_malloc(100*i);
+//  usage2 = vmsize();
+//  g_print(" (+%d)\n",usage2-usage1);
+
+  g_print("creating %d buffers...",max);
+  buffers = g_new(GstBuffer,i);
+  usage1 = vmsize();
+  for (i=0;i<max;i++)
+    buffers[i] = gst_buffer_new();
+//    buffers[i] = (GstBuffer *)g_malloc(1024);
+  usage2 = vmsize();
+  g_print(" (+%d bytes), and destroying them...",usage2-usage1);
+  usage1 = vmsize();
+  for (i=0;i<max;i++)
+    gst_buffer_unref(buffers[i]);
+//    g_free(buffers[i]);
+  usage2 = vmsize();
+  g_print("(-%d)\n",usage1-usage2);
+  g_free(buffers);
+
+  g_print("buffer is %d bytes, list is %d bytes\n",
+          sizeof(GstBuffer),sizeof(GList));
+
+  g_print("memory usage is %d\n",vmsize());
+}
diff --git a/test/cobin.c b/test/cobin.c
new file mode 100644 (file)
index 0000000..7f96309
--- /dev/null
@@ -0,0 +1,42 @@
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+int main(int argc,char *argv[]) {
+  GstElement *bin;
+  GstElement *src, *identity, *sink;
+
+  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  bin = gst_elementfactory_make("bin","bin");
+  g_return_if_fail(bin != NULL);
+
+  g_print("--- creating src and sink elements\n");
+  src = gst_elementfactory_make("fakesrc","src");
+  g_return_if_fail(src != NULL);
+  identity = gst_elementfactory_make(argv[1],"identity");
+  g_return_if_fail(identity != NULL);
+  sink = gst_elementfactory_make("fakesink","sink");
+  g_return_if_fail(sink != NULL);
+
+  g_print("--- about to add the elements to the pipeline\n");
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(identity));
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(sink));
+
+  g_print("--- connecting\n");
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                 gst_element_get_pad(identity,"sink"));
+  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));
+
+  g_print("\n");
+}
diff --git a/test/cothreads/Makefile.am b/test/cothreads/Makefile.am
new file mode 100644 (file)
index 0000000..f60104e
--- /dev/null
@@ -0,0 +1,5 @@
+bin_PROGRAMS = test simple
+
+test_SOURCES = test.c cothreads.c object.c looper.c
+
+simple_SOURCES = simple.c cothreads.c
diff --git a/test/cothreads/cothreads.c b/test/cothreads/cothreads.c
new file mode 100644 (file)
index 0000000..8b12b67
--- /dev/null
@@ -0,0 +1,143 @@
+#include <sys/time.h>
+#include <linux/linkage.h>
+#include <stdio.h>   
+#include <stdlib.h>
+#include <signal.h>   
+#include <setjmp.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "cothreads.h"
+
+pthread_key_t _cothread_key = -1;
+
+cothread_state *cothread_create(cothread_context *ctx) {
+  cothread_state *s;
+
+  if (pthread_self() == 0) {
+    s = (cothread_state *)malloc(sizeof(int) * COTHREAD_STACKSIZE);
+  } else {
+    char *sp = CURRENT_STACK_FRAME;
+    unsigned long *stack_end = (unsigned long *)((unsigned long)sp &
+      ~(STACK_SIZE - 1));
+    s = (cothread_state *)(stack_end + ((ctx->nthreads - 1) *
+                           COTHREAD_STACKSIZE));
+    if (mmap((char *)s,COTHREAD_STACKSIZE*(sizeof(int)),
+             PROT_READ|PROT_WRITE|PROT_EXEC,MAP_PRIVATE|MAP_ANONYMOUS,
+             -1,0) < 0) {
+      perror("mmap'ing cothread stack space");
+      return NULL;
+    }
+  }
+
+  s->ctx = ctx;
+  s->threadnum = ctx->nthreads;
+  s->flags = 0;
+  s->sp = (int *)(s + COTHREAD_STACKSIZE);
+
+  ctx->threads[ctx->nthreads++] = s;
+
+//  printf("created cothread at %p\n",s);
+
+  return s;
+}
+
+void cothread_setfunc(cothread_state *thread,cothread_func func,int argc,char **argv) {
+  thread->func = func;
+  thread->argc = argc;
+  thread->argv = argv;
+  thread->pc = (int *)func;
+}
+
+cothread_context *cothread_init() {
+  cothread_context *ctx = (cothread_context *)malloc(sizeof(cothread_context));
+
+  if (_cothread_key == -1) {
+    if (pthread_key_create(&_cothread_key,NULL) != 0) {
+      perror("pthread_key_create");
+      return;
+    }
+  }
+  pthread_setspecific(_cothread_key,ctx);
+
+  memset(ctx->threads,0,sizeof(ctx->threads));
+
+  ctx->threads[0] = (cothread_state *)malloc(sizeof(cothread_state));
+  ctx->threads[0]->ctx = ctx;
+  ctx->threads[0]->threadnum = 0;
+  ctx->threads[0]->func = NULL;
+  ctx->threads[0]->argc = 0;
+  ctx->threads[0]->argv = NULL;
+  ctx->threads[0]->flags = COTHREAD_STARTED;
+  ctx->threads[0]->sp = CURRENT_STACK_FRAME;
+  ctx->threads[0]->pc = 0;
+
+//  fprintf(stderr,"0th thread is at %p\n",ctx->threads[0]);
+
+  // we consider the initiating process to be cothread 0
+  ctx->nthreads = 1;
+  ctx->current = 0;
+
+  return ctx;
+}
+
+cothread_state *cothread_main(cothread_context *ctx) {
+//  fprintf(stderr,"returning %p, the 0th cothread\n",ctx->threads[0]);
+  return ctx->threads[0];
+}
+
+void cothread_stub() {
+  cothread_context *ctx = pthread_getspecific(_cothread_key);
+  register cothread_state *thread = ctx->threads[ctx->current];
+
+  thread->flags |= COTHREAD_STARTED;
+  thread->func(thread->argc,thread->argv);
+  thread->flags &= ~COTHREAD_STARTED;
+  thread->pc = 0;
+//  printf("uh, yeah, we shouldn't be here, but we should deal anyway\n");
+}
+
+void cothread_switch(cothread_state *thread) {
+  cothread_context *ctx;
+  cothread_state *current;
+  int enter = 0;
+//  int i;
+
+  if (thread == NULL)
+    return;
+
+  ctx = thread->ctx;
+
+  current = ctx->threads[ctx->current];
+  if (current == NULL) {
+    fprintf(stderr,"there's no current thread, help!\n");
+    exit(2);
+  }
+
+  if (current == thread) {
+    fprintf(stderr,"trying to switch to same thread, legal but not necessary\n");
+    return;
+  }
+
+  // find the number of the thread to switch to
+  ctx->current = thread->threadnum;
+//  fprintf(stderr,"about to switch to thread #%d\n",ctx->current);
+
+  /* save the current stack pointer, frame pointer, and pc */
+  __asm__("movl %%esp, %0" : "=m"(current->sp) : : "esp", "ebp");
+  enter = setjmp(current->jmp);
+  if (enter != 0)
+    return;
+  enter = 1;
+
+  /* restore stack pointer and other stuff of new cothread */
+  __asm__("movl %0, %%esp\n" : "=m"(thread->sp));
+  if (thread->flags & COTHREAD_STARTED) {
+    // switch to it
+    longjmp(thread->jmp,1);
+  } else {
+    // start it
+    __asm__("jmp " SYMBOL_NAME_STR(cothread_stub));
+  }
+}
diff --git a/test/cothreads/cothreads.h b/test/cothreads/cothreads.h
new file mode 100644 (file)
index 0000000..63fef42
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef __COTHREADS_H__
+#define __COTHREADS_H__
+
+#include <setjmp.h>
+#include <pthread.h>
+
+#define COTHREAD_STACKSIZE 8192
+#define COTHREAD_MAXTHREADS 16
+#define STACK_SIZE 0x200000
+
+#ifndef CURRENT_STACK_FRAME
+#define CURRENT_STACK_FRAME  ({ char __csf; &__csf; })
+#endif /* CURRENT_STACK_FRAME */
+
+typedef struct _cothread_state cothread_state;
+typedef struct _cothread_context cothread_context;
+
+typedef int (*cothread_func)(int argc,char **argv);
+
+#define COTHREAD_STARTED       0x01
+
+struct _cothread_state {
+  cothread_context *ctx;
+  int threadnum;
+
+  cothread_func func;
+  int argc;
+  char **argv;
+
+  int flags;
+  int *sp;
+  int *pc;
+  jmp_buf jmp;
+};
+
+struct _cothread_context {
+  cothread_state *threads[COTHREAD_MAXTHREADS];
+  int nthreads;
+  int current;
+};
+
+cothread_context *cothread_init();
+cothread_state *cothread_create(cothread_context *ctx);
+void cothread_setfunc(cothread_state *thread,cothread_func func,int argc,char **argv);
+void cothread_switch(cothread_state *thread);
+cothread_state *cothread_main(cothread_context *ctx);
+
+#endif /* __COTHREAD_H__ */
diff --git a/test/cothreads/looper.c b/test/cothreads/looper.c
new file mode 100644 (file)
index 0000000..5b67a01
--- /dev/null
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include "looper.h"
+
+void looper_loopfunc(object *obj);
+
+void looper_init(looper *l,int source) {
+  l->source = source;
+  object_setloopfunc(OBJECT(l),looper_loopfunc);
+}
+
+looper *looper_create(char *name,int source,cothread_context *ctx) {
+  looper *l = malloc(sizeof(looper));
+
+  if (l == NULL) {
+    fprintf(stderr,"sorry, couldn't allocate memory for looper\n");
+    exit(2);
+  }
+  object_init(OBJECT(l),name,ctx);
+  looper_init(l,source);
+
+  return l;
+}
+
+
+void looper_loopfunc(object *obj) {
+  looper *l = LOOPER(obj);
+
+  if (l->source) {
+    while (1) {
+      char *buf = malloc(11);
+      sprintf(buf,"Hello World!");
+      fprintf(stderr,"\npushing buffer %p with '%s'\n",buf,buf);
+      object_push(OBJECT(l)->peer,buf);                // this should switch
+    }
+  } else {
+    while (1) {
+      char *buf;
+      fprintf(stderr,"\npulling buffer\n");
+      buf = object_pull(OBJECT(l));
+      printf("got %p: '%s' from peer\n",buf,buf);
+      free(buf);
+      // return to the main process now
+      cothread_switch(cothread_main(OBJECT(l)->threadstate->ctx));
+      sleep(1000);
+    }
+  }
+}
diff --git a/test/cothreads/looper.h b/test/cothreads/looper.h
new file mode 100644 (file)
index 0000000..397f91b
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __LOOPER_H__
+#define __LOOPER_H__
+
+#include "object.h"
+
+#define LOOPER(l) ((looper *)(l))
+
+typedef struct _looper looper;
+struct _looper {
+  object object;
+
+  int source;
+};
+
+void looper_init(looper *l,int source);
+looper *looper_create(char *name,int source,cothread_context *ctx);
+
+#endif /* __LOOPER_H__ */
diff --git a/test/cothreads/object.c b/test/cothreads/object.c
new file mode 100644 (file)
index 0000000..2b03e69
--- /dev/null
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include "object.h"
+
+int object_loop_function(int argc,char **argv) {
+  object *obj = OBJECT(argv);
+  printf("hey, in loopfunc for object %p\n",obj);
+  obj->loopfunc(obj);
+}
+
+void object_init(object *obj,char *name,cothread_context *ctx) {
+  obj->threadstate = cothread_create(ctx);
+  cothread_setfunc(obj->threadstate,object_loop_function,0,(char **)obj);
+  if (obj->threadstate == NULL) {
+    fprintf(stderr,"sorry, couldn't init threadstate\n");
+    exit(2);
+  }
+  obj->loopfunc = NULL;
+  obj->name = malloc(strlen(name));
+  memcpy(obj->name,name,strlen(name));
+  obj->peer = NULL;
+}
+
+object *object_create(char *name,cothread_context *ctx) {
+  object *obj = malloc(sizeof(object));
+
+  if (obj == NULL) {
+    printf("ack!\n");
+    exit(2);
+  }
+  memset(obj,0,sizeof(object));
+  object_init(obj,name,ctx);
+
+  return obj;
+}
+
+void object_setloopfunc(object *obj,object_loopfunc func) {
+  obj->loopfunc = func;
+  fprintf(stderr,"setting object loopfunc to %p\n",func);
+}
+
+void object_setpeer(object *obj,object *peer) {
+  obj->peer = peer;
+  peer->peer = obj;
+  printf("peered %p and %p\n",obj,peer);
+}
+
+void object_push(object *obj,char *buf) {
+  obj->pen = buf;
+  cothread_switch(obj->threadstate);
+}
+
+char *object_pull(object *obj) {
+  char *buf,i=0;
+
+  if (obj == NULL) fprintf(stderr,"obj is null\n");
+  if (obj->peer == NULL) fprintf(stderr,"obj->peer is null\n");
+  if (obj->peer->threadstate == NULL) fprintf(stderr,"obj->peer->threadstate is null\n");
+
+  while (obj->pen == NULL)
+    cothread_switch(obj->peer->threadstate),i++;
+  buf = obj->pen;
+  obj->pen = NULL;
+
+  fprintf(stderr,"took %d switches to get %p from pen\n",i,buf);
+
+  return buf;
+}
+
+void object_start(object *obj) {
+  if (!obj->threadstate || !obj->loopfunc) {
+    fprintf(stderr,"ack, not complete\n");
+    fprintf(stderr,"obj->threadstate is %p, obj->loopfunc is %p\n",
+            obj->threadstate,obj->loopfunc);
+    exit(2);
+  }
+  cothread_switch(obj->threadstate);
+  fprintf(stderr,"returned from cothread stuff into end of object_start()\n");
+}
diff --git a/test/cothreads/object.h b/test/cothreads/object.h
new file mode 100644 (file)
index 0000000..9b4ebb6
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __OBJECT_H__
+#define __OBJECT_H__
+
+#include "cothreads.h"
+
+#define OBJECT(obj) ((object*)(obj))
+
+typedef struct _object object;
+
+typedef void (*object_loopfunc)(object *obj);
+
+struct _object {
+  cothread_state *threadstate;
+  object_loopfunc loopfunc;
+
+  char *name;
+  object *peer;
+
+  void *pen;
+};
+
+void object_init(object *obj,char *name,cothread_context *ctx);
+object *object_create(char *name,cothread_context *ctx);
+void object_setloopfunc(object *obj,object_loopfunc func);
+void object_setpeer(object *obj,object *peer);
+void object_push(object *obj,char *buf);
+char *object_pull(object *obj);
+int object_loop_function(int argc,char **argv);
+
+#endif /* __OBJECT_H__ */
diff --git a/test/cothreads/simple.c b/test/cothreads/simple.c
new file mode 100644 (file)
index 0000000..3e1e950
--- /dev/null
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include "cothreads.h"
+
+// cothread_context is passed in argv
+int loopfunc(int argc,char **argv) {
+  fprintf(stderr,"SIMPLE: in loopfunc\n");
+  cothread_switch((cothread_context *)cothread_main(argv));
+}
+
+int main(int argc,char *argv[]) {
+  cothread_context *ctx;
+  cothread_state *state;
+
+  ctx = cothread_init();
+  state = cothread_create(ctx);
+  cothread_setfunc(state,loopfunc,0,(char **)ctx);
+
+  fprintf(stderr,"SIMPLE: about to switch to cothread 1\n");
+  cothread_switch(state);
+  fprintf(stderr,"SIMPLE: back from cothread_switch\n");
+
+  return 0;
+}
diff --git a/test/cothreads/test.c b/test/cothreads/test.c
new file mode 100644 (file)
index 0000000..42934ac
--- /dev/null
@@ -0,0 +1,17 @@
+#include <stdio.h>
+
+#include "cothreads.h"
+#include "object.h"
+#include "looper.h"
+
+int main(int argc,char *argv[]) {
+  cothread_context *ctx = cothread_init();
+  looper *l1,*l2;
+
+  l1 = looper_create("looperone",1,ctx);
+  l2 = looper_create("loopertwo",0,ctx);
+  object_setpeer(OBJECT(l1),OBJECT(l2));
+
+  fprintf(stderr,"about to start l1\n\n");
+  object_start(l1);
+}
diff --git a/test/dvdcat.c b/test/dvdcat.c
new file mode 100644 (file)
index 0000000..4e712e7
--- /dev/null
@@ -0,0 +1,46 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+int main(int argc,char *argv[]) {
+  GstElement *pipeline;
+  GstElement *src, *sink;
+  int fd;
+
+  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+//  gst_plugin_load("dvdsrc");
+
+  fd = creat("output.vob",0644);
+
+  pipeline = gst_elementfactory_make("pipeline","dvdcat");
+  g_return_if_fail(pipeline != NULL);
+
+  src = gst_elementfactory_make("dvdsrc","src");
+  g_return_if_fail(src != NULL);
+  gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL);
+  if (argc >= 3)
+    gtk_object_set(GTK_OBJECT(src),"offset",atoi(argv[2]),NULL);
+  sink = gst_elementfactory_make("fdsink","sink");
+  g_return_if_fail(sink != NULL);
+  gtk_object_set(GTK_OBJECT(sink),"fd",fd,NULL);
+
+  // construct the outer pipeline
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(sink));
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(sink,"sink"));
+
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
+
+//  while (GST_STATE_IS_SET(src,GST_STATE_RUNNING))
+//  while (1)
+  while (GST_STATE_IS_SET(src,1<<16))
+    gst_src_push(GST_SRC(src));
+}
diff --git a/test/fake.c b/test/fake.c
new file mode 100644 (file)
index 0000000..794e22e
--- /dev/null
@@ -0,0 +1,37 @@
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+int main(int argc,char *argv[]) {
+  GstPipeline *pipeline;
+  GstElement *src, *sink;
+  GstPad *srcpad, *sinkpad;
+
+//  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+
+  pipeline = gst_pipeline_new("pipeline");
+  g_return_if_fail(pipeline != NULL);
+
+  g_print("--- creating src and sink elements\n");
+  src = gst_elementfactory_make("fakesrc","src");
+  g_return_if_fail(src != NULL);
+  sink = gst_elementfactory_make("fakesink","sink");
+  g_return_if_fail(sink != NULL);
+
+  g_print("--- about to add the elements to the pipeline\n");
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(sink));
+
+  g_print("--- getting pads\n");
+  srcpad = gst_element_get_pad(src,"src");
+  g_return_if_fail(srcpad != NULL);
+  sinkpad = gst_element_get_pad(sink,"sink");
+  g_return_if_fail(srcpad != NULL);
+
+  g_print("--- connecting\n");
+  gst_pad_connect(srcpad,sinkpad);
+
+  g_print("--- setting up\n");
+  gst_pipeline_iterate(pipeline);
+}
diff --git a/test/gsttracedump.c b/test/gsttracedump.c
new file mode 100644 (file)
index 0000000..f5159bb
--- /dev/null
@@ -0,0 +1,15 @@
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <gst/gsttrace.h>
+
+int main(int argc,char *argv[]) {
+  gchar *filename = argv[1];
+  int fd = open(filename,O_RDONLY);
+  GstTraceEntry entry;
+
+  while (read(fd,&entry,sizeof(entry)))
+    g_print("%Ld(%ld) 0x%08lx: %s\n",entry.timestamp,entry.sequence,
+            entry.data,entry.message);
+}
diff --git a/test/jitter.c b/test/jitter.c
new file mode 100644 (file)
index 0000000..8d66064
--- /dev/null
@@ -0,0 +1,26 @@
+#include <stdio.h>
+
+int main() {
+  char line[256];
+  unsigned long long a = 0,b;
+  unsigned long long difference;
+  unsigned long long mindiff = -1, maxdiff = 0;
+  unsigned long long total = 0;
+  int samples = 0;
+
+  while (gets(line)) {
+    sscanf(line,"%Ld",&b);
+    if (a) {
+      difference = b - a;
+      printf("difference is %Ld\n",difference);
+      if (difference > maxdiff) maxdiff = difference;
+      if (difference < mindiff) mindiff = difference;
+      total += difference;
+      samples++;
+    }
+    a = b;
+  }
+  printf("min difference is %Ld, avg %Ld, max is %Ld\n",
+         mindiff,total/samples,maxdiff);
+  printf("jitter is %Ld\n",maxdiff-mindiff);
+}
diff --git a/test/m.c b/test/m.c
new file mode 100644 (file)
index 0000000..f210e06
--- /dev/null
+++ b/test/m.c
@@ -0,0 +1,76 @@
+#include <gst/gst.h>
+#include <unistd.h>
+
+void eof(GstSrc *src) {
+  g_print("eof\n");
+  exit(0);
+}
+
+int main(int argc,char *argv[]) {
+  guint16 type;
+  GList *factories;
+  GstElementFactory *parsefactory;
+  GstElement *bin, *src, *parse, *sink;
+  GList *padlist;
+  guchar *filename;
+
+  if (argc == 2)
+    filename = argv[1];
+  else
+    filename = "-";
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  bin = gst_bin_new("bin");
+
+  if (!strcmp(filename,"-"))
+    src = gst_fdsrc_new_with_fd("src",STDIN_FILENO);
+  else if (!strncmp(filename,"http://",7))
+    src = gst_httpsrc_new_with_url("src",filename);
+  else
+    src = gst_asyncdisksrc_new_with_file("src",filename);
+
+  /* now it's time to get the parser */
+  type = gst_type_find_by_mime("audio/mpeg");
+  factories = gst_type_get_sinks(type);
+  if (factories != NULL)
+    parsefactory = GST_ELEMENTFACTORY(factories->data);
+  else {
+    g_print("sorry, can't find anyone registered to sink 'mp3'\n");
+    return 1;
+  }
+  parse = gst_elementfactory_create(parsefactory,"parser");
+  if (parse == NULL) {
+    g_print("sorry, couldn't create parser\n");
+    return 1;
+  }
+
+
+  sink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(src),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),src);
+  gst_bin_add(GST_BIN(bin),parse);
+  gst_bin_add(GST_BIN(bin),sink);
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(sink,"sink"));
+
+  while(1) {
+    g_print(".");
+    gst_src_push(GST_SRC(src));
+  }
+
+  gst_object_destroy(GST_OBJECT(sink));
+  gst_object_destroy(GST_OBJECT(parse));
+  gst_object_destroy(GST_OBJECT(src));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/mcut.c b/test/mcut.c
new file mode 100644 (file)
index 0000000..95f1969
--- /dev/null
@@ -0,0 +1,77 @@
+#include <gst/gst.h>
+
+void eof(GstSrc *src) {
+  g_print("have eof, quitting\n");
+  exit(0);
+}
+
+int main(int argc,char *argv[]) {
+  GstType *type;
+  GList *factories;
+  GstElementFactory *parsefactory;
+  GstElement *bin, *src, *parse, *sink;
+  GList *padlist;
+  guchar *filename;
+  glong length = 0, size = 4180, skip = 8360, offset = 0;
+
+  if (argc == 2)
+    filename = argv[1];
+  else {
+    g_print("sorry, need a filename now\n");
+    exit(1);
+  }
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  bin = gst_bin_new("bin");
+
+  src = gst_asyncdisksrc_new("src");
+  g_print("created disksrc\n");
+  gtk_object_set(GTK_OBJECT(src),"location",filename,NULL);
+  length = gst_util_get_long_arg(GST_OBJECT(src),"length");
+  g_print("file is %d bytes long\n",length);
+
+  /* now it's time to get the parser */
+  type = gst_type_get_by_mime("audio/mp3");
+  factories = gst_type_get_sinks(type);
+  if (factories != NULL)
+    parsefactory = GST_ELEMENTFACTORY(factories->data);
+  else {
+    g_print("sorry, can't find anyone registered to sink 'mp3'\n");
+    return 1;
+  }
+  parse = gst_elementfactory_create(parsefactory,"parser");
+  if (parse == NULL) {
+    g_print("sorry, couldn't create parser\n");
+    return 1;
+  }
+
+
+  sink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(src),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),src);
+  gst_bin_add(GST_BIN(bin),parse);
+  gst_bin_add(GST_BIN(bin),sink);
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(sink,"sink"));
+
+  while(offset < length) {
+    gst_src_push_region(GST_SRC(src),offset,size);
+    offset += skip;
+  }
+
+  gst_object_destroy(GST_OBJECT(sink));
+  gst_object_destroy(GST_OBJECT(parse));
+  gst_object_destroy(GST_OBJECT(src));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/mem.c b/test/mem.c
new file mode 100644 (file)
index 0000000..8e1c573
--- /dev/null
@@ -0,0 +1,22 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+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/test/mem.h b/test/mem.h
new file mode 100644 (file)
index 0000000..28999db
--- /dev/null
@@ -0,0 +1 @@
+int vmsize();
diff --git a/test/mp3.c b/test/mp3.c
new file mode 100644 (file)
index 0000000..1fd09e4
--- /dev/null
@@ -0,0 +1,63 @@
+#include <gnome.h>
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+static gboolean playing = TRUE;
+
+void eof(GstSrc *src) {
+  DEBUG("have EOF\n");
+  playing = FALSE;
+}
+
+int main(int argc,char *argv[]) {
+  GstElement *bin;
+  GstElementFactory *srcfactory;
+  GstElement *src;
+  GstElementFactory *mp3factory;
+  GstElement *mp3;
+  GstElementFactory *sinkfactory;
+  GstElement *sink;
+
+  GtkWidget *appwindow;
+
+  _gst_plugin_spew = TRUE;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  bin = gst_bin_new("bin");
+
+  srcfactory = gst_elementfactory_find("disksrc");
+  if (argc == 3)
+    mp3factory = gst_elementfactory_find(argv[2]);
+  else
+    mp3factory = gst_elementfactory_find("xa");
+  sinkfactory = gst_elementfactory_find("audiosink");
+
+  src = gst_elementfactory_create(srcfactory,"src");
+  g_return_if_fail(src != NULL);
+  gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL);
+  mp3 = gst_elementfactory_create(mp3factory,"mp3");
+  g_return_if_fail(mp3 != NULL);
+  sink = gst_elementfactory_create(sinkfactory,"sink");
+  g_return_if_fail(sink != NULL);
+
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(mp3));
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(sink));
+
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(mp3,"sink"));
+  gst_pad_connect(gst_element_get_pad(mp3,"src"),
+                  gst_element_get_pad(sink,"sink"));
+
+  gtk_signal_connect(GTK_OBJECT(src),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);   
+
+  gst_element_set_state(GST_ELEMENT(bin),GST_STATE_RUNNING);
+  gst_element_set_state(GST_ELEMENT(bin),GST_STATE_PLAYING);
+
+  while (playing)
+    gst_src_push(GST_SRC(src));
+}
diff --git a/test/mp3parse.c b/test/mp3parse.c
new file mode 100644 (file)
index 0000000..284601f
--- /dev/null
@@ -0,0 +1,55 @@
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+void mp3parse_info_chain(GstPad *pad,GstBuffer *buf) {
+  g_print("got buffer of size %d\n",GST_BUFFER_SIZE(buf));
+  gst_buffer_unref(buf);
+}
+
+int main(int argc,char *argv[]) {
+  GstPipeline *pipeline;
+  GstElementFactory *srcfactory, *parsefactory;
+  GstElement *src, *parse;
+  GstPad *infopad;
+
+  g_print("have %d args\n",argc);
+
+  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+// gst_plugin_load("mp3parse");
+  gst_plugin_load_all();
+
+  pipeline = gst_pipeline_new("pipeline");
+  g_return_if_fail(pipeline != NULL);
+
+  srcfactory = gst_elementfactory_find("disksrc");
+  g_return_if_fail(srcfactory != NULL);
+  parsefactory = gst_elementfactory_find("mp3parse");
+  g_return_if_fail(parsefactory != NULL);
+
+  src = gst_elementfactory_create(srcfactory,"src");
+  g_return_if_fail(src != NULL);
+  gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL);
+  g_print("should be using file '%s'\n",argv[1]);
+  parse = gst_elementfactory_create(parsefactory,"parse");
+  g_return_if_fail(parse != NULL);
+
+  infopad = gst_pad_new("sink",GST_PAD_SINK);
+  gst_pad_set_chain_function(infopad,mp3parse_info_chain);
+
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(parse));
+
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  infopad);
+
+  g_print("setting to RUNNING state\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
+
+  g_print("about to enter loop\n");
+  while (1)
+    gst_src_push(GST_SRC(src));
+}
diff --git a/test/mp3play.c b/test/mp3play.c
new file mode 100644 (file)
index 0000000..4f00807
--- /dev/null
@@ -0,0 +1,59 @@
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+int main(int argc,char *argv[]) {
+  GstPipeline *pipeline;
+  GstElementFactory *srcfactory, *parsefactory, *decodefactory, *playfactory;
+  GstElement *src, *parse, *decode, *play;
+  GstPad *infopad;
+
+  g_print("have %d args\n",argc);
+
+  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+// gst_plugin_load("mp3parse");
+  gst_plugin_load_all();
+
+  pipeline = gst_pipeline_new("pipeline");
+  g_return_if_fail(pipeline != NULL);
+
+  srcfactory = gst_elementfactory_find("disksrc");
+  g_return_if_fail(srcfactory != NULL);
+  parsefactory = gst_elementfactory_find("mp3parse");
+  g_return_if_fail(parsefactory != NULL);
+  decodefactory = gst_elementfactory_find("mpg123");
+  g_return_if_fail(decodefactory != NULL);
+  playfactory = gst_elementfactory_find("audiosink");
+  g_return_if_fail(playfactory != NULL);
+
+  src = gst_elementfactory_create(srcfactory,"src");
+  g_return_if_fail(src != NULL);
+  gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL);
+  g_print("should be using file '%s'\n",argv[1]);
+  parse = gst_elementfactory_create(parsefactory,"parse");
+  g_return_if_fail(parse != NULL);
+  decode = gst_elementfactory_create(decodefactory,"decode");
+  g_return_if_fail(decode != NULL);
+  play = gst_elementfactory_create(playfactory,"play");
+  g_return_if_fail(play != NULL);
+
+  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(decode));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(play));
+
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(decode,"sink"));
+  gst_pad_connect(gst_element_get_pad(decode,"src"),
+                  gst_element_get_pad(play,"sink"));
+
+  g_print("setting to RUNNING state\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
+
+  g_print("about to enter loop\n");
+  while (1)
+    gst_src_push(GST_SRC(src));
+}
diff --git a/test/mpeg2parse.c b/test/mpeg2parse.c
new file mode 100644 (file)
index 0000000..fa3c633
--- /dev/null
@@ -0,0 +1,95 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+static int ac3fd;
+static gchar *desired_stream;
+
+void mpeg2parse_write_ac3(GstPad *pad,GstBuffer *buf) {
+  g_print(".");
+//  g_print("MPEG2PARSE: got AC3 buffer of size %d\n",GST_BUFFER_SIZE(buf));
+  write(ac3fd,GST_BUFFER_DATA(buf),GST_BUFFER_SIZE(buf));
+  gst_buffer_unref(buf);
+}
+
+void mpeg2parse_info_chain(GstPad *pad,GstBuffer *buf) {
+//  g_print("MPEG2PARSE: got buffer of size %d\n",GST_BUFFER_SIZE(buf));
+  gst_buffer_unref(buf);
+}
+
+void mpeg2parse_newpad(GstElement *parser,GstPad *pad) {
+  GstPad *infopad;
+
+  g_print("MPEG2PARSE: have new pad \"%s\" from parser\n",
+          gst_pad_get_name(pad));
+
+  infopad = gst_pad_new("sink",GST_PAD_SINK);
+  if (strcmp(gst_pad_get_name(pad),desired_stream) == 0)
+    gst_pad_set_chain_function(infopad,mpeg2parse_write_ac3);
+  else
+    gst_pad_set_chain_function(infopad,mpeg2parse_info_chain);
+  gst_pad_connect(pad,infopad);
+}
+
+int main(int argc,char *argv[]) {
+  GstPipeline *pipeline;
+  GstElement *src, *parse, *out;
+  GstPad *infopad;
+  int i,c;
+
+  g_print("have %d args\n",argc);
+
+  _gst_plugin_spew = TRUE;
+  gst_init(&argc,&argv);
+//  gst_plugin_load("mpeg2parse");
+  gst_plugin_load_all();
+
+  ac3fd = creat("output.ac3",S_IREAD|S_IWRITE);
+
+  pipeline = gst_pipeline_new("pipeline");
+  g_return_if_fail(pipeline != NULL);
+
+  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_if_fail(src != NULL);
+  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");
+  g_return_if_fail(parse != NULL);
+
+  gtk_signal_connect(GTK_OBJECT(parse),"new_pad",mpeg2parse_newpad,NULL);
+
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(parse));
+
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(parse,"sink"));
+
+  g_print("setting to RUNNING state\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
+
+  if (argc >= 4) c = atoi(argv[3]);
+  else c = 4;
+  g_print("c is %d\n",c);
+
+  if (argc >= 5) desired_stream = argv[4];
+  else desired_stream = "private_stream_1.0";
+
+  g_print("\n");
+  for (i=0;i<c;i++) {
+    g_print("\n");
+    gst_src_push(GST_SRC(src));
+  }
+}
diff --git a/test/mpg123.c b/test/mpg123.c
new file mode 100644 (file)
index 0000000..e242b9c
--- /dev/null
@@ -0,0 +1,67 @@
+#include <gst/gst.h>
+
+void eof(GstSrc *src) {
+  g_print("have eof, quitting\n");
+  exit(0);
+}
+
+int main(int argc,char *argv[]) {
+  GList *factories;
+  GstElementFactory *parsefactory;
+  GstElement *bin, *disksrc, *parse, *audiosink;
+  GList *padlist;
+  guchar *filename;
+  int i;
+
+  if (argc == 2)
+    filename = argv[1];
+  else
+    filename = "ctp2.mp3";
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+  g_print("\n");
+
+  bin = gst_bin_new("bin");
+
+  disksrc = gst_disksrc_new("disksrc");
+  g_print("created disksrc\n");
+  gtk_object_set(GTK_OBJECT(disksrc),"location",filename,NULL);
+  gtk_object_set(GTK_OBJECT(disksrc),"bytesperread",1048576,NULL);
+
+  /* now it's time to get the parser */
+  parsefactory = gst_plugin_find_elementfactory("xing");
+  parse = gst_elementfactory_create(parsefactory,"parser");
+  if (parse == NULL) {
+    g_print("sorry, couldn't create parser\n");
+    return 1;
+  }
+
+
+  audiosink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(disksrc),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(disksrc));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(parse));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(audiosink));
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(disksrc,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(audiosink,"sink"));
+
+  for (i=0;i<4;i++) {
+    g_print("\n");
+    gst_disksrc_push(GST_SRC(disksrc));
+  }
+
+  gst_object_destroy(GST_OBJECT(audiosink));
+  gst_object_destroy(GST_OBJECT(parse));
+  gst_object_destroy(GST_OBJECT(disksrc));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/p.c b/test/p.c
new file mode 100644 (file)
index 0000000..457e4b3
--- /dev/null
+++ b/test/p.c
@@ -0,0 +1,55 @@
+#include <glib.h>
+#include <gst/gst.h>
+
+void eof(GstSrc *src) {
+  g_print("have eof, quitting\n");
+  exit(0);
+}
+
+int main(int argc,char *argv[]) {
+  GstElement *bin, *disksrc, *p, *audiosink;
+  GList *padlist;
+
+  gst_init(&argc,&argv);
+
+  bin = gst_bin_new("bin");
+
+  disksrc = gst_disksrc_new("disksrc");
+  g_print("created disksrc\n");
+  if (argc == 2)
+    gst_disksrc_set_filename(disksrc,argv[1]);
+  else
+    gst_disksrc_set_filename(disksrc,"mendelssohn.1.raw");
+  gst_disksrc_set_bytesperread(disksrc,32768);
+  g_print("loaded file '%s'\n",gst_disksrc_get_filename(disksrc));
+
+  p = gst_plugin_find_elementfactory("pipe");
+  audiosink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(disksrc),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(disksrc));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(p));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(audiosink));
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(disksrc,"src"),
+                  gst_element_get_pad(p,"sink"));
+  gst_pad_connect(gst_element_get_pad(p,"src"),
+                  gst_element_get_pad(audiosink,"sink"));
+
+  /* set soundcard properties */
+  gst_audiosink_set_format(GST_AUDIOSINK(audiosink),AFMT_S16_BE);
+  gst_audiosink_set_channels(GST_AUDIOSINK(audiosink),2);
+  gst_audiosink_set_frequency(GST_AUDIOSINK(audiosink),44100);
+
+  while(1)
+    gst_disksrc_push(GST_SRC(disksrc));
+
+  gst_object_destroy(GST_OBJECT(audiosink));
+  gst_object_destroy(GST_OBJECT(disksrc));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/plugin.c b/test/plugin.c
new file mode 100644 (file)
index 0000000..4da003f
--- /dev/null
@@ -0,0 +1,17 @@
+#include <gst/gst.h>
+
+int main(int argc,char *argv[]) {
+  GstElementFactory *parseau_factory;
+  GstElement *parseau;
+
+  gst_init(&argc,&argv);
+
+  gst_plugin_load_all();
+
+  parseau_factory = gst_plugin_find_elementfactory("parseau");
+  g_print("parseau_factory is %p\n",parseau_factory);
+
+  parseau = gst_elementfactory_create(parseau_factory,"parser");
+  g_print("got parseau '%s' from plugin!!!\n",
+          gst_object_get_name(GST_OBJECT(parseau)));
+}
diff --git a/test/push.c b/test/push.c
new file mode 100644 (file)
index 0000000..7af0ea2
--- /dev/null
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+
+#include <gst/gst.h>
+
+int eofflag = 0;
+
+void eof(GstSrc *src) {
+  eofflag = 1;
+}
+
+int main(int argc,char *argv[]) {
+  struct sockaddr_in src_addr, dst_addr;
+  int sockaddrlen;
+  int lsock;
+  int one = 1;
+  int sndbuf = 4096;
+  int sock;
+  GstElement *src,*sink;
+
+  gst_init(&argc,&argv);
+
+  lsock = socket(AF_INET,SOCK_STREAM,0);
+  if (lsock < 0) {
+    perror("creating socket");
+    exit(1);
+  }
+
+  if (setsockopt(lsock,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one))) {
+    perror("setsockopt(SO_REUSEADDR)");
+    exit(1);
+  }
+
+  src_addr.sin_family = AF_INET;
+  src_addr.sin_addr.s_addr = INADDR_ANY;
+  src_addr.sin_port = htons(8001);
+
+  if (bind(lsock,(struct sockaddr *)&src_addr,sizeof(src_addr))) {
+    perror("binding");
+    exit(1);
+  }
+
+  if (setsockopt(lsock,SOL_SOCKET,SO_SNDBUF,(char *)&sndbuf,sizeof(sndbuf))) {
+    perror("setsockopt(SO_SNDBUF)");
+    exit(1);
+  }
+
+  g_print("listening\n");
+  listen(lsock,8);
+
+  sock = accept(lsock,(struct sockaddr *)&dst_addr,&sockaddrlen);
+  g_print("connected\n");
+
+  close(lsock);
+
+  g_print("creating pipeline\n");
+  src = gst_disksrc_new_with_location("src",argv[1]);
+  g_print("have src\n");
+  gtk_signal_connect(GTK_OBJECT(src),"eof",GTK_SIGNAL_FUNC(eof),NULL);
+  g_print("have eof signal\n");
+  sink = gst_fdsink_new_with_fd("sink",sock);
+  g_print("have sink\n");
+
+  g_print("connecting\n");
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(sink,"sink"));
+
+  g_print("pushing...\n");
+  while (!eofflag)
+    gst_src_push(GST_SRC(src));
+
+  sleep(1);
+  close(sock);
+}
diff --git a/test/qtest.c b/test/qtest.c
new file mode 100644 (file)
index 0000000..e7c2d52
--- /dev/null
@@ -0,0 +1,104 @@
+#include <glib.h>
+#include <gst/gst.h>
+#include <gst/elements/gstqueue.h>
+
+extern gboolean _gst_plugin_spew;
+
+/* we don't need a lock around the application's state yet, since it's 1
+   bit.  as it gets more fleshed in, we'll need a lock so the callbacks
+   don't screw around with state unexpectedly */
+static gboolean playing = TRUE;
+
+void eof(GstSrc *src) {
+  DEBUG("have EOF\n");
+  playing = FALSE;
+}
+
+int main(int argc,char *argv[]) {
+  GstElement *pipeline;
+  GstElement *decodethread;
+  GstElementFactory *srcfactory;
+  GstElement *src;
+  GstElementFactory *decodefactory;
+  GstElement *decode;
+  GstElementFactory *queuefactory;
+  GstElement *queue;
+  GstElement *playthread;
+  GstElementFactory *sinkfactory;
+  GstElement *sink;
+
+  gst_init(&argc,&argv);
+
+  /* first create the main pipeline */
+  pipeline = gst_pipeline_new("pipeline");
+
+  /* then the decode thread, source, and decoder */
+  decodethread = gst_thread_new("decodethread");
+
+  srcfactory = gst_elementfactory_find("asyncdisksrc");
+  src = gst_elementfactory_create(srcfactory,"src");
+  gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL);
+  gst_bin_add(GST_BIN(decodethread),GST_ELEMENT(src));
+
+  _gst_plugin_spew = TRUE;
+
+  if (argc > 2)
+    gst_plugin_load(argv[2]);
+  else
+    gst_plugin_load_all();
+  decodefactory = gst_elementfactory_find("mpg123");
+  decode = gst_elementfactory_create(decodefactory,"decode");
+  gst_bin_add(GST_BIN(decodethread),GST_ELEMENT(decode));
+  gst_element_add_ghost_pad(GST_ELEMENT(decodethread),
+                            gst_element_get_pad(decode,"src"));
+
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(decode,"sink"));
+
+  /* then the play thread and sink */
+  playthread = gst_thread_new("playthread");
+
+  sinkfactory = gst_elementfactory_find("audiosink");
+  sink = gst_elementfactory_create(sinkfactory,"sink");
+  gst_bin_add(GST_BIN(playthread),GST_ELEMENT(sink));
+  gst_element_add_ghost_pad(GST_ELEMENT(playthread),
+                            gst_element_get_pad(sink,"sink"));
+
+  /* create the queue */
+  queuefactory = gst_elementfactory_find("queue");
+  queue = gst_elementfactory_create(queuefactory,"queue");
+
+  /* add threads to the main pipeline */
+  gst_bin_add(GST_BIN(pipeline),decodethread);
+  gst_bin_add(GST_BIN(pipeline),queue);
+  gst_bin_add(GST_BIN(pipeline),playthread);
+
+  gst_pad_connect(gst_element_get_pad(decodethread,"src"),
+//                  gst_element_get_pad(queue,"sink"));
+//  gst_pad_connect(gst_element_get_pad(queue,"src"),
+                  gst_element_get_pad(playthread,"sink"));
+
+  gtk_signal_connect(GTK_OBJECT(src),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  g_print("\nsetting up the decode thread to *NOT* thread\n");
+//  gtk_object_set(GTK_OBJECT(decodethread),"create_thread",TRUE,NULL);
+  gtk_object_set(GTK_OBJECT(playthread),"create_thread",FALSE,NULL);
+
+  g_print("\neverything's built, setting it up to be runnable\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
+
+  g_print("\nok, runnable, hitting 'play'...\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
+
+  g_print("\niterating on %p and %p\n",decodethread,playthread);
+  while (playing) {
+    gst_thread_iterate(GST_THREAD(playthread));
+    /* buffers got wedged in the queue, unstick them */
+//    while (((GstQueue *)queue)->buffers_queued)
+//      gst_connection_push(GST_CONNECTION(queue));
+//      gst_thread_iterate(GST_THREAD(playthread));
+//    g_print("stuffed and unstuck the queue\n");
+//    sleep(1);
+  }
+}
diff --git a/test/r.c b/test/r.c
new file mode 100644 (file)
index 0000000..5acc622
--- /dev/null
+++ b/test/r.c
@@ -0,0 +1,51 @@
+#include <glib.h>
+#include <gst/gst.h>
+
+void eof(GstSrc *src) {
+  g_print("have eof, quitting\n");
+  exit(0);
+}
+
+int main(int argc,char *argv[]) {
+  GstElement *bin, *disksrc, *audiosink;
+  GList *padlist;
+
+  gst_init(&argc,&argv);
+
+  bin = gst_bin_new("bin");
+
+  disksrc = gst_disksrc_new("disksrc");
+  g_print("created disksrc\n");
+  if (argc == 2)
+    gst_disksrc_set_filename(disksrc,argv[1]);
+  else
+    gst_disksrc_set_filename(disksrc,"mendelssohn.1.raw");
+  gtk_object_set(GTK_OBJECT(disksrc),"bytesperread",32768,NULL);
+  g_print("loaded file '%s'\n",gst_disksrc_get_filename(disksrc));
+
+  audiosink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(disksrc),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(disksrc));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(audiosink));
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(disksrc,"src"),
+                  gst_element_get_pad(audiosink,"sink"));
+
+  /* set soundcard properties */
+  gst_audiosink_set_format(GST_AUDIOSINK(audiosink),AFMT_S16_BE);
+  gst_audiosink_set_channels(GST_AUDIOSINK(audiosink),2);
+  gst_audiosink_set_frequency(GST_AUDIOSINK(audiosink),44100);
+
+  while(1)
+    gst_disksrc_push(GST_SRC(disksrc));
+
+  gst_object_destroy(GST_OBJECT(audiosink));
+  gst_object_destroy(GST_OBJECT(disksrc));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/record.c b/test/record.c
new file mode 100644 (file)
index 0000000..fe5b96c
--- /dev/null
@@ -0,0 +1,43 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <gst/gst.h>
+
+int main(int argc,char *argv[]) {
+  int fd;
+  GstElement *pipeline, *audiosrc, *fdsink;
+  GstElementFactory *audiosrcfactory, *fdsinkfactory;
+  GList *padlist;
+
+  gst_init(&argc,&argv);
+
+  pipeline = gst_pipeline_new("pipeline");
+
+  audiosrcfactory = gst_elementfactory_find("audiosrc");
+  audiosrc = gst_elementfactory_create(audiosrcfactory,"audiosrc");
+
+  fd = open(argv[1],O_CREAT|O_RDWR);
+
+  fdsinkfactory = gst_elementfactory_find("fdsink");
+  fdsink = gst_elementfactory_create(fdsinkfactory,"fdsink");
+  gtk_object_set(GTK_OBJECT(fdsink),"fd",fd,NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(audiosrc));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(fdsink));
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(audiosrc,"src"),
+                  gst_element_get_pad(fdsink,"sink"));
+
+  g_print("\neverything's built, setting it up to be runnable\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
+
+  g_print("\nok, runnable, hitting 'play'...\n");
+  gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
+
+  while(1)
+    gst_src_push(GST_SRC(audiosrc));
+}
+
diff --git a/test/s.c b/test/s.c
new file mode 100644 (file)
index 0000000..cefab58
--- /dev/null
+++ b/test/s.c
@@ -0,0 +1,116 @@
+#include <glib.h>
+#include <gst/gst.h>
+#include <ghttp.h>
+
+void eof(GstSrc *src) {
+  g_print("have eof, quitting\n");
+  exit(0);
+}
+
+int main(int argc,char *argv[]) {
+  guint16 mp3type;
+  GList *factories;
+  GstElementFactory *parsefactory;
+  GstElement *bin, *src, *parse, *audiosink;
+  GList *padlist;
+  ghttp_request *pls;
+  guchar *plsbuf;
+  gint plsbuflen,parsedlen = 0;
+  guchar *url,*local;
+  GSList *urls = NULL;
+
+  pls = ghttp_request_new();
+  if (argc >= 2)
+    ghttp_set_uri(pls,argv[1]);
+  else
+    ghttp_set_uri(pls,"http://209.127.18.4:9000");
+  ghttp_prepare(pls);
+  ghttp_process(pls);
+  plsbuf = ghttp_get_body(pls);
+  plsbuflen = ghttp_get_body_len(pls);
+
+  while (parsedlen < plsbuflen) {
+    local = plsbuf + parsedlen;
+    if ((*local != '[') && (*local != '\n')) {         /* t/v pair */
+      if (!strncmp(local,"File1=",4)) {                        /* if file */
+        url = strchr(local,'=') + 1;                   /* url after = */
+        local = strchr(url,'\n');                      /* ffwd after = */
+        *(local)++ = 0;                                        /* nullz url */
+        g_print("prepending '%s' to list\n",url);
+        urls = g_slist_prepend(urls,g_strdup(url));
+       /* local should point to next line now */
+      } else {
+        local = strchr(local,'\n') + 1;                        /* skip line */
+      }
+    } else {
+      local = strchr(local,'\n') + 1;                  /* skip line */
+    }
+    /* we can consider that line parsed... */
+    parsedlen = local - plsbuf;
+  }
+  if (urls == NULL) {
+    g_print("couldn't find any streams\n");
+    exit(1);
+  }
+  ghttp_request_destroy(pls);
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  bin = gst_bin_new("bin");
+
+  src = gst_httpsrc_new("src");
+  if (argc == 3) {
+    int i;
+    for (i=1;i<atoi(argv[2]);i++)
+      urls = g_slist_next(urls);
+  }
+  g_print("loading shoutcast server %s\n",urls->data);
+  gtk_object_set(GTK_OBJECT(src),"location",urls->data,NULL);
+  g_print("created src\n");
+
+  /* now it's time to get the parser */
+  mp3type = gst_type_find_by_mime("audio/mpeg");
+  factories = gst_type_get_sinks(mp3type);
+  if (factories != NULL)
+    parsefactory = GST_ELEMENTFACTORY(factories->data);
+  else {
+    g_print("sorry, can't find anyone registered to sink 'mp3'\n");
+    return 1;
+  }
+  parse = gst_elementfactory_create(parsefactory,"parser");
+  if (parse == NULL) {
+    g_print("sorry, couldn't create parser\n");
+    return 1;
+  }
+
+
+  audiosink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(src),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(src));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(parse));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(audiosink));
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(audiosink,"sink"));
+
+
+  sleep(5); /* to let the network buffer fill a bit */
+  while(1) {
+    g_print("calling gst_httpsrc_push\n");
+    gst_httpsrc_push(GST_SRC(src));
+  }
+
+  gst_object_destroy(GST_OBJECT(audiosink));
+  gst_object_destroy(GST_OBJECT(parse));
+  gst_object_destroy(GST_OBJECT(src));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/spectrum.c b/test/spectrum.c
new file mode 100644 (file)
index 0000000..190157c
--- /dev/null
@@ -0,0 +1,80 @@
+#include <gnome.h>
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+void spectrum_chain(GstPad *pad,GstBuffer *buf);
+gboolean idle_func(gpointer data);
+
+GtkWidget *drawingarea;
+
+int main(int argc,char *argv[]) {
+  GstElement *bin;
+  GstElementFactory *srcfactory;
+  GstElement *src;
+  GstElementFactory *spectrumfactory;
+  GstElement *spectrum;
+  GstPad *spectrumpad;
+
+  GtkWidget *appwindow;
+
+  _gst_plugin_spew = TRUE;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load("libgstspectrum.so");
+  gnome_init("Spectrum","0.0.1",argc,argv);
+
+  bin = gst_bin_new("bin");
+
+  srcfactory = gst_elementfactory_find("audiosrc");
+  spectrumfactory = gst_elementfactory_find("gstspectrum");
+
+  src = gst_elementfactory_create(srcfactory,"src");
+  gtk_object_set(GTK_OBJECT(src),"bytes_per_read",(gulong)1024,NULL);
+  spectrum = gst_elementfactory_create(spectrumfactory,"spectrum");
+  gtk_object_set(GTK_OBJECT(spectrum),"width",256,NULL);
+
+
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(spectrum));
+
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(spectrum,"sink"));
+
+  spectrumpad = gst_pad_new("sink",GST_PAD_SINK);
+  gst_pad_set_chain_function(spectrumpad,spectrum_chain);
+
+  gst_pad_connect(gst_element_get_pad(spectrum,"src"),spectrumpad);
+
+  appwindow = gnome_app_new("spectrum","Spectrum");
+  drawingarea = gtk_drawing_area_new();
+  gtk_drawing_area_size(GTK_DRAWING_AREA(drawingarea),256,32);
+  gnome_app_set_contents(GNOME_APP(appwindow),drawingarea);
+  gtk_widget_show_all(appwindow);
+
+  gst_element_set_state(GST_ELEMENT(bin),GST_STATE_RUNNING);
+  gst_element_set_state(GST_ELEMENT(bin),GST_STATE_PLAYING);
+
+  g_idle_add(idle_func,src);
+
+  gtk_main();
+}
+
+
+void spectrum_chain(GstPad *pad,GstBuffer *buf) {
+  gint i,size;
+  guchar *data = buf->data;
+
+  gdk_draw_rectangle(drawingarea->window,drawingarea->style->black_gc,
+                     TRUE,0,0,GST_BUFFER_SIZE(buf),25);
+  for (i=0;i<GST_BUFFER_SIZE(buf);i++) {
+    gdk_draw_rectangle(drawingarea->window,drawingarea->style->white_gc,
+                       TRUE,i,32-data[i],1,data[i]);
+  }
+  gst_buffer_unref(buf);
+}
+
+gboolean idle_func(gpointer data) {
+  gst_src_push(GST_SRC(data));
+  return TRUE;
+}
diff --git a/test/states.c b/test/states.c
new file mode 100644 (file)
index 0000000..e16c161
--- /dev/null
@@ -0,0 +1,75 @@
+#include <glib.h>
+#include <gst/gst.h>
+
+int main(int argc,char *argv[]) {
+  GstBin *bin,
+  GstElement *src, *identity, *sink;
+
+  gst_init(&argc,&argv);
+
+  bin = gst_bin_new("bin");
+
+  src = gst_disksrc_new("fakesrc");
+  gst_disksrc_set_filename(src,"demo.mp3");
+  list_pads(src);
+
+  binf = gst_bin_new("binf");
+
+  filter1 = gst_fakefilter_new("filter1");
+  list_pads(filter1);
+
+  filter2 = gst_fakefilter_new("filter2");
+  list_pads(filter2);
+
+  sink = gst_fakesink_new("fakesink");
+  list_pads(sink);
+
+  gtk_signal_connect(GTK_OBJECT(bin),"object_added",
+                     GTK_SIGNAL_FUNC(added_child),NULL);
+  gtk_signal_connect(GTK_OBJECT(binf),"object_added",
+                     GTK_SIGNAL_FUNC(added_child),NULL);
+
+  gtk_signal_connect(GTK_OBJECT(binf),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+  gtk_signal_connect(GTK_OBJECT(src),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+  gtk_signal_connect(GTK_OBJECT(filter1),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+  gtk_signal_connect(GTK_OBJECT(filter2),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+  gtk_signal_connect(GTK_OBJECT(sink),"parent_set",
+                     GTK_SIGNAL_FUNC(added_parent),NULL);
+
+  /* add filter1 to the subbin */
+  gst_bin_add(GST_BIN(binf),GST_ELEMENT(filter1));
+  gst_bin_add(GST_BIN(binf),GST_ELEMENT(filter2));
+  /* connect the two together */
+  gst_pad_connect(gst_element_get_pad(filter1,"src"),
+                  gst_element_get_pad(filter2,"sink"));
+  /* export the pads */
+  gst_element_add_ghost_pad(binf,gst_element_get_pad(filter1,"sink"));
+  gst_element_add_ghost_pad(binf,gst_element_get_pad(filter2,"src"));
+  list_pads(binf);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(src));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(binf));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(sink));
+
+  /* connect src to binf */
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(binf,"sink"));
+  /* connect binf to sink */
+  gst_pad_connect(gst_element_get_pad(binf,"src"),
+                  gst_element_get_pad(sink,"sink"));
+
+  gst_disksrc_push(GST_SRC(src));
+
+  gst_object_destroy(GST_OBJECT(src));
+  gst_object_destroy(GST_OBJECT(filter1));
+  gst_object_destroy(GST_OBJECT(filter2));
+  gst_object_destroy(GST_OBJECT(binf));
+  gst_object_destroy(GST_OBJECT(sink));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/teardown.c b/test/teardown.c
new file mode 100644 (file)
index 0000000..bdd6828
--- /dev/null
@@ -0,0 +1,69 @@
+#include <gst/gst.h>
+
+#include "mem.h"
+
+extern gboolean _gst_plugin_spew;
+
+GstPipeline *teardown_create_pipeline() {
+  GstPipeline *pipeline;
+  GstElementFactory *srcfactory, *sinkfactory;
+  GstElement *src, *sink;
+  GstPad *srcpad, *sinkpad;
+
+  pipeline = gst_pipeline_new("pipeline");
+  g_return_if_fail(pipeline != NULL);
+
+  srcfactory = gst_elementfactory_find("fakesrc");
+  g_return_if_fail(srcfactory != NULL);
+  sinkfactory = gst_elementfactory_find("fakesink");
+  g_return_if_fail(sinkfactory != NULL);
+  src = gst_elementfactory_create(srcfactory,"src");
+  g_return_if_fail(src != NULL);
+  sink = gst_elementfactory_create(sinkfactory,"sink");
+  g_return_if_fail(sink != NULL);
+
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(sink));
+
+  srcpad = gst_element_get_pad(src,"src");
+  g_return_if_fail(srcpad != NULL);
+  sinkpad = gst_element_get_pad(sink,"sink");
+  g_return_if_fail(srcpad != NULL);
+
+  gst_pad_connect(srcpad,sinkpad);
+
+  return GST_PIPELINE(pipeline);
+}
+
+void teardown_destroy_pipeline(GstPipeline *pipeline) {
+  gst_element_destroy(pipeline);
+}
+
+
+int main(int argc,char *argv[]) {
+  GstElement *pipeline, *src;
+  int i,j,max = 1;
+  long usage1,usage2;
+
+//  _gst_plugin_spew = TRUE;
+
+  gst_init(&argc,&argv);
+
+  if (argc == 2)
+    max = atoi(argv[1]);
+
+  usage1 = vmsize();
+  for (i=0;i<max;i++) {
+    pipeline = teardown_create_pipeline();
+    src = gst_bin_get_by_name(GST_BIN(pipeline),"src");
+//    g_print("got source %p, pushing",src);
+//    for (j=0;j<max;j++) {
+//      gst_src_push(GST_SRC(src));
+//      g_print(".");
+//    }
+//    g_print("\n");
+    teardown_destroy_pipeline(pipeline);
+  }
+  usage2 = vmsize();
+  g_print("uses %d bytes\n",usage2-usage1);
+}
diff --git a/test/typefind.c b/test/typefind.c
new file mode 100644 (file)
index 0000000..a9867c6
--- /dev/null
@@ -0,0 +1,63 @@
+#include <gst/gst.h>
+
+int main(int argc,char *argv[]) {
+  GstType *mp3type;
+  GList *factories;
+  GstElement *src;
+  GList *padlist;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  bin = gst_bin_new("bin");
+
+  disksrc = gst_disksrc_new("disksrc");
+  g_print("created disksrc\n");
+  if (argc == 2)
+    gst_disksrc_set_filename(disksrc,argv[1]);
+  else
+    gst_disksrc_set_filename(disksrc,"Thank_you_very_much.au");
+  g_print("loaded file '%s'\n",gst_disksrc_get_filename(disksrc));
+
+
+  /* now it's time to get the parser */
+  autype = gst_type_get_by_mime("audio/au");
+  factories = gst_type_get_sinks(autype);
+  if (factories != NULL)
+    parsefactory = GST_ELEMENTFACTORY(factories->data);
+  else {
+    g_print("sorry, can't find anyone registered to sink 'au'\n");
+    return 1;
+  }
+  parse = gst_elementfactory_create(parsefactory,"parser");
+  if (parse == NULL) {
+    g_print("sorry, couldn't create parser\n");
+    return 1;
+  }
+
+
+  audiosink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(disksrc),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(disksrc));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(parse));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(audiosink));
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(disksrc,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(audiosink,"sink"));
+
+  while(1)
+    gst_disksrc_push(GST_SRC(disksrc));
+
+  gst_object_destroy(GST_OBJECT(audiosink));
+  gst_object_destroy(GST_OBJECT(parse));
+  gst_object_destroy(GST_OBJECT(disksrc));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/types.c b/test/types.c
new file mode 100644 (file)
index 0000000..7b00327
--- /dev/null
@@ -0,0 +1,44 @@
+#include <glib.h>
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+GstTypeFactory testfactory = { "test/test", ".tst", NULL };
+
+int main(int argc,char *argv[]) {
+  guint16 id;
+  GstType *type;
+  GstElementFactory *element;
+  GList *types, *elements;
+
+//  _gst_plugin_spew = TRUE;
+
+  gst_init(&argc,&argv);
+//  gst_plugin_load_all();
+  gst_plugin_load("libgstparseau.so");
+  gst_plugin_load("libgstparsewav.so");
+  gst_plugin_load("libgstxa.so");
+  gst_plugin_load("libstereo.so");
+  gst_plugin_load("libvolume.so");
+  gst_plugin_load("libsmoothwave.so");
+  gst_plugin_load("libgstspectrum.so");
+  gst_plugin_load("libsynaesthesia.so");
+  gst_plugin_load("libvumeter.so");
+
+  id = gst_type_register(&testfactory);
+
+  types = gst_type_get_list();
+  while (types) {
+    type = (GstType *)types->data;
+    g_print("%d: have type '%s'\n",type->id,type->mime);
+    types = g_list_next(types);
+  }
+
+  elements = gst_elementfactory_get_list();
+  while (elements) {
+    element = (GstElementFactory *)elements->data;
+    g_print("%d: have elementfactory '%s': \"%s\"\n",element->type,
+            element->name,element->details->longname);
+    elements = g_list_next(elements);
+  }
+}
diff --git a/test/w.c b/test/w.c
new file mode 100644 (file)
index 0000000..1173e9e
--- /dev/null
+++ b/test/w.c
@@ -0,0 +1,72 @@
+#include <gst/gst.h>
+
+void eof(GstSrc *src) {
+  g_print("have eof, quitting\n");
+  exit(0);
+}
+
+int main(int argc,char *argv[]) {
+  GstType *autype;
+  GList *factories;
+  GstElementFactory *parsefactory;
+  GstElement *bin, *disksrc, *parse, *audiosink;
+  GList *padlist;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  bin = gst_bin_new("bin");
+
+  disksrc = gst_disksrc_new("disksrc");
+  g_print("created disksrc\n");
+  if (argc == 2)
+    gst_disksrc_set_filename(disksrc,argv[1]);
+  else
+    gst_disksrc_set_filename(disksrc,"futile.wav");
+//  gst_disksrc_set_bytesperread(disksrc,32768);
+  g_print("loaded file '%s'\n",gst_disksrc_get_filename(disksrc));
+
+
+  /* now it's time to get the parser */
+  autype = gst_type_get_by_mime("audio/wav");
+  factories = gst_type_get_sinks(autype);
+  if (factories != NULL)
+    parsefactory = GST_ELEMENTFACTORY(factories->data);
+  else {
+    g_print("sorry, can't find anyone registered to sink 'wav'\n");
+    return 1;
+  }
+  parse = gst_elementfactory_create(parsefactory,"parser");
+  if (parse == NULL) {
+    g_print("sorry, couldn't create parser\n");
+    return 1;
+  }
+
+
+  audiosink = gst_audiosink_new("audiosink");
+
+  gtk_signal_connect(GTK_OBJECT(disksrc),"eof",
+                     GTK_SIGNAL_FUNC(eof),NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(disksrc));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(parse));
+  gst_bin_add(GST_BIN(bin),GST_OBJECT(audiosink));
+
+  /* connect src to sink */
+  gst_pad_connect(gst_element_get_pad(disksrc,"src"),
+                  gst_element_get_pad(parse,"sink"));
+  gst_pad_connect(gst_element_get_pad(parse,"src"),
+                  gst_element_get_pad(audiosink,"sink"));
+
+  while(1) {
+    g_print("\n");
+    gst_disksrc_push(GST_SRC(disksrc));
+  }
+
+  gst_object_destroy(GST_OBJECT(audiosink));
+  gst_object_destroy(GST_OBJECT(parse));
+  gst_object_destroy(GST_OBJECT(disksrc));
+  gst_object_destroy(GST_OBJECT(bin));
+}
+
diff --git a/test/wave.c b/test/wave.c
new file mode 100644 (file)
index 0000000..0aae799
--- /dev/null
@@ -0,0 +1,60 @@
+#include <gnome.h>
+#include <gst/gst.h>
+
+extern gboolean _gst_plugin_spew;
+
+gboolean idle_func(gpointer data);
+
+GtkWidget *drawingarea;
+
+int main(int argc,char *argv[]) {
+  GstElement *bin;
+  GstElementFactory *srcfactory;
+  GstElement *src;
+  GstElementFactory *wavefactory;
+  GstElement *wave;
+
+  GtkWidget *appwindow;
+
+  _gst_plugin_spew = TRUE;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load("libsmoothwave.so");
+  gnome_init("Wave","0.0.1",argc,argv);
+
+  bin = gst_bin_new("bin");
+
+  srcfactory = gst_elementfactory_find("audiosrc");
+  g_return_if_fail(srcfactory != NULL);
+  wavefactory = gst_elementfactory_find("smoothwave");
+  g_return_if_fail(wavefactory != NULL);
+
+  src = gst_elementfactory_create(srcfactory,"src");
+  gtk_object_set(GTK_OBJECT(src),"bytes_per_read",(gulong)2048,NULL);
+  wave = gst_elementfactory_create(wavefactory,"wave");
+  gtk_object_set(GTK_OBJECT(wave),"width",256,"height",100,NULL);
+
+
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(src));
+  gst_bin_add(GST_BIN(bin),GST_ELEMENT(wave));
+
+  gst_pad_connect(gst_element_get_pad(src,"src"),
+                  gst_element_get_pad(wave,"sink"));
+
+  appwindow = gnome_app_new("wave","Wave");
+  drawingarea = gtk_drawing_area_new();
+  gnome_app_set_contents(GNOME_APP(appwindow),gst_util_get_widget_arg(GTK_OBJECT(wave),"widget"));
+  gtk_widget_show_all(appwindow);
+
+  gst_element_set_state(GST_ELEMENT(bin),GST_STATE_RUNNING);
+  gst_element_set_state(GST_ELEMENT(bin),GST_STATE_PLAYING);
+
+  g_idle_add(idle_func,src);
+
+  gtk_main();
+}
+
+gboolean idle_func(gpointer data) {
+  gst_src_push(GST_SRC(data));
+  return TRUE;
+}
diff --git a/test/xml/Makefile.am b/test/xml/Makefile.am
new file mode 100644 (file)
index 0000000..59c91ed
--- /dev/null
@@ -0,0 +1,6 @@
+bin_PROGRAMS = readreg createreg
+
+INCLUDES = $(GLIB_CFLAGS) $(GTK_CFLAGS) $(XML_CFLAGS) -I$(top_srcdir)
+LDADD = $(GLIB_LIBS) $(GTK_LIBS) $(XML_LIBS) $(top_srcdir)/gst/libgst.la
+
+EXTRA_DIST = README registry.xml
diff --git a/test/xml/createreg.c b/test/xml/createreg.c
new file mode 100644 (file)
index 0000000..fc93c76
--- /dev/null
@@ -0,0 +1,62 @@
+#include <glib.h>
+#include <gnome-xml/parser.h>
+#include <gst/gst.h>
+
+typedef struct _GstRegistryPlugin GstRegistryPlugin;
+typedef struct _GstRegistryElement GstRegistryElement;
+
+struct _GstRegistryPlugin {
+  gchar *name;
+  gchar *filename;
+};
+
+struct _GstRegistryElement {
+  GstRegistryPlugin *plugin;
+  gchar *name;
+  GstElementDetails details;
+};
+
+int main(int argc,char *argv[]) {
+  xmlDocPtr doc;
+  xmlNodePtr tree, subtree;
+  GList *plugins = NULL, *elements = NULL;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  doc = xmlNewDoc("1.0");
+  doc->root = xmlNewDocNode(doc,NULL,"GST-PluginRegistry",NULL);
+  plugins = gst_plugin_get_list();
+  while (plugins) {
+    GstPlugin *plugin = (GstPlugin *)plugins->data;
+    tree = xmlNewChild(doc->root,NULL,"plugin",NULL);
+    subtree = xmlNewChild(tree,NULL,"name",plugin->name);
+    subtree = xmlNewChild(tree,NULL,"longname",plugin->longname);
+    subtree = xmlNewChild(tree,NULL,"filename",plugin->filename);
+    elements = plugin->elements;
+    while (elements) {
+      GstElementFactory *element = (GstElementFactory *)elements->data;
+      tree = xmlNewChild(doc->root,NULL,"element",NULL);
+      subtree = xmlNewChild(tree,NULL,"plugin",plugin->name);
+      subtree = xmlNewChild(tree,NULL,"name",element->name);
+      subtree = xmlNewChild(tree,NULL,"longname",
+                            element->details->longname);
+      subtree = xmlNewChild(tree,NULL,"class",
+                            element->details->class);
+      subtree = xmlNewChild(tree,NULL,"description",
+                            element->details->description);
+      subtree = xmlNewChild(tree,NULL,"version",
+                            element->details->version);
+      subtree = xmlNewChild(tree,NULL,"author",
+                            element->details->author);
+      subtree = xmlNewChild(tree,NULL,"copyright",
+                            element->details->copyright);
+      elements = g_list_next(elements);
+    }
+    plugins = g_list_next(plugins);
+  }
+
+  xmlSaveFile("newreg.xml",doc);
+
+  exit(0);
+}
diff --git a/test/xml/readreg.c b/test/xml/readreg.c
new file mode 100644 (file)
index 0000000..6e2d46f
--- /dev/null
@@ -0,0 +1,126 @@
+#include <glib.h>
+#include <gnome-xml/parser.h>
+#include <gst/gst.h>
+
+typedef struct _GstRegistryPlugin GstRegistryPlugin;
+typedef struct _GstRegistryElement GstRegistryElement;
+
+struct _GstRegistryPlugin {
+  gchar *name;
+  gchar *filename;
+};
+
+struct _GstRegistryElement {
+  GstRegistryPlugin *plugin;
+  gchar *name;
+  GstElementDetails details;
+};
+
+gchar *getcontents(xmlDocPtr doc,xmlNodePtr cur) {
+  return g_strdup(xmlNodeListGetString(doc,cur->childs,1));
+}
+
+int main(int argc,char *argv[]) {
+  xmlDocPtr doc;
+  xmlNodePtr cur;
+  int i;
+
+  GSList *plugins = NULL, *elements = NULL;
+
+//  gst_init(&argc,&argv);
+
+  doc = xmlParseFile("registry.xml");
+  g_assert(doc != NULL);
+
+  cur = doc->root;
+  if (cur == NULL) {
+    g_print("registry is empty\n");
+    xmlFreeDoc(doc);
+    exit(0);
+  }
+
+  if (strcmp(cur->name,"GST-PluginRegistry")) {
+    g_print("document not the right type\n");
+    xmlFreeDoc(doc);
+    exit(1);
+  }
+
+  cur = cur->childs;   /* 'childs'???  He (Daniel) is Dutch, so... */
+  while (cur != NULL) {
+    if (!strcmp(cur->name,"plugin")) {
+      xmlNodePtr field = cur->childs;
+      GstRegistryPlugin *plugin = g_new0(GstRegistryPlugin,1);
+
+      while (field) {
+        if (!strcmp(field->name,"name"))
+          plugin->name = getcontents(doc,field);
+        else if (!strcmp(field->name,"filename"))
+          plugin->filename = getcontents(doc,field);
+        field = field->next;
+      }
+      g_print("new plugin '%s' at '%s'\n",plugin->name,plugin->filename);
+      plugins = g_slist_prepend(plugins,plugin);
+    } else if (!strcmp(cur->name,"element")) {
+      xmlNodePtr field = cur->childs;
+      GstRegistryElement *element = g_new0(GstRegistryElement,1);
+
+      while (field) {
+        if (!strcmp(field->name,"plugin")) {
+          gchar *pluginname = getcontents(doc,field);
+          GSList *list = plugins;
+          element->plugin = NULL;
+          while (list) {
+            GstRegistryPlugin *plugin = (GstRegistryPlugin *)list->data;
+            if (!strcmp(pluginname,plugin->name)) {
+              element->plugin = plugin;
+              break;
+            }
+            list = g_slist_next(list);
+          }
+        } else if (!strcmp(field->name,"name"))
+          element->name = getcontents(doc,field);
+        else if (!strcmp(field->name,"longname"))
+          element->details.longname = getcontents(doc,field);
+        else if (!strcmp(field->name,"class"))
+          element->details.class = getcontents(doc,field);
+        else if (!strcmp(field->name,"description"))
+          element->details.description = getcontents(doc,field);
+        else if (!strcmp(field->name,"version"))
+          element->details.version = getcontents(doc,field);
+        else if (!strcmp(field->name,"author"))
+          element->details.author = getcontents(doc,field);
+        else if (!strcmp(field->name,"copyright"))
+          element->details.copyright = getcontents(doc,field);
+        field = field->next;
+      }
+      g_print("new element '%s'in '%s'\n",element->name,element->plugin->name);
+      elements = g_slist_prepend(elements,element);
+    }
+    cur = cur->next;
+  }
+
+  for (i=1;i<argc;i++) {
+    GSList *list;
+    g_print("searching for element '%s'\n",argv[i]);
+    list = elements;
+    while (list) {
+      GstRegistryElement *element = (GstRegistryElement *)list->data;
+//      g_print("comparing against '%s'\n",element->name);
+      if (!strcmp(argv[i],element->name)) {
+        g_print("Plugin name: %s\n",element->plugin->name);
+        g_print("Plugin filename: %s\n",element->plugin->filename);
+        g_print("Element name: %s\n",element->name);
+        g_print("Element long name: %s\n",element->details.longname);
+        g_print("Element class: %s\n",element->details.class);
+        g_print("Element description: %s\n",element->details.description);
+        g_print("Element version: %s\n",element->details.version);
+        g_print("Element author: %s\n",element->details.author);
+        g_print("Element copyright: %s\n",element->details.copyright);
+//        gst_plugin_load_absolute(element->plugin->filename);
+      }
+      list = g_slist_next(list);
+    }
+  }
+
+  exit(0);
+}
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644 (file)
index 0000000..d79c8bf
--- /dev/null
@@ -0,0 +1,4 @@
+bin_PROGRAMS = launch
+
+LDADD = $(GLIB_LIBS) $(GTK_LIBS) $(top_builddir)/gst/libgst.la
+INCLUDES = $(GLIB_CFLAGS) $(GTK_CFLAGS) -I$(top_srcdir)
diff --git a/tools/README b/tools/README
new file mode 100644 (file)
index 0000000..5d9ffe8
--- /dev/null
@@ -0,0 +1,16 @@
+launch
+======
+
+This is a tool that will construct pipelines based on a command-line
+syntax.  The syntax is rather complex to enable all the features I want it
+to have, but should be easy to use for most people.  Multi-pathed and
+feedback pipelines are the most complex.
+
+A simple commandline looks like:
+
+./launch disksrc demo.mp3 | mp3parse | mpg123 | audiosink-oss
+
+A more complex pipeline looks like:
+
+./launch disksrc redpill.vob | css-descramble | private_stream_1.0| \
+(ac3parse | ac3dec | audioink-oss) video_0| (mpeg2dec | videosink)
diff --git a/tools/launch.c b/tools/launch.c
new file mode 100644 (file)
index 0000000..7be7f2c
--- /dev/null
@@ -0,0 +1,115 @@
+#include <glib.h>
+#include <gst/gst.h>
+
+typedef struct _launch_delayed_pad launch_delayed_pad;
+struct _launch_delayed_pad {
+  gchar *name;
+  GstPad *peer;
+};
+
+void launch_newpad(GstElement *element,GstPad *pad,launch_delayed_pad *peer) {
+  gst_info("have NEW_PAD signal\n");
+  // if it matches, connect it
+  if (!strcmp(gst_pad_get_name(pad),peer->name)) {
+    gst_pad_connect(pad,peer->peer);
+    gst_info("delayed connect of '%s' to '%s'\n",
+             gst_pad_get_name(pad),gst_pad_get_name(peer->peer));
+  }
+}
+
+gint parse(int argc,char *argv[],GstElement *parent,gint offset,gchar endchar) {
+  gint i = offset;
+  gchar *plugin;
+  GstElement *element, *prevelement;
+  GstPad *prevpad,*nextpad;
+  gchar *prevpadname = NULL,*nextpadname = NULL;
+  gchar *ptr;
+  gint len;
+  launch_delayed_pad *delayed;
+
+  gst_info("at offset %d, argc is %d\n",i,argc);
+
+  // loop through all the arguments
+  while (i < argc) {
+    // first is the plugin name
+    plugin = argv[i++];
+    gst_info("plugin is \"%s\"\n",plugin);
+    // record previous element
+    prevelement = element;
+    // create the element and add it to the parent
+    element = gst_elementfactory_make(plugin,plugin);
+    gst_bin_add(GST_BIN(parent),element);
+    // connect it to the previous if there is one
+    if (nextpadname != NULL) {
+      // grab the pad of this element
+      nextpad = gst_element_get_pad(element,nextpadname);
+      g_return_if_fail(nextpad != NULL);
+      // check to see if the pad exists yet, connect it if it does
+      if (prevpad != NULL) {
+        gst_pad_connect(prevpad,nextpad);
+        gst_info("wired '%s' to '%s'\n",
+                 gst_pad_get_name(prevpad),gst_pad_get_name(nextpad));
+      }
+      // otherwise we have to attach and wait for it to show
+      else {
+        delayed = g_new0(launch_delayed_pad,1);
+        delayed->name = prevpadname;
+        delayed->peer = nextpad;
+        gtk_signal_connect(GTK_OBJECT(prevelement),"new_pad",
+                           launch_newpad,delayed);
+      }
+    }
+     // then come all the other things
+    while (i < argc) {
+      // snag the length in advance;
+      len = strlen(argv[i]);
+      // if it's just a connection, pick the 'src' pad and move on
+      if (ptr = strchr(argv[i],'|')) {
+        // if there's a previous pad name
+        if (ptr != argv[i]) {
+          ptr[0] = '\0';
+          prevpadname = argv[i];
+          prevpad = gst_element_get_pad(element,prevpadname);
+        } else
+          prevpad = gst_element_get_pad(element,"src");
+        // if there's a next pad name
+        if (((ptr - argv[i]) + 1) < len) {
+          nextpadname = ptr + 1;
+        } else
+          nextpadname = "sink";
+        i++;
+        break;
+      } else {
+        gst_info("have unknown argument '%s'\n",argv[i]);
+        gtk_object_set(GTK_OBJECT(element),"location",argv[i],NULL);
+        i++;
+      }
+    }
+  }
+}
+
+int main(int argc,char *argv[]) {
+  int t;
+  GstElement *pipeline;
+
+  gst_init(&argc,&argv);
+  gst_plugin_load_all();
+
+  gst_info("\n\n");
+
+  pipeline = gst_elementfactory_make("thread","launch");
+  if (t = atoi(argv[1]))
+    parse(argc,argv,pipeline,2,0);
+  else
+    parse(argc,argv,pipeline,1,0);
+
+  xmlSaveFile("launch.xml",gst_xml_write(pipeline));
+
+  gst_element_set_state(pipeline,GST_STATE_RUNNING);
+  gst_element_set_state(pipeline,GST_STATE_PLAYING);
+
+  if (t)
+    sleep(t);
+  else
+    sleep(5);
+}