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