Hi all,

See attachment.

Regards,

Hans
RFC: a processing plugin API for libv4l
=======================================

Since libv4l-0.6.x libv4l has the ability to do various video
processing steps in software:
- white balance
- gamma correction
- autogain and exposure (simply called autogain inside libv4l)
- H- and V-flipping

During the Plumbers conference in 2009 various parties expresse interest
in extending libv4l's processing capabilities. Some hardware can do some
processing steps in hardware, but this needs to be setup from userspace
and sometimes still need some regulation from userspace as streaming
happens, hardware specific libv4l plugins could be a solution here.

Fundementally most processing consists of 2 steps:
1) Measuring
2) Correcting

IE, libv4l's current whitebalance algorithm, calculates the average
r, g and b values for the image every few frames and calculates
correction factors for all r, g and b pixel values to make the averages
equal (measure), and then applies these correction factors to every
frame when the frame is dequeued (correction). Some processing consists
only of the correction step such as flipping.

Both of these steps can be done either in software or in hardware. When
either of these steps is done in software, the frames need to be in a
format the processing code understands.

So a processing plugin API will need to provide a way for the plugin
to communicate which of the steps it needs / wants to do. And when it
does software processing which formats it accepts for each of the steps.

In some cases plugings will need to be notified of setfmt calls so that
they can program statistics gathering hardware to match the selected
mode.


API proposal
------------

/* Plugin flags */

/* This processing step is done in software, the presence of this flag
   for a step forces libv4lconvert to convert the frame data to RGB24,
   BGR24 or raw bayer before calling the processing functions. */
#define LIBV4LPROCESSING_SOFTWARE 0x01
/* This software processing step also accepts YUV420 and YVU420
   formats, (this avoids the need for a possible double conversion when
   the apps wants YUV data). */
#define LIBV4LPROCESSING_SOFTWARE_YUV 0x02
/* Normally the processing code will only call a plugins measure function
   every few (5 by default) frames to save cpu. This flag indicates the
   measure function should be called on every dequeued frame. Note that
   plugins using this flag may not use the lookup table mechanism for
   correction ! */
#define LIBV4LPROCESSING_NO_LAZY_MEASURE 0x04

/* Plugin measure and correction function return values (these can be
   or-ed together when needed). */

/* The measure function has modified the lookup tables, so the processing
   core should apply these to dequeued frames during the correction fase. */
#define LIBV4LPROCESSING_MODIFIED_LOOKUP_TABLE 0x01
/* The function has modified hardware settings so lazy measure. Returning
   this results in lazy measuring temporarily being disabled so that measure
   functions can adjust the correction of their plugin for the new settings.

   Note that adjustment of hardware settings may be done directly from the
   measure function, so this can be returned by either the measure or the
   correction function. */
#define LIBV4LPROCESSING_HW_SETTINGS_CHANGED 0x02


struct libv4lprocessing_plugin {
  int flags;

  /* Intialize the plugin, fd is the fd of the video device, control is
     a (the) libv4lcontrol instance which can be used to add fake
     controls to for controlling the plugin.
     The returned void * can be used for plugin private data and will be
     passed in to all other functions. Return NULL to tell the processing
     core that this plugin could not load (for example because it is
     for specific hardware which is not present). */
  void *(*init)(int fd, struct libv4lcontrol_data *control);

  /* Cleanup function called upon unloading of the plugin. */
  void (*cleanup)(void *plugin_data);

  /* This function returns if this plugin is active at this time, returning
     0 results in the measure and correction functions not getting called
     (for the frame being processed). */
  int (*active)(void *plugin_data);

  /* Measure function (may be NULL when no measuring is done). 
     * When LIBV4LPROCESSING_SOFTWARE is set in flags this function 
       function gets passed in (treat as read only) the data of a just
       dequeued frame in a suitable format.
     * When LIBV4LPROCESSING_SOFTWARE is set in measure_flags and
       LIBV4LPROCESSING_SOFTWARE_YUV is not set, this function gets
       passed in 3 (rgb) lookuptable pointers. This function may modify
       the lookup tables (taking into account the value put in there by
       other plugins' measure functions), so that they result in the
       desired correction. This way libv4lprocessing can do the
       correction for multiple plugins in one pass, significantly
       reducing cpu cost. */
  int (*measure)(void *plugin_data, const unsigned char *buf,
                 const struct v4l2_format *fmt, unsigned char *lookup[3]);

