diff options
author | Steve Baker <steve@stevebaker.org> | 2002-03-10 19:57:31 +0000 |
---|---|---|
committer | Steve Baker <steve@stevebaker.org> | 2002-03-10 19:57:31 +0000 |
commit | 99e581dc9402bb27ec550932286658a5de9ffa01 (patch) | |
tree | 4b2f07895dd278cf151b0159bfb5e6642541dc12 | |
parent | 2a4712c20fd69b6b70000ad8e9122aa8cd94eb9c (diff) | |
download | gstreamer-99e581dc9402bb27ec550932286658a5de9ffa01.tar.gz |
first cut of documentation for dparams for plugin writers.
Original commit message from CVS:
first cut of documentation for dparams for plugin writers.
who would have thought that writing docs was so much fun
-rw-r--r-- | docs/fwg/dparams.xml | 380 | ||||
-rw-r--r-- | docs/fwg/gst-plugin-writers-guide.xml | 62 | ||||
-rw-r--r-- | docs/fwg/titlepage.xml | 10 |
3 files changed, 452 insertions, 0 deletions
diff --git a/docs/fwg/dparams.xml b/docs/fwg/dparams.xml new file mode 100644 index 0000000000..f8e0bd9965 --- /dev/null +++ b/docs/fwg/dparams.xml @@ -0,0 +1,380 @@ +<chapter id="cha-dparam-start"> + <title>Getting Started</title> + + <para> + The dparams subsystem is contained within the <filename>gstcontrol</filename> library. + You need to include the header in your element's source file: + </para> + <programlisting> + #include <gst/control/control.h> + </programlisting> + + <para> + Even though the <filename>gstcontrol</filename> library may be linked into the host + application, you should make sure it is loaded in your <filename>plugin_init</filename> + function: + </para> + <programlisting> + static gboolean + plugin_init (GModule *module, GstPlugin *plugin) + { + ... + + /* load dparam support library */ + if (!gst_library_load ("gstcontrol")) + { + gst_info ("example: could not load support library: 'gstcontrol'\n"); + return FALSE; + } + + ... + } + </programlisting> + + <para> + You need to store an instance of <filename>GstDParamManager</filename> in your element's struct: + </para> + <programlisting> + struct _GstExample { + GstElement element; + ... + + GstDParamManager *dpman; + + ... + }; + </programlisting> + + <para> + The <filename>GstDParamManager</filename> can be initialised in your element's + init function: + </para> + <programlisting> + static void + gst_example_init (GstExample *example) + { + ... + + example->dpman = gst_dpman_new ("example_dpman", GST_ELEMENT(example)); + + ... + } + </programlisting> + +</chapter> + +<chapter id="cha-dparam-define"> + <title>Defining Parameter Specificiations</title> + <para> + You can define the dparams you need anywhere within your element but will usually + need to do so in only a couple of places: + <itemizedlist> + <listitem> + In the element <filename>init</filename> function, + just after the call to <filename>gst_dpman_new</filename> + </listitem> + <listitem> + Whenever a new pad is created so that parameters can affect data going into + or out of a specific pad. An example of this would be a mixer element where + a seperate volume parameter is needed on every pad. + </listitem> + </itemizedlist> + </para> + <para> + There are three different ways the dparams subsystem can pass parameters into your element. + Which one you use will depend on how that parameter is used within your element. + Each of these methods has its own function to define a required dparam: + <itemizedlist> + <listitem><filename>gst_dpman_add_required_dparam_direct</filename></listitem> + <listitem><filename>gst_dpman_add_required_dparam_callback</filename></listitem> + <listitem><filename>gst_dpman_add_required_dparam_array</filename></listitem> + </itemizedlist> + These functions will return TRUE if the required dparam was added successfully. + </para> + <para> + The following function will be used as an example. + <programlisting> + gboolean + gst_dpman_add_required_dparam_direct (GstDParamManager *dpman, + GParamSpec *param_spec, + gboolean is_log, + gboolean is_rate, + gpointer update_data) + </programlisting> + The common parameters to these functions are: + <itemizedlist> + <listitem><filename>GstDParamManager *dpman</filename> the element's dparam manager</listitem> + <listitem><filename>GParamSpec *param_spec</filename> the param spec which defines the required dparam</listitem> + <listitem> + <filename>gboolean is_log</filename> whether this dparam value should be + interpreted on a log scale (such as a frequency or a decibel value) + </listitem> + <listitem> + <filename>gboolean is_rate</filename> whether this dparam value is a proportion of the + sample rate. For example with a sample rate of 44100, 0.5 would be 22050 Hz and 0.25 would be 11025 Hz. + </listitem> + </itemizedlist> + </para> + <sect2 id="sect-dparam-direct"> + <title>Direct Method</title> + <para> + This method is the simplest and has the lowest overhead for parameters which change + less frequently than the sample rate. First you need somewhere to store the parameter - + this will usually be in your element's stuct. + </para> + <programlisting> + struct _GstExample { + GstElement element; + ... + + GstDParamManager *dpman; + gfloat volume; + ... + }; + </programlisting> + <para> + Then to define the required dparam just call <filename>gst_dpman_add_required_dparam_direct</filename> + and pass in the location of the parameter to change. + In this case the location is <filename>&(example->volume)</filename>. + </para> + <programlisting> + gst_dpman_add_required_dparam_direct ( + example->dpman, + g_param_spec_float("volume","Volume","Volume of the audio", + 0.0, 1.0, 0.8, G_PARAM_READWRITE), + FALSE, + FALSE, + &(example->volume) + ); + </programlisting> + <para> + You can now use <filename>example->volume</filename> anywhere in your element knowing + that it will always contain the correct value to use. + </para> + </sect2> + <sect2 id="sect-dparam-callback"> + <title>Callback Method</title> + <para> + This should be used if the you have other values to calculate whenever a parameter changes. + If you used the direct method you wouldn't know if a parameter had changed so you would have to + recalculate the other values every time you needed them. By using the callback method, other values + only have to be recalculated when the dparam value actually changes. + </para> + <para> + The following code illustrates an instance where you might want to use the callback method. + If you had a volume dparam which was represented by a gfloat number, your element may only deal + with integer arithmatic. The callback could be used to calculate the integer scaler when the volume + changes. First you will need somewhere to store these values. + </para> + <programlisting> + struct _GstExample { + GstElement element; + ... + + GstDParamManager *dpman; + gfloat volume_f; + gint volume_i; + ... + }; + </programlisting> + <para> + When the required dparam is defined, the callback function <filename>gst_example_update_volume</filename> + and some user data (which in this case is our element instance) is passed in to the call to + <filename>gst_dpman_add_required_dparam_callback</filename>. + </para> + <programlisting> + gst_dpman_add_required_dparam_callback ( + example->dpman, + g_param_spec_float("volume","Volume","Volume of the audio", + 0.0, 1.0, 0.8, G_PARAM_READWRITE), + FALSE, + FALSE, + gst_example_update_volume, + example + ); + </programlisting> + <para> + The callback function needs to conform to this signiture + </para> + <programlisting> +typedef void (*GstDPMUpdateFunction) (GValue *value, gpointer data); + </programlisting> + <para> + In our example the callback function looks like this + </para> + <programlisting> +static void +gst_example_update_volume(GValue *value, gpointer data) +{ + GstExample *example = (GstExample*)data; + g_return_if_fail(GST_IS_EXAMPLE(example)); + + example->volume_f = g_value_get_float(value); + example->volume_i = example->volume_f * 8192; +} + </programlisting> + <para> + Now <filename>example->volume_i</filename> can be used elsewhere and it will always contain the correct value. + </para> + </sect2> + <sect2 id="sect-dparam-array"> + <title>Array Method</title> + <para> + This method is quite different from the other two. It could be thought of as + a specialised method which should only be used if you need the advantages that it + provides. Instead of giving the element a single value it provides an array of values + where each item in the array corresponds to a sample of audio in your buffer. + There are a couple of reasons why this might be useful. + </para> + + <itemizedlist> + <listitem> + Certain optimisations may be possible since you can iterate over your dparams array + and your buffer data together. + </listitem> + <listitem> + Some dparams may be able to interpolate changing values at the sample rate. This would allow + the array to contain very smoothly changing values which may be required for the stability + and quality of some DSP algorithms. + </listitem> + </itemizedlist> + <para> + The array method is currently the least mature of the three methods and is not yet ready to be + used in elements, but plugin writers should be aware of its existance for the future. + </para> + </sect2> +</chapter> + +<chapter id="cha-dparam-loop"> + <title>The Data Processing Loop</title> + <para> + This is the most critical aspect of the dparams subsystem as it relates to elements. + In a traditional audio processing loop, a <filename>for</filename> loop will usually iterate over each + sample in the buffer, processing one sample at a time until the buffer is finished. + A simplified loop with no error checking might look something like this. + </para> + <programlisting> +static void +example_chain (GstPad *pad, GstBuffer *buf) +{ + ... + gfloat *float_data; + int j; + GstExample *example = GST_EXAMPLE(GST_OBJECT_PARENT (pad)); + int num_samples = GST_BUFFER_SIZE(buf)/sizeof(gfloat); + float_data = (gfloat *)GST_BUFFER_DATA(buf); + ... + for (j = 0; j < num_samples; j++) { + float_data[j] *= example->volume; + } + ... +} + </programlisting> + <para> + To make this dparams aware, a couple of changes are needed. + </para> + <programlisting> +static void +example_chain (GstPad *pad, GstBuffer *buf) +{ + ... + int j = 0; + GstExample *example = GST_EXAMPLE(GST_OBJECT_PARENT (pad)); + int num_samples = GST_BUFFER_SIZE(buf)/sizeof(gfloat); + gfloat *float_data = (gfloat *)GST_BUFFER_DATA(buf); + int frame_countdown = GST_DPMAN_PREPROCESS(example->dpman, num_samples, GST_BUFFER_TIMESTAMP(buf)); + ... + while (GST_DPMAN_PROCESS_COUNTDOWN(example->dpman, frame_countdown, j)) { + float_data[j++] *= example->volume; + } + ... +} + </programlisting> + <para> + The biggest changes here are 2 new macros, <filename>GST_DPMAN_PREPROCESS</filename> + and <filename>GST_DPMAN_PROCESS_COUNTDOWN</filename>. + You will also notice that the for loop has become a while loop. <filename>GST_DPMAN_PROCESS_COUNTDOWN</filename> + is called as the condition for the while loop so that any required dparams can be updated in the + middle of a buffer if required. This is because one of the required behaviours of dparams is that they + can be <emphasis>sample accurate</emphasis>. This means that parameters change at the exact timestamp + that they are supposed to - not after the buffer has finished being processed. + </para> + <para> + It may be alarming to see a macro as the condition for a while loop, but it is actually very efficient. + The macro expands to the following. + </para> + <programlisting> +#define GST_DPMAN_PROCESS_COUNTDOWN(dpman, frame_countdown, frame_count) \ + (frame_countdown-- || \ + (frame_countdown = GST_DPMAN_PROCESS(dpman, frame_count))) + </programlisting> + <para> + So as long as <filename>frame_countdown</filename> is greater than 0, <filename>GST_DPMAN_PROCESS</filename> + will not be called at all. + Also in many cases, <filename>GST_DPMAN_PROCESS</filename> will do nothing and simply + return 0, meaning that there is no more data in the buffer to process. + </para> + <para> + The macro <filename>GST_DPMAN_PREPROCESS</filename> will do the following: + <itemizedlist> + <listitem> + Update any dparams which are due to be updated. + </listitem> + <listitem> + Calculate how many samples should be processed before the next required update + </listitem> + <listitem> + Return the number of samples until next update, or the number of samples in the buffer - + whichever is less. + </listitem> + </itemizedlist> + In fact <filename>GST_DPMAN_PROCESS</filename> may do the same things as <filename>GST_DPMAN_PREPROCESS</filename> + depending on the mode that the dparam manager is running in (see below). + </para> + <sect2 id="sect-dparam-modes"> + <title>DParam Manager Modes</title> + <para> + A brief explanation of dparam manager modes might be useful here even though it doesn't generally affect + the way your element is written. There are different ways media applications will be used which + require that an element's parameters be updated in differently. These include: + <itemizedlist> + <listitem> + <emphasis>Timelined</emphasis> - all parameter changes are known in advance before the pipeline is run. + </listitem> + <listitem> + <emphasis>Realtime low-latency</emphasis> - Nothing is known ahead of time about when a parameter + might change. Changes need to be propagated to the element as soon as possible. + </listitem> + </itemizedlist> + When a dparam-aware application gets the dparam manager for an element, the first thing it will do + is set the dparam manager mode. Current modes are <filename>"synchronous"</filename> + and <filename>"asynchronous"</filename>. + </para> + <para> + If you are in a realtime low-latency situation then the <filename>"synchronous"</filename> mode is appropriate. + During <filename>GST_DPMAN_PREPROCESS</filename> this mode will poll all dparams for required updates + and propagate them. <filename>GST_DPMAN_PROCESS</filename> will do nothing in this mode. + To then achieve the desired latency, the size of the buffers needs to be reduced so that the dparams will be + polled for updates at the desired frequency. + </para> + <para> + In a timelined situation, the <filename>"asynchronous"</filename> mode will be required. This mode + hasn't actually been implemented yet but will be described anyway. + The <filename>GST_DPMAN_PREPROCESS</filename> call will precalculate when and how often each dparam needs + to update for the duration of the current buffer. From then on <filename>GST_DPMAN_PROCESS</filename> will + propagate the calculated updates each time it is called until end of the buffer. If the application is rendering + to disk in non-realtime, the render could be sped up by increasing the buffer size. In the <filename>"asynchronous"</filename> + mode this could be done without affecting the sample accuracy of the parameter updates + </para> + </sect2> + <sect2 id="sect-dparam-audio-video"> + <title>DParam Manager Modes</title> + <para> + All of the explanation so far has presumed that the buffer contains audio data with many samples. + Video should be regarded differently since a video buffer often contains only 1 frame. In this case + some of the complexity of dparams isn't required but the other benefits still make it useful for video + parameters. If a buffer only contains one frame of video, only a single call to <filename>GST_DPMAN_PREPROCESS</filename> + should be required. For more than one frame per buffer, treat it the same as the audio case. + </para> + </sect2> +</chapter> diff --git a/docs/fwg/gst-plugin-writers-guide.xml b/docs/fwg/gst-plugin-writers-guide.xml index d17edd21b1..3774220067 100644 --- a/docs/fwg/gst-plugin-writers-guide.xml +++ b/docs/fwg/gst-plugin-writers-guide.xml @@ -12,6 +12,7 @@ <!ENTITY SRCNSINK SYSTEM "srcnsink.xml"> <!ENTITY STATEMANAGE SYSTEM "statemanage.xml"> <!ENTITY CHECKLIST SYSTEM "checklist.xml"> +<!ENTITY DPARAMS SYSTEM "dparams.xml"> <!ENTITY GStreamer "<application>GStreamer</application>"> ]> @@ -749,6 +750,67 @@ <!-- ############ part ############# --> + <part id="dparams"><title>Supporting Dynamic Parameters</title> + <partintro> + <para> + Sometimes object properties are not powerful enough to control the + parameters that affect the behaviour of your element. When this is + the case you can expose these parameters as Dynamic Parameters + which can be manipulated by any Dynamic Parameters aware application. + </para> + <para> + Throughout this section, the term dparams will be used as an abbreviation for Dynamic Parameters. + </para> + </partintro> + <sect2 id="sect-dparams-compare"> + <title>Comparing Dynamic Parameters with GObject Properties</title> + <para> + Your first exposure to dparams may be to convert an existing element from + using object properties to using dparams. The following table gives an overview + of the difference between these approaches. The significance of these + differences should become apparent later on. + </para> + <informaltable frame="all"> + <tgroup cols="3"> + <thead> + <row> + <entry></entry> + <entry>Object Properties</entry> + <entry>Dynamic Parameters</entry> + </row> + </thead> + <tbody> + <row> + <entry><emphasis>Parameter definition</emphasis></entry> + <entry>Class level at compile time</entry> + <entry>Any level at run time</entry> + </row> + <row> + <entry><emphasis>Getting and setting</emphasis></entry> + <entry>Implemented by element subclass as functions</entry> + <entry>Handled entirely by dparams subsystem</entry> + </row> + <row> + <entry><emphasis>Extra objects required</emphasis></entry> + <entry>None - all functionality is derived from base GObject</entry> + <entry>Element needs to create and store a <filename>GstDParamManager</filename> at object creation</entry> + </row> + <row> + <entry><emphasis>Frequency and resolution of updates</emphasis></entry> + <entry>Object properties will only be updated between calls to _get, _chain or _loop</entry> + <entry>dparams can be updated at any rate independant of calls to _get, _chain or _loop + up to sample-level accuracy</entry> + </row> + </tbody> + </tgroup> + </informaltable> + </sect2> + + &DPARAMS; + </part> + +<!-- ############ part ############# --> + <part id="test-app"><title>Building a simple test application</title> <partintro> <para> diff --git a/docs/fwg/titlepage.xml b/docs/fwg/titlepage.xml index c7b46aca1b..1a7dd862a2 100644 --- a/docs/fwg/titlepage.xml +++ b/docs/fwg/titlepage.xml @@ -21,6 +21,16 @@ </para> </authorblurb> </author> + + <author> + <firstname>Steve</firstname> + <surname>Baker</surname> + <authorblurb> + <para> + <email>stevebaker_org@yahoo.co.uk</email> + </para> + </authorblurb> + </author> </authorgroup> <legalnotice id="legalnotice"> |