= Technical Specification = Livido High level ================ livido ports livido properties Livido low level ================= atoms This is still in development, especially GUI port and Keyframe Port are under active development. This specification is subtly different from Salsaman's ; It seperates plugin from GUI in OO fashion by introducing a super port that references a parameter port as well as 2 functions to deal with GUI requests (install handler for allocating the port and an index function to put/get/... values). In these functions the Plugin provides means to scale/unscale or in general how to represent this parameter to display in a GUI ? This feature is not needed for conventional host applications and is an optional addition (...) The second feature in development is Keyframing , and with that parameter interpolation by plugin on a per parameter basis. It tries to provide a function for interpolation by allowing host to set a list of timecodes for a parameter and a interpolate_func to call to produce a new value. Also, it tries to provide a function to both get and process keyframes which surely has errors in its template definition. The third feature in development is Index function a plugin can use to scale to specific host features. Here, both plugin and host use a private set of properties. == livido_setup == Each plugin must implement the function: {{{ livido_plugin_t *livido_setup(void); }}} {{{ typedef livido_plugin_t * livido_port_t; }}} This function returns a linked list of livido_filter_t *.[[BR]] If the plugin has only 1 filter there will be only 1 livido_filter_t property in the returned livido_port. The plugin uses livido_port_new to initialize and build ports. == livido_filter == A livido_filter has 5 port sets: * 1 info port * 1 in_channels port array * 1 out_channels port array * 1 in_parameters port array * 1 out_parameters port array The index of the parameters and channels define the logical order of these ports. {{{ typedef struct livido_filter { livido_port_t *info; livido_port_t **in_channels; livido_port_t **out_channels; livido_port_t **in_parameters; livido_port_t **out_parameters; } livido_filter_t; }}} == Port == A port has a linked list of properties: {{{ typedef livido_property_t * livido_port_t; }}} Some properties are mandatory, some are optional.[[BR]] For each port type/port hint, there is a mandatory set of properties and an optional set of properties.[[BR]] The plugin can create a new Port with the function {{{ livido_port_t *livido_port_new( int port_type ); }}} The plugin passes in the port_type to describe its family The livido_port_new function returns a linked list of properties with the property 'port_type' set to readonly. The livido_port_new function should use livido_put_value (and livido_set_readonly). == Properties == Each port is a linked list of properties.[[BR]] A property has: * a key (which is a non-NULL string - (const char *)) * a value (which is a livido_storage) * a next_pointer to the next property in the list (or a NULL for the last property) {{{ typedef struct livido_property { const char *key; ///< unique type: min,max,value,step_size,page_size,decimals livido_storage_t *data; ///< either 1 atom or N atoms livido_property_t *next; } livido_property_t; }}} == Property PortType == Each port has a mandatory property "type". The type is used to distinguish ports.The types are: * LIVIDO_PORT_TYPE_INFO * LIVIDO_PORT_TYPE_CHANNEL * LIVIDO_PORT_TYPE_PARAMETER * LIVIDO_PORT_TYPE_KEYFRAME * LIVIDO_PORT_TYPE_GUI The port is stored as a single valued property of atom type LIVIDO_ATOM_TYPE_INT == Property PortHint == The property 'porthint' is optional and is only used to hint hosts about the 'meaning' of parameters. If the porthint is not present, see the atom type of the property. For now, only hints for parameters have been defined: "hint" may be one of:[[BR]] * NUMBER * TEXT * SWITCH * LIST * CHOICE * COLORKEY * COORDINATE[[BR]] The property hint is stored as a single valued atom of type INT NUMBER: The number hint is used for all single valued parameters of atom type INT or FLOAT TEXT The text hint is used for all single valued parameters of atom type STRING SWITCH The switch is used to indicate on/off parameters of atom type INT or BOOLEAN For off we use 0, for on we use 1. CHOICE The choice is used for pulldown or mode parameters. It always has an array of atoms of type STRING. The index number of the atom array can be used to retrieve the single valued atom of type STRING. COLORKEY The colorkey is used for colorkey parameters. It is always RGBA and has atom_type FLOAT or INT If it is FLOAT, its value ranges from 0.0 to 1.0. If its INT, its value ranges from 0 to 65535 COORDINATE The coordinate is used for coordinate parameters. It is always an array of atoms of type FLOAT. For 2D coordinates, 0=X, 1=Y, for 3D 2=Z. The values range from 0.0 to 1.0 == Getting/setting values == As well as a function to init a port, there are utility functions to get and set properties Get/set can be by atom or by storage in the library. Property values are copied on a get/set, except for pointer type atoms which are copied by reference. There is no built in type checking to ensure that the correct atom type is set in a port/property pair, as this can be done in a separate utility library. Atoms of type charptr are also copied by value on get/set. == Functions == In use by plugin / Host: * livido_port_t *livido_port_new (int port_type) livido_property_t *livido_property_new (livido_port_t *port, const char *key, int atom_type); * livido_set_value (defined elsewhere in this document) * livido_get_value (defined elsewhere in this document) Only by Host: * livido_atom_t *livido_atom_new () - only used internally by livido * void livido_atom_free () - only used internally by livido * livido_storage_t *livido_storage_new() * void livido_storage_free() - only used internally by livido * void livido_property_free (livido_property_t *property); * void livido_port_free (livido_port_t *port) * char **livido_list_properties (livido_port_t *port) // returns a NULL terminated string array Only for Plugin: {{{ int init(livido_filter_t *) }}} The host calls this and passes back the port set, with some values changed. Prior to this, the host can change the "value" property of parameters. It returns an int livido error code. The init function allows the plugin to create any internal memory structures it needs; these can be referred to in the info port, property "internal" which takes a void * atom_type. The host must also attend to "disabled" in the channel ports, and set the "current_palette" property in the channel ports and respect the flags set in the plugin. {{{ int process(livido_filter_t *) }}} host calls this for each processing cycle; the plugin can do its frame processing here. In process, the plugin does not update the current values of its parameters. {{{ int deinit(livido_filter_t *) }}} host calls this allow the plugin to free any internal memory held in the info port "internal" property. Following this the host may free the livido_filter. The plugin does not need to free any ports or paramters, the host should take care of this. == Flow of operation == * Host loads plugin * Host calls the livido_setup() function in the plugin. * For each filter in the plugin, the plugin creates the livido_filter, initialises an info port, an array of in_channel ports, an array of out_channel ports, an array of in_parameter ports and an array of out_parameter ports. The filter list is returned to the host. * Host selects a filter to use, makes a copy, then examines the in_channel and out_channel ports, and clears the "disabled" flag for any optional channels it wishes to use. It also checks "palette_list", selects a palette it would like to start using on that channel and sets the chosen value in the "current_palette" property. Also, it will setup the width and height * Host can change "value" for any parameter, thus overriding the plugin's default. * Host calls init in the plugin, passing in the filter copy it would like to use. * Plugin now knows the channel sizes, palettes and which are in use and allocate buffers , do initialization or error (if width,height,palette etc are unknown or not supported) Also here the plugin can create a new property "internal" to store points to privatly owned memory space. * Host may now change parameter values (respecting "max" and "min" properties) and it may call process() in the plugin whenever it likes, passing in the initialised filter. * In process() the plugin is allowed to check for parameter values (by comparing value to a previously stored value) and set the re-init flag before returning so host can call init() before calling process() or get_keyframes() * When the host has finished with the filter, or if it needs to re-initialise it, the host must call deinit() in the plugin, passing in the filter. The plugin can now free any internal data which it allocated and assume that the next call by host is to init() In the deinit() the plugin must reset the properties it used to trigger re-initialization: the REINIT flag and (prvate) reference data that how to (partly) deinit. If the REINIT flag is not present at deinit(), the plugin must free all data (the host will remove it) == Conversion functions == We provide functions for converting between atom types for some port/property pairs (i.e. those pairs which have a choice of atom types) TODO {{{ == Ports and properties == Here we specify the port/property pairs, whether they are mandatory or optional, and their types. Port type: "type" == LIVIDO_PORT_TYPE_INFO Mandatory properties: "name" : LIVIDO_ATOM_TYPE_CHARPTR : the filter name "author" : LIVIDO_ATOM_TYPE_CHARPTR : the filter author name "version" : LIVIDO_ATOM_TYPE_INT : filter version "api_version" : LIVIDO_ATOM_TYPE_INT : livido api verison "flags" : LIVIDO_ATOM_TYPE_INT : see instance flags "init_func" : LIVIDO_ATOM_TYPE_VOIDPTR : pointer to a livido_init_f "process_func" : LIVIDO_ATOM_TYPE_VOIDPTR : pointer to a livido_process_f "deinit_func" : LIVIDO_ATOM_TYPE_VOIDPTR : pointer to a livido_deinit_f "num_in_channels" : LIVIDO_ATOM_TYPE_INT : total number in array "num_out_channels" : LIVIDO_ATOM_TYPE_INT : total number in array "num_in_parameters" : LIVIDO_ATOM_TYPE_INT : total number in array "num_out_parameters" : LIVIDO_ATOM_TYPE_INT : total number in array ////////////////// optional /////////////////// "description" : LIVIDO_ATOM_TYPE_CHARPTR : filter description "license" : LIVIDO_ATOM_TYPE_CHARPTR "url" : LIVIDO_ATOM_TYPE_CHARPTR "scale_x" : LIVIDO_ATOM_TYPE_FLOAT : "scale_y" : LIVIDO_ATOM_TYPE_FLOAT : "num_threads" : LIVIDO_ATOM_TYPE_INT : (==1 if not present) "fps" : LIVIDO_ATOM_TYPE_FLOAT : plugin can set this in the setup function if it requires fixed FPS. "view" : LIVIDO_ATOM_TYPE_VOIDPTR : this is used by plugins which have trigger functions. See the section entitled "View/Trigger_func" "internal" : LIVIDO_ATOM_TYPE_VOIDPTR : the plugin can set this in its init() function to store internal data. The plugin should free() it in its deinit() function. "extension_func: LIVIDO_ATOM_TYPEVOIDPTR: this can be used by plugins to scale for specific host features (TODO) int livido_extension_func( livido_filter_t *t, int index ) "process_keyframes" : LIVIDO_ATOM_TYPE_VOIDPTR: pointer to keyframe function. See elsewhere in this document /////////////////////////////////////////////////// Port type: "type" == LIVIDO_PORT_TYPE_CHANNEL Mandatory properties: "name" : LIVIDO_ATOM_TYPE_CHARPTR : name of the channel "flags" : LIVIDO_ATOM_TYPE_INT : bitmap of channel_flags "timecode" : LIVIDO_ATOM_TYPE_LONG : timecode /////////// The following are mandatory for channels with video: "palette_list" : LIVIDO_ATOM_TYPE_INT : the plugin sets this to allowed palettes for the channel "current_palette" : LIVIDO_ATOM_TYPE_INT : the host sets this to the chosen palette, which must be one of the palettes contained in "palette_list". "width" : LIVIDO_ATOM_TYPE_INT : frame width in pixels, plugin can set this in setup() to a non-zero value (zero means any - host can choose) "height" : LIVIDO_ATOM_TYPE_INT : frame height in pixels, plugin can set this in setup() to a non-zero value (zero means any - host can choose) "pixel_data" : LIVIDO_ATOM_TYPE_*PTR : number of elements varies depending on "current_palette" "rowstrides" : LIVIDO_ATOM_TYPE_INT : length of one row in bytes (include padding) : same number of elements as "pixel_data" ////////////// The following are mandatory for channels with audio ////////////// This part is not worked out. "audio_data" : Audio channels will be specified in the LiViDO Audio Extension : TODO "audio_rate" : "audio_bps" "audio_channels" "audio_samples" ///////////////// optional /////////////// "description" : LIVIDO_ATOM_TYPE_CHARPTR "window" : array of LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "v_shift" : LIVIDO_ATOM_TYPE_INT : used for YUV type palettes "h_shift" : LIVIDO_ATOM_TYPE_INT : used for YUV type palettes "interlacing" : LIVIDO_ATOM_TYPE_INT : Progressive,None (default) ,Topfirst,Bot.first - if not present, then no interlacing is assumed. TODO "fps" : LIVIDO_ATOM_TYPE_FLOAT : default is same as instance "fps" "same_as" : LIVIDO_ATOM_TYPE_INT : plugin can set this to indicate that this channel must match with another channel. A positive value indicates an in_channel, a negative, an out_channel. Channels may only refer to in_channels with number smaller than themselves. Out_channels may refer to any in_channel or any out_channel with number smaller. The flags field indicates whether frame size (height/width) or palette must match (or both). "disabled" : LIVIDO_ATOM_TYPE_BOOLEAN : the plugin may set this to TRUE in setup(), host may set its value to FALSE before calling init(). ///////////////////////////////////////////// Port PARAMETER: Mandatory: "type" : ATOM_TYPE_INT "name" : ATOM_TYPE_CHARPTR "timecode" LIVIDO_ATOM_TYPE_LONG "value" : current value of this parameter, can be any atom type. Optional: "hint" "description" : LIVIDO_ATOM_TYPE_CHARPTR "disabled" : LIVIDO_ATOM_TYPE_BOOLEAN Mandatory for numeric parameters: "min" : LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "max" : LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "decimals" : LIVIDO_ATOM_TYPE_INT : this property is mandatory if the atom_type of "value" is LIVIDO_ATOM_TYPE_FLOAT Optional for all NON text and NON switch parameters: "interpolate_func" : LIVIDO_ATOM_TYPE_VOIDPTR : int interpolate_func(livido_port_t *self, livido_property_t *output_value) the interpolate func can use the property "timecode" to interpolate new parameter values. The data storage of the timecode indicates the number of timecodes. Optional for all numeric parameters: "step_size" : LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "page_size" : LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "wrap" : LIVIDO_ATOM_TYPE_BOOLEAN : indicates whether values wrap-around min->max/max->min Optional for all text parameters: "min_length" : LIVIDO_ATOM_TYPE_INT : min text length (the LENGTH of text - not strlen() size !) "max_length" : LIVIDO_ATOM_TYPE_INT : max text length "syschr" : Use system representation else use UTF8 Mandatory for all CHOICE parameters: "list_values" : Pull down list "value" : current selected INDEX value of the list_value array . atomtype INT Optional for COORDINATE parameters: "geometry" : names of coordinate spaces (ATOM_TYPE_CHARPTR) (e.g. "cartesian", "logarithm", "polar") : "cartesian" assumed if not set /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// The port type GUI carries and deals with the GUI part of a plugin. It is kept to a minimum to allow at least some description about how to model this parameter for GUI displaying purposes, including displaying and using scaled values. The GUI port always deals with systems representation. The application should convert this to UTF 8. The host can store a UTF8 string after conversion in the Port - this does not hurt processing - but the Host must use some self-invented property name that does not collide with the existing ones. Mand. "type" == LIVIDO_PORT_TYPE_GUI "parent: LIVIDO_ATOM_TYPE_VOIDPTR to reference Parameter Port "value" : LIVIDO_ATOM_TYPE_CHARPTR the value to represent in the GUI Optional : "padding_char" : LIVIDO_ATOM_TYPE_CHARPTR : padding character "padding_adjust" : LIVIDO_ATOM_TYPE_INT : if present, can be one of LIVIDO_TEXT_PAD_LEFT (the default), LIVIDO_TEXT_PAD_RIGHT, LIVIDO_TEXT_PAD_CENTER : the host is expected to do the padding. "hint" : LIVIDO_ATOM_TYPE_CHARPTR : string to display in the GUI "help" : LIVIDO_ATOM_TYPE_CHARPTR : extensive information about this parameter "disabled" : LIVIDO_ATOM_TYPE_BOOLEAN "step_size" : LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "page_size" : LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "wrap" : LIVIDO_ATOM_TYPE_BOOLEAN : indicates whether values wrap-around min->max/max->min "min" : LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "max" : LIVIDO_ATOM_TYPE_INT or LIVIDO_ATOM_TYPE_FLOAT "decimals" : LIVIDO_ATOM_TYPE_INT : this property is mandatory if the atom_type of "value" is LIVIDO_ATOM_TYPE_FLOAT "width" : LIVIDO_ATOM_TYPE_INT: width of parameter size request "height" : LIVIDO_ATOM_TYPE_INT: height of parameter size request The function that interact with GUI port are : install func (plugin allocates a set of new ports (livido_filter_t*) with livido_port_new and fills the properties) : view func (the host passes in the data returned from install func , an index value and property ) In the view the plugin switches on index value to GET or SET from / to property . The host will use SET to update from its own view to the GUI port, and use GET to get the current values in the plugin. In both cases, the plugin needs to supply its own logic to scale its linear values to GUI representation - and back again. The plugin will use livido_put and livido_get property to retrieve, manipulate and store properties from and into the GUI port. ////////////////////////////////////////////////////////////// "type" == LIVIDO_PORT_TYPE_KEYFRAME Also see interpolate_func in port parameter. The key frame port is only a model of (all) parameters at a certain timecode. Mandatory/Optional properties: "type" "ip[i..N]" : references Input parameters. ATOM_INT, see structure of livido_filter_t "op[i..N]" : references Output parameters "iv[i..N]" : The Plugin stores the new input parameter value where i matches the input parameter index number "ov[i..N]" : The Plugin stores the new output parameter value where i matches the output parameter index number "timecode" : The timecode passed in at get_keyframe(). Plugin has opportunity to correct timecode. The host provides a function to allocate and initialize only part of Keyframe: "type", "ip[i..N]" , "op[i..N]" and "timecode". It will store references to all input and output parameters. Flow of interaction: =================== The plugin provides a function int process_keyframe( livido_filter_t *filter, long timecode, livido_port **keyframes, livido_port *output_kf) It is allowed to call interpolate_func for each parameter that supports it. Otherwise, just the current values should be stored. The first call to get_keyframe can be used to explore the needs of a plugin for calculating a keyframe. If no keyframes are passed in, the plugin is allowed to set property "in_points" for each parameter that supports interpolation. The host can read it to decide how many points the user must define to use the interpolation function of this parameter. Here, the plugin returns SUCCESS but output_kf will be NULL. If keyframes were passed in, the plugin can return an error NEED_MORE_KEYFRAMES if the current amount of keyframes is insufficient. Upon SUCESS and output_kf is not NULL the plugin has produces a KF from the given timecode. While producing the keyframe, the plugin clears the property "in_points" and stops calling the interpolator if its not needed anymore. MEDIATION LAYER ///////////////////////////////////////////////////////////// The mediation layer deals with the communication from the high level to the lower level. == livido_storage == This is the data which is stored in a property. A livido_storage has:[[BR]] * a (fixed) int num_elements * a (fixed) int atom_type * a flags field * data struct livido_storage { int num_elements; ///< if its 1 or 0, it's an ATOM, otherwise its an ARRAY int atom_type; union { livido_atom_t *atom; // NULL if num_elements==0 livido_atom_t **array; } data; int flags; }; the union "data" contains a pointer to some atom For 0 element: The storage is empty. This is for properties that dont need a value For 1 element: The storage is an atom For > 1 element: The storage is an array * int livido_set_readonly (livido_property_t *property) * int livido_get_readonly (livido_property_t *property) // returns 0 (read/write) or 1 (read_only) * int livido_set_value (livido_port_t *port, const char *key, int atom_type, int num_elems, void *value, size_t *sizes) // num_elems is >= 0, returns a livido_error * int livido_get_value (livido_port_t *port, const char *key, int idx, void *value) // * int livido_get_num_elements (livido_port_t *port, const char *key) // can return 0 for NULL storages * size_t livido_get_atom_size(livido_port_t *port, const char *key, int idx) // idx starts at 0, can return 0 for a NULL storage * int livido_get_atom_type(livido_port_t *port, const char *key) LOW LEVEL LAYER ///////////////////////////////////////////////////////////// = Livido low level layer = The low level layer in livido deals with data abstraction in OO fashion. Its structure contains: * the atom type * a size of type size_t * a void * value typedef struct livido_atom { int atom_type size_t size; void *value; } livido_atom_t; == Atom types == LiViDO implements the following atom types:[[BR]] * LIVIDO_ATOM_TYPE_INT * LIVIDO_ATOM_TYPE_FLOAT * LIVIDO_ATOM_TYPE_LONG * LIVIDO_ATOM_TYPE_BOOLEAN * LIVIDO_ATOM_TYPE_CHARPTR * LIVIDO_ATOM_TYPE_VOIDPTR * LIVIDO_ATOM_TYPE_UINT8PTR * LIVIDO_ATOM_TYPE_INT8PTR * LIVIDO_ATOM_TYPE_UINT16PTR * LIVIDO_ATOM_TYPE_INT16PTR * LIVIDO_ATOM_TYPE_UINT32PTR * LIVIDO_ATOM_TYPE_INT32PTR * LIVIDO_ATOM_TYPE_FLOATPTR [[BR]] }}}