3 Copyright (C) 2005 Ushodaya Enterprises Limited
4 Authors: Charles Yates <charles.yates@pandora.be>
5 Last Revision: 2005-03-16
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:
24 #include <MltMiracle.h>
27 int main( int argc, char **argv )
29 Miracle server( "miracle++", 5290 );
30 if ( server.start( ) )
32 server.execute( "uadd sdl" );
33 server.execute( "play u0" );
34 server.wait_for_shutdown( );
38 cerr << "Failed to start server" << endl;
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.
56 This document focusses on the following areas of customisation:
58 * the Miracle server class
59 * extending the command set
62 * handling pushed westley documents
66 THE MIRACLE SERVER CLASS
68 The full public interface of the server is as follows:
70 class Miracle : public Properties
73 Miracle( char *name, int port = 5290, char *config = NULL );
75 mlt_properties get_properties( );
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 );
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:
101 #include <MltMiracle.h>
102 #include <MltResponse.h>
109 Custom( char *name = "Custom", int port = 5290, char *config = NULL ) :
110 Miracle( name, port, config )
114 Response *execute( char *command )
116 cerr << "command = " << command << endl;
117 return Miracle::execute( command );
121 int main( int argc, char **argv )
123 Custom server( "miracle++", 5290 );
124 if ( server.start( ) )
126 server.execute( "uadd sdl" );
127 server.execute( "play u0" );
128 server.wait_for_shutdown( );
132 cerr << "Failed to start server" << endl;
137 All this does is output each command and pass control over to the original
140 When you execute this, you will see the following output:
142 (5) Starting server on 5290.
144 (5) miracle++ version 0.0.1 listening on port 5290
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.
158 As a simple example we can replace our execute method above with the following:
160 Response *execute( char *command )
162 if ( !strcmp( command, "debug" ) )
165 while( unit( i ) != NULL )
166 unit( i ++ )->debug( );
167 return new Response( 200, "Diagnostics output" );
169 return Miracle::execute( command );
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", NULL ) ) );
185 Consumer consumer( ( mlt_consumer )( unit( i )->get_data( "consumer", NULL ) ) );
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
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
204 Response *execute( char *command )
206 Response *response = NULL;
207 if ( !strcmp( command, "debug" ) )
209 response = new Response( 200, "Diagnostics output" );
210 for( int i = 0; unit( i ) != NULL; i ++ )
212 Properties *properties = unit( i );
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( ) );
220 return response == NULL ? Miracle::execute( command ) : response;
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
228 Connected to localhost (127.0.0.1).
229 Escape character is '^]'.
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).
250 HANDLING PUSHED DOCUMENTS
252 The custom class receives PUSH'd westley either via the received or push
255 The default handling is to simply append a pushed document on to the end of
258 You can test this in the server defined above from the command line, for
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 )
270 Playlist playlist( ( mlt_playlist )( unit( 0 )->get_data( "playlist", size ) ) );
271 Producer producer( *service );
272 if ( producer.is_valid( ) && playlist.is_valid( ) )
276 playlist.append( producer );
278 return new Response( 200, "OK" );
280 return new Response( 400, "Invalid" );
283 With this method, each service pushed into the server will automatically
284 replace whatever is currently playing.
286 Note that the 'received' method is not invoked by default - if you wish to
287 receive the XML document and carry out any additional processing prior to
288 processing, you should set the 'push-parser-off' property on the server to 1.
289 This can be done by placing the following line in your classes constructor:
291 set( "push-parser-off", 1 );
293 When this property is set, the received method is used instead of the push -
294 in this scenario, your implementation is responsible for all handling
297 To simulate this, you can try the following method:
299 Response *received( char *command, char *document )
302 Producer producer( "westley-xml", document );
303 return push( command, &producer );
306 When you push your videos in to the server via the inigo command above (or
307 from other tools, such as those in the shotcut suite), you will see the xml
308 in the servers stderr output. If you need to carry out some operations on the
309 xml document (such as replacing low quality videos used in the editing process
310 with their original) the received mechanism is the one that you would want to
316 What you do with the received MLT Service is largely up to you. As shown above,
317 you have flexibility in how the item is scheduled and you can carry out
318 manipulations on either the xml document and/or the deserialised producer.
320 Typically, shotcut and inigo produce 'tractor' objects - these can be easily
321 manipulated in the push method - for example, to remove a track from the
322 output, we could do something like:
329 The MLT framework generates events which your custom server can use to do
330 various runtime manipulations. For the purpose of this document, I'll focus
331 on 'consumer-frame-render' - this event is fired immediately before a frame
339 In some cases, it is desirable to fully disable the entire DVCP command set
340 and handle the PUSH in an application specific way (for example, the shotcut
341 applications all do this). The simplest way of doing this is to generate a
342 response that signifies the rejection of the command. In this example, the
343 'shutdown' command is also handled:
345 Response *execute( char *command )
347 if ( !strcmp( command, "shutdown" ) )
349 return new Response( 400, "Invalid Command" );
352 If you use this method in the code above, your server does nothing - no units
353 are defined, so even a PUSH will be rejected.