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