2 * melted_local.c -- Local Melted Parser
3 * Copyright (C) 2002-2009 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
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.
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.
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.
25 /* System header files */
30 /* Needed for backtrace on linux */
35 /* mvcp header files */
36 #include <mvcp/mvcp_util.h>
38 /* MLT header files. */
39 #include <framework/mlt_factory.h>
41 /* Application header files */
42 #include "melted_local.h"
43 #include "melted_connection.h"
44 #include "melted_commands.h"
45 #include "melted_unit_commands.h"
46 #include "melted_log.h"
48 /** Private melted_local structure.
56 *melted_local
, melted_local_t
;
58 /** Forward declarations.
61 static mvcp_response
melted_local_connect( melted_local
);
62 static mvcp_response
melted_local_execute( melted_local
, char * );
63 static mvcp_response
melted_local_push( melted_local
, char *, mlt_service
);
64 static mvcp_response
melted_local_receive( melted_local
, char *, char * );
65 static void melted_local_close( melted_local
);
66 response_codes
melted_help( command_argument arg
);
67 response_codes
melted_run( command_argument arg
);
68 response_codes
melted_shutdown( command_argument arg
);
70 /** MVCP Parser constructor.
73 mvcp_parser
melted_parser_init_local( )
75 mvcp_parser parser
= malloc( sizeof( mvcp_parser_t
) );
76 melted_local local
= malloc( sizeof( melted_local_t
) );
80 memset( parser
, 0, sizeof( mvcp_parser_t
) );
82 parser
->connect
= (parser_connect
)melted_local_connect
;
83 parser
->execute
= (parser_execute
)melted_local_execute
;
84 parser
->push
= (parser_push
)melted_local_push
;
85 parser
->received
= (parser_received
)melted_local_receive
;
86 parser
->close
= (parser_close
)melted_local_close
;
91 memset( local
, 0, sizeof( melted_local_t
) );
92 local
->parser
= parser
;
93 local
->root_dir
[0] = '/';
96 // Construct the factory
97 mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
102 /** response status code/message pair
112 /** response messages
115 static responses_t responses
[] =
117 {RESPONSE_SUCCESS
, "OK"},
118 {RESPONSE_SUCCESS_N
, "OK"},
119 {RESPONSE_SUCCESS_1
, "OK"},
120 {RESPONSE_UNKNOWN_COMMAND
, "Unknown command"},
121 {RESPONSE_TIMEOUT
, "Operation timed out"},
122 {RESPONSE_MISSING_ARG
, "Argument missing"},
123 {RESPONSE_INVALID_UNIT
, "Unit not found"},
124 {RESPONSE_BAD_FILE
, "Failed to locate or open clip"},
125 {RESPONSE_OUT_OF_RANGE
, "Argument value out of range"},
126 {RESPONSE_TOO_MANY_FILES
, "Too many files open"},
127 {RESPONSE_ERROR
, "Server Error"}
143 /** A command definition.
148 /* The command string corresponding to this operation (e.g. "play") */
150 /* The function associated with it */
151 response_codes (*operation
) ( command_argument
);
152 /* a boolean to indicate if this is a unit or global command
153 unit commands require a unit identifier as first argument */
155 /* What type is the argument (RTTI :-) ATYPE_whatever */
157 /* online help information */
162 /* The following define the queue of commands available to the user. The
163 first entry is the name of the command (the string which must be typed),
164 the second command is the function associated with it, the third argument
165 is for the type of the argument, and the last argument specifies whether
166 this is something which should be handled immediately or whether it
167 should be queued (only robot motion commands need to be queued). */
169 static command_t vocabulary
[] =
171 {"BYE", NULL
, 0, ATYPE_NONE
, "Terminates the session. Units are not removed and task queue is not flushed."},
172 {"HELP", melted_help
, 0, ATYPE_NONE
, "Display this information!"},
173 {"NLS", melted_list_nodes
, 0, ATYPE_NONE
, "List the AV/C nodes on the 1394 bus."},
174 {"UADD", melted_add_unit
, 0, ATYPE_STRING
, "Create a new playout unit (virtual VTR) to transmit to receiver specified in GUID argument."},
175 {"ULS", melted_list_units
, 0, ATYPE_NONE
, "Lists the units that have already been added to the server."},
176 {"CLS", melted_list_clips
, 0, ATYPE_STRING
, "Lists the clips at directory name argument."},
177 {"SET", melted_set_global_property
, 0, ATYPE_PAIR
, "Set a server configuration property."},
178 {"GET", melted_get_global_property
, 0, ATYPE_STRING
, "Get a server configuration property."},
179 {"RUN", melted_run
, 0, ATYPE_STRING
, "Run a batch file." },
180 {"LIST", melted_list
, 1, ATYPE_NONE
, "List the playlist associated to a unit."},
181 {"LOAD", melted_load
, 1, ATYPE_STRING
, "Load clip specified in absolute filename argument."},
182 {"INSERT", melted_insert
, 1, ATYPE_STRING
, "Insert a clip at the given clip index."},
183 {"REMOVE", melted_remove
, 1, ATYPE_NONE
, "Remove a clip at the given clip index."},
184 {"CLEAN", melted_clean
, 1, ATYPE_NONE
, "Clean a unit by removing all but the currently playing clip."},
185 {"WIPE", melted_wipe
, 1, ATYPE_NONE
, "Clean a unit by removing everything before the currently playing clip."},
186 {"CLEAR", melted_clear
, 1, ATYPE_NONE
, "Clear a unit by removing all clips."},
187 {"MOVE", melted_move
, 1, ATYPE_INT
, "Move a clip to another clip index."},
188 {"APND", melted_append
, 1, ATYPE_STRING
, "Append a clip specified in absolute filename argument."},
189 {"PLAY", melted_play
, 1, ATYPE_NONE
, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."},
190 {"STOP", melted_stop
, 1, ATYPE_NONE
, "Stop a loaded and playing clip."},
191 {"PAUSE", melted_pause
, 1, ATYPE_NONE
, "Pause a playing clip."},
192 {"REW", melted_rewind
, 1, ATYPE_NONE
, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."},
193 {"FF", melted_ff
, 1, ATYPE_NONE
, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."},
194 {"STEP", melted_step
, 1, ATYPE_INT
, "Step argument number of frames forward or backward."},
195 {"GOTO", melted_goto
, 1, ATYPE_INT
, "Jump to frame number supplied as argument."},
196 {"SIN", melted_set_in_point
, 1, ATYPE_INT
, "Set the IN point of the loaded clip to frame number argument. -1 = reset in point to 0"},
197 {"SOUT", melted_set_out_point
, 1, ATYPE_INT
, "Set the OUT point of the loaded clip to frame number argument. -1 = reset out point to maximum."},
198 {"USTA", melted_get_unit_status
, 1, ATYPE_NONE
, "Report information about the unit."},
199 {"USET", melted_set_unit_property
, 1, ATYPE_PAIR
, "Set a unit configuration property."},
200 {"UGET", melted_get_unit_property
, 1, ATYPE_STRING
, "Get a unit configuration property."},
201 {"XFER", melted_transfer
, 1, ATYPE_STRING
, "Transfer the unit's clip to another unit specified as argument."},
202 {"SHUTDOWN", melted_shutdown
, 0, ATYPE_NONE
, "Shutdown the server."},
203 {NULL
, NULL
, 0, ATYPE_NONE
, NULL
}
209 static char helpstr
[] =
210 "melted -- A Multimedia Playout Server\n"
211 " Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n"
213 " Dan Dennedy <dan@dennedy.org>\n"
214 " Charles Yates <charles.yates@pandora.be>\n"
215 "Available commands:\n";
217 /** Lookup the response message for a status code.
220 inline const char *get_response_msg( int code
)
223 for ( i
= 0; responses
[ i
].message
!= NULL
&& code
!= responses
[ i
].code
; i
++ ) ;
224 return responses
[ i
].message
;
227 /** Tell the user the melted command set
230 response_codes
melted_help( command_argument cmd_arg
)
234 mvcp_response_printf( cmd_arg
->response
, 10240, "%s", helpstr
);
236 for ( i
= 0; vocabulary
[ i
].command
!= NULL
; i
++ )
237 mvcp_response_printf( cmd_arg
->response
, 1024,
239 vocabulary
[ i
].command
,
240 vocabulary
[ i
].help
);
242 mvcp_response_printf( cmd_arg
->response
, 2, "\n" );
244 return RESPONSE_SUCCESS_N
;
247 /** Execute a batch file.
250 response_codes
melted_run( command_argument cmd_arg
)
252 mvcp_response temp
= mvcp_parser_run( cmd_arg
->parser
, (char *)cmd_arg
->argument
);
258 mvcp_response_set_error( cmd_arg
->response
,
259 mvcp_response_get_error_code( temp
),
260 mvcp_response_get_error_string( temp
) );
262 for ( index
= 1; index
< mvcp_response_count( temp
); index
++ )
263 mvcp_response_printf( cmd_arg
->response
, 10240, "%s\n", mvcp_response_get_line( temp
, index
) );
265 mvcp_response_close( temp
);
268 return mvcp_response_get_error_code( cmd_arg
->response
);
271 response_codes
melted_shutdown( command_argument cmd_arg
)
274 return RESPONSE_SUCCESS
;
277 /** Processes 'thread' id
280 static pthread_t self
;
282 /* Signal handler to deal with various shutdown signals. Basically this
283 should clean up and power down the motor. Note that the death of any
284 child thread will kill all thrads. */
286 void signal_handler( int sig
)
288 if ( pthread_equal( self
, pthread_self( ) ) )
292 melted_log( LOG_DEBUG
, "Received %s - shutting down.", strsignal(sig
) );
294 melted_log( LOG_DEBUG
, "Received signal %i - shutting down.", sig
);
301 static void sigsegv_handler()
309 melted_log( LOG_CRIT
, "\a\nmelted experienced a segmentation fault.\n"
310 "Dumping stack from the offending thread\n\n" );
311 size
= backtrace( array
, 10 );
312 strings
= backtrace_symbols( array
, size
);
314 melted_log( LOG_CRIT
, "Obtained %zd stack frames.\n", size
);
316 for ( i
= 0; i
< size
; i
++ )
317 melted_log( LOG_CRIT
, "%s", strings
[ i
] );
321 melted_log( LOG_CRIT
, "\nDone dumping - exiting.\n" );
323 melted_log( LOG_CRIT
, "\a\nmelted experienced a segmentation fault.\n" );
325 exit( EXIT_FAILURE
);
330 /** Local 'connect' function.
333 static mvcp_response
melted_local_connect( melted_local local
)
335 mvcp_response response
= mvcp_response_init( );
337 self
= pthread_self( );
339 mvcp_response_set_error( response
, 100, "VTR Ready" );
341 signal( SIGHUP
, signal_handler
);
342 signal( SIGINT
, signal_handler
);
343 signal( SIGTERM
, SIG_DFL
);
344 signal( SIGSTOP
, signal_handler
);
345 signal( SIGPIPE
, signal_handler
);
346 signal( SIGALRM
, signal_handler
);
347 signal( SIGCHLD
, SIG_IGN
);
348 if ( getenv( "MLT_SIGSEGV" ) )
349 signal( SIGSEGV
, sigsegv_handler
);
354 /** Set the error and determine the message associated to this command.
357 void melted_command_set_error( command_argument cmd
, response_codes code
)
359 mvcp_response_set_error( cmd
->response
, code
, get_response_msg( code
) );
362 /** Parse the unit argument.
365 int melted_command_parse_unit( command_argument cmd
, int argument
)
368 char *string
= mvcp_tokeniser_get_string( cmd
->tokeniser
, argument
);
369 if ( string
!= NULL
&& ( string
[ 0 ] == 'U' || string
[ 0 ] == 'u' ) && strlen( string
) > 1 )
370 unit
= atoi( string
+ 1 );
374 /** Parse a normal argument.
377 void *melted_command_parse_argument( command_argument cmd
, int argument
, arguments_types type
, char *command
)
380 char *value
= mvcp_tokeniser_get_string( cmd
->tokeniser
, argument
);
390 ret
= malloc( sizeof( float ) );
392 *( float * )ret
= atof( value
);
396 ret
= strdup( value
);
400 if ( strchr( command
, '=' ) )
402 char *ptr
= strchr( command
, '=' );
403 while ( *( ptr
- 1 ) != ' ' )
407 while( ptr
[ strlen( ptr
) - 1 ] == ' ' )
408 ptr
[ strlen( ptr
) - 1 ] = '\0';
413 ret
= malloc( sizeof( int ) );
415 *( int * )ret
= atoi( value
);
423 /** Get the error code - note that we simply the success return.
426 response_codes
melted_command_get_error( command_argument cmd
)
428 response_codes ret
= mvcp_response_get_error_code( cmd
->response
);
429 if ( ret
== RESPONSE_SUCCESS_N
|| ret
== RESPONSE_SUCCESS_1
)
430 ret
= RESPONSE_SUCCESS
;
434 /** Execute the command.
437 static mvcp_response
melted_local_execute( melted_local local
, char *command
)
439 command_argument_t cmd
;
440 cmd
.parser
= local
->parser
;
441 cmd
.response
= mvcp_response_init( );
442 cmd
.tokeniser
= mvcp_tokeniser_init( );
443 cmd
.command
= command
;
446 cmd
.root_dir
= local
->root_dir
;
448 /* Set the default error */
449 melted_command_set_error( &cmd
, RESPONSE_UNKNOWN_COMMAND
);
451 /* Parse the command */
452 if ( mvcp_tokeniser_parse_new( cmd
.tokeniser
, command
, " " ) > 0 )
455 char *value
= mvcp_tokeniser_get_string( cmd
.tokeniser
, 0 );
458 /* Strip quotes from all tokens */
459 for ( index
= 0; index
< mvcp_tokeniser_count( cmd
.tokeniser
); index
++ )
460 mvcp_util_strip( mvcp_tokeniser_get_string( cmd
.tokeniser
, index
), '\"' );
462 /* Search the vocabulary array for value */
463 for ( index
= 1; !found
&& vocabulary
[ index
].command
!= NULL
; index
++ )
464 if ( ( found
= !strcasecmp( vocabulary
[ index
].command
, value
) ) )
467 /* If we found something, the handle the args and call the handler. */
472 melted_command_set_error( &cmd
, RESPONSE_SUCCESS
);
474 if ( vocabulary
[ index
].is_unit
)
476 cmd
.unit
= melted_command_parse_unit( &cmd
, position
);
477 if ( cmd
.unit
== -1 )
478 melted_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
482 if ( melted_command_get_error( &cmd
) == RESPONSE_SUCCESS
)
484 cmd
.argument
= melted_command_parse_argument( &cmd
, position
, vocabulary
[ index
].type
, command
);
485 if ( cmd
.argument
== NULL
&& vocabulary
[ index
].type
!= ATYPE_NONE
)
486 melted_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
490 if ( melted_command_get_error( &cmd
) == RESPONSE_SUCCESS
)
492 response_codes error
= vocabulary
[ index
].operation( &cmd
);
493 melted_command_set_error( &cmd
, error
);
496 free( cmd
.argument
);
500 mvcp_tokeniser_close( cmd
.tokeniser
);
505 static mvcp_response
melted_local_receive( melted_local local
, char *command
, char *doc
)
507 command_argument_t cmd
;
508 cmd
.parser
= local
->parser
;
509 cmd
.response
= mvcp_response_init( );
510 cmd
.tokeniser
= mvcp_tokeniser_init( );
511 cmd
.command
= command
;
514 cmd
.root_dir
= local
->root_dir
;
516 /* Set the default error */
517 melted_command_set_error( &cmd
, RESPONSE_SUCCESS
);
519 /* Parse the command */
520 if ( mvcp_tokeniser_parse_new( cmd
.tokeniser
, command
, " " ) > 0 )
525 /* Strip quotes from all tokens */
526 for ( index
= 0; index
< mvcp_tokeniser_count( cmd
.tokeniser
); index
++ )
527 mvcp_util_strip( mvcp_tokeniser_get_string( cmd
.tokeniser
, index
), '\"' );
529 cmd
.unit
= melted_command_parse_unit( &cmd
, position
);
530 if ( cmd
.unit
== -1 )
531 melted_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
534 melted_receive( &cmd
, doc
);
535 melted_command_set_error( &cmd
, RESPONSE_SUCCESS
);
537 free( cmd
.argument
);
540 mvcp_tokeniser_close( cmd
.tokeniser
);
545 static mvcp_response
melted_local_push( melted_local local
, char *command
, mlt_service service
)
547 command_argument_t cmd
;
548 cmd
.parser
= local
->parser
;
549 cmd
.response
= mvcp_response_init( );
550 cmd
.tokeniser
= mvcp_tokeniser_init( );
551 cmd
.command
= command
;
554 cmd
.root_dir
= local
->root_dir
;
556 /* Set the default error */
557 melted_command_set_error( &cmd
, RESPONSE_SUCCESS
);
559 /* Parse the command */
560 if ( mvcp_tokeniser_parse_new( cmd
.tokeniser
, command
, " " ) > 0 )
565 /* Strip quotes from all tokens */
566 for ( index
= 0; index
< mvcp_tokeniser_count( cmd
.tokeniser
); index
++ )
567 mvcp_util_strip( mvcp_tokeniser_get_string( cmd
.tokeniser
, index
), '\"' );
569 cmd
.unit
= melted_command_parse_unit( &cmd
, position
);
570 if ( cmd
.unit
== -1 )
571 melted_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
574 melted_push( &cmd
, service
);
575 melted_command_set_error( &cmd
, RESPONSE_SUCCESS
);
577 free( cmd
.argument
);
580 mvcp_tokeniser_close( cmd
.tokeniser
);
585 /** Close the parser.
588 static void melted_local_close( melted_local local
)
590 melted_delete_all_units();
592 //pthread_kill_other_threads_np();
593 melted_log( LOG_DEBUG
, "Clean shutdown." );
595 //mlt_factory_close( );