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