Merge mlt++/CUSTOMISING into docs/melted++.
authorDan Dennedy <dan@dennedy.org>
Fri, 15 May 2009 05:05:10 +0000 (22:05 -0700)
committerDan Dennedy <dan@dennedy.org>
Fri, 15 May 2009 05:05:10 +0000 (22:05 -0700)
Signed-off-by: Dan Dennedy <dan@dennedy.org>

docs/melted++.txt
mlt++/CUSTOMISING [deleted file]

index 843dea4..711d901 100644 (file)
-Servers and Westley Docs
-------------------------
+Server Customisation
 
-       One of the key features of MLT is its server capabilities. This feature
-       allows you to pass westley documents seamlessly from one process to 
-       another and even to different computers on your network. 
+Copyright (C) 2005-2009 Ushodaya Enterprises Limited
+Authors: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2009-05-14
 
-       The miracle playout server is one such example of an application which 
-       uses this functionality - you can build your own servers into your own
-       processes with ease.
+
+INTRODUCTION
+
+       This document describes how miracle can be customised. The emphasis is on
+       showing simple examples of various aspects of the servers capabilities 
+       rather than on focussing on the MLT++ API.
+
+
+THE BASIC CUSTOM SERVER
+
+       The most basic custom server exposes the entire DVCP protocol and is roughly 
+       equivalent to the miracle server iteself, but in this case, it lacks the 
+       initialisation from /etc/miracle.conf and the port is hardcoded to 5290:
+
+       #include <iostream.h>
+       using namespace std;
+
+       #include <MltMiracle.h>
+       using namespace Mlt;
+       
+       int main( int argc, char **argv )
+       {
+               Miracle server( "miracle++", 5290 );
+               if ( server.start( ) )
+               {
+                       server.execute( "uadd sdl" );
+                       server.execute( "play u0" );
+                       server.wait_for_shutdown( );
+               }
+               else
+               {
+                       cerr << "Failed to start server" << endl;
+               }
+               return 0;
+       }
+
+       Note that after the server is started, this example submits the hard coded
+       commands specified - further units and property settings can of course be
+       specified via the DVCP protocol.
+
+       To specify initial DVCP commands from /etc/miracle.conf, it is sufficient to
+       specify an additional argument in the server constructor.
+
+       The wait_for_shutdown call is not required if the server is integrated in
+       a user interface application.
+
+
+CUSTOMISATION
+
+       This document focusses on the following areas of customisation:
+
+       * the Miracle server class
+       * extending the command set
+       * accessing the units
+       * the Response object
+       * pushing documents
+       * handling pushed documents
+       * accessiving events
+
+
+THE MIRACLE SERVER CLASS
+
+       The full public interface of the server is as follows:
+
+       class Miracle : public Properties
+       {
+               public:
+                       Miracle( char *name, int port = 5290, char *config = NULL );
+                       virtual ~Miracle( );
+                       mlt_properties get_properties( );
+                       bool start( );
+                       bool is_running( );
+                       virtual Response *execute( char *command );
+                       virtual Response *received( char *command, char *doc );
+                       virtual Response *push( char *command, Service *service );
+                       void wait_for_shutdown( );
+                       static void log_level( int );
+                       Properties *unit( int );
+       };
+
+       The focus of this document is on the 3 virtual methods (execute, received and
+       push). Some further information is provided about the unit properties method
+       and the types of functionality that it provides.
+
+
+EXTENDING THE COMMAND SET
+
+       The simplest customisation is carried out by overriding the the 'execute' 
+       method - the following shows a simple example:
+
+       #include <iostream.h>
+       #include <string>
+       #include <sstring>
+       using namespace std;
+
+       #include <MltMiracle.h>
+       #include <MltResponse.h>
+       using namespace Mlt;
+
+       class Custom : 
+               public Miracle
+       {
+               public:
+                       Custom( char *name = "Custom", int port = 5290, char *config = NULL ) :
+                               Miracle( name, port, config )
+                       {
+                       }
+
+                       Response *execute( char *command )
+                       {
+                               cerr << "command = " << command << endl;
+                               return Miracle::execute( command );
+                       }
+       };
+       
+       int main( int argc, char **argv )
+       {
+               Custom server( "miracle++", 5290 );
+               if ( server.start( ) )
+               {
+                       server.execute( "uadd sdl" );
+                       server.execute( "play u0" );
+                       server.wait_for_shutdown( );
+               }
+               else
+               {
+                       cerr << "Failed to start server" << endl;
+               }
+               return 0;
+       }
+
+       All this does is output each command and pass control over to the original
+       implementation. 
+
+       When you execute this, you will see the following output:
+
+       (5) Starting server on 5290.
+       command = uadd sdl
+       (5) miracle++ version 0.0.1 listening on port 5290
+       command = play u0
+       (7) Received signal 2 - shutting down.
+
+       Note that all commands except the PUSH are passed through this method before 
+       they are executed and this includes those coming from the main function itself. 
+
+
+ACCESSING UNIT PROPERTIES
+
+       A unit consists of two objects - a playlist and a consumer. Your custom 
+       server can access these by obtaining the Properties object associated to a unit
+       via the 'unit' method. 
+       
+       As a simple example we can replace our execute method above with the following:
+
+               Response *execute( char *command )
+               {
+                       if ( !strcmp( command, "debug" ) )
+                       {
+                               int i = 0;
+                               while( unit( i ) != NULL )
+                                       unit( i ++ )->debug( );
+                               return new Response( 200, "Diagnostics output" );
+                       }
+                       return Miracle::execute( command );
+               }
+
+       When this runs and you send a 'debug' command via DVCP, the server will output
+       some information on stderr, like:
+
+       (5) Starting server on 5290.
+       (5) Server version 0.0.1 listening on port 5290
+       (5) Connection established with localhost (7)
+       Object: [ ref=3, unit=0, generation=0, constructor=sdl, id=sdl, arg=(nil), 
+       consumer=0x80716a0, playlist=0x807f8a8, root=/, notifier=0x8087c28 ]
+       (6) localhost "debug" 100
+
+       You can extract the objects using:
+
+               Playlist playlist( ( mlt_playlist )( unit( i )->get_data( "playlist" ) ) );
+               Consumer consumer( ( mlt_consumer )( unit( i )->get_data( "consumer" ) ) );
+       
+       and use the standard MLT++ wrapping methods to interact with them or you can 
+       bypass these and using the C API directly.
+
+       Obviously, this opens a lot of possibilities for the types of editing operations
+       than can be carried out over the DVCP protocol - for example, you can attach filters
+       apply mixes/transitions between neighbouring cuts or carry out specific operations
+       on cuts.
+
+
+THE RESPONSE OBJECT
+
+       The example above doesn't do anything particularly useful - in order to extend 
+       things in more interesting ways, we should be able to carry information back to 
+       the client. In the code above, we introduced the Response object to carry an 
+       error code and a description - it can also be used to carry arbitrary large
+       blocks of data.
+
+               Response *execute( char *command )
+               {
+                       Response *response = NULL;
+                       if ( !strcmp( command, "debug" ) )
+                       {
+                               response = new Response( 200, "Diagnostics output" );
+                               for( int i = 0; unit( i ) != NULL; i ++ )
+                               {
+                                       Properties *properties = unit( i );
+                                       stringstream output;
+                                       output << string( "Unit " ) << i << endl;
+                                       for ( int j = 0; j < properties->count( ); j ++ )
+                                               output << properties->get_name( j ) << " = " << properties->get( j ) << endl;
+                                       response->write( output.str( ).c_str( ) );
+                               }
+                       }
+                       return response == NULL ? Miracle::execute( command ) : response;
+               }
+
+       Now when you connect to the server via a telnet session, you can access the 
+       'debug' command as follows:
+
+               $ telnet localhost 5290
+               Trying 127.0.0.1...
+               Connected to localhost (127.0.0.1).
+               Escape character is '^]'.
+               100 VTR Ready
+               debug
+               201 OK
+               Unit 0
+               unit = 0
+               generation = 0
+               constructor = sdl
+               id = sdl
+               arg =
+
+       Note that the '200' return code specified is automatically promoted to a 201
+       because of the multiple lines.
+
+       Alternatively, you can invoke response->write as many times as you like - each
+       string submitted is simply appended to the object in a similar way to writing
+       to a file or socket. Note that the client doesn't receive anything until the
+       response is returned from this method (ie: there's currently no support to 
+       stream results back to the client).
+       
+
+PUSHING DOCUMENTS
+
+       This feature allows you to pass MLT XML documents seamlessly from one
+       process to another and even to different computers on your network.
 
        A server process would be running as follows:
 