  /* Correction function (may be NULL)
     * When LIBV4LPROCESSING_SOFTWARE is set in flags this function 
       function gets passed in (treat as read only) the data of a just
       dequeued frame in a suitable format. */
  int (*correct)(void *plugin_data, unsigned char *buf,
                 const struct v4l2_format *fmt);

  /* Called when a s_fmt call is made *to the hardware*, iow this functions
     argument represents the format as seen by the hardware, not necessarily
     as seen by the application (as libv4l may be doing format conversion) */
};


Controlling processing plugins through "fake" controls
------------------------------------------------------

Since libv4l-0.6.x libv4l, libv4l has the concept of fake controls, this
are controls at the libv4l level, libv4l intercepts ctrl related ioctls
and will emulate them when they apply to controls which it is using to
control its processing code, this is handled inside the libv4lcontrol
part of libv4l.

These fake controls use a shared memory (1 page) to store the control
values, this way changes made in 1 app (ie a control panel app) can be
made to happen in another app (ie an app streaming video). Each faked
control uses a 32 bit integer with a fixed index into the page. The
index has to be fixed so that all apps interpret the shared memory
the same indepenent of for example then order in which processing
plugins get loaded.

With the new plugin API deciding which controls to fake gets moved to
the plugins. Plugins can ask libv4lcontrol to fake a specific control,
after which libv4l will intercept calls to it.

The following libv4lcontrol functions (some of which are new) are
relevant for processing plugins. Note the enum with the fixed indexes, this
means that if a plugin wants to use a control which was not yet assigned an
idx, an libv4lcontrol change (extension) will be necessary. This is
deliberate, because of the need of fixed indexes into the shared memory.

enum v4lcontrol_idx {
        V4LCONTROL_WHITEBALANCE,
        V4LCONTROL_HFLIP,
        V4LCONTROL_VFLIP,
        V4LCONTROL_GAMMA,
        V4LCONTROL_AUTOGAIN,
        V4LCONTROL_AUTOGAIN_TARGET,
        V4LCONTROL_COUNT
};

/* Tell libv4lcontrol to intercept ctrl ioctl's for the control referred
   to by idx. type, min, max, step and default are used in the faked query
   ctrl answer. If the shared memory for storing control data did not exist
   when the v4lcontrol instance was created, the value of the control gets
   set to default.
   For menu type controls menu_entries should point to max strings for
   VIDIOC_QUERYMENU answers.
   The CID and name are determined by libv4lcontrol itself based on the idx.
   Note that libv4lcontrol *refuses* to fake controls which are also present
   in hardware, when this or an other error happens, this functions returns
   -1 and sets errno. On success 0 is returned. */
int v4lcontrol_add_control(struct v4lcontrol_data *control_data,
                           enum v4lcontrol_idx idx,
                           enum v4l2_ctrl_type type,
                           int min, int max, int step, int default,
                           const char *menu_entries[]);

/* This function returns the current value of the control, this can be used
   by processing plugins active, measure and correct callbacks. */
int v4lcontrol_get_ctrl(struct v4lcontrol_data *control_data,
                        enum v4lcontrol_idx idx);

/* Check if the controls have changed since the last time this function
   was called. This function is used by the processing core, to force
   calling all plugins measure methods on a control change (iow to
   temporarily disable lazy measuring). */
int v4lcontrol_controls_changed(struct v4lcontrol_data *control_data); 


Processing plugins which need to handle asynchroneous events
------------------------------------------------------------

libv4l's processing code is all driven by the application, so plugins
methods get called on dqbuf. Some plugins however may want to respond
to hardware generated "measuring" information sooner, for example when
the hardware gathers information about the center part of the frames,
as soon as the last line of that center part is scanned out the hardware
(and driver) could generate an event and a processing plugin may want
to pick this up and act on it to get new settings in place before
the next frame starts (when waiting till dqbuf, these settings would
come into effect one frame later assuming they are synced to vsync).

Plugins can do this by using a thread to listen to these events, and
make the necessary hardware settings from this thread. Plugins should
use regular pthreads for this, and plugins are responsible to do their
own synchronisation between callbacks from the main thread (such as a 
s_fmt notification) and their event listening thread.

Also plugins should report a LIBV4LPROCESSING_HW_SETTINGS_CHANGED return
status from their measure callback, on the first call to their measure
callback after making changes to hardware settings which may effect the
measuring done by other plugins (there is no need to do this when the
changed settings do not potentially change things like colorbalance
and brightness level of the picture).

Reply via email to