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