diff --git a/docs/manual/advanced.sgml b/docs/manual/advanced.sgml new file mode 100644 index 0000000000..1c67a53e60 --- /dev/null +++ b/docs/manual/advanced.sgml @@ -0,0 +1,318 @@ + + Threads + + The small application we created in the previous chapter used the + concept of a factory to create the elements. In this chapter we will + show you how to use the factory concepts. + + + + The problems with the helloworld example + + If we take a look at how the elements were created in the previous + example we used a rather crude mechanism: + + + + ... + /* now it's time to get the parser */ + parse = gst_elementfactory_make("mp3parse","parse"); + decoder = gst_elementfactory_make("mpg123","decoder"); + ... + + + + While this mechanism is quite effective it also has one big problems: + The elements are created base on their name. Indeed, we create an + element mpg123 by explicitly stating the mpg123 elements name. + Our little program therefore always uses the mpg123 decoder element + to decode the MP3 audio stream, even if there are 3 other MP3 decoders + in the system. We will see how we can use a more general way to create + an MP3 decoder element. + + + We have to introduce the concept of MIME types added to the source and + sink pads. + + + + + MIME Types + + GStreamer uses MIME types to indentify the different types of data + that can be handled by the elements. They are the high level + mechanisms to make sure that everyone is talking about the right + kind of data. + + + A MIME (Multipurpose Internet Mail Extension) types are a set of + string that donote a certain type of data. examples include: + + + + audio/raw : raw audio samples + + + + + audio/mpeg : mpeg audio + + + + + video/mpeg : mpeg video + + + + + + An element must associate a MIME type to its source and sink pads + when it is loaded into the system. GStreamer knows about the + different elements and what type of data they expect and emit. + This allows for very dynamic and extensible element creation as we + will see. + + + + In our helloworld example the elements we constructed would have the + following MIME types associated with their source and sink pads: + + + We will see how you can create an element based on the MIME types + of its source and sink pads. This way the end-user will have the + ability to choose his/her favorite audio/mpeg decoder without + you even having to care about it. + + + The typing of the source and sink pads also makes it possible to + 'autoplug' a pipeline. We will have the ability to say: "construct + me a pipeline that does an audio/mpeg to audio/raw conversion". + + + + The basic GStreamer library does not try to solve all of your + autoplug problems. It leaves the hard decisions to the application + programmer, where they belong. + + + + + + + GStreamer types + + GStreamer assigns a unique number to all registered MIME types. It + also maintains a list of all elements that either uses this type + as a source or as a sink. GStreamer also keeps a reference to + a function that can be used to determine if a given buffer is of + the given MIME type. + + + There is also an association between a MIME type and a file + extension. + + + The type information is maintained in a list of + GstType. The definition of a + GstType is like: + + +typedef gboolean (*GstTypeFindFunc) (GstBuffer *buf,gpointer *priv); + +typedef struct _GstType GstType; + +struct _GstType { + guint16 id; /* type id (assigned) */ + + gchar *mime; /* MIME type */ + gchar *exts; /* space-delimited list of extensions */ + + GstTypeFindFunc typefindfunc; /* typefind function */ + + GList *srcs; /* list of src objects for this type */ + GList *sinks; /* list of sink objects for type */ +}; + + + All operations on GstType occur via their + guint16 id numbers, with GstType + structure private to the GStreamer library. + + + + MIME type to id conversion + + + We can obtain the id for a given MIME type + with the following piece of code: + + + guint16 id; + + id = gst_type_find_by_mime("audio/mpeg"); + + + This function will return 0 if the type was not known. + + + + + id to <classname>GstType</classname> conversion + + We can obtain the GstType for a given id + with the following piece of code: + + + GstType *type; + + type = gst_type_find_by_id(id); + + + This function will return NULL if the id was associated with + any known GstType + + + + + extension to id conversion + + We can obtain the id for a given file extension + with the following piece of code: + + + guint16 id; + + id = gst_type_find_by_ext(".mp3"); + + + This function will return 0 if the extension was not known. + + + + + id to <classname>GstElementFactory</classname> conversion + + When we have obtained a given type id using one of the above methods, + we can obtain a list of all the elements that operate on this MIME + type or extension. + + + Obtain a list of all the elements that use this id as source with: + + + GList *list; + + list = gst_type_gst_srcs(id); + + + + Obtain a list of all the elements that use this id as sink with: + + + GList *list; + + list = gst_type_gst_sinks(id); + + + When you have a list of elements, you can simply take the first + element of the list to obtain an appropriate element. + + + + As you can see, there might be a multitude of elements that + are able to operate on video/raw types. some might include: + + + + an MP3 audio encoder. + + + + + an audio sink. + + + + + an audio resampler. + + + + + a spectrum filter. + + + + Depending on the application, you might want to use a different + element. This is why GStreamer leaves that decision up to the + application programmer. + + + + + + + id to id path detection + + You can obtain a GList of elements that + will transform the source id into the destination id. + + + GList *list; + + list = gst_type_gst_sink_to_src(sourceid, sinkid); + + + This piece of code will give you the elements needed to construct + a path from sourceid to sinkid. This function is mainly used in + autoplugging the pipeline. + + + + + + creating elements with the factory + + In the previous section we described how you could obtain + an element factory using MIME types. One the factory has been + obtained, you can create an element using: + + + GstElementFactory *factory; + GstElement *element; + + // obtain the factory + factory = ... + + element = gst_elementfactory_create(factory, "name"); + + + This way, you do not have to create elements by name which + allows the end-user to select the elements he/she prefers for the + given MIME types. + + + + + GStreamer basic types + + GStreamer only has two builtin types: + + + + + audio/raw : raw audio samples + + + + + video/raw and image/raw : raw video data + + + + + All other MIME types are maintained by the plugin elements. + + + + diff --git a/docs/manual/cothreads.sgml b/docs/manual/cothreads.sgml new file mode 100644 index 0000000000..2b0080bbdc --- /dev/null +++ b/docs/manual/cothreads.sgml @@ -0,0 +1,6 @@ + + Cothreads + + + + diff --git a/docs/manual/dynamic.sgml b/docs/manual/dynamic.sgml new file mode 100644 index 0000000000..b7b3fdc6cd --- /dev/null +++ b/docs/manual/dynamic.sgml @@ -0,0 +1,6 @@ + + Dynamic pipelines + + + + diff --git a/docs/manual/factories.sgml b/docs/manual/factories.sgml index d9423325c0..5b156e01fe 100644 --- a/docs/manual/factories.sgml +++ b/docs/manual/factories.sgml @@ -3,9 +3,14 @@ The small application we created in the previous chapter used the concept of a factory to create the elements. In this chapter we will - show you how to use the factory concepts. + show you how to use the factory concepts to create elements based + on what they do instead of how they are called. + + We will first explain the concepts involved before we move on + to the reworked helloworld example using autoplugging. + The problems with the helloworld example @@ -194,7 +199,7 @@ struct _GstType { - id to <classname>GstElement</classname> conversion + id to <classname>GstElementFactory</classname> conversion When we have obtained a given type id using one of the above methods, we can obtain a list of all the elements that operate on this MIME @@ -254,7 +259,49 @@ struct _GstType { + + + id to id path detection + + You can obtain a GList of elements that + will transform the source id into the destination id. + + + GList *list; + + list = gst_type_gst_sink_to_src(sourceid, sinkid); + + + This piece of code will give you the elements needed to construct + a path from sourceid to sinkid. This function is mainly used in + autoplugging the pipeline. + + + + + creating elements with the factory + + In the previous section we described how you could obtain + an element factory using MIME types. One the factory has been + obtained, you can create an element using: + + + GstElementFactory *factory; + GstElement *element; + + // obtain the factory + factory = ... + + element = gst_elementfactory_create(factory, "name"); + + + This way, you do not have to create elements by name which + allows the end-user to select the elements he/she prefers for the + given MIME types. + + + GStreamer basic types diff --git a/docs/manual/ghostpads.sgml b/docs/manual/ghostpads.sgml new file mode 100644 index 0000000000..276ab416d5 --- /dev/null +++ b/docs/manual/ghostpads.sgml @@ -0,0 +1,6 @@ + + Ghostpads + + + + diff --git a/docs/manual/gstreamer-manual.sgml b/docs/manual/gstreamer-manual.sgml index c80881776c..eb7fb5d12b 100644 --- a/docs/manual/gstreamer-manual.sgml +++ b/docs/manual/gstreamer-manual.sgml @@ -11,8 +11,20 @@ -]> + + + + + + + + + + + + +]> @@ -110,6 +122,7 @@ &FACTORIES; + &HELLOWORLD2; @@ -118,10 +131,27 @@ - Wanna know more? + In this part we will cover the more advanced features of GStreamer. + With the basics you learned in the prevous part you should be + able to create a 'simple' pipeline. If you want more control over + the media types and the pipeline you should use the more + low-level features of GStreamer. + &THREADS; + + &QUEUES; + + &COTHREADS; + + &DYNAMIC; + + &GHOSTPADS; + + &TYPEDETECTION; + + &UTILITY; @@ -130,10 +160,13 @@ - Just say how we use it... + GStreamer has the posibility to externalize the pipelines + you create using an XML format. You can load a previously + created pipeline by loading the XML file. + &XML; @@ -142,10 +175,12 @@ - A lot of text will follow... + In this part we will describe how you create a new plugin + to be used in GStreamer. + &PLUGINS; @@ -155,8 +190,12 @@ + GStreamer comes prepackaged with a few programs. + + &PROGRAMS; + diff --git a/docs/manual/helloworld2.sgml b/docs/manual/helloworld2.sgml new file mode 100644 index 0000000000..bc9db8e0ab --- /dev/null +++ b/docs/manual/helloworld2.sgml @@ -0,0 +1,182 @@ + + Your second application + + In the previous chapter we created a first version of the helloworld + application. We then explained a better way of creating the elements + using factories identified by MIME types. + + + In this chapter we will introduce you to autoplugging. Using the MIME + types of the elements GStreamer can automatically create a pipeline + for you. + + + + Autoplugging helloworld + + We will create a second version of the helloworld application using + autoplugging. Its source code is considerably more easy to write and + can also much more data types. + + + + +#include <gst/gst.h> + +static gboolean playing; + +/* eos will be called when the src element has an end os stream */ +void eos(GstSrc *src) +{ + g_print("have eos, quitting\n"); + + playing = FALSE; +} + +int main(int argc,char *argv[]) +{ + GstElement *disksrc, *audiosink; + GstPipeline *pipeline; + + if (argc != 2) { + g_print("usage: %s <filename>\n", argv[0]); + exit(-1); + } + + gst_init(&argc,&argv); + gst_plugin_load_all(); + g_print("\n"); + + /* create a new bin to hold the elements */ + pipeline = gst_pipeline_new("pipeline"); + + /* create a disk reader */ + disksrc = gst_elementfactory_make("disksrc", "disk_source"); + gtk_object_set(GTK_OBJECT(disksrc),"location", argv[1],NULL); + gtk_signal_connect(GTK_OBJECT(disksrc),"eos", + GTK_SIGNAL_FUNC(eos),NULL); + + /* and an audio sink */ + audiosink = gst_elementfactory_make("audiosink", "play_audio"); + + /* add objects to the main pipeline */ + gst_bin_add(GST_BIN(pipeline), disksrc); + gst_bin_add(GST_BIN(pipeline), audiosink); + + if (!gst_pipeline_autoplug(pipeline)) { + g_print("unable to handle stream\n"); + exit(-1); + } + + /* find out how to handle this bin */ + gst_bin_create_plan(GST_BIN(pipeline)); + + /* make it ready */ + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_READY); + /* start playing */ + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); + + playing = TRUE; + + while (playing) { + gst_bin_iterate(GST_BIN(pipeline)); + } + + /* stop the bin */ + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL); + + gst_pipeline_destroy(pipeline); + + exit(0); +} + + + + + first of all, we do not use any mpg123 or mp3parse element in this example. + In fact, we only specify a source element and a sink element and add them + to a pipeline. + + + + The most interesting change however is the following: + + + + ... + if (!gst_pipeline_autoplug(pipeline)) { + g_print("unable to handle stream\n"); + exit(-1); + } + ... + + + + + This piece of code does all the magic. + + + + + + + The source and the sink elements will be found inside the pipeline + + + + + Since the source has no type, a typedetection will be started on + the source element. + + + + + The best set of elements that connect the MIME type of the source + element to the MIME type of the sink are found. + + + + + The elements are added to the pipeline and their pads are connected. + + + + + + + After this autoplugging, the pipeline is ready to play. Remember that this + pipeline will be able to playback all of the media types for which an + appropriate plugin exists since the autoplugging is all done using MIME + types. + + + + I you really want, you can use the GSteamer components to do the + autoplugging yourself. + + + + To compile the helloworld2 example, use: + + + gcc -Wall `gstreamer-config --cflags` `gtk-config --cflags` helloworld2.c \ + -o helloworld2 `gstreamer-config --libs` `gtk-config --libs` + + + You can run the example with (substitute helloworld.mp3 with you favorite MP3 file): + + + ./helloworld2 helloworld.mp3 + + + You can also try to use an AVI or MPEG file as its input. Using autoplugging, + GStreamer will automatically figure out how to handle the stream. Remember that + only the audio part will be played because we have only added an audiosink to + the pipeline. + + + ./helloworld2 helloworld.mpeg + + + + diff --git a/docs/manual/outline.txt b/docs/manual/outline.txt index ee0aece6e7..def2e4720c 100644 --- a/docs/manual/outline.txt +++ b/docs/manual/outline.txt @@ -53,6 +53,8 @@ Building apps MIME types GStreamer types Basic types + Your second application + advanced concepts @@ -61,24 +63,27 @@ advanced concepts cothreads dynamic pipeline construction ghost pads - types type detection - plugin registry - bufferpools - Quality of service utility functions - XML in GStreamer (saving) (loading a pipeline) Plugin development + plugin types + chain based + loop based buffers metadata subbufers adding pads libraries + plugin registry + types + type detection + QoS messages + clocks GStreamer programs editor diff --git a/docs/manual/plugins.sgml b/docs/manual/plugins.sgml new file mode 100644 index 0000000000..00ffa18986 --- /dev/null +++ b/docs/manual/plugins.sgml @@ -0,0 +1,6 @@ + + Plugin development + + + + diff --git a/docs/manual/programs.sgml b/docs/manual/programs.sgml new file mode 100644 index 0000000000..d2fcd39991 --- /dev/null +++ b/docs/manual/programs.sgml @@ -0,0 +1,6 @@ + + Programs + + + + diff --git a/docs/manual/queues.sgml b/docs/manual/queues.sgml new file mode 100644 index 0000000000..1a77d71aff --- /dev/null +++ b/docs/manual/queues.sgml @@ -0,0 +1,6 @@ + + Queues + + + + diff --git a/docs/manual/threads.sgml b/docs/manual/threads.sgml new file mode 100644 index 0000000000..cb6592dd9d --- /dev/null +++ b/docs/manual/threads.sgml @@ -0,0 +1,6 @@ + + Threads + + + + diff --git a/docs/manual/typedetection.sgml b/docs/manual/typedetection.sgml new file mode 100644 index 0000000000..1ec4f42794 --- /dev/null +++ b/docs/manual/typedetection.sgml @@ -0,0 +1,6 @@ + + Typedetection + + + + diff --git a/docs/manual/utility.sgml b/docs/manual/utility.sgml new file mode 100644 index 0000000000..aa3a0ee45e --- /dev/null +++ b/docs/manual/utility.sgml @@ -0,0 +1,6 @@ + + Utility functions + + + + diff --git a/docs/manual/xml.sgml b/docs/manual/xml.sgml new file mode 100644 index 0000000000..5099bcd11b --- /dev/null +++ b/docs/manual/xml.sgml @@ -0,0 +1,6 @@ + + XML in GStreamer + + + + diff --git a/examples/helloworld2/helloworld2.c b/examples/helloworld2/helloworld2.c index 019998500c..e2a0ab7ef1 100644 --- a/examples/helloworld2/helloworld2.c +++ b/examples/helloworld2/helloworld2.c @@ -24,8 +24,6 @@ int main(int argc,char *argv[]) gst_plugin_load_all(); g_print("\n"); - gst_type_dump(); - /* create a new bin to hold the elements */ pipeline = gst_pipeline_new("pipeline"); diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c index 56bdd1ba39..104a500737 100644 --- a/gst/gstpipeline.c +++ b/gst/gstpipeline.c @@ -54,6 +54,7 @@ static GstElementStateReturn gst_pipeline_change_state(GstElement *element); static void gst_pipeline_prepare(GstPipeline *pipeline); static void gst_pipeline_have_type(GstSink *sink, GstSink *sink2, gpointer data); +static void gst_pipeline_pads_autoplug(GstElement *src, GstElement *sink); static GstBin *parent_class = NULL; //static guint gst_pipeline_signals[LAST_SIGNAL] = { 0 }; @@ -160,6 +161,66 @@ static guint16 gst_pipeline_typefind(GstPipeline *pipeline, GstElement *element) return type_id; } +static void gst_pipeline_pads_autoplug_func(GstElement *src, GstPad *pad, GstElement *sink) { + GList *sinkpads; + GstPad *sinkpad; + + g_print("gstpipeline: autoplug pad connect function type %d\n", pad->type); + + sinkpads = gst_element_get_pad_list(sink); + while (sinkpads) { + sinkpad = (GstPad *)sinkpads->data; + + // if we have a match, connect the pads + if (sinkpad->type == pad->type && sinkpad->direction == GST_PAD_SINK && !GST_PAD_CONNECTED(sinkpad)) { + gst_pad_connect(pad, sinkpad); + g_print("gstpipeline: autoconnect pad \"%s\" (%d) in element %s <-> ", pad->name, pad->type, gst_element_get_name(src)); + g_print("pad \"%s\" (%d) in element %s\n", sinkpad->name, sinkpad->type, gst_element_get_name(sink)); + break; + } + sinkpads = g_list_next(sinkpads); + } +} + +static void gst_pipeline_pads_autoplug(GstElement *src, GstElement *sink) { + GList *srcpads, *sinkpads; + gboolean connected = FALSE; + + srcpads = gst_element_get_pad_list(src); + + while (srcpads) { + GstPad *srcpad = (GstPad *)srcpads->data; + GstPad *sinkpad; + + if (srcpad->direction == GST_PAD_SRC && !GST_PAD_CONNECTED(srcpad)) { + + sinkpads = gst_element_get_pad_list(sink); + // FIXME could O(n) if the types were sorted... + while (sinkpads) { + sinkpad = (GstPad *)sinkpads->data; + + // if we have a match, connect the pads + if (sinkpad->type == srcpad->type && sinkpad->direction == GST_PAD_SINK && !GST_PAD_CONNECTED(sinkpad)) { + gst_pad_connect(srcpad, sinkpad); + g_print("gstpipeline: autoconnect pad \"%s\" (%d) in element %s <-> ", srcpad->name, srcpad->type, gst_element_get_name(src)); + g_print("pad \"%s\" (%d) in element %s\n", sinkpad->name, sinkpad->type, gst_element_get_name(sink)); + connected = TRUE; + goto end; + } + sinkpads = g_list_next(sinkpads); + } + } + srcpads = g_list_next(srcpads); + } + +end: + if (!connected) { + g_print("gstpipeline: delaying pad connections\n"); + gtk_signal_connect(GTK_OBJECT(src),"new_pad", + GTK_SIGNAL_FUNC(gst_pipeline_pads_autoplug_func), sink); + } +} + gboolean gst_pipeline_autoplug(GstPipeline *pipeline) { GList *elements; GstElement *element, *srcelement, *sinkelement; @@ -175,7 +236,7 @@ gboolean gst_pipeline_autoplug(GstPipeline *pipeline) { elements = gst_bin_get_list(GST_BIN(pipeline)); - // fase 1, find all the sinks and sources... + // fase 1, find all the sinks and sources... FIXME need better way to do this... while (elements) { element = GST_ELEMENT(elements->data); @@ -250,10 +311,7 @@ gboolean gst_pipeline_autoplug(GstPipeline *pipeline) { element = gst_elementfactory_create(factory, factory->name); gst_bin_add(GST_BIN(pipeline), element); - // FIXME match paths to connect with MIME types instead - // of names. - gst_pad_connect(gst_element_get_pad(srcelement,"src"), - gst_element_get_pad(element,"sink")); + gst_pipeline_pads_autoplug(srcelement, element); srcelement = element; @@ -263,8 +321,7 @@ gboolean gst_pipeline_autoplug(GstPipeline *pipeline) { } if (complete) { - gst_pad_connect(gst_element_get_pad(srcelement,"src"), - gst_element_get_pad(sinkelement,"sink")); + gst_pipeline_pads_autoplug(srcelement, sinkelement); return TRUE; } @@ -282,8 +339,6 @@ static GstElementStateReturn gst_pipeline_change_state(GstElement *element) { switch (GST_STATE_PENDING(pipeline)) { case GST_STATE_READY: // we need to set up internal state - g_print("preparing pipeline \"%s\" for iterations:\n", - gst_element_get_name(GST_ELEMENT(element))); gst_pipeline_prepare(pipeline); break; default: diff --git a/gst/gsttype.c b/gst/gsttype.c index a3aea32e62..29c1dbf782 100644 --- a/gst/gsttype.c +++ b/gst/gsttype.c @@ -33,6 +33,8 @@ GList *_gst_types; guint16 _gst_maxtype; +#define MAX_COST 999999 + struct _gst_type_node { int iNode; @@ -302,7 +304,7 @@ static GList *construct_path (gst_type_node *rgnNodes, gint chNode) GstType *type; GList *converters; - while (current != 999) + while (current != MAX_COST) { type = gst_type_find_by_id(current); converters = (GList *)g_hash_table_lookup(type->converters, GUINT_TO_POINTER(src)); @@ -322,7 +324,7 @@ static guint gst_type_find_cost(gint src, gint dest) { GList *converters = (GList *)g_hash_table_lookup(type->converters, GUINT_TO_POINTER(dest)); if (converters) return 1; - return 999; + return MAX_COST; } GList *gst_type_get_sink_to_src(guint16 sinkid, guint16 srcid) { @@ -339,13 +341,13 @@ GList *gst_type_get_sink_to_src(guint16 sinkid, guint16 srcid) { for (i=0; i< _gst_maxtype; i++) { rgnNodes[i].iNode = i; - rgnNodes[i].iDist = 999; - rgnNodes[i].iPrev = 999; + rgnNodes[i].iDist = MAX_COST; + rgnNodes[i].iPrev = MAX_COST; } rgnNodes[sinkid].iDist = 0; - rgnNodes[sinkid].iPrev = 999; + rgnNodes[sinkid].iPrev = MAX_COST; - queue = gst_type_enqueue(queue, sinkid, 0, 999); + queue = gst_type_enqueue(queue, sinkid, 0, MAX_COST); while (g_list_length(queue) > 0) { @@ -353,8 +355,8 @@ GList *gst_type_get_sink_to_src(guint16 sinkid, guint16 srcid) { for (i=0; i< _gst_maxtype; i++) { iCost = gst_type_find_cost(iNode, i); - if (iCost != 999) { - if((999 == rgnNodes[i].iDist) || + if (iCost != MAX_COST) { + if((MAX_COST == rgnNodes[i].iDist) || (rgnNodes[i].iDist > (iCost + iDist))) { rgnNodes[i].iDist = iDist + iCost; rgnNodes[i].iPrev = iNode; diff --git a/tests/old/examples/helloworld2/helloworld2.c b/tests/old/examples/helloworld2/helloworld2.c index 019998500c..e2a0ab7ef1 100644 --- a/tests/old/examples/helloworld2/helloworld2.c +++ b/tests/old/examples/helloworld2/helloworld2.c @@ -24,8 +24,6 @@ int main(int argc,char *argv[]) gst_plugin_load_all(); g_print("\n"); - gst_type_dump(); - /* create a new bin to hold the elements */ pipeline = gst_pipeline_new("pipeline");