diff --git a/docs/random/autoplug2 b/docs/random/autoplug2 new file mode 100644 index 0000000000..1c727973b9 --- /dev/null +++ b/docs/random/autoplug2 @@ -0,0 +1,289 @@ + +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 +repeadedly 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 prefered 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... +