| |
| 1) The Autoplugger API |
| ---------------------- |
| |
| We'll first describe how to use the autoplugger. We will provide |
| a use case: autoplug an mpeg1 system stream for audio/video playback. |
| |
| |
| a) creating an autoplugger |
| -------------------------- |
| |
| Before any autoplugging can be done, you'll have to create an |
| autoplugger object. Autoplugger objects (autopluggers) are |
| provided by plugins and are created with gst_autoplugfactor_make(). |
| |
| GStreamer has provisions for two types of autopluggers: |
| |
| - regular autopluggers, which act as a complex element construction |
| mechanism. They usually don't create threads and operate solely on |
| GstCaps* for the source and destination. The complex elements |
| created by regular autopluggers have src and sink pad compatible |
| with the requested GstCaps*. |
| |
| - renderer autopluggers, which are designed to create a complex |
| object that can be used to playback media. Renderer autoplugged |
| complex elements have no src pads, only one sink pad. |
| |
| We'll create a renderer autoplugger like this: |
| |
| ! |
| ! GstAutoplug *autoplug; |
| ! |
| ! autoplug = gst_autoplugfactory_make ("staticrender"); |
| ! |
| |
| |
| b) finding out the source media type. |
| ------------------------------------- |
| |
| Before we can start the autoplugger, we have to find out the |
| source media type. This can be done using the typefind functions |
| provided by various plugins. |
| |
| We will create a little pipeline to detect the media type by connecting |
| a disksrc element to a typefind element. The typefind element will |
| repeatedly call all registered typefind functions with the buffer it |
| receives on its sink pad. when a typefind function returns a non NULL |
| GstCaps*, that caps is set to the sink pad of the typefind element and |
| a signal is emitted to notify the app. |
| |
| Due to caps negotiation, the disksrc will have the detected GstCaps* |
| set on its src pad. |
| |
| We typically use a function like below to detect the type of a media stream |
| on an element (typically a disksrc). The function accepts a pipeline and the |
| element inside the pipeline on which the typefind should be performed (passing |
| a GstPad* is probably a better option FIXME). |
| |
| ! |
| ! static GstCaps* |
| ! gst_play_typefind (GstBin *bin, GstElement *element) |
| ! { |
| ! GstElement *typefind; |
| ! GstCaps *caps = NULL; |
| ! |
| ! typefind = gst_elementfactory_make ("typefind", "typefind"); |
| ! g_return_val_if_fail (typefind != NULL, FALSE); |
| ! |
| ! gst_pad_connect (gst_element_get_pad (element, "src"), |
| ! gst_element_get_pad (typefind, "sink")); |
| ! |
| ! gst_bin_add (bin, typefind); |
| ! |
| ! gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); |
| ! |
| ! // push a buffer... the have_type signal handler will set the found flag |
| ! gst_bin_iterate (bin); |
| ! |
| ! gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL); |
| ! |
| ! caps = gst_pad_get_caps (gst_element_get_pad (element, "src")); |
| ! |
| ! gst_pad_disconnect (gst_element_get_pad (element, "src"), |
| ! gst_element_get_pad (typefind, "sink")); |
| ! gst_bin_remove (bin, typefind); |
| ! gst_object_unref (GST_OBJECT (typefind)); |
| ! |
| ! return caps; |
| ! } |
| ! |
| |
| Also note that the disksrc was added to the pipeline before calling this |
| typefind function. |
| |
| When the function returns a non-NULL pointer, the media type has been |
| determined and autoplugging can commence. |
| |
| Assume that in our mpeg1 use case the above function returns a GstCaps* |
| like: |
| |
| ! |
| ! srccaps = GST_CAPS_NEW ("mpeg1system_typefind", |
| ! "video/mpeg", |
| ! "mpegversion", GST_PROPS_INT (1), |
| ! "systemstream", GST_PROPS_BOOLEAN (TRUE) |
| ! ); |
| ! |
| |
| |
| c) Performing the autoplugging |
| ------------------------------ |
| |
| Since we use the renderer API, we have to create the output elements |
| that are going to be used as the final sink elements. |
| |
| ! |
| ! osssink = gst_elementfactory_make("osssink", "play_audio"); |
| ! videosink = gst_elementfactory_make("xvideosink", "play_video"); |
| ! |
| |
| We then create a complex element using the following code. |
| |
| ! |
| ! new_element = gst_autoplug_to_renderers (autoplug, |
| ! srccaps, |
| ! videosink, |
| ! osssink, |
| ! NULL); |
| ! |
| ! if (!new_element) { |
| ! g_print ("could not autoplug, no suitable codecs found...\n"); |
| ! exit (-1); |
| ! } |
| ! |
| |
| 2) Autoplugging internals |
| ------------------------- |
| |
| We will now describe the internals of the above gst_autoplug_to_renderers() |
| function call. This code is implemented in a plugin found in: |
| |
| gst/autoplug/gststaticautoplugrender.c |
| |
| |
| |
| a) phase1: create lists of factories. |
| --------------------------------------- |
| |
| The autoplugger will start with executing the following piece of |
| code: |
| |
| ! |
| ! i = 0; |
| ! |
| ! for each sink: |
| ! { |
| ! sinkpad = take the first sinkpad of the sink (HACK) |
| ! |
| ! list[i] = gst_autoplug_caps (srccaps, sinkpad->caps); |
| ! |
| ! i++; |
| ! } |
| ! |
| |
| gst_autoplug_caps will figure out (based on the padtemplates) |
| which elementfactories are needed to connect srccaps to sinkpad->caps |
| and will return them in a list. |
| |
| The element list is created by using a modified shortest path algorithm |
| by Dijkstra (http://www.orie.cornell.edu/~or115/handouts/handout3/handout3.html). |
| The nodes of the graph are the elementfactories and the weight of the |
| arcs is based on the pad compatibility of the padtemplates of the |
| elementfactory. For incompatible elementfactories, we use a weight of |
| MAX_COST (999999) and for compatible padtemplates we use 1. |
| |
| ex. we have two sinks with following caps: |
| |
| ! |
| ! video/raw audio/raw |
| ! "...." "...." |
| ! |
| |
| gst_autoplug_caps will figure out that for the first sink the following |
| elements are needed: |
| |
| ! |
| ! mpeg1parse, mp1videoparse, mpeg_play |
| ! |
| |
| for the second sink the following is needed: |
| |
| ! |
| ! mpeg1parse, mad |
| ! |
| |
| Note that for the audio connection the element list "mpeg1parse, mp3parse, |
| mpg123" would also connect the srccaps to the audiosink caps. Since the |
| "mpeg1parse, mad" list is shorter, it it always preferred by the autoplugger. |
| |
| We now have two lists of elementfactories. |
| |
| |
| b) phase2: collect common elements from the lists and add them to a bin. |
| ------------------------------------------------------------------------ |
| |
| The rationale is that from the lists we have created in phase1, there |
| must be some element that is a splitter and that it has to come first (HACK) |
| We try to find that element by comparing the lists until an element differs. |
| |
| We start by creating a toplevel bin that is going to be our complex element. |
| |
| In our use-case we find that mpeg1parse is an element common to both lists, |
| so we add it to the bin. We then try to find a good ghostpad for the resulting |
| complex element. This is done by looping over the sink pads of the first common |
| element and taking the pad that is compatible with the srcaps. |
| |
| We end up with a bin like this: |
| ! |
| ! (----------------------) |
| ! ! autoplug_bin ! |
| ! ! ! |
| ! ! (------------) ! |
| ! ! ! mpeg1parse ! ! |
| ! ! - sink ! ! |
| ! ! / (------------) ! |
| ! sink ! |
| ! (----------------------) |
| ! |
| |
| |
| c) phase3: add remaining elements |
| --------------------------------- |
| |
| now we loop over all the list and try to add the remaining elements |
| |
| (HACK) we always use a new thread for the elements when there is a common |
| element found. |
| |
| if a new thread is needed (either becuase the previous element is a common |
| element or the object flag of the next element is set to GST_SUGGEST_THREAD) |
| we add a queue to the bin and we add a new thread. We add the elements to |
| the bin and connect them using gst_pipeline_pads_autoplug. |
| |
| we finally arrive at the sink element and we're done. |
| |
| ex. |
| |
| we have just found our mpeg1parse common element, so we start a thread. |
| We add a queue to the bin and a new thread, we add the elements |
| mp1videoparse and mpeg_play to the thread. We arrive at the videosink, we |
| see that the SUGGEST_THREAD flag is set, we add a queue and a thread and |
| add the videosink in the thread. |
| |
| the same procedure happens for the audio part. We are now left with the |
| following pipeline: |
| |
| We will also have set a signal "new_pad" on the mpeg1parse element because |
| the element mp1videoparse could not be connected to the element just yet. |
| |
| (---------------------------------------------------------------------------------------------) |
| !autoplug_bin ! |
| ! ! |
| ! (----------------------------------------) (------------) ! |
| ! !thread ! ! thread ! ! |
| ! (-----) ! (-------------) (---------) (-----) ! ! (---------)! ! |
| ! !queue! ! !mp1videoparse! !mpeg_play! !queue! ! ! !videosink!! ! |
| ! sink src-sink src-sink src-sink src-sink !! ! |
| ! (-----------) (-----) ! (-------------) (---------) (-----) ! ! (---------)! ! |
| ! ! mpeg1parse! (----------------------------------------) (------------) ! |
| ! - sink ! ! |
| ! / (-----------) ! |
| sink (----------------------------------------) (------------) ! |
| ! !thread ! ! thread ! ! |
| ! (-----) ! (-------------) (-----) ! ! (---------)! ! |
| ! !queue! ! !mad ! !queue! ! ! !videosink!! ! |
| ! sink src-sink src ------------ sink src-sink !! ! |
| ! (-----) ! (-------------) (-----) ! ! (---------)! ! |
| ! (----------------------------------------) (------------) ! |
| (---------------------------------------------------------------------------------------------) |
| |
| The autoplugger will return the autoplug_bin. the app will then connect the |
| disksrc to the sinkpad of the autoplugged bin. |
| |
| Then we play, create_plan happens, data is flowing and the "new_pad" signal is called |
| from mpeg1parse, gst_pipeline_pad_autoplug is called and the connection between |
| mpeg1parse and the videoqueue is made. same for audio. |
| |
| Et voila. same procedure for mp3/vorbis/avi/qt/mpeg2 etc... |
| |