From 99e581dc9402bb27ec550932286658a5de9ffa01 Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Sun, 10 Mar 2002 19:57:31 +0000 Subject: [PATCH] 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 --- docs/fwg/dparams.xml | 380 ++++++++++++++++++++++++++ docs/fwg/gst-plugin-writers-guide.xml | 62 +++++ docs/fwg/titlepage.xml | 10 + 3 files changed, 452 insertions(+) create mode 100644 docs/fwg/dparams.xml 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 @@ + + Getting Started + + + The dparams subsystem is contained within the gstcontrol library. + You need to include the header in your element's source file: + + + #include <gst/control/control.h> + + + + Even though the gstcontrol library may be linked into the host + application, you should make sure it is loaded in your plugin_init + function: + + + 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; + } + + ... + } + + + + You need to store an instance of GstDParamManager in your element's struct: + + + struct _GstExample { + GstElement element; + ... + + GstDParamManager *dpman; + + ... + }; + + + + The GstDParamManager can be initialised in your element's + init function: + + + static void + gst_example_init (GstExample *example) + { + ... + + example->dpman = gst_dpman_new ("example_dpman", GST_ELEMENT(example)); + + ... + } + + + + + + Defining Parameter Specificiations + + You can define the dparams you need anywhere within your element but will usually + need to do so in only a couple of places: + + + In the element init function, + just after the call to gst_dpman_new + + + 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. + + + + + 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: + + gst_dpman_add_required_dparam_direct + gst_dpman_add_required_dparam_callback + gst_dpman_add_required_dparam_array + + These functions will return TRUE if the required dparam was added successfully. + + + The following function will be used as an example. + + gboolean + gst_dpman_add_required_dparam_direct (GstDParamManager *dpman, + GParamSpec *param_spec, + gboolean is_log, + gboolean is_rate, + gpointer update_data) + + The common parameters to these functions are: + + GstDParamManager *dpman the element's dparam manager + GParamSpec *param_spec the param spec which defines the required dparam + + gboolean is_log whether this dparam value should be + interpreted on a log scale (such as a frequency or a decibel value) + + + gboolean is_rate 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. + + + + + Direct Method + + 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. + + + struct _GstExample { + GstElement element; + ... + + GstDParamManager *dpman; + gfloat volume; + ... + }; + + + Then to define the required dparam just call gst_dpman_add_required_dparam_direct + and pass in the location of the parameter to change. + In this case the location is &(example->volume). + + + 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) + ); + + + You can now use example->volume anywhere in your element knowing + that it will always contain the correct value to use. + + + + Callback Method + + 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. + + + 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. + + + struct _GstExample { + GstElement element; + ... + + GstDParamManager *dpman; + gfloat volume_f; + gint volume_i; + ... + }; + + + When the required dparam is defined, the callback function gst_example_update_volume + and some user data (which in this case is our element instance) is passed in to the call to + gst_dpman_add_required_dparam_callback. + + + 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 + ); + + + The callback function needs to conform to this signiture + + +typedef void (*GstDPMUpdateFunction) (GValue *value, gpointer data); + + + In our example the callback function looks like this + + +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; +} + + + Now example->volume_i can be used elsewhere and it will always contain the correct value. + + + + Array Method + + 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. + + + + + Certain optimisations may be possible since you can iterate over your dparams array + and your buffer data together. + + + 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. + + + + 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. + + + + + + The Data Processing Loop + + This is the most critical aspect of the dparams subsystem as it relates to elements. + In a traditional audio processing loop, a for 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. + + +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; + } + ... +} + + + To make this dparams aware, a couple of changes are needed. + + +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; + } + ... +} + + + The biggest changes here are 2 new macros, GST_DPMAN_PREPROCESS + and GST_DPMAN_PROCESS_COUNTDOWN. + You will also notice that the for loop has become a while loop. GST_DPMAN_PROCESS_COUNTDOWN + 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 sample accurate. This means that parameters change at the exact timestamp + that they are supposed to - not after the buffer has finished being processed. + + + 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. + + +#define GST_DPMAN_PROCESS_COUNTDOWN(dpman, frame_countdown, frame_count) \ + (frame_countdown-- || \ + (frame_countdown = GST_DPMAN_PROCESS(dpman, frame_count))) + + + So as long as frame_countdown is greater than 0, GST_DPMAN_PROCESS + will not be called at all. + Also in many cases, GST_DPMAN_PROCESS will do nothing and simply + return 0, meaning that there is no more data in the buffer to process. + + + The macro GST_DPMAN_PREPROCESS will do the following: + + + Update any dparams which are due to be updated. + + + Calculate how many samples should be processed before the next required update + + + Return the number of samples until next update, or the number of samples in the buffer - + whichever is less. + + + In fact GST_DPMAN_PROCESS may do the same things as GST_DPMAN_PREPROCESS + depending on the mode that the dparam manager is running in (see below). + + + DParam Manager Modes + + 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: + + + Timelined - all parameter changes are known in advance before the pipeline is run. + + + Realtime low-latency - Nothing is known ahead of time about when a parameter + might change. Changes need to be propagated to the element as soon as possible. + + + 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 "synchronous" + and "asynchronous". + + + If you are in a realtime low-latency situation then the "synchronous" mode is appropriate. + During GST_DPMAN_PREPROCESS this mode will poll all dparams for required updates + and propagate them. GST_DPMAN_PROCESS 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. + + + In a timelined situation, the "asynchronous" mode will be required. This mode + hasn't actually been implemented yet but will be described anyway. + The GST_DPMAN_PREPROCESS call will precalculate when and how often each dparam needs + to update for the duration of the current buffer. From then on GST_DPMAN_PROCESS 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 "asynchronous" + mode this could be done without affecting the sample accuracy of the parameter updates + + + + DParam Manager Modes + + 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 GST_DPMAN_PREPROCESS + should be required. For more than one frame per buffer, treat it the same as the audio case. + + + 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 @@ + GStreamer"> ]> @@ -747,6 +748,67 @@ + + + Supporting Dynamic Parameters + + + 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. + + + Throughout this section, the term dparams will be used as an abbreviation for Dynamic Parameters. + + + + Comparing Dynamic Parameters with GObject Properties + + 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. + + + + + + + Object Properties + Dynamic Parameters + + + + + Parameter definition + Class level at compile time + Any level at run time + + + Getting and setting + Implemented by element subclass as functions + Handled entirely by dparams subsystem + + + Extra objects required + None - all functionality is derived from base GObject + Element needs to create and store a GstDParamManager at object creation + + + Frequency and resolution of updates + Object properties will only be updated between calls to _get, _chain or _loop + dparams can be updated at any rate independant of calls to _get, _chain or _loop + up to sample-level accuracy + + + + + + + &DPARAMS; + + Building a simple test application 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 @@ + + + Steve + Baker + + + stevebaker_org@yahoo.co.uk + + +