test not only if header frei0r.h exists, also use an item
[mlt-frei0r-support.git] / mlt++ / CUSTOMISING
blob7fdce7f0c9439276079f314aa9ee6e4c35cbbf3c
1 Server Customisation
3 Copyright (C) 2005 Ushodaya Enterprises Limited
4 Authors: Charles Yates <charles.yates@pandora.be>
5 Last Revision: 2005-03-16
8 INTRODUCTION
10         This document describes how miracle can be customised. The emphasis is on
11         showing simple examples of various aspects of the servers capabilities 
12         rather than on focussing on the MLT++ API.
15 THE BASIC CUSTOM SERVER
17         The most basic custom server exposes the entire DVCP protocol and is roughly 
18         equivalent to the miracle server iteself, but in this case, it lacks the 
19         initialisation from /etc/miracle.conf and the port is hardcoded to 5290:
21         #include <iostream.h>
22         using namespace std;
24         #include <MltMiracle.h>
25         using namespace Mlt;
26         
27         int main( int argc, char **argv )
28         {
29                 Miracle server( "miracle++", 5290 );
30                 if ( server.start( ) )
31                 {
32                         server.execute( "uadd sdl" );
33                         server.execute( "play u0" );
34                         server.wait_for_shutdown( );
35                 }
36                 else
37                 {
38                         cerr << "Failed to start server" << endl;
39                 }
40                 return 0;
41         }
43         Note that after the server is started, this example submits the hard coded
44         commands specified - further units and property settings can of course be
45         specified via the DVCP protocol.
47         To specify initial DVCP commands from /etc/miracle.conf, it is sufficient to
48         specify an additional argument in the server constructor.
50         The wait_for_shutdown call is not required if the server is integrated in
51         a user interface application.
54 CUSTOMISATION
56         This document focusses on the following areas of customisation:
58         * the Miracle server class
59         * extending the command set
60         * accessing the units
61         * the Response object
62         * handling pushed westley documents
63         * accessiving events
66 THE MIRACLE SERVER CLASS
68         The full public interface of the server is as follows:
70         class Miracle : public Properties
71         {
72                 public:
73                         Miracle( char *name, int port = 5290, char *config = NULL );
74                         virtual ~Miracle( );
75                         mlt_properties get_properties( );
76                         bool start( );
77                         bool is_running( );
78                         virtual Response *execute( char *command );
79                         virtual Response *received( char *command, char *doc );
80                         virtual Response *push( char *command, Service *service );
81                         void wait_for_shutdown( );
82                         static void log_level( int );
83                         Properties *unit( int );
84         };
86         The focus of this document is on the 3 virtual methods (execute, received and
87         push). Some further information is provided about the unit properties method
88         and the types of functionality that it provides.
91 EXTENDING THE COMMAND SET
93         The simplest customisation is carried out by overriding the the 'execute' 
94         method - the following shows a simple example:
96         #include <iostream.h>
97         #include <string>
98         #include <sstring>
99         using namespace std;
101         #include <MltMiracle.h>
102         #include <MltResponse.h>
103         using namespace Mlt;
105         class Custom : 
106                 public Miracle
107         {
108                 public:
109                         Custom( char *name = "Custom", int port = 5290, char *config = NULL ) :
110                                 Miracle( name, port, config )
111                         {
112                         }
114                         Response *execute( char *command )
115                         {
116                                 cerr << "command = " << command << endl;
117                                 return Miracle::execute( command );
118                         }
119         };
120         
121         int main( int argc, char **argv )
122         {
123                 Custom server( "miracle++", 5290 );
124                 if ( server.start( ) )
125                 {
126                         server.execute( "uadd sdl" );
127                         server.execute( "play u0" );
128                         server.wait_for_shutdown( );
129                 }
130                 else
131                 {
132                         cerr << "Failed to start server" << endl;
133                 }
134                 return 0;
135         }
137         All this does is output each command and pass control over to the original
138         implementation. 
140         When you execute this, you will see the following output:
142         (5) Starting server on 5290.
143         command = uadd sdl
144         (5) miracle++ version 0.0.1 listening on port 5290
145         command = play u0
146         (7) Received signal 2 - shutting down.
148         Note that all commands except the PUSH are passed through this method before 
149         they are executed and this includes those coming from the main function itself. 
152 ACCESSING UNIT PROPERTIES
154         A unit consists of two objects - a playlist and a consumer. Your custom 
155         server can access these by obtaining the Properties object associated to a unit
156         via the 'unit' method. 
157         
158         As a simple example we can replace our execute method above with the following:
160                 Response *execute( char *command )
161                 {
162                         if ( !strcmp( command, "debug" ) )
163                         {
164                                 int i = 0;
165                                 while( unit( i ) != NULL )
166                                         unit( i ++ )->debug( );
167                                 return new Response( 200, "Diagnostics output" );
168                         }
169                         return Miracle::execute( command );
170                 }
172         When this runs and you send a 'debug' command via DVCP, the server will output
173         some information on stderr, like:
175         (5) Starting server on 5290.
176         (5) Server version 0.0.1 listening on port 5290
177         (5) Connection established with localhost (7)
178         Object: [ ref=3, unit=0, generation=0, constructor=sdl, id=sdl, arg=(nil), 
179         consumer=0x80716a0, playlist=0x807f8a8, root=/, notifier=0x8087c28 ]
180         (6) localhost "debug" 100
182         You can extract the objects using:
184                 Playlist playlist( ( mlt_playlist )( unit( i )->get_data( "playlist" ) ) );
185                 Consumer consumer( ( mlt_consumer )( unit( i )->get_data( "consumer" ) ) );
186         
187         and use the standard MLT++ wrapping methods to interact with them or you can 
188         bypass these and using the C API directly.
190         Obviously, this opens a lot of possibilities for the types of editing operations
191         than can be carried out over the DVCP protocol - for example, you can attach filters
192         apply mixes/transitions between neighbouring cuts or carry out specific operations
193         on cuts.
196 THE RESPONSE OBJECT
198         The example above doesn't do anything particularly useful - in order to extend 
199         things in more interesting ways, we should be able to carry information back to 
200         the client. In the code above, we introduced the Response object to carry an 
201         error code and a description - it can also be used to carry arbitrary large
202         blocks of data.
204                 Response *execute( char *command )
205                 {
206                         Response *response = NULL;
207                         if ( !strcmp( command, "debug" ) )
208                         {
209                                 response = new Response( 200, "Diagnostics output" );
210                                 for( int i = 0; unit( i ) != NULL; i ++ )
211                                 {
212                                         Properties *properties = unit( i );
213                                         stringstream output;
214                                         output << string( "Unit " ) << i << endl;
215                                         for ( int j = 0; j < properties->count( ); j ++ )
216                                                 output << properties->get_name( j ) << " = " << properties->get( j ) << endl;
217                                         response->write( output.str( ).c_str( ) );
218                                 }
219                         }
220                         return response == NULL ? Miracle::execute( command ) : response;
221                 }
223         Now when you connect to the server via a telnet session, you can access the 
224         'debug' command as follows:
226                 $ telnet localhost 5290
227                 Trying 127.0.0.1...
228                 Connected to localhost (127.0.0.1).
229                 Escape character is '^]'.
230                 100 VTR Ready
231                 debug
232                 201 OK
233                 Unit 0
234                 unit = 0
235                 generation = 0
236                 constructor = sdl
237                 id = sdl
238                 arg =
240         Note that the '200' return code specified is automatically promoted to a 201
241         because of the multiple lines.
243         Alternatively, you can invoke response->write as many times as you like - each
244         string submitted is simply appended to the object in a similar way to writing
245         to a file or socket. Note that the client doesn't receive anything until the
246         response is returned from this method (ie: there's currently no support to 
247         stream results back to the client).
248         
250 HANDLING PUSHED DOCUMENTS
252         The custom class receives PUSH'd westley either via the received or push 
253         method. 
255         The default handling is to simply append a pushed document on to the end of
256         first unit 0.
258         You can test this in the server defined above from the command line, for
259         example:
261         $ inigo noise: -consumer valerie:localhost:5290
263         By default, the 'push' method is used - this means that the xml document 
264         received is automatically deserialised by the server itself and then offered
265         to the push method for handling - an example of this would be:
267                 Response *push( char *command, Service *service )
268                 {
269                         Playlist playlist( ( mlt_playlist )( unit( 0 )->get_data( "playlist" ) ) );
270                         Producer producer( *service );
271                         if ( producer.is_valid( ) && playlist.is_valid( ) )
272                         {
273                                 playlist.lock( );
274                                 playlist.clear( );
275                                 playlist.append( producer );
276                                 playlist.unlock( );
277                                 return new Response( 200, "OK" );
278                         }
279                         return new Response( 400, "Invalid" );
280                 }
282         With this method, each service pushed into the server will automatically
283         replace whatever is currently playing.
285         Note that the 'received' method is not invoked by default - if you wish to
286         receive the XML document and carry out any additional processing prior to
287         processing, you should set the 'push-parser-off' property on the server to 1.
288         This can be done by placing the following line in your classes constructor:
290                 set( "push-parser-off", 1 );
292         When this property is set, the received method is used instead of the push - 
293         in this scenario, your implementation is responsible for all handling
294         of the document.
296         To simulate this, you can try the following method:
298                 Response *received( char *command, char *document )
299                 {
300                         cerr << document;
301                         Producer producer( "westley-xml", document );
302                         return push( command, &producer );
303                 }
305         When you push your videos in to the server via the inigo command above (or 
306         from other tools, such as those in the shotcut suite), you will see the xml 
307         in the servers stderr output. If you need to carry out some operations on the 
308         xml document (such as replacing low quality videos used in the editing process 
309         with their original) the received mechanism is the one that you would want to 
310         use.
313 OTHER MANIPULATIONS
315         What you do with the received MLT Service is largely up to you. As shown above,
316         you have flexibility in how the item is scheduled and you can carry out 
317         manipulations on either the xml document and/or the deserialised producer.
319         Typically, shotcut and inigo produce 'tractor' objects - these can be easily
320         manipulated in the push method - for example, to remove a track from the 
321         output, we could do something like:
323                 Response *push( char *command, Service *service )
324                 {
325                         Playlist playlist( ( mlt_playlist )( unit( 0 )->get_data( "playlist" ) ) );
326                         Tractor *tractor( *service );
327                         if ( tractor.is_valid( ) && playlist.is_valid( ) )
328                         {
329                                 // Remove track 2 (NB: tracks are indexed from 0 like everything else)
330                                 Producer *producer = tractor.track( 2 );
331                                 Playlist track( producer );
333                                 // If we have a valid track then hide video and audio
334                                 // This is a bit pattern - 1 is video, 2 is audio
335                                 if ( track.is_valid( ) )
336                                         track.set( "hide", 3 );
338                                 // You need to delete the reference to the playlist producer here
339                                 delete producer;
341                                 // Play it
342                                 playlist.lock( );
343                                 playlist.clear( );
344                                 playlist.append( producer );
345                                 playlist.unlock( );
346                                 return new Response( 200, "OK" );
347                         }
348                         return new Response( 400, "Invalid" );
349                 }
352 EVENT HANDLING
354         The MLT framework generates events which your custom server can use to do
355         various runtime manipulations. For the purpose of this document, I'll focus
356         on 'consumer-frame-render' - this event is fired immediately before a frame
357         is rendered.
359         See example in test/server.cpp
362 DISABLING DVCP
364         In some cases, it is desirable to fully disable the entire DVCP command set
365         and handle the PUSH in an application specific way (for example, the shotcut 
366         applications all do this). The simplest way of doing this is to generate a
367         response that signifies the rejection of the command. In this example, the 
368         'shutdown' command is also handled:
370                 Response *execute( char *command )
371                 {
372                         if ( !strcmp( command, "shutdown" ) )
373                                 exit( 0 );
374                         return new Response( 400, "Invalid Command" );
375                 }
376                 
377         If you use this method in the code above, your server does nothing - no units 
378         are defined, so even a PUSH will be rejected.