diff options
author | Erik Walthinsen <omega@temple-baptist.org> | 2000-01-30 10:44:33 +0000 |
---|---|---|
committer | Erik Walthinsen <omega@temple-baptist.org> | 2000-01-30 10:44:33 +0000 |
commit | 0ec400890cca169763f5d26446bc048279c1630f (patch) | |
tree | 6a7a132ed5aee76bcae214fe2acb675ec607b0eb | |
parent | 1762dfbf982a75d895676b0063379e33b4f9b96a (diff) |
initial checkin
Original commit message from CVS:
initial checkin
99 files changed, 7268 insertions, 0 deletions
diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 000000000..e9a72e904 --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1 @@ +#EXTRA_DIST = random slides diff --git a/docs/random/arch b/docs/random/arch new file mode 100644 index 000000000..ba7e88a7c --- /dev/null +++ b/docs/random/arch @@ -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 index 000000000..dd657d921 --- /dev/null +++ b/docs/random/buffers @@ -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 index 000000000..d4e677bc3 --- /dev/null +++ b/docs/random/coroutines @@ -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 index 000000000..24b1d5fed --- /dev/null +++ b/docs/random/design @@ -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 index 000000000..0bb166b60 --- /dev/null +++ b/docs/random/example @@ -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 index 000000000..9a5888804 --- /dev/null +++ b/docs/random/factoryinfo @@ -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 index 000000000..866cb6f39 --- /dev/null +++ b/docs/random/gboolean @@ -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 index 000000000..d305d5357 --- /dev/null +++ b/docs/random/intro @@ -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 index 000000000..afe0a754f --- /dev/null +++ b/docs/random/metadata @@ -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 index 000000000..74a094449 --- /dev/null +++ b/docs/random/mutability @@ -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 index 000000000..3ceed6de5 --- /dev/null +++ b/docs/random/padarch @@ -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 index 000000000..51810d11d --- /dev/null +++ b/docs/random/sequence @@ -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 index 000000000..4752afec8 --- /dev/null +++ b/docs/random/state-transitions @@ -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 index 000000000..ef05903b3 --- /dev/null +++ b/docs/random/states @@ -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 index 000000000..74d7de163 --- /dev/null +++ b/docs/random/states.old @@ -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 index 000000000..0a14f8f62 --- /dev/null +++ b/docs/random/types @@ -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 index 000000000..a83e7c01d --- /dev/null +++ b/docs/random/vis-transform @@ -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 index 000000000..f6b21fad8 --- /dev/null +++ b/docs/random/walkthrough @@ -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 index 000000000..923e6dc5a --- /dev/null +++ b/docs/slides/README @@ -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 index 000000000..b4bc016b5 --- /dev/null +++ b/docs/slides/abstract @@ -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 index 000000000..b317c5a66 --- /dev/null +++ b/docs/slides/abstract.save @@ -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 index 000000000..5808dbb01 --- /dev/null +++ b/docs/slides/outline @@ -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 index 000000000..b43481159 --- /dev/null +++ b/docs/slides/slides @@ -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 index 000000000..394cf839c --- /dev/null +++ b/editor/Makefile.am @@ -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 index 000000000..a16a0f254 --- /dev/null +++ b/editor/editor.c @@ -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 index 000000000..2f4c60acf --- /dev/null +++ b/editor/editor.glade @@ -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 index 000000000..5711e0191 --- /dev/null +++ b/editor/editorelement.glade @@ -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 index 000000000..62f4eee39 --- /dev/null +++ b/editor/gsteditor.c @@ -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 index 000000000..4bc04e123 --- /dev/null +++ b/editor/gsteditor.h @@ -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 index 000000000..44ebeab87 --- /dev/null +++ b/editor/gsteditorbin.c @@ -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 index 000000000..76b4600e7 --- /dev/null +++ b/editor/gsteditorcanvas.c @@ -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 index 000000000..dbe4147dc --- /dev/null +++ b/editor/gsteditorconnection.c @@ -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 index 000000000..011edd3a8 --- /dev/null +++ b/editor/gsteditorconnection.h @@ -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 index 000000000..bfadd83d4 --- /dev/null +++ b/editor/gsteditorcreate.c @@ -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 index 000000000..448de9157 --- /dev/null +++ b/editor/gsteditorcreate.h @@ -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 index 000000000..d13c40f4a --- /dev/null +++ b/editor/gsteditorelement.c @@ -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 index 000000000..9aea1f6c2 --- /dev/null +++ b/editor/gsteditorpad.c @@ -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 index 000000000..ed3f8ef82 --- /dev/null +++ b/editor/gsteditorpad.h @@ -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 index 000000000..e467b9cd2 --- /dev/null +++ b/editor/gstelementselect.c @@ -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 index 000000000..65ad3ed5f --- /dev/null +++ b/editor/gstelementselect.h @@ -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 index 000000000..bff38573a --- /dev/null +++ b/gst/xml/Makefile.am @@ -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 index 000000000..5ed0566da --- /dev/null +++ b/gst/xml/notes @@ -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 index 000000000..5e41244a1 --- /dev/null +++ b/gst/xml/save.c @@ -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 index 000000000..50c6b26f5 --- /dev/null +++ b/test/Makefile.am @@ -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 index 000000000..59ad5d6a5 --- /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 index 000000000..e018dc864 --- /dev/null +++ b/test/ac3parse.c @@ -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 index 000000000..24e18e3e7 --- /dev/null +++ b/test/ac3play.c @@ -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 index 000000000..67e79dd66 --- /dev/null +++ b/test/ac3sync.c @@ -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 index 000000000..751a7af70 --- /dev/null +++ b/test/args.c @@ -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 index 000000000..bb6d65972 --- /dev/null +++ b/test/basic.c @@ -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 index 000000000..1f7486113 --- /dev/null +++ b/test/bindings/Makefile.am @@ -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 index 000000000..0172eb449 --- /dev/null +++ b/test/bindings/dummy.c @@ -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 index 000000000..7b8ad841d --- /dev/null +++ b/test/bindings/dummy.h @@ -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 index 000000000..75dc3f2e1 --- /dev/null +++ b/test/bindings/test.c @@ -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 index 000000000..baeaa347a --- /dev/null +++ b/test/buffer.c @@ -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 index 000000000..7f9630959 --- /dev/null +++ b/test/cobin.c @@ -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 index 000000000..f60104e51 --- /dev/null +++ b/test/cothreads/Makefile.am @@ -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 index 000000000..8b12b6708 --- /dev/null +++ b/test/cothreads/cothreads.c @@ -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 index 000000000..63fef4290 --- /dev/null +++ b/test/cothreads/cothreads.h @@ -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 index 000000000..5b67a019f --- /dev/null +++ b/test/cothreads/looper.c @@ -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 index 000000000..397f91bab --- /dev/null +++ b/test/cothreads/looper.h @@ -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 index 000000000..2b03e69db --- /dev/null +++ b/test/cothreads/object.c @@ -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 index 000000000..9b4ebb6b6 --- /dev/null +++ b/test/cothreads/object.h @@ -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 index 000000000..3e1e950de --- /dev/null +++ b/test/cothreads/simple.c @@ -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 index 000000000..42934ac0b --- /dev/null +++ b/test/cothreads/test.c @@ -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 index 000000000..4e712e7fa --- /dev/null +++ b/test/dvdcat.c @@ -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 index 000000000..794e22e04 --- /dev/null +++ b/test/fake.c @@ -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 index 000000000..f5159bb14 --- /dev/null +++ b/test/gsttracedump.c @@ -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 index 000000000..8d66064e1 --- /dev/null +++ b/test/jitter.c @@ -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 index 000000000..f210e068e --- /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 index 000000000..95f1969e4 --- /dev/null +++ b/test/mcut.c @@ -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 index 000000000..8e1c573bd --- /dev/null +++ b/test/mem.c @@ -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 index 000000000..28999db2c --- /dev/null +++ b/test/mem.h @@ -0,0 +1 @@ +int vmsize(); diff --git a/test/mp3.c b/test/mp3.c new file mode 100644 index 000000000..1fd09e492 --- /dev/null +++ b/test/mp3.c @@ -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 index 000000000..284601f59 --- /dev/null +++ b/test/mp3parse.c @@ -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 index 000000000..4f0080769 --- /dev/null +++ b/test/mp3play.c @@ -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 index 000000000..fa3c63373 --- /dev/null +++ b/test/mpeg2parse.c @@ -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 index 000000000..e242b9c8e --- /dev/null +++ b/test/mpg123.c @@ -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 index 000000000..457e4b374 --- /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 index 000000000..4da003fea --- /dev/null +++ b/test/plugin.c @@ -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 index 000000000..7af0ea22a --- /dev/null +++ b/test/push.c @@ -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 index 000000000..e7c2d5218 --- /dev/null +++ b/test/qtest.c @@ -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 index 000000000..5acc6225f --- /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 index 000000000..fe5b96c35 --- /dev/null +++ b/test/record.c @@ -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 index 000000000..cefab58e4 --- /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 index 000000000..190157c0d --- /dev/null +++ b/test/spectrum.c @@ -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 index 000000000..e16c16172 --- /dev/null +++ b/test/states.c @@ -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 index 000000000..bdd68283f --- /dev/null +++ b/test/teardown.c @@ -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 index 000000000..a9867c6b0 --- /dev/null +++ b/test/typefind.c @@ -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 index 000000000..7b003273d --- /dev/null +++ b/test/types.c @@ -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 index 000000000..1173e9e95 --- /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 index 000000000..0aae79906 --- /dev/null +++ b/test/wave.c @@ -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 index 000000000..59c91ed89 --- /dev/null +++ b/test/xml/Makefile.am @@ -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 index 000000000..fc93c7607 --- /dev/null +++ b/test/xml/createreg.c @@ -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 index 000000000..6e2d46f79 --- /dev/null +++ b/test/xml/readreg.c @@ -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 index 000000000..d79c8bf84 --- /dev/null +++ b/tools/Makefile.am @@ -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 index 000000000..5d9ffe8fa --- /dev/null +++ b/tools/README @@ -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 index 000000000..7be7f2cf8 --- /dev/null +++ b/tools/launch.c @@ -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); +} |