Valerie API Documentation Copyright (C) 2004 Ushodaya Enterprised Limited Author: Charles Yates Last Revision: 2004-03-20 TABLE OF CONTENTS ----------------- 0. Overview 0.1. Intended Audience 0.2. Terminology 1. Definition of a Parser 1.1. Construction of a Local Parser 1.2. Construction of a Remote Parser 1.3. Using the Parser 1.4. Closing the Parser 2. The High Level Parser Wrapper 2.1. Connecting 2.2. valerie_error_code 2.3. Using the High Level Wrapper 2.4. Obtaining Directory Contents 2.5. Obtaining the Node List 2.6. Obtaining the Unit List 2.7. Unit Status Information 2.8. Server Side Queuing APIs 2.9. Accessing the Low Level Parser Directly 2.10. Cleaning up 2.11. Examples 3. The Low Level Parser API 3.1. Executing a Command 3.2. Interpreting valerie_response 3.3. Accessing Unit Status APPENDIX A - COMPILATION AND LINKING APPENDIX B - COMPLETE HIGH LEVEL PARSER WRAPPER API LISTING APPENDIX C - COMPLETE LOW LEVEL PARSER API LISTING APPENDIX D - REFERENCES 0. Overview ----------- This document details how applications interface to DVCP functionality. 0.1. Intended Audience ---------------------- This document draws heavily upon the DVCP design (1) and assumes a basic knowledge of the functionality provided by the DVCP core. It is aimed at developers who wish to use or maintain the API. 0.2. Terminology ---------------- The API is designed to allow client applications the ability to communicate to a standalone miracle server or entirely embed the DVCP core in an instance of a client application. The distinction between the two is defined by the construction of the 'parser'. This 'parser' can be used to issue DVCP commands and receive responses and a 'high level parser wrapper' is provided to simplify the usage and decouple the application from the DVCP command set. 1. Definition of a Parser ------------------------- The parser provides a low level API which allows text DVCP commands to be executed with responses being returned to the caller. Commands and responses are ASCII formatted text. Two parsers are provided - local and remote. The local parser is the physical implementation which takes commands and executes them. The remote parser is a network abstraction that forwards commands to a miracle instance that hosts a local parser. 1.1. Construction of a Local Parser ----------------------------------- To construct a local parser you must have: #include and code to initialise the parser is as follows: valerie_parser parser = miracle_parser_init_local( ); See Appendix A for compilation and linking details. 1.2. Construction of a Remote Parser ------------------------------------ To construct a remote parser you must have: #include and code to initialise the parser is as follows: valerie_parser parser = valerie_parser_init_remote( "server", port ); See Appendix A for compilation and linking details. 1.3. Using the Parser --------------------- Although the parser can be used directly to send commands and receive responses, this low level usage puts the onus on the developer to parse the responses in a meaningful way. Although this usage is not strictly forbidden by applications, it is discouraged as construction of commands and meaningful parsing of responses leads to the clients being unnecessarily dependent on the servers input and output. As a result, a higher level Parser Wrapper API is provided - this API encapsulates the command construction and response parsing. The following 2 sections provide details on these modes of access. 1.4. Closing the Parser ----------------------- Regardless of use, it is the constructors responsibility to close the parser before it goes out of scope. This is done via: valerie_parser_close( parser ); 2. The High Level Parser Wrapper -------------------------------- The recommended way to access the parser, is via the valerie API. To use this API, you must have: #include and code to construct the wrapper is: valerie dv = valerie_init( parser ); Note that either remote or local parsers can be used here and there is no difference in usage, though some error returns will not be applicable to both. It is recommended that applications honour and deal with the error returns of both as this allows applications to interchange parsers. Also note that valerie is not threadsafe, so you should not use the same structure in multiple threads. The correct solution to this is to create a valerie per thread - you may safely use the same parser for each thread ie: /* valerie for the application */ valerie dv = valerie_init( parser ); /* valerie for the status handling thread. */ valerie dv_status = valerie_init( parser ); For the purposes of simplification, the remainder of this section assumes that a remote parser is in use. 2.1. Connecting --------------- Once constructed, the next thing to do is 'connect': valerie_error_code error = valerie_connect( dv ); This function call initialises the parser (ie: if it's remote, it establishes a connection to the server, or if it's local, it initialises the state of the units and supporting objects). Note that if you have multiple valerie instances on the same parser you should only connect one of the instances. 2.2. valerie_error_code ---------------------- All but a couple of the functions that make up the valerie API return a valerie_error_code. These are defined as follows: valerie_ok = 0, valerie_malloc_failed, valerie_unknown_error, valerie_no_response, valerie_invalid_command, valerie_server_timeout, valerie_missing_argument, valerie_server_unavailable, valerie_unit_creation_failed, valerie_unit_unavailable, valerie_invalid_file, valerie_invalid_position In most cases, it is sufficient to check on a return of valerie_ok. To obtain a textual description of a particular error, you can use: char *valerie_error_description( valerie_error_code ); 2.3. Using the High Level Wrapper --------------------------------- The following code snippet assumes that dv is an initialised and connected valerie structure: valerie_error_code error = valerie_unit_play( dv, 0 ); if ( error == valerie_ok ) fprintf( stderr, "Unit 0 is now playing\n" ); else fprintf( stderr, "Play on unit 0 failed: %s\n", valerie_error_description( error ) ); The complete interface to valerie is listed in Appendix B of this document. 2.4. Obtaining Directory Contents -------------------------------- To obtain a list of files and subdirectories in a given directory relative to the ROOT property of the server, DVCP provides the CLS command. A valid execution of CLS would be something like: CLS "/Stuff" would provide a response formatted as follows: 201 OK "More Stuff/" "file0001.dv" 15552000 "file0002.dv" 15552000 with a trailing empty line. The first line indicates the error value, the second line shows an example of a subdirectory and the 3rd and 4th line lists two files that happen to exist in the directory. valerie provides a high level view on this which automatically parses the response from the server correctly via the valerie_dir structures and related functions. An example of use is as follows: valerie_dir dir = valerie_dir_init( dv, "/Stuff" ); valerie_error_code error = valerie_dir_get_error_code( dir ); if ( error == valerie_ok ) { if ( valerie_dir_count( dir ) > 0 ) { valerie_dir_entry_t entry; int index = 0; for ( index = 0; index < valerie_dir_count( dir ); index ++ ) { valerie_dir_get( dir, index, &entry ); if ( entry.dir ) printf( "<%s>\n", entry.name ); else printf( "%30s %8d", entry.name, entry.size ); } } else { fprintf( stderr, "Directory is empty\n" ); } } else { fprintf( stderr, "Directory listing failed: %s\n", valerie_error_description( error ) ); } valerie_dir_close( dir ); Note that entry.name provides the name of the file or directory without the directory prefix. As a convenience, entry.full provides the prefixed name, so you could subsequently use: error = valerie_unit_load( dv, 0, entry.full ); to load unit 0 with an entry. 2.5. Obtaining the Node List ---------------------------- Currently not defined by miracle. 2.6. Obtaining the Unit List ---------------------------- To obtain a list of defined units, DVCP provides the ULS command. A valid execution of ULS would be: ULS and would provide a response formatted as follows: 201 OK U0 00 sdl:360x288 1 with a trailing empty line. The fields of each record in the response dictate unit, node, mlt consumer and online status respectively. valerie provides a high level view on this which automatically parses the response from the server correctly via the valerie_units structures and related functions. An example of use is as follows: valerie_units units = valerie_units_init( dv ); valerie_error_code error = valerie_units_get_error_code( units ); if ( error == valerie_ok ) { if ( valerie_units_count( units ) > 0 ) { valerie_unit_entry_t entry; int index = 0; for ( index = 0; index < valerie_units_count( units ); index ++ ) { valerie_units_get( units, index, &entry ); printf( "U%d %02d %s %s\n", entry.unit, entry.node, entry.guid, entry.online ? "online" : "offline" ); } } else { fprintf( stderr, "Unit list is empty\n" ); } } else { fprintf( stderr, "Unit listing failed: %s\n", valerie_error_description( error ) ); } valerie_units_close( units ); 2.7. Unit Status Information ---------------------------- There are two methods for a client to obtain unit status information. The first is via the DVCP USTA command, which would normally be accessed via: USTA U0 and would provide a response formated as follows: 202 OK 0 playing "a.dv" 58 1000 25.00 0 6999 7000 "a.dv" 157 0 6999 7000 1 4 0 with no trailing empty line. The entries in the record are: * Unit * State (undefined, offline, not_loaded, stopped, playing, paused, disconnected [when server dies]) * Name of Clip * Position in clip * Speed * 1000 * Frames per second * Start of clip (in point) * End of clip (out point) * Length of clip * Read ahead clip * Read ahead position * Read ahead clip in * Read ahead clip out * Read ahead clip length * Seekable flag * Playlist generation * Clip index Again, valerie provides a high level means for obtaining this via the valerie_unit_status function and valerie_status structures: valerie_status_t status; valerie_error_code error = valerie_unit_status( dv, 0, &status ); if ( error == valerie_ok ) { switch( status.status ) { case unit_offline: printf( "offline " ); break; case unit_undefined: printf( "undefined " ); break; case unit_not_loaded: printf( "unloaded " ); break; case unit_stopped: printf( "stopped " ); break; case unit_playing: printf( "playing " ); break; default: printf( "unknown " ); break; } printf( "%06lld %06lld %06lld %s\n", status.in, status.position, status.out, status.clip ); } else { fprintf( stderr, "Unit status failed: %s\n", valerie_error_description( error ) ); } The second approach for obtaining a units status is via automatic notification. This is done via the valerie_notifier API. To obtain the notifier from the high level API, you can use: valerie_notifier notifier = valerie_get_notifier( dv ); To obtain the last status associated to a unit, you can use: int unit = 1; valerie_status_t status; valerie_notifier_get( notifier, &status, unit ); To wait for the next status from any unit, you can use: valerie_notifier_wait( notifier, &status ); If you wish to trigger the action associated to your applications wait handling of a particular unit, you can use: valerie_notifier_get( notifier, &status, unit ); valerie_notifier_put( notifier, &status ); See Examples below for details on this. The complete list of fields in the status structure are: int unit; unit_status status; char clip[ 2048 ]; int64_t position; int speed; double fps; int64_t in; int64_t out; int64_t length; char tail_clip[ 2048 ]; int64_t tail_position; int64_t tail_in; int64_t tail_out; int64_t tail_length; int seekable; int generation; int clip_index; You will always receive a status record for every frame output. The read ahead information is provided for client side queuing. Client side queuing assumes that uset eof=pause is applied to the unit. A client can detect when the previously scheduled clip is played out by using the read ahead information and schedule the next clip. While this mode of operation is still supported, it is recommended that new clients use the server side queuing mechanism which is described in the following section. 2.8. Server Side Queueing APIs ------------------------------ This section describes the APIs available to provide server side queueing. The concept is that each unit maintains its own playlist, containing multiple clips. Associated to the playlist is a generation number which is incremented on each modification to the playlist. The current playlist generation is provided in the status record in order for a client to know when to refresh its presentation of the list. The status record also indicates which clip is currently active. Actions that can be carried out on the playlist are summarised as: * list - list all the clips and associated in/out points and size * loading a clip - a load will wipe the current list and replace it with the specified clip * appending a clip - append will always place the specified clip at the end of the playlist * inserting a clip - insert will place a new clip at the specified position in the playlist * moving a clip - move will allow clips can be moved in the playlist * removing a clip - remove will remove the specified clip from the playlist * clean - clean will remove all but the playing clip from the playlist Additionally, the following existing actions are clip aware: * goto allows you to move the current play position to a specific clip position * set in/out points allows you to modify clip in and out points Backward compatability has been maintained by the addition of a clip-aware family of APIs which have the naming convention of valerie_unit_clip_*. These are listed in Appendix B. The following shows an example of obtaining the clips queued on unit 0: valerie_list list = valerie_list_init( dv, 0 ); valerie_list_entry_t entry; int index; printf( "Generation = %d\n", list->generation ); for ( index = 0; index < valerie_list_count( list ); index ++ ) { valerie_list_get( list, index, &entry ); printf( "%d %s %d %d %d %d\n", entry.clip, entry.full, entry.in, entry.out, entry.max, entry.size ); } valerie_list_close( list ); To load a clip on unit 0: valerie_unit_load( dv, 0, "/path/clip.dv" ); To append a clip on unit 0: valerie_unit_append( dv, 0, "/path/clip.dv", -1, -1 ); Note that the last two arguments specify the in and out points of the clip with -1 denoting dfaults of the entirety of the file. To insert a clip at position 0 on unit 0, we can use the following: valerie_unit_clip_insert( dv, 0, clip_absolute, 0, "/path/clip.dv", -1, -1 ); The 3rd and 4th arguments here are common to all the valerie_unit_clip functions. They take the form of either [clip_absolute, n] to indicate an absolute clip index, or [clip_relative, n] to indicate a clip index relative to the currently playing clip. So, to insert a clip immediately before the currently playing clip, we can use: valerie_unit_clip_insert( dv, 0, clip_relative, -1, "/path/clip.dv", -1, -1 ); To move the current clip to the next position in the list: valerie_unit_clip_move( dv, 0, clip_relative, 0, clip_relative, 1 ); To remove a specific clip: valerie_unit_clip_remove( dv, 0, clip_absolute, index ); To remove all but the currently playing clip: valerie_unit_clean( dv, 0 ); To goto the first frame in the first clip, you can use: valerie_unit_clip_goto( dv, 0, clip_absolute, 0, 0 ); To set the in and out points on the current clip: valerie_unit_clip_set_in( dv, 0, clip_relative, 0, 0 ); valerie_unit_clip_set_out( dv, 0, clip_relative, 0, 1000 ); A more complete example of use of the server side can queuing can be found at: http://users.pandora.be/acp/rugen The demo client provided with valerie is used for retaining backward compatability with the client side queuing API. 2.9. Accessing the Low Level Parser Directly -------------------------------------------- The low level parser and its associated structures can be accessed directly from the high level API, but is very occasionally actually needed. The methods are provided via a pair of high level methods: valerie_error_code error = valerie_execute( dv, 1024, "USTA U%d", unit ); valerie_response response = valerie_get_last_response( dv ); int index = 0; for ( index = 0; index < valerie_response_count( response ); index ++ ) printf( "%d: %s\n", index, valerie_response_get_line( response,index ) ); More details on the valerie_response structure can be found in section 3 of this document. 2.10. Cleaning up ----------------- Before the valerie and parser go out of scope, you need to run: valerie_close( dv ); valerie_parser_close( parser ); Note that you should close all valerie instances before closing the parser. 2.11. Examples -------------- Please refer to albino and humperdink source for examples provided with the project. Additional examples can be found via google with gdv1394 and poldo. 3. The Low Level Parser API --------------------------- The low level parser API provides a very simple mechanism for constructing commands and receiving responses. As described in section 2, a parser is constructed as local or remote and this is sufficient for constructing the low level parser. 3.1. Executing a Command ------------------------ All commands can be executed via the single variable argument function valerie_parser_executef and this function returns a valerie_response, ie: valerie_response response = valerie_parser_executef( parser, "CLS \"%s\"", dir ); Note that no carriage return/line feed is required (adding this is erroneous). It is the receiver of the response who is responsible for closing it. valerie_response_close( response ); 3.2. Interpreting valerie_response ----------------------------- The response received can be NULL, but it is safe to call: int error = valerie_response_get_error_code( response ); which will return: * -1 if response is NULL, * -2 if there is no content to the response, * 0 if the responses first line does not correspond to a valid DVCP response * or the DVCP protocol error code returned on the first line of the response A simple use of a valerie_response structure is as follows: valerie_response response = valerie_parser_executef( parser, "CLS \"%s\"", dir ); int error = valerie_response_get_error_code( response ); if ( error >= 0 ) { int index = 0; for ( index = 0; index < valerie_response_count( response ); index ++ ) printf( "%3d: %s\n", index, valerie_response_get_line( response, index ) ); } else { /* interpret error */ } valerie_response_close( response ); Note that it is safe to call valerie_response_close regardless of the error condition indicated. 3.3. Accessing Unit Status -------------------------- As with the high level parser, there are two alternatives to obtain unit status information - either via the USTA DVCP command or via the valerie1394_notifier. The latter is the recommended way for any applications which wish to extract meaningful information from the status while avoiding the requirement to duplicate the parsing process in a specific client. The notifier can be obtained by: valerie_notifier notifier = valerie_parser_get_notifier( parser ); The use of the notifier with the low level parser is identical to that dictated in Section 2 - to obtain the last status associated to a unit, you can use: int unit = 1; valerie_status_t status; valerie_notifier_get( notifier, &status, unit ); To wait for the next status from any unit, you can use: valerie_notifier_wait( notifier, &status ); APPENDIX A - COMPILATION AND LINKING ------------------------------------ Compilation flags are: -I /include where prefix defaults to /usr/local. Linking flags for a client are: -L /lib/ -lvalerie Or for a local parser: -L /lib/ -lmiracle Note that you never need both libs. APPENDIX B - COMPLETE HIGH LEVEL PARSER WRAPPER API LISTING ----------------------------------------------------------- valerie valerie_init( valerie_parser ); valerie_error_code valerie_connect( valerie ); valerie_error_code valerie_set( valerie, char *, char * ); valerie_error_code valerie_get( valerie, char *, char *, int ); valerie_error_code valerie_unit_add( valerie, char * ); valerie_error_code valerie_unit_load( valerie, int, char * ); valerie_error_code valerie_unit_load_clipped( valerie,int,char *,long,long ); valerie_error_code valerie_unit_load_back( valerie, int, char * ); valerie_error_code valerie_unit_load_back_clipped(valerie,int,char *,long,long) valerie_error_code valerie_unit_play( valerie, int ); valerie_error_code valerie_unit_play_at_speed( valerie, int, int ); valerie_error_code valerie_unit_stop( valerie, int ); valerie_error_code valerie_unit_pause( valerie, int ); valerie_error_code valerie_unit_rewind( valerie, int ); valerie_error_code valerie_unit_fast_forward( valerie, int ); valerie_error_code valerie_unit_step( valerie, int, int ); valerie_error_code valerie_unit_goto( valerie, int, int ); valerie_error_code valerie_unit_set_in( valerie, int, int ); valerie_error_code valerie_unit_set_out( valerie, int, int ); valerie_error_code valerie_unit_clear_in( valerie, int ); valerie_error_code valerie_unit_clear_out( valerie, int ); valerie_error_code valerie_unit_clear_in_out( valerie, int ); valerie_error_code valerie_unit_set( valerie, int, char *, char * ); valerie_error_code valerie_unit_get( valerie, int, char * ); valerie_error_code valerie_unit_status( valerie, int, valerie_status ); valerie_notifier valerie_get_notifier( valerie ); valerie_dir valerie_dir_init( valerie, char * ); valerie_error_code valerie_dir_get( valerie_dir, int, valerie_dir_entry ); int valerie_dir_count( valerie_dir ); void valerie_dir_close( valerie_dir ); valerie_nodes valerie_nodes_init( valerie ); valerie_error_code valerie_nodes_get(valerie_nodes,int,valerie_node_entry); int valerie_nodes_count( valerie_nodes ); void valerie_nodes_close( valerie_nodes ); valerie_units valerie_units_init( valerie ); valerie_error_code valerie_units_get(valerie_units,int,valerie_unit_entry); int valerie_units_count( valerie_units ); void valerie_units_close( valerie_units ); valerie_response valerie_get_last_response( valerie ); valerie_error_code valerie_execute( valerie, size_t, char *, ... ); void valerie_close( valerie ); Notifier Functions ------------------ void valerie_notifier_get( valerie_notifier, valerie_status, int ); void valerie_notifier_put( valerie_notifier, valerie_status ); int valerie_notifier_wait( valerie_notifier, valerie_status ); void valerie_notifier_close( valerie_notifier ); Server Side Queuing ------------------- valerie_list valerie_list_init( valerie, int ) valerie_error_code valerie_list_get_error_code( valerie_list ) valerie_error_code valerie_list_get( valerie_list, int, valerie_list_entry ) int valerie_list_count( valerie_list ) void valerie_list_close( valerie_list ) valerie_error_code valerie_unit_clean( valerie dv, int unit ) valerie_error_code valerie_unit_append( valerie dv, int unit, char *file, int in, int out ) valerie_error_code valerie_unit_remove_current_clip( valerie dv, int unit ) valerie_error_code valerie_unit_clip_goto( valerie dv, int unit, valerie_clip_offset offset, int clip, int position ) valerie_error_code valerie_unit_clip_set_in( valerie dv, int unit, valerie_clip_offset offset, int clip, int in ) valerie_error_code valerie_unit_clip_set_out( valerie dv, int unit, valerie_clip_offset offset, int clip, int in ) valerie_error_code valerie_unit_clip_move( valerie dv, int unit, valerie_clip_offset offset, int src, valerie_clip_offset offset, int dest ) valerie_error_code valerie_unit_clip_remove( valerie dv, int unit, valerie_clip_offset offset, int clip ) valerie_error_code valerie_unit_clip_insert( valerie dv, int unit, valerie_clip_offset offset, int clip, char *file, int in, int out ) APPENDIX C - COMPLETE LOW LEVEL PARSER API LISTING -------------------------------------------------- valerie_response valerie_parser_connect( valerie_parser ); valerie_response valerie_parser_execute( valerie_parser, char * ); valerie_response valerie_parser_executef( valerie_parser, char *, ... ); valerie_response valerie_parser_run( valerie_parser, char * ); valerie_notifier valerie_parser_get_notifier( valerie_parser ); void valerie_parser_close( valerie_parser ); valerie_response valerie_response_init( ); valerie_response valerie_response_clone( valerie_response ); int valerie_response_get_error_code( valerie_response ); char *valerie_response_get_error_string( valerie_response ); char *valerie_response_get_line( valerie_response, int ); int valerie_response_count( valerie_response ); void valerie_response_set_error( valerie_response, int, char * ); int valerie_response_printf( valerie_response, size_t, char *, ... ); int valerie_response_write( valerie_response, char *, int ); void valerie_response_close( valerie_response ); APPENDIX D - REFERENCES ----------------------- (1) doc/dvcp.txt - DVCP protocol (2) doc/testing.txt - Test procedures