]> git.sesse.net Git - mlt/blob - mlt/src/miracle/miracle_local.c
Initial revision
[mlt] / mlt / src / miracle / miracle_local.c
1 /*
2  * dvlocal.c -- Local dv1394d Parser
3  * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 /* System header files */
26 #include <stdlib.h>
27 #include <string.h>
28 #include <signal.h>
29
30 /* Library header files */
31 #include <dvutil.h>
32
33 /* Application header files */
34 #include <dvclipfactory.h>
35 #include <dvframepool.h>
36 #include "dvlocal.h"
37 #include "dvconnection.h"
38 #include "global_commands.h"
39 #include "unit_commands.h"
40 #include "log.h"
41 #include "raw1394util.h"
42
43 /** Private dv_local structure.
44 */
45
46 typedef struct
47 {
48         dv_parser parser;
49         char root_dir[1024];
50 }
51 *dv_local, dv_local_t;
52
53 /** Forward declarations.
54 */
55
56 static dv_response dv_local_connect( dv_local );
57 static dv_response dv_local_execute( dv_local, char * );
58 static void dv_local_close( dv_local );
59 response_codes print_help( command_argument arg );
60 response_codes dv1394d_run( command_argument arg );
61 response_codes dv1394d_shutdown( command_argument arg );
62
63 /** DV Parser constructor.
64 */
65
66 dv_parser dv_parser_init_local( )
67 {
68         dv_parser parser = malloc( sizeof( dv_parser_t ) );
69         dv_local local = malloc( sizeof( dv_local_t ) );
70
71         if ( parser != NULL )
72         {
73                 memset( parser, 0, sizeof( dv_parser_t ) );
74
75                 parser->connect = (parser_connect)dv_local_connect;
76                 parser->execute = (parser_execute)dv_local_execute;
77                 parser->close = (parser_close)dv_local_close;
78                 parser->real = local;
79
80                 if ( local != NULL )
81                 {
82                         memset( local, 0, sizeof( dv_local_t ) );
83                         local->parser = parser;
84                         local->root_dir[0] = '/';
85                 }
86         }
87         return parser;
88 }
89
90 /** response status code/message pair 
91 */
92
93 typedef struct 
94 {
95         int code;
96         char *message;
97
98 responses_t;
99
100 /** response messages 
101 */
102
103 static responses_t responses [] = 
104 {
105         {RESPONSE_SUCCESS, "OK"},
106         {RESPONSE_SUCCESS_N, "OK"},
107         {RESPONSE_SUCCESS_1, "OK"},
108         {RESPONSE_UNKNOWN_COMMAND, "Unknown command"},
109         {RESPONSE_TIMEOUT, "Operation timed out"},
110         {RESPONSE_MISSING_ARG, "Argument missing"},
111         {RESPONSE_INVALID_UNIT, "Unit not found"},
112         {RESPONSE_BAD_FILE, "Failed to locate or open clip"},
113         {RESPONSE_OUT_OF_RANGE, "Argument value out of range"},
114         {RESPONSE_TOO_MANY_FILES, "Too many files open"},
115         {RESPONSE_ERROR, "Server Error"}
116 };
117
118 /** Argument types.
119 */
120
121 typedef enum 
122 {
123         ATYPE_NONE,
124         ATYPE_FLOAT,
125         ATYPE_STRING,
126         ATYPE_INT
127
128 arguments_types;
129
130 /** A command definition.
131 */
132
133 typedef struct 
134 {
135 /* The command string corresponding to this operation (e.g. "play") */
136         char *command;
137 /* The function associated with it */
138         response_codes (*operation) ( command_argument );
139 /* a boolean to indicate if this is a unit or global command
140    unit commands require a unit identifier as first argument */
141         int is_unit;
142 /* What type is the argument (RTTI :-) ATYPE_whatever */
143         int type;
144 /* online help information */
145         char *help;
146
147 command_t;
148
149 /* The following define the queue of commands available to the user. The
150    first entry is the name of the command (the string which must be typed),
151    the second command is the function associated with it, the third argument
152    is for the type of the argument, and the last argument specifies whether
153    this is something which should be handled immediately or whether it
154    should be queued (only robot motion commands need to be queued). */
155
156 static command_t vocabulary[] = 
157 {
158         {"BYE", NULL, 0, ATYPE_NONE, "Terminates the session. Units are not removed and task queue is not flushed."},
159         {"HELP", print_help, 0, ATYPE_NONE, "Display this information!"},
160         {"NLS", dv1394d_list_nodes, 0, ATYPE_NONE, "List the AV/C nodes on the 1394 bus."},
161         {"UADD", dv1394d_add_unit, 0, ATYPE_STRING, "Create a new DV unit (virtual VTR) to transmit to receiver specified in GUID argument."},
162         {"ULS", dv1394d_list_units, 0, ATYPE_NONE, "Lists the units that have already been added to the server."},
163         {"CLS", dv1394d_list_clips, 0, ATYPE_STRING, "Lists the clips at directory name argument."},
164         {"SET", dv1394d_set_global_property, 0, ATYPE_STRING, "Set a server configuration property."},
165         {"GET", dv1394d_get_global_property, 0, ATYPE_STRING, "Get a server configuration property."},
166         {"RUN", dv1394d_run, 0, ATYPE_STRING, "Run a batch file." },
167         {"LIST", dv1394d_list, 1, ATYPE_NONE, "List the playlist associated to a unit."},
168         {"LOAD", dv1394d_load, 1, ATYPE_STRING, "Load clip specified in absolute filename argument."},
169         {"INSERT", dv1394d_insert, 1, ATYPE_STRING, "Insert a clip at the given clip index."},
170         {"REMOVE", dv1394d_remove, 1, ATYPE_NONE, "Remove a clip at the given clip index."},
171         {"CLEAN", dv1394d_clean, 1, ATYPE_NONE, "Clean a unit by removing all but the currently playing clip."},
172         {"MOVE", dv1394d_move, 1, ATYPE_INT, "Move a clip to another clip index."},
173         {"APND", dv1394d_append, 1, ATYPE_STRING, "Append a clip specified in absolute filename argument."},
174         {"PLAY", dv1394d_play, 1, ATYPE_NONE, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."},
175         {"STOP", dv1394d_stop, 1, ATYPE_NONE, "Stop a loaded and playing clip."},
176         {"PAUSE", dv1394d_pause, 1, ATYPE_NONE, "Pause a playing clip."},
177         {"REW", dv1394d_rewind, 1, ATYPE_NONE, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."},
178         {"FF", dv1394d_ff, 1, ATYPE_NONE, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."},
179         {"STEP", dv1394d_step, 1, ATYPE_INT, "Step argument number of frames forward or backward."},
180         {"GOTO", dv1394d_goto, 1, ATYPE_INT, "Jump to frame number supplied as argument."},
181         {"SIN", dv1394d_set_in_point, 1, ATYPE_INT, "Set the IN point of the loaded clip to frame number argument. -1 = reset in point to 0"},
182         {"SOUT", dv1394d_set_out_point, 1, ATYPE_INT, "Set the OUT point of the loaded clip to frame number argument. -1 = reset out point to maximum."},
183         {"USTA", dv1394d_get_unit_status, 1, ATYPE_NONE, "Report information about the unit."},
184         {"USET", dv1394d_set_unit_property, 1, ATYPE_STRING, "Set a unit configuration property."},
185         {"UGET", dv1394d_get_unit_property, 1, ATYPE_STRING, "Get a unit configuration property."},
186         {"XFER", dv1394d_transfer, 1, ATYPE_STRING, "Transfer the unit's clip to another unit specified as argument."},
187         {"SHUTDOWN", dv1394d_shutdown, 0, ATYPE_NONE, "Shutdown the server."},
188         {NULL, NULL, 0, ATYPE_NONE, NULL}
189 };
190
191 /** Usage message 
192 */
193
194 static char helpstr [] = 
195         "dv1394d -- A DV over IEEE 1394 TCP Server\n" 
196         "       Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n"
197         "       Authors:\n"
198         "               Dan Dennedy <dan@dennedy.org>\n"
199         "               Charles Yates <charles.yates@pandora.be>\n"
200         "Available commands:\n";
201
202 /** Lookup the response message for a status code.
203 */
204
205 inline char *get_response_msg( int code )
206 {
207         int i = 0;
208         for ( i = 0; responses[ i ].message != NULL && code != responses[ i ].code; i ++ ) ;
209         return responses[ i ].message;
210 }
211
212 /** Tell the user the dv1394d command set
213 */
214
215 response_codes print_help( command_argument cmd_arg )
216 {
217         int i = 0;
218         
219         dv_response_printf( cmd_arg->response, 10240, "%s", helpstr );
220         
221         for ( i = 0; vocabulary[ i ].command != NULL; i ++ )
222                 dv_response_printf( cmd_arg->response, 1024,
223                                                         "%-10.10s%s\n", 
224                                                         vocabulary[ i ].command, 
225                                                         vocabulary[ i ].help );
226
227         dv_response_printf( cmd_arg->response, 2, "\n" );
228
229         return RESPONSE_SUCCESS_N;
230 }
231
232 /** Execute a batch file.
233 */
234
235 response_codes dv1394d_run( command_argument cmd_arg )
236 {
237         dv_response temp = dv_parser_run( cmd_arg->parser, (char *)cmd_arg->argument );
238
239         if ( temp != NULL )
240         {
241                 int index = 0;
242
243                 dv_response_set_error( cmd_arg->response, 
244                                                            dv_response_get_error_code( temp ),
245                                                            dv_response_get_error_string( temp ) );
246
247                 for ( index = 1; index < dv_response_count( temp ); index ++ )
248                         dv_response_printf( cmd_arg->response, 10240, "%s\n", dv_response_get_line( temp, index ) );
249
250                 dv_response_close( temp );
251         }
252
253         return dv_response_get_error_code( cmd_arg->response );
254 }
255
256 response_codes dv1394d_shutdown( command_argument cmd_arg )
257 {
258         exit( 0 );
259         return RESPONSE_SUCCESS;
260 }
261
262 /** Processes 'thread' id
263 */
264
265 static pthread_t self;
266
267 /* Signal handler to deal with various shutdown signals. Basically this
268    should clean up and power down the motor. Note that the death of any
269    child thread will kill all thrads. */
270
271 void signal_handler( int sig )
272 {
273         if ( pthread_equal( self, pthread_self( ) ) )
274         {
275
276 #ifdef _GNU_SOURCE
277                 dv1394d_log( LOG_DEBUG, "Received %s - shutting down.", strsignal(sig) );
278 #else
279                 dv1394d_log( LOG_DEBUG, "Received signal %i - shutting down.", sig );
280 #endif
281
282                 exit(EXIT_SUCCESS);
283         }
284 }
285
286 /** Local 'connect' function.
287 */
288
289 static dv_response dv_local_connect( dv_local local )
290 {
291         dv_response response = dv_response_init( );
292
293         self = pthread_self( );
294
295         dv_response_set_error( response, 100, "VTR Ready" );
296
297         signal( SIGHUP, signal_handler );
298         signal( SIGINT, signal_handler );
299         signal( SIGTERM, signal_handler );
300         signal( SIGSTOP, signal_handler );
301         signal( SIGCHLD, SIG_IGN );
302         
303         raw1394_reconcile_bus();
304         /* Start the raw1394 service threads for handling bus resets */
305         raw1394_start_service_threads();
306
307         return response;
308 }
309
310 /** Set the error and determine the message associated to this command.
311 */
312
313 void dv_command_set_error( command_argument cmd, response_codes code )
314 {
315         dv_response_set_error( cmd->response, code, get_response_msg( code ) );
316 }
317
318 /** Parse the unit argument.
319 */
320
321 int dv_command_parse_unit( command_argument cmd, int argument )
322 {
323         int unit = -1;
324         char *string = dv_tokeniser_get_string( cmd->tokeniser, argument );
325         if ( string != NULL && ( string[ 0 ] == 'U' || string[ 0 ] == 'u' ) && strlen( string ) > 1 )
326                 unit = atoi( string + 1 );
327         return unit;
328 }
329
330 /** Parse a normal argument.
331 */
332
333 void *dv_command_parse_argument( command_argument cmd, int argument, arguments_types type )
334 {
335         void *ret = NULL;
336         char *value = dv_tokeniser_get_string( cmd->tokeniser, argument );
337
338         if ( value != NULL )
339         {
340                 switch( type )
341                 {
342                         case ATYPE_NONE:
343                                 break;
344
345                         case ATYPE_FLOAT:
346                                 ret = malloc( sizeof( float ) );
347                                 if ( ret != NULL )
348                                         *( float * )ret = atof( value );
349                                 break;
350
351                         case ATYPE_STRING:
352                                 ret = strdup( value );
353                                 break;
354                                         
355                         case ATYPE_INT:
356                                 ret = malloc( sizeof( int ) );
357                                 if ( ret != NULL )
358                                         *( int * )ret = atoi( value );
359                                 break;
360                 }
361         }
362
363         return ret;
364 }
365
366 /** Get the error code - note that we simply the success return.
367 */
368
369 response_codes dv_command_get_error( command_argument cmd )
370 {
371         response_codes ret = dv_response_get_error_code( cmd->response );
372         if ( ret == RESPONSE_SUCCESS_N || ret == RESPONSE_SUCCESS_1 )
373                 ret = RESPONSE_SUCCESS;
374         return ret;
375 }
376
377 /** Execute the command.
378 */
379
380 static dv_response dv_local_execute( dv_local local, char *command )
381 {
382         command_argument_t cmd;
383         cmd.parser = local->parser;
384         cmd.response = dv_response_init( );
385         cmd.tokeniser = dv_tokeniser_init( );
386         cmd.command = command;
387         cmd.unit = -1;
388         cmd.argument = NULL;
389         cmd.root_dir = local->root_dir;
390
391         /* Set the default error */
392         dv_command_set_error( &cmd, RESPONSE_UNKNOWN_COMMAND );
393
394         /* Parse the command */
395         if ( dv_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
396         {
397                 int index = 0;
398                 char *value = dv_tokeniser_get_string( cmd.tokeniser, 0 );
399                 int found = 0;
400
401                 /* Strip quotes from all tokens */
402                 for ( index = 0; index < dv_tokeniser_count( cmd.tokeniser ); index ++ )
403                         dv_util_strip( dv_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
404
405                 /* Search the vocabulary array for value */
406                 for ( index = 1; !found && vocabulary[ index ].command != NULL; index ++ )
407                         if ( ( found = !strcasecmp( vocabulary[ index ].command, value ) ) )
408                                 break;
409
410                 /* If we found something, the handle the args and call the handler. */
411                 if ( found )
412                 {
413                         int position = 1;
414
415                         dv_command_set_error( &cmd, RESPONSE_SUCCESS );
416
417                         if ( vocabulary[ index ].is_unit )
418                         {
419                                 cmd.unit = dv_command_parse_unit( &cmd, position );
420                                 if ( cmd.unit == -1 )
421                                         dv_command_set_error( &cmd, RESPONSE_MISSING_ARG );
422                                 position ++;
423                         }
424
425                         if ( dv_command_get_error( &cmd ) == RESPONSE_SUCCESS )
426                         {
427                                 cmd.argument = dv_command_parse_argument( &cmd, position, vocabulary[ index ].type );
428                                 if ( cmd.argument == NULL && vocabulary[ index ].type != ATYPE_NONE )
429                                         dv_command_set_error( &cmd, RESPONSE_MISSING_ARG );
430                                 position ++;
431                         }
432
433                         if ( dv_command_get_error( &cmd ) == RESPONSE_SUCCESS )
434                         {
435                                 response_codes error = vocabulary[ index ].operation( &cmd );
436                                 dv_command_set_error( &cmd, error );
437                         }
438
439                         free( cmd.argument );
440                 }
441         }
442
443         dv_tokeniser_close( cmd.tokeniser );
444
445         return cmd.response;
446 }
447
448 /** Close the parser.
449 */
450
451 static void dv_local_close( dv_local local )
452 {
453         raw1394_stop_service_threads();
454         dv1394d_delete_all_units();
455         pthread_kill_other_threads_np();
456         dv1394d_log( LOG_DEBUG, "Clean shutdown." );
457         free( local );
458         dv_clip_factory_close( );
459         dv_frame_pool_close( );
460 }