@@ -25,14 +269,141 @@ Servers and Westley Docs
            }
 
        Typically, when you have an MLT object such as a producer or a playlist,
-       you can send a westley representation of this to a running server with:
+       you can send a XML representation of this to a running server with:
 
-           Conumser valerie( "valerie", "localhost:5250" );
-           valerie.connect( producer );
-           valerie.start( );
+           Consumer mvsp( "mvsp", "localhost:5250" );
+           mvsp.connect( producer );
+           mvsp.start( );
 
        The effect of the push will be to append the producer on to the first
        unit (u0).
 
-       You can completely customise the miracle server - an example of this
-       is shown below.
+HANDLING PUSHED DOCUMENTS
+
+       The custom class receives PUSH'd westley either via the received or push 
+       method. 
+
+       The default handling is to simply append a pushed document on to the end of
+       first unit 0.
+
+       You can test this in the server defined above from the command line, for
+       example:
+
+       $ inigo noise: -consumer valerie:localhost:5290
+
+       By default, the 'push' method is used - this means that the xml document 
+       received is automatically deserialised by the server itself and then offered
+       to the push method for handling - an example of this would be:
+
+               Response *push( char *command, Service *service )
+               {
+                       Playlist playlist( ( mlt_playlist )( unit( 0 )->get_data( "playlist" ) ) );
+                       Producer producer( *service );
+                       if ( producer.is_valid( ) && playlist.is_valid( ) )
+                       {
+                               playlist.lock( );
+                               playlist.clear( );
+                               playlist.append( producer );
+                               playlist.unlock( );
+                               return new Response( 200, "OK" );
+                       }
+                       return new Response( 400, "Invalid" );
+               }
+
+       With this method, each service pushed into the server will automatically
+       replace whatever is currently playing.
+
+       Note that the 'received' method is not invoked by default - if you wish to
+       receive the XML document and carry out any additional processing prior to
+       processing, you should set the 'push-parser-off' property on the server to 1.
+       This can be done by placing the following line in your classes constructor:
+
+               set( "push-parser-off", 1 );
+
+       When this property is set, the received method is used instead of the push - 
+       in this scenario, your implementation is responsible for all handling
+       of the document.
+
+       To simulate this, you can try the following method:
+
+               Response *received( char *command, char *document )
+               {
+                       cerr << document;
+                       Producer producer( "westley-xml", document );
+                       return push( command, &producer );
+               }
+
+       When you push your videos in to the server via the inigo command above (or 
+       from other tools, such as those in the shotcut suite), you will see the xml 
+       in the servers stderr output. If you need to carry out some operations on the 
+       xml document (such as replacing low quality videos used in the editing process 
+       with their original) the received mechanism is the one that you would want to 
+       use.
+
+
+OTHER MANIPULATIONS
+
+       What you do with the received MLT Service is largely up to you. As shown above,
+       you have flexibility in how the item is scheduled and you can carry out 
+       manipulations on either the xml document and/or the deserialised producer.
+
+       Typically, shotcut and inigo produce 'tractor' objects - these can be easily
+       manipulated in the push method - for example, to remove a track from the 
+       output, we could do something like:
+
+               Response *push( char *command, Service *service )
+               {
+                       Playlist playlist( ( mlt_playlist )( unit( 0 )->get_data( "playlist" ) ) );
+                       Tractor *tractor( *service );
+                       if ( tractor.is_valid( ) && playlist.is_valid( ) )
+                       {
+                               // Remove track 2 (NB: tracks are indexed from 0 like everything else)
+                               Producer *producer = tractor.track( 2 );
+                               Playlist track( producer );
+
+                               // If we have a valid track then hide video and audio
+                               // This is a bit pattern - 1 is video, 2 is audio
+                               if ( track.is_valid( ) )
+                                       track.set( "hide", 3 );
+
+                               // You need to delete the reference to the playlist producer here
+                               delete producer;
+
+                               // Play it
+                               playlist.lock( );
+                               playlist.clear( );
+                               playlist.append( producer );
+                               playlist.unlock( );
+                               return new Response( 200, "OK" );
+                       }
+                       return new Response( 400, "Invalid" );
+               }
+
+
+EVENT HANDLING
+
+       The MLT framework generates events which your custom server can use to do
+       various runtime manipulations. For the purpose of this document, I'll focus
+       on 'consumer-frame-render' - this event is fired immediately before a frame
+       is rendered.
+
+       See example in test/server.cpp
+
+
+DISABLING DVCP
+
+       In some cases, it is desirable to fully disable the entire DVCP command set
+       and handle the PUSH in an application specific way (for example, the shotcut 
+       applications all do this). The simplest way of doing this is to generate a
+       response that signifies the rejection of the command. In this example, the 
+       'shutdown' command is also handled:
+
+               Response *execute( char *command )
+               {
+                       if ( !strcmp( command, "shutdown" ) )
+                               exit( 0 );
+                       return new Response( 400, "Invalid Command" );
+               }
+               
+       If you use this method in the code above, your server does nothing - no units 
+       are defined, so even a PUSH will be rejected. 
diff --git a/mlt++/CUSTOMISING b/mlt++/CUSTOMISING
deleted file mode 100644 (file)
index 7fdce7f..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-Server Customisation
-
-Copyright (C) 2005 Ushodaya Enterprises Limited
-Authors: Charles Yates <charles.yates@pandora.be>
-Last Revision: 2005-03-16
-
-
-INTRODUCTION
-
-       This document describes how miracle can be customised. The emphasis is on
-       showing simple examples of various aspects of the servers capabilities 
-       rather than on focussing on the MLT++ API.
-
-
-THE BASIC CUSTOM SERVER
-
-       The most basic custom server exposes the entire DVCP protocol and is roughly 
-       equivalent to the miracle server iteself, but in this case, it lacks the 
-       initialisation from /etc/miracle.conf and the port is hardcoded to 5290:
-
-       #include <iostream.h>
-       using namespace std;
-
-       #include <MltMiracle.h>
-       using namespace Mlt;
-       
-       int main( int argc, char **argv )
-       {
-               Miracle server( "miracle++", 5290 );
-               if ( server.start( ) )
-               {
-                       server.execute( "uadd sdl" );
-                       server.execute( "play u0" );
-                       server.wait_for_shutdown( );
-               }
-               else
-               {
-                       cerr << "Failed to start server" << endl;
-               }
-               return 0;
-       }
-
-       Note that after the server is started, this example submits the hard coded
-       commands specified - further units and property settings can of course be
-       specified via the DVCP protocol.
-
-       To specify initial DVCP commands from /etc/miracle.conf, it is sufficient to
-       specify an additional argument in the server constructor.
-
-       The wait_for_shutdown call is not required if the server is integrated in
-       a user interface application.
-
-
-CUSTOMISATION
-
-       This document focusses on the following areas of customisation:
-
-       * the Miracle server class
-       * extending the command set
-       * accessing the units
-       * the Response object
-       * handling pushed westley documents
-       * accessiving events
-
-
-THE MIRACLE SERVER CLASS
-
-       The full public interface of the server is as follows:
-
-       class Miracle : public Properties
-       {
-               public:
-                       Miracle( char *name, int port = 5290, char *config = NULL );
-                       virtual ~Miracle( );
-                       mlt_properties get_properties( );
-                       bool start( );
-                       bool is_running( );
-                       virtual Response *execute( char *command );
-                       virtual Response *received( char *command, char *doc );
-                       virtual Response *push( char *command, Service *service );
-                       void wait_for_shutdown( );
-                       static void log_level( int );
-                       Properties *unit( int );
-       };
-
-       The focus of this document is on the 3 virtual methods (execute, received and
-       push). Some further information is provided about the unit properties method
-       and the types of functionality that it provides.
-
-
-EXTENDING THE COMMAND SET
-
-       The simplest customisation is carried out by overriding the the 'execute' 
-       method - the following shows a simple example:
-
-       #include <iostream.h>
-       #include <string>
-       #include <sstring>
-       using namespace std;
-
-       #include <MltMiracle.h>
-       #include <MltResponse.h>
-       using namespace Mlt;
-
-       class Custom : 
-               public Miracle
-       {
-               public:
-                       Custom( char *name = "Custom", int port = 5290, char *config = NULL ) :
-                               Miracle( name, port, config )
-                       {
-                       }
-
-                       Response *execute( char *command )
-                       {
-                               cerr << "command = " << command << endl;
-                               return Miracle::execute( command );
-                       }
-       };
-       
-       int main( int argc, char **argv )
-       {
-               Custom server( "miracle++", 5290 );
-               if ( server.start( ) )
-               {
-                       server.execute( "uadd sdl" );
-                       server.execute( "play u0" );
-                       server.wait_for_shutdown( );
-               }
-               else
-               {
-                       cerr << "Failed to start server" << endl;
-               }
-               return 0;
-       }
-
-       All this does is output each command and pass control over to the original
-       implementation. 
-
-       When you execute this, you will see the following output:
-
-       (5) Starting server on 5290.
-       command = uadd sdl
-       (5) miracle++ version 0.0.1 listening on port 5290
-       command = play u0
-       (7) Received signal 2 - shutting down.
-
-       Note that all commands except the PUSH are passed through this method before 
-       they are executed and this includes those coming from the main function itself. 
-
-
-ACCESSING UNIT PROPERTIES
-
-       A unit consists of two objects - a playlist and a consumer. Your custom 
-       server can access these by obtaining the Properties object associated to a unit
-       via the 'unit' method. 
-       
-       As a simple example we can replace our execute method above with the following:
-
-               Response *execute( char *command )
-               {
-                       if ( !strcmp( command, "debug" ) )
-                       {
-                               int i = 0;
-                               while( unit( i ) != NULL )
-                                       unit( i ++ )->debug( );
-                               return new Response( 200, "Diagnostics output" );
-                       }
-                       return Miracle::execute( command );
-               }
-
-       When this runs and you send a 'debug' command via DVCP, the server will output
-       some information on stderr, like:
-
-       (5) Starting server on 5290.
-       (5) Server version 0.0.1 listening on port 5290
-       (5) Connection established with localhost (7)
-       Object: [ ref=3, unit=0, generation=0, constructor=sdl, id=sdl, arg=(nil), 
-       consumer=0x80716a0, playlist=0x807f8a8, root=/, notifier=0x8087c28 ]
-       (6) localhost "debug" 100
-
-       You can extract the objects using:
-
-               Playlist playlist( ( mlt_playlist )( unit( i )->get_data( "playlist" ) ) );
-               Consumer consumer( ( mlt_consumer )( unit( i )->get_data( "consumer" ) ) );
-       
-       and use the standard MLT++ wrapping methods to interact with them or you can 
-       bypass these and using the C API directly.
-
-       Obviously, this opens a lot of possibilities for the types of editing operations
-       than can be carried out over the DVCP protocol - for example, you can attach filters
-       apply mixes/transitions between neighbouring cuts or carry out specific operations
-       on cuts.
-
-
-THE RESPONSE OBJECT
-
-       The example above doesn't do anything particularly useful - in order to extend 
-       things in more interesting ways, we should be able to carry information back to 
-       the client. In the code above, we introduced the Response object to carry an 
-       error code and a description - it can also be used to carry arbitrary large
-       blocks of data.
-
-               Response *execute( char *command )
-               {
-                       Response *response = NULL;
-                       if ( !strcmp( command, "debug" ) )
-                       {
-                               response = new Response( 200, "Diagnostics output" );
-                               for( int i = 0; unit( i ) != NULL; i ++ )
-                               {
-                                       Properties *properties = unit( i );
-                                       stringstream output;
-                                       output << string( "Unit " ) << i << endl;
-                                       for ( int j = 0; j < properties->count( ); j ++ )
-                                               output << properties->get_name( j ) << " = " << properties->get( j ) << endl;
-                                       response->write( output.str( ).c_str( ) );
-                               }
-                       }
-                       return response == NULL ? Miracle::execute( command ) : response;
-               }
-
-       Now when you connect to the server via a telnet session, you can access the 
-       'debug' command as follows:
-
-               $ telnet localhost 5290
-               Trying 127.0.0.1...
-               Connected to localhost (127.0.0.1).
-               Escape character is '^]'.
-               100 VTR Ready
-               debug
-               201 OK
-               Unit 0
-               unit = 0
-               generation = 0
-               constructor = sdl
-               id = sdl
-               arg =
-
-       Note that the '200' return code specified is automatically promoted to a 201
-       because of the multiple lines.
-
-       Alternatively, you can invoke response->write as many times as you like - each
-       string submitted is simply appended to the object in a similar way to writing
-       to a file or socket. Note that the client doesn't receive anything until the
-       response is returned from this method (ie: there's currently no support to 
-       stream results back to the client).
-       
-
-HANDLING PUSHED DOCUMENTS
-
-       The custom class receives PUSH'd westley either via the received or push 
-       method. 
-
-       The default handling is to simply append a pushed document on to the end of
-       first unit 0.
-
-       You can test this in the server defined above from the command line, for
-       example:
-
-       $ inigo noise: -consumer valerie:localhost:5290
-
-       By default, the 'push' method is used - this means that the xml document 
-       received is automatically deserialised by the server itself and then offered
-       to the push method for handling - an example of this would be:
-
-               Response *push( char *command, Service *service )
-               {
-                       Playlist playlist( ( mlt_playlist )( unit( 0 )->get_data( "playlist" ) ) );
-                       Producer producer( *service );
-                       if ( producer.is_valid( ) && playlist.is_valid( ) )
-                       {
-                               playlist.lock( );
-                               playlist.clear( );
-                               playlist.append( producer );
-                               playlist.unlock( );
-                               return new Response( 200, "OK" );
-                       }
-                       return new Response( 400, "Invalid" );
-               }
-
-       With this method, each service pushed into the server will automatically
-       replace whatever is currently playing.
-
-       Note that the 'received' method is not invoked by default - if you wish to
-       receive the XML document and carry out any additional processing prior to
-       processing, you should set the 'push-parser-off' property on the server to 1.
-       This can be done by placing the following line in your classes constructor:
-
-               set( "push-parser-off", 1 );
-
-       When this property is set, the received method is used instead of the push - 
-       in this scenario, your implementation is responsible for all handling
-       of the document.
-
-       To simulate this, you can try the following method:
-
-               Response *received( char *command, char *document )
-               {
-                       cerr << document;
-                       Producer producer( "westley-xml", document );
-                       return push( command, &producer );
-               }
-
-       When you push your videos in to the server via the inigo command above (or 
-       from other tools, such as those in the shotcut suite), you will see the xml 
-       in the servers stderr output. If you need to carry out some operations on the 
-       xml document (such as replacing low quality videos used in the editing process 
-       with their original) the received mechanism is the one that you would want to 
-       use.
-
-
-OTHER MANIPULATIONS
-
-       What you do with the received MLT Service is largely up to you. As shown above,
-       you have flexibility in how the item is scheduled and you can carry out 
-       manipulations on either the xml document and/or the deserialised producer.
-
-       Typically, shotcut and inigo produce 'tractor' objects - these can be easily
-       manipulated in the push method - for example, to remove a track from the 
-       output, we could do something like:
-
-               Response *push( char *command, Service *service )
-               {
-                       Playlist playlist( ( mlt_playlist )( unit( 0 )->get_data( "playlist" ) ) );
-                       Tractor *tractor( *service );
-                       if ( tractor.is_valid( ) && playlist.is_valid( ) )
-                       {
-                               // Remove track 2 (NB: tracks are indexed from 0 like everything else)
-                               Producer *producer = tractor.track( 2 );
-                               Playlist track( producer );
-
-                               // If we have a valid track then hide video and audio
-                               // This is a bit pattern - 1 is video, 2 is audio
-                               if ( track.is_valid( ) )
-                                       track.set( "hide", 3 );
-
-                               // You need to delete the reference to the playlist producer here
-                               delete producer;
-
-                               // Play it
-                               playlist.lock( );
-                               playlist.clear( );
-                               playlist.append( producer );
-                               playlist.unlock( );
-                               return new Response( 200, "OK" );
-                       }
-                       return new Response( 400, "Invalid" );
-               }
-
-
-EVENT HANDLING
-
-       The MLT framework generates events which your custom server can use to do
-       various runtime manipulations. For the purpose of this document, I'll focus
-       on 'consumer-frame-render' - this event is fired immediately before a frame
-       is rendered.
-
-       See example in test/server.cpp
-
-
-DISABLING DVCP
-
-       In some cases, it is desirable to fully disable the entire DVCP command set
-       and handle the PUSH in an application specific way (for example, the shotcut 
-       applications all do this). The simplest way of doing this is to generate a
-       response that signifies the rejection of the command. In this example, the 
-       'shutdown' command is also handled:
-
-               Response *execute( char *command )
-               {
-                       if ( !strcmp( command, "shutdown" ) )
-                               exit( 0 );
-                       return new Response( 400, "Invalid Command" );
-               }
-               
-       If you use this method in the code above, your server does nothing - no units 
-       are defined, so even a PUSH will be rejected. 
-
-
-