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