]> git.sesse.net Git - mlt/blob - src/melt/melt.c
Restore the terminal when melt exits abnormally
[mlt] / src / melt / melt.c
1 /*
2  * melt.c -- MLT command line utility
3  * Copyright (C) 2002-2013 Ushodaya Enterprises Limited
4  * Authors: Charles Yates <charles.yates@pandora.be>
5  *          Dan Dennedy <dan@dennedy.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #ifndef _GNU_SOURCE
23 #define _GNU_SOURCE
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sched.h>
29 #include <libgen.h>
30 #include <limits.h>
31 #include <unistd.h>
32 #include <signal.h>
33
34 #include <framework/mlt.h>
35
36 #if (defined(__DARWIN__) || defined(WIN32)) && !defined(MELT_NOSDL)
37 #include <SDL.h>
38 #endif
39
40 #include "io.h"
41
42 static mlt_producer melt = NULL;
43
44 static void stop_handler(int signum)
45 {
46         if ( melt )
47         {
48                 mlt_properties properties = MLT_PRODUCER_PROPERTIES( melt );
49                 mlt_properties_set_int( properties, "done", 1 );
50         }
51 }
52
53 static void abnormal_exit_handler(int signum)
54 {
55         // The process is going down hard. Restore the terminal first.
56         term_exit();
57         // Reset the default handler so the core gets dumped.
58         signal(signum, SIG_DFL);
59         kill(getpid(), signum);
60 }
61
62 static void transport_action( mlt_producer producer, char *value )
63 {
64         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
65         mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
66         mlt_consumer consumer = mlt_properties_get_data( properties, "transport_consumer", NULL );
67         mlt_properties jack = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES( consumer ), "jack_filter", NULL );
68         mlt_position position = producer? mlt_producer_position( producer ) : 0;
69
70         mlt_properties_set_int( properties, "stats_off", 1 );
71
72         if ( strlen( value ) == 1 )
73         {
74                 switch( value[ 0 ] )
75                 {
76                         case 'q':
77                         case 'Q':
78                                 mlt_properties_set_int( properties, "done", 1 );
79                                 mlt_events_fire( jack, "jack-stop", NULL );
80                                 break;
81                         case '0':
82                                 position = 0;
83                                 mlt_producer_set_speed( producer, 1 );
84                                 mlt_producer_seek( producer, position );
85                                 mlt_consumer_purge( consumer );
86                                 mlt_events_fire( jack, "jack-seek", &position, NULL );
87                                 break;
88                         case '1':
89                                 mlt_producer_set_speed( producer, -10 );
90                                 break;
91                         case '2':
92                                 mlt_producer_set_speed( producer, -5 );
93                                 break;
94                         case '3':
95                                 mlt_producer_set_speed( producer, -2 );
96                                 break;
97                         case '4':
98                                 mlt_producer_set_speed( producer, -1 );
99                                 break;
100                         case '5':
101                                 mlt_producer_set_speed( producer, 0 );
102                                 mlt_consumer_purge( consumer );
103                                 mlt_events_fire( jack, "jack-stop", NULL );
104                                 break;
105                         case '6':
106                         case ' ':
107                                 if ( !jack || mlt_producer_get_speed( producer ) != 0 )
108                                         mlt_producer_set_speed( producer, 1 );
109                                 mlt_consumer_purge( consumer );
110                                 mlt_events_fire( jack, "jack-start", NULL );
111                                 break;
112                         case '7':
113                                 mlt_producer_set_speed( producer, 2 );
114                                 break;
115                         case '8':
116                                 mlt_producer_set_speed( producer, 5 );
117                                 break;
118                         case '9':
119                                 mlt_producer_set_speed( producer, 10 );
120                                 break;
121                         case 'd':
122                                 if ( multitrack != NULL )
123                                 {
124                                         int i = 0;
125                                         mlt_position last = -1;
126                                         fprintf( stderr, "\n" );
127                                         for ( i = 0; 1; i ++ )
128                                         {
129                                                 position = mlt_multitrack_clip( multitrack, mlt_whence_relative_start, i );
130                                                 if ( position == last )
131                                                         break;
132                                                 last = position;
133                                                 fprintf( stderr, "%d: %d\n", i, (int)position );
134                                         }
135                                 }
136                                 break;
137
138                         case 'g':
139                                 if ( multitrack != NULL )
140                                 {
141                                         position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 );
142                                         mlt_producer_seek( producer, position );
143                                         mlt_consumer_purge( consumer );
144                                         mlt_events_fire( jack, "jack-seek", &position, NULL );
145                                 }
146                                 break;
147                         case 'H':
148                                 if ( producer != NULL )
149                                 {
150                                         position -= mlt_producer_get_fps( producer ) * 60;
151                                         mlt_consumer_purge( consumer );
152                                         mlt_producer_seek( producer, position );
153                                         mlt_events_fire( jack, "jack-seek", &position, NULL );
154                                 }
155                                 break;
156                         case 'h':
157                                 if ( producer != NULL )
158                                 {
159                                         position--;
160                                         mlt_producer_set_speed( producer, 0 );
161                                         mlt_consumer_purge( consumer );
162                                         mlt_producer_seek( producer, position );
163                                         mlt_events_fire( jack, "jack-stop", NULL );
164                                         mlt_events_fire( jack, "jack-seek", &position, NULL );
165                                 }
166                                 break;
167                         case 'j':
168                                 if ( multitrack != NULL )
169                                 {
170                                         position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 );
171                                         mlt_consumer_purge( consumer );
172                                         mlt_producer_seek( producer, position );
173                                         mlt_events_fire( jack, "jack-seek", &position, NULL );
174                                 }
175                                 break;
176                         case 'k':
177                                 if ( multitrack != NULL )
178                                 {
179                                         position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 );
180                                         mlt_consumer_purge( consumer );
181                                         mlt_producer_seek( producer, position );
182                                         mlt_events_fire( jack, "jack-seek", &position, NULL );
183                                 }
184                                 break;
185                         case 'l':
186                                 if ( producer != NULL )
187                                 {
188                                         position++;
189                                         mlt_consumer_purge( consumer );
190                                         if ( mlt_producer_get_speed( producer ) != 0 )
191                                         {
192                                                 mlt_producer_set_speed( producer, 0 );
193                                                 mlt_events_fire( jack, "jack-stop", NULL );
194                                         }
195                                         else
196                                         {
197                                                 mlt_producer_seek( producer, position );
198                                                 mlt_events_fire( jack, "jack-seek", &position, NULL );
199                                         }
200                                 }
201                                 break;
202                         case 'L':
203                                 if ( producer != NULL )
204                                 {
205                                         position += mlt_producer_get_fps( producer ) * 60;
206                                         mlt_consumer_purge( consumer );
207                                         mlt_producer_seek( producer, position );
208                                         mlt_events_fire( jack, "jack-seek", &position, NULL );
209                                 }
210                                 break;
211                 }
212
213                 mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 );
214         }
215
216         mlt_properties_set_int( properties, "stats_off", 0 );
217 }
218
219 static void on_jack_started( mlt_properties owner, mlt_consumer consumer, mlt_position *position )
220 {
221         mlt_producer producer = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "transport_producer", NULL );
222         if ( producer )
223         {
224                 if ( mlt_producer_get_speed( producer ) != 0 )
225                 {
226                         mlt_properties jack = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES( consumer ), "jack_filter", NULL );
227                         mlt_events_fire( jack, "jack-stop", NULL );
228                 }
229                 else
230                 {
231                         mlt_producer_set_speed( producer, 1 );
232                         mlt_consumer_purge( consumer );
233                         mlt_producer_seek( producer, *position );
234                         mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 );
235                 }
236         }
237 }
238
239 static void on_jack_stopped( mlt_properties owner, mlt_consumer consumer, mlt_position *position )
240 {
241         mlt_producer producer = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "transport_producer", NULL );
242         if ( producer )
243         {
244                 mlt_producer_set_speed( producer, 0 );
245                 mlt_consumer_purge( consumer );
246                 mlt_producer_seek( producer, *position );
247                 mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 );
248         }
249 }
250
251 static void setup_jack_transport( mlt_consumer consumer, mlt_profile profile )
252 {
253         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
254         mlt_filter jack = mlt_factory_filter( profile, "jackrack", NULL );
255         mlt_properties jack_properties = MLT_FILTER_PROPERTIES(jack);
256
257         mlt_service_attach( MLT_CONSUMER_SERVICE(consumer), jack );
258         mlt_properties_set_int( properties, "audio_off", 1 );
259         mlt_properties_set_data( properties, "jack_filter", jack, 0, (mlt_destructor) mlt_filter_close, NULL );
260 //      mlt_properties_set( jack_properties, "out_1", "system:playback_1" );
261 //      mlt_properties_set( jack_properties, "out_2", "system:playback_2" );
262         mlt_events_listen( jack_properties, consumer, "jack-started", (mlt_listener) on_jack_started );
263         mlt_events_listen( jack_properties, consumer, "jack-stopped", (mlt_listener) on_jack_stopped );
264 }
265
266 static mlt_consumer create_consumer( mlt_profile profile, char *id )
267 {
268         char *myid = id ? strdup( id ) : NULL;
269         char *arg = myid ? strchr( myid, ':' ) : NULL;
270         if ( arg != NULL )
271                 *arg ++ = '\0';
272         mlt_consumer consumer = mlt_factory_consumer( profile, myid, arg );
273         if ( consumer != NULL )
274         {
275                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
276                 mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL );
277         }
278         if ( myid )
279                 free( myid );
280         return consumer;
281 }
282
283 static void load_consumer( mlt_consumer *consumer, mlt_profile profile, int argc, char **argv )
284 {
285         int i;
286         int multi = 0;
287
288         for ( i = 1; i < argc; i ++ )
289                 multi += !strcmp( argv[ i ], "-consumer" );
290
291         if ( multi > 1 )
292         {
293                 // If there is more than one -consumer use the 'multi' consumer.
294                 int k = 0;
295                 char key[20];
296
297                 if ( *consumer )
298                         mlt_consumer_close( *consumer );
299                 *consumer = create_consumer( profile, "multi" );
300                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( *consumer );
301                 for ( i = 1; i < argc; i ++ )
302                 {
303                         if ( !strcmp( argv[ i ], "-consumer" ) && argv[ i + 1 ])
304                         {
305                                 // Create a properties object for each sub-consumer
306                                 mlt_properties new_props = mlt_properties_new();
307                                 snprintf( key, sizeof(key), "%d", k++ );
308                                 mlt_properties_set_data( properties, key, new_props, 0,
309                                         (mlt_destructor) mlt_properties_close, NULL );
310                                 if ( strchr( argv[i + 1], ':' ) )
311                                 {
312                                         char *temp = strdup( argv[++i] );
313                                         char *service = temp;
314                                         char *target = strchr( temp, ':' );
315                                         *target++ = 0;
316                                         mlt_properties_set( new_props, "mlt_service", service );
317                                         mlt_properties_set( new_props, "target", target );
318                                 }
319                                 else
320                                 {
321                                         mlt_properties_set( new_props, "mlt_service", argv[ ++i ] );
322                                 }
323                                 while ( argv[ i + 1 ] && strchr( argv[ i + 1 ], '=' ) )
324                                         mlt_properties_parse( new_props, argv[ ++ i ] );
325                         }
326                 }
327         }
328         else for ( i = 1; i < argc; i ++ )
329         {
330                 if ( !strcmp( argv[ i ], "-consumer" ) )
331                 {
332                         if ( *consumer )
333                                 mlt_consumer_close( *consumer );
334                         *consumer = create_consumer( profile, argv[ ++ i ] );
335                         if ( *consumer )
336                         {
337                                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( *consumer );
338                                 while ( argv[ i + 1 ] != NULL && strchr( argv[ i + 1 ], '=' ) )
339                                         mlt_properties_parse( properties, argv[ ++ i ] );
340                         }
341                 }
342         }
343 }
344
345 #if (defined(__DARWIN__) || defined(WIN32)) && !defined(MELT_NOSDL)
346
347 static void event_handling( mlt_producer producer, mlt_consumer consumer )
348 {
349         SDL_Event event;
350
351         while ( SDL_PollEvent( &event ) )
352         {
353                 switch( event.type )
354                 {
355                         case SDL_QUIT:
356                                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( consumer ), "done", 1 );
357                                 break;
358
359                         case SDL_KEYDOWN:
360                                 if ( event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
361                                 {
362                                         char keyboard[ 2 ] = { event.key.keysym.unicode, 0 };
363                                         transport_action( producer, keyboard );
364                                 }
365                                 break;
366                 }
367         }
368 }
369
370 #endif
371
372 static void transport( mlt_producer producer, mlt_consumer consumer )
373 {
374         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
375         int silent = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "silent" );
376         int progress = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "progress" );
377         struct timespec tm = { 0, 40000000 };
378         int total_length = mlt_producer_get_length( producer );
379         int last_position = 0;
380
381         if ( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
382         {
383                 if ( !silent && !progress )
384                 {
385                         term_init( );
386
387                         fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
388                         fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5=  0| |6=  1| |7=  2| |8=  5| |9= 10|\n" );
389                         fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
390
391                         fprintf( stderr, "+---------------------------------------------------------------------+\n" );
392                         fprintf( stderr, "|               H = back 1 minute,  L = forward 1 minute              |\n" );
393                         fprintf( stderr, "|                 h = previous frame,  l = next frame                 |\n" );
394                         fprintf( stderr, "|           g = start of clip, j = next clip, k = previous clip       |\n" );
395                         fprintf( stderr, "|                0 = restart, q = quit, space = play                  |\n" );
396                         fprintf( stderr, "+---------------------------------------------------------------------+\n" );
397                 }
398
399                 while( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
400                 {
401                         int value = ( silent || progress )? -1 : term_read( );
402
403                         if ( value != -1 )
404                         {
405                                 char string[ 2 ] = { value, 0 };
406                                 transport_action( producer, string );
407                         }
408
409 #if (defined(__DARWIN__) || defined(WIN32)) && !defined(MELT_NOSDL)
410                         event_handling( producer, consumer );
411 #endif
412
413                         if ( !silent && mlt_properties_get_int( properties, "stats_off" ) == 0 )
414                         {
415                                 if ( progress )
416                                 {
417                                         int current_position = mlt_producer_position( producer );
418                                         if ( current_position > last_position )
419                                         {
420                                                 fprintf( stderr, "Current Frame: %10d, percentage: %10d%c",
421                                                         current_position, 100 * current_position / total_length,
422                                                         progress == 2 ? '\n' : '\r' );
423                                                 last_position = current_position;
424                                         }
425                                 }
426                                 else
427                                 {
428                                         fprintf( stderr, "Current Position: %10d\r", (int)mlt_consumer_position( consumer ) );
429                                 }
430                                 fflush( stderr );
431                         }
432
433                         if ( silent || progress )
434                                 nanosleep( &tm, NULL );
435                 }
436
437                 if ( !silent )
438                         fprintf( stderr, "\n" );
439         }
440 }
441
442 static void show_usage( char *program_name )
443 {
444         fprintf( stdout,
445 "Usage: %s [options] [producer [name=value]* ]+\n"
446 "Options:\n"
447 "  -attach filter[:arg] [name=value]*       Attach a filter to the output\n"
448 "  -attach-cut filter[:arg] [name=value]*   Attach a filter to a cut\n"
449 "  -attach-track filter[:arg] [name=value]* Attach a filter to a track\n"
450 "  -attach-clip filter[:arg] [name=value]*  Attach a filter to a producer\n"
451 "  -audio-track | -hide-video               Add an audio-only track\n"
452 "  -blank frames                            Add blank silence to a track\n"
453 "  -consumer id[:arg] [name=value]*         Set the consumer (sink)\n"
454 "  -debug                                   Set the logging level to debug\n"
455 "  -filter filter[:arg] [name=value]*       Add a filter to the current track\n"
456 "  -group [name=value]*                     Apply properties repeatedly\n"
457 "  -help                                    Show this message\n"
458 "  -jack                                    Enable JACK transport synchronization\n"
459 "  -join clips                              Join multiple clips into one cut\n"
460 "  -mix length                              Add a mix between the last two cuts\n"
461 "  -mixer transition                        Add a transition to the mix\n"
462 "  -null-track | -hide-track                Add a hidden track\n"
463 "  -profile name                            Set the processing settings\n"
464 "  -progress                                Display progress along with position\n"
465 "  -remove                                  Remove the most recent cut\n"
466 "  -repeat times                            Repeat the last cut\n"
467 "  -query                                   List all of the registered services\n"
468 "  -query \"consumers\" | \"consumer\"=id       List consumers or show info about one\n"
469 "  -query \"filters\" | \"filter\"=id           List filters or show info about one\n"
470 "  -query \"producers\" | \"producer\"=id       List producers or show info about one\n"
471 "  -query \"transitions\" | \"transition\"=id   List transitions, show info about one\n"
472 "  -query \"profiles\" | \"profile\"=id         List profiles, show info about one\n"
473 "  -query \"presets\" | \"preset\"=id           List presets, show info about one\n"
474 "  -query \"formats\"                         List audio/video formats\n"
475 "  -query \"audio_codecs\"                    List audio codecs\n"
476 "  -query \"video_codecs\"                    List video codecs\n"
477 "  -serialise [filename]                    Write the commands to a text file\n"
478 "  -silent                                  Do not display position/transport\n"
479 "  -split relative-frame                    Split the last cut into two cuts\n"
480 "  -swap                                    Rearrange the last two cuts\n"
481 "  -track                                   Add a track\n"
482 "  -transition id[:arg] [name=value]*       Add a transition\n"
483 "  -verbose                                 Set the logging level to verbose\n"
484 "  -version                                 Show the version and copyright\n"
485 "  -video-track | -hide-audio               Add a video-only track\n"
486 "For more help: <http://www.mltframework.org/>\n",
487         basename( program_name ) );
488 }
489
490 static void query_metadata( mlt_repository repo, mlt_service_type type, const char *typestr, char *id )
491 {
492         mlt_properties metadata = mlt_repository_metadata( repo, type, id );
493         if ( metadata )
494         {
495                 char *s = mlt_properties_serialise_yaml( metadata );
496                 fprintf( stdout, "%s", s );
497                 free( s );
498         }
499         else
500         {
501                 fprintf( stdout, "# No metadata for %s \"%s\"\n", typestr, id );
502         }
503 }
504
505 static int is_service_hidden(mlt_repository repo, mlt_service_type type, const char *service_name )
506 {
507         mlt_properties metadata = NULL;
508         mlt_properties tags = NULL;
509         metadata = mlt_repository_metadata(repo, type, service_name);
510
511         if( metadata )
512         {
513                 tags = mlt_properties_get_data( metadata, "tags", NULL );
514                 if( tags )
515                 {
516                         int k;
517                         for ( k = 0; k < mlt_properties_count( tags ); k++ )
518                         {
519                                 const char* value = mlt_properties_get_value(tags, k);
520                                 if( !strcmp("Hidden", value) )
521                                 {
522                                         return 1;
523                                 }
524                         }
525                 }
526         }
527         return 0;
528 }
529
530 static void query_services( mlt_repository repo, mlt_service_type type )
531 {
532         mlt_properties services = NULL;
533         const char *typestr = NULL;
534         switch ( type )
535         {
536                 case consumer_type:
537                         services = mlt_repository_consumers( repo );
538                         typestr = "consumers";
539                         break;
540                 case filter_type:
541                         services = mlt_repository_filters( repo );
542                         typestr = "filters";
543                         break;
544                 case producer_type:
545                         services = mlt_repository_producers( repo );
546                         typestr = "producers";
547                         break;
548                 case transition_type:
549                         services = mlt_repository_transitions( repo );
550                         typestr = "transitions";
551                         break;
552                 default:
553                         return;
554         }
555         fprintf( stdout, "---\n%s:\n", typestr );
556         if ( services )
557         {
558                 int j;
559                 for ( j = 0; j < mlt_properties_count( services ); j++ )
560                 {
561                         const char* service_name = mlt_properties_get_name( services, j );
562                         if( !is_service_hidden(repo, type, service_name ) )
563                                 fprintf( stdout, "  - %s\n", service_name );
564                 }
565         }
566         fprintf( stdout, "...\n" );
567 }
568
569 static void query_profiles()
570 {
571         mlt_properties profiles = mlt_profile_list();
572         fprintf( stdout, "---\nprofiles:\n" );
573         if ( profiles )
574         {
575                 int j;
576                 for ( j = 0; j < mlt_properties_count( profiles ); j++ )
577                         fprintf( stdout, "  - %s\n", mlt_properties_get_name( profiles, j ) );
578         }
579         fprintf( stdout, "...\n" );
580         mlt_properties_close( profiles );
581 }
582
583 static void query_profile( const char *id )
584 {
585         mlt_properties profiles = mlt_profile_list();
586         mlt_properties profile = mlt_properties_get_data( profiles, id, NULL );
587         if ( profile )
588         {
589                 char *s = mlt_properties_serialise_yaml( profile );
590                 fprintf( stdout, "%s", s );
591                 free( s );
592         }
593         else
594         {
595                 fprintf( stdout, "# No metadata for profile \"%s\"\n", id );
596         }
597         mlt_properties_close( profiles );
598 }
599
600 static void query_presets()
601 {
602         mlt_properties presets = mlt_repository_presets();
603         fprintf( stdout, "---\npresets:\n" );
604         if ( presets )
605         {
606                 int j;
607                 for ( j = 0; j < mlt_properties_count( presets ); j++ )
608                         fprintf( stdout, "  - %s\n", mlt_properties_get_name( presets, j ) );
609         }
610         fprintf( stdout, "...\n" );
611         mlt_properties_close( presets );
612 }
613
614 static void query_preset( const char *id )
615 {
616         mlt_properties presets = mlt_repository_presets();
617         mlt_properties preset = mlt_properties_get_data( presets, id, NULL );
618         if ( preset )
619         {
620                 char *s = mlt_properties_serialise_yaml( preset );
621                 fprintf( stdout, "%s", s );
622                 free( s );
623         }
624         else
625         {
626                 fprintf( stdout, "# No metadata for preset \"%s\"\n", id );
627         }
628         mlt_properties_close( presets );
629 }
630
631 static void query_formats( )
632 {
633         mlt_consumer consumer = mlt_factory_consumer( NULL, "avformat", NULL );
634         if ( consumer )
635         {
636                 mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "f", "list" );
637                 mlt_consumer_start( consumer );
638                 mlt_consumer_close( consumer );
639         }
640         else
641         {
642                 fprintf( stdout, "# No formats - failed to load avformat consumer\n" );
643         }
644 }
645
646 static void query_acodecs( )
647 {
648         mlt_consumer consumer = mlt_factory_consumer( NULL, "avformat", NULL );
649         if ( consumer )
650         {
651                 mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "acodec", "list" );
652                 mlt_consumer_start( consumer );
653                 mlt_consumer_close( consumer );
654         }
655         else
656         {
657                 fprintf( stdout, "# No audio codecs - failed to load avformat consumer\n" );
658         }
659 }
660
661 static void query_vcodecs( )
662 {
663         mlt_consumer consumer = mlt_factory_consumer( NULL, "avformat", NULL );
664         if ( consumer )
665         {
666                 mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "vcodec", "list" );
667                 mlt_consumer_start( consumer );
668                 mlt_consumer_close( consumer );
669         }
670         else
671         {
672                 fprintf( stdout, "# No video codecs - failed to load avformat consumer\n" );
673         }
674 }
675
676 static void on_fatal_error( mlt_properties owner, mlt_consumer consumer )
677 {
678         mlt_consumer_stop( consumer );
679         exit( EXIT_FAILURE );
680 }
681
682 int main( int argc, char **argv )
683 {
684         int i;
685         mlt_consumer consumer = NULL;
686         FILE *store = NULL;
687         char *name = NULL;
688         mlt_profile profile = NULL;
689         int is_progress = 0;
690         int is_silent = 0;
691         mlt_profile backup_profile;
692
693         // Handle abnormal exit situations.
694         signal( SIGSEGV, abnormal_exit_handler );
695         signal( SIGILL, abnormal_exit_handler );
696         signal( SIGABRT, abnormal_exit_handler );
697
698         // Construct the factory
699         mlt_repository repo = mlt_factory_init( NULL );
700
701 #if defined(WIN32) && !defined(MELT_NOSDL)
702         is_silent = 1;
703 #endif
704         
705         for ( i = 1; i < argc; i ++ )
706         {
707                 // Check for serialisation switch
708                 if ( !strcmp( argv[ i ], "-serialise" ) )
709                 {
710                         name = argv[ ++ i ];
711                         if ( name != NULL && strstr( name, ".melt" ) )
712                                 store = fopen( name, "w" );
713                         else
714                         {
715                                 if ( name == NULL || name[0] == '-' )
716                                         store = stdout;
717                                 name = NULL;
718                         }
719                 }
720                 // Look for the profile option
721                 else if ( !strcmp( argv[ i ], "-profile" ) )
722                 {
723                         const char *pname = argv[ ++ i ];
724                         if ( pname && pname[0] != '-' )
725                                 profile = mlt_profile_init( pname );
726                 }
727                 else if ( !strcmp( argv[ i ], "-progress" ) )
728                 {
729                         is_progress = 1;
730                 }
731                 else if ( !strcmp( argv[ i ], "-progress2" ) )
732                 {
733                         is_progress = 2;
734                 }
735                 // Look for the query option
736                 else if ( !strcmp( argv[ i ], "-query" ) )
737                 {
738                         const char *pname = argv[ ++ i ];
739                         if ( pname && pname[0] != '-' )
740                         {
741                                 if ( !strcmp( pname, "consumers" ) || !strcmp( pname, "consumer" ) )
742                                         query_services( repo, consumer_type );
743                                 else if ( !strcmp( pname, "filters" ) || !strcmp( pname, "filter" ) )
744                                         query_services( repo, filter_type );
745                                 else if ( !strcmp( pname, "producers" ) || !strcmp( pname, "producer" ) )
746                                         query_services( repo, producer_type );
747                                 else if ( !strcmp( pname, "transitions" ) || !strcmp( pname, "transition" ) )
748                                         query_services( repo, transition_type );
749                                 else if ( !strcmp( pname, "profiles" ) || !strcmp( pname, "profile" ) )
750                                         query_profiles();
751                                 else if ( !strcmp( pname, "presets" ) || !strcmp( pname, "preset" ) )
752                                         query_presets();
753                                 else if ( !strncmp( pname, "format", 6 ) )
754                                         query_formats();
755                                 else if ( !strncmp( pname, "acodec", 6 ) || !strcmp( pname, "audio_codecs" ) )
756                                         query_acodecs();
757                                 else if ( !strncmp( pname, "vcodec", 6 ) || !strcmp( pname, "video_codecs" ) )
758                                         query_vcodecs();
759
760                                 else if ( !strncmp( pname, "consumer=", 9 ) )
761                                         query_metadata( repo, consumer_type, "consumer", strchr( pname, '=' ) + 1 );
762                                 else if ( !strncmp( pname, "filter=", 7 ) )
763                                         query_metadata( repo, filter_type, "filter", strchr( pname, '=' ) + 1 );
764                                 else if ( !strncmp( pname, "producer=", 9 ) )
765                                         query_metadata( repo, producer_type, "producer", strchr( pname, '=' ) + 1 );
766                                 else if ( !strncmp( pname, "transition=", 11 ) )
767                                         query_metadata( repo, transition_type, "transition", strchr( pname, '=' ) + 1 );
768                                 else if ( !strncmp( pname, "profile=", 8 ) )
769                                         query_profile( strchr( pname, '=' ) + 1 );
770                                 else if ( !strncmp( pname, "preset=", 7 ) )
771                                         query_preset( strchr( pname, '=' ) + 1 );
772                                 else
773                                         goto query_all;
774                         }
775                         else
776                         {
777 query_all:
778                                 query_services( repo, consumer_type );
779                                 query_services( repo, filter_type );
780                                 query_services( repo, producer_type );
781                                 query_services( repo, transition_type );
782                                 fprintf( stdout, "# You can query the metadata for a specific service using:\n"
783                                         "# -query <type>=<identifer>\n"
784                                         "# where <type> is one of: consumer, filter, producer, or transition.\n" );
785                         }
786                         goto exit_factory;
787                 }
788                 else if ( !strcmp( argv[ i ], "-silent" ) )
789                 {
790                         is_silent = 1;
791                 }
792                 else if ( !strcmp( argv[ i ], "-verbose" ) )
793                 {
794                         mlt_log_set_level( MLT_LOG_VERBOSE );
795                 }
796                 else if ( !strcmp( argv[ i ], "-version" ) || !strcmp( argv[ i ], "--version" ) )
797                 {
798                         fprintf( stdout, "%s " VERSION "\n"
799                                 "Copyright (C) 2002-2013 Ushodaya Enterprises Limited\n"
800                                 "<http://www.mltframework.org/>\n"
801                                 "This is free software; see the source for copying conditions.  There is NO\n"
802                                 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
803                                 basename( argv[0] ) );
804                         goto exit_factory;
805                 }
806                 else if ( !strcmp( argv[ i ], "-debug" ) )
807                 {
808                         mlt_log_set_level( MLT_LOG_DEBUG );
809                 }
810         }
811         if ( !is_silent && !isatty( STDIN_FILENO ) && !is_progress )
812                 is_progress = 1;
813
814         // Create profile if not set explicitly
815         if ( getenv( "MLT_PROFILE" ) )
816                 profile = mlt_profile_init( NULL );
817         if ( profile == NULL )
818                 profile = mlt_profile_init( NULL );
819         else
820                 profile->is_explicit = 1;
821
822         // Look for the consumer option to load profile settings from consumer properties
823         backup_profile = mlt_profile_clone( profile );
824         load_consumer( &consumer, profile, argc, argv );
825
826         // If the consumer changed the profile, then it is explicit.
827         if ( backup_profile && !profile->is_explicit && (
828              profile->width != backup_profile->width ||
829              profile->height != backup_profile->height ||
830              profile->sample_aspect_num != backup_profile->sample_aspect_num ||
831              profile->sample_aspect_den != backup_profile->sample_aspect_den ||
832              profile->frame_rate_den != backup_profile->frame_rate_den ||
833              profile->frame_rate_num != backup_profile->frame_rate_num ||
834              profile->colorspace != backup_profile->colorspace ) )
835                 profile->is_explicit = 1;
836         mlt_profile_close( backup_profile );
837
838         // Get melt producer
839         if ( argc > 1 )
840                 melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] );
841
842         if ( melt )
843         {
844                 // Generate an automatic profile if needed.
845                 if ( ! profile->is_explicit )
846                 {
847                         mlt_profile_from_producer( profile, melt );
848                         mlt_producer_close( melt );
849                         melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] );
850                 }
851                 
852                 // Reload the consumer with the fully qualified profile.
853                 // The producer or auto-profile could have changed the profile.
854                 load_consumer( &consumer, profile, argc, argv );
855
856                 // See if producer has consumer already attached
857                 if ( !store && !consumer )
858                 {
859                         consumer = MLT_CONSUMER( mlt_service_consumer( MLT_PRODUCER_SERVICE( melt ) ) );
860                         if ( consumer )
861                         {
862                                 mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES(consumer) ); // because we explicitly close it
863                                 mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(consumer),
864                                         "transport_callback", transport_action, 0, NULL, NULL );
865                         }
866                 }
867
868                 // If we have no consumer, default to sdl
869                 if ( store == NULL && consumer == NULL )
870                         consumer = create_consumer( profile, NULL );
871         }
872         
873         // Set transport properties on consumer and produder
874         if ( consumer != NULL && melt != NULL )
875         {
876                 mlt_properties_set_data( MLT_CONSUMER_PROPERTIES( consumer ), "transport_producer", melt, 0, NULL, NULL );
877                 mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( melt ), "transport_consumer", consumer, 0, NULL, NULL );
878                 if ( is_progress )
879                         mlt_properties_set_int(  MLT_CONSUMER_PROPERTIES( consumer ), "progress", is_progress );
880                 if ( is_silent )
881                         mlt_properties_set_int(  MLT_CONSUMER_PROPERTIES( consumer ), "silent", is_silent );
882         }
883
884         if ( argc > 1 && melt != NULL && mlt_producer_get_length( melt ) > 0 )
885         {
886                 // Parse the arguments
887                 for ( i = 1; i < argc; i ++ )
888                 {
889                         if ( !strcmp( argv[ i ], "-jack" ) )
890                         {
891                                 setup_jack_transport( consumer, profile );
892                         }
893                         else if ( !strcmp( argv[ i ], "-serialise" ) )
894                         {
895                                 if ( store != stdout )
896                                         i ++;
897                         }
898                         else
899                         {
900                                 if ( store != NULL )
901                                         fprintf( store, "%s\n", argv[ i ] );
902
903                                 i ++;
904
905                                 while ( argv[ i ] != NULL && argv[ i ][ 0 ] != '-' )
906                                 {
907                                         if ( store != NULL )
908                                                 fprintf( store, "%s\n", argv[ i ] );
909                                         i += 1;
910                                 }
911
912                                 i --;
913                         }
914                 }
915
916                 if ( consumer != NULL && store == NULL )
917                 {
918                         // Get melt's properties
919                         mlt_properties melt_props = MLT_PRODUCER_PROPERTIES( melt );
920         
921                         // Get the last group
922                         mlt_properties group = mlt_properties_get_data( melt_props, "group", 0 );
923         
924                         // Apply group settings
925                         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
926                         mlt_properties_inherit( properties, group );
927
928                         // Connect consumer to melt
929                         mlt_consumer_connect( consumer, MLT_PRODUCER_SERVICE( melt ) );
930
931                         // Start the consumer
932                         mlt_events_listen( properties, consumer, "consumer-fatal-error", ( mlt_listener )on_fatal_error );
933                         if ( mlt_consumer_start( consumer ) == 0 )
934                         {
935                                 // Try to exit gracefully upon these signals
936                                 signal( SIGINT, stop_handler );
937                                 signal( SIGTERM, stop_handler );
938 #ifndef WIN32
939                                 signal( SIGHUP, stop_handler );
940                                 signal( SIGPIPE, stop_handler );
941 #endif
942
943                                 // Transport functionality
944                                 transport( melt, consumer );
945                                 
946                                 // Stop the consumer
947                                 mlt_consumer_stop( consumer );
948                         }       
949                 }
950                 else if ( store != NULL && store != stdout && name != NULL )
951                 {
952                         fprintf( stderr, "Project saved as %s.\n", name );
953                         fclose( store );
954                 }
955         }
956         else
957         {
958                 show_usage( argv[0] );
959         }
960
961         // Disconnect producer from consumer to prevent ref cycles from closing services
962         if ( consumer )
963                 mlt_consumer_connect( consumer, NULL );
964
965         // Close the producer
966         if ( melt != NULL )
967                 mlt_producer_close( melt );
968
969         // Close the consumer
970         if ( consumer != NULL )
971                 mlt_consumer_close( consumer );
972
973         // Close the factory
974         mlt_profile_close( profile );
975
976 exit_factory:
977                 
978         mlt_factory_close( );
979
980         return 0;
981 }