]> git.sesse.net Git - mlt/blob - src/melt/melt.c
Merge branch 'roto' of git://github.com/ttill/MLT-roto into roto
[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
46         mlt_properties_set_int( properties, "stats_off", 1 );
47
48         if ( strlen( value ) == 1 )
49         {
50                 switch( value[ 0 ] )
51                 {
52                         case 'q':
53                                 mlt_properties_set_int( properties, "done", 1 );
54                                 break;
55                         case '0':
56                                 mlt_producer_set_speed( producer, 1 );
57                                 mlt_producer_seek( producer, 0 );
58                                 break;
59                         case '1':
60                                 mlt_producer_set_speed( producer, -10 );
61                                 break;
62                         case '2':
63                                 mlt_producer_set_speed( producer, -5 );
64                                 break;
65                         case '3':
66                                 mlt_producer_set_speed( producer, -2 );
67                                 break;
68                         case '4':
69                                 mlt_producer_set_speed( producer, -1 );
70                                 break;
71                         case '5':
72                                 mlt_producer_set_speed( producer, 0 );
73                                 break;
74                         case '6':
75                         case ' ':
76                                 mlt_producer_set_speed( producer, 1 );
77                                 break;
78                         case '7':
79                                 mlt_producer_set_speed( producer, 2 );
80                                 break;
81                         case '8':
82                                 mlt_producer_set_speed( producer, 5 );
83                                 break;
84                         case '9':
85                                 mlt_producer_set_speed( producer, 10 );
86                                 break;
87                         case 'd':
88                                 if ( multitrack != NULL )
89                                 {
90                                         int i = 0;
91                                         mlt_position last = -1;
92                                         fprintf( stderr, "\n" );
93                                         for ( i = 0; 1; i ++ )
94                                         {
95                                                 mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_start, i );
96                                                 if ( time == last )
97                                                         break;
98                                                 last = time;
99                                                 fprintf( stderr, "%d: %d\n", i, (int)time );
100                                         }
101                                 }
102                                 break;
103
104                         case 'g':
105                                 if ( multitrack != NULL )
106                                 {
107                                         mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 );
108                                         mlt_producer_seek( producer, time );
109                                 }
110                                 break;
111                         case 'H':
112                                 if ( producer != NULL )
113                                 {
114                                         mlt_position position = mlt_producer_position( producer );
115                                         mlt_producer_seek( producer, position - ( mlt_producer_get_fps( producer ) * 60 ) );
116                                 }
117                                 break;
118                         case 'h':
119                                 if ( producer != NULL )
120                                 {
121                                         mlt_position position = mlt_producer_position( producer );
122                                         mlt_producer_set_speed( producer, 0 );
123                                         mlt_producer_seek( producer, position - 1 );
124                                 }
125                                 break;
126                         case 'j':
127                                 if ( multitrack != NULL )
128                                 {
129                                         mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 );
130                                         mlt_producer_seek( producer, time );
131                                 }
132                                 break;
133                         case 'k':
134                                 if ( multitrack != NULL )
135                                 {
136                                         mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 );
137                                         mlt_producer_seek( producer, time );
138                                 }
139                                 break;
140                         case 'l':
141                                 if ( producer != NULL )
142                                 {
143                                         mlt_position position = mlt_producer_position( producer );
144                                         if ( mlt_producer_get_speed( producer ) != 0 )
145                                                 mlt_producer_set_speed( producer, 0 );
146                                         else
147                                                 mlt_producer_seek( producer, position + 1 );
148                                 }
149                                 break;
150                         case 'L':
151                                 if ( producer != NULL )
152                                 {
153                                         mlt_position position = mlt_producer_position( producer );
154                                         mlt_producer_seek( producer, position + ( mlt_producer_get_fps( producer ) * 60 ) );
155                                 }
156                                 break;
157                 }
158
159                 mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 );
160         }
161
162         mlt_properties_set_int( properties, "stats_off", 0 );
163 }
164
165 static mlt_consumer create_consumer( mlt_profile profile, char *id )
166 {
167         char *myid = id ? strdup( id ) : NULL;
168         char *arg = myid ? strchr( myid, ':' ) : NULL;
169         if ( arg != NULL )
170                 *arg ++ = '\0';
171         mlt_consumer consumer = mlt_factory_consumer( profile, myid, arg );
172         if ( consumer != NULL )
173         {
174                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
175                 mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL );
176         }
177         if ( myid )
178                 free( myid );
179         return consumer;
180 }
181
182 static void load_consumer( mlt_consumer *consumer, mlt_profile profile, int argc, char **argv )
183 {
184         int i;
185         for ( i = 1; i < argc; i ++ )
186         {
187                 if ( !strcmp( argv[ i ], "-consumer" ) )
188                 {
189                         if ( *consumer )
190                                 mlt_consumer_close( *consumer );
191                         *consumer = create_consumer( profile, argv[ ++ i ] );
192                         if ( *consumer )
193                         {
194                                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( *consumer );
195                                 while ( argv[ i + 1 ] != NULL && strstr( argv[ i + 1 ], "=" ) )
196                                         mlt_properties_parse( properties, argv[ ++ i ] );
197                         }
198                 }
199         }
200 }
201
202 #if defined(__DARWIN__) || defined(WIN32)
203
204 static void event_handling( mlt_producer producer, mlt_consumer consumer )
205 {
206         SDL_Event event;
207
208         while ( SDL_PollEvent( &event ) )
209         {
210                 switch( event.type )
211                 {
212                         case SDL_QUIT:
213                                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( consumer ), "done", 1 );
214                                 break;
215
216                         case SDL_KEYDOWN:
217                                 if ( event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
218                                 {
219                                         char keyboard[ 2 ] = { event.key.keysym.unicode, 0 };
220                                         transport_action( producer, keyboard );
221                                 }
222                                 break;
223                 }
224         }
225 }
226
227 #endif
228
229 static void transport( mlt_producer producer, mlt_consumer consumer )
230 {
231         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
232         int silent = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "silent" );
233         int progress = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "progress" );
234         struct timespec tm = { 0, 40000 };
235         int total_length = mlt_producer_get_length( producer );
236         int last_position = 0;
237
238         if ( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
239         {
240                 if ( !silent && !progress )
241                 {
242                         term_init( );
243
244                         fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
245                         fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5=  0| |6=  1| |7=  2| |8=  5| |9= 10|\n" );
246                         fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
247
248                         fprintf( stderr, "+---------------------------------------------------------------------+\n" );
249                         fprintf( stderr, "|               H = back 1 minute,  L = forward 1 minute              |\n" );
250                         fprintf( stderr, "|                 h = previous frame,  l = next frame                 |\n" );
251                         fprintf( stderr, "|           g = start of clip, j = next clip, k = previous clip       |\n" );
252                         fprintf( stderr, "|                0 = restart, q = quit, space = play                  |\n" );
253                         fprintf( stderr, "+---------------------------------------------------------------------+\n" );
254                 }
255
256                 while( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
257                 {
258                         int value = ( silent || progress )? -1 : term_read( );
259
260                         if ( value != -1 )
261                         {
262                                 char string[ 2 ] = { value, 0 };
263                                 transport_action( producer, string );
264                         }
265
266 #if defined(__DARWIN__) || defined(WIN32)
267                         event_handling( producer, consumer );
268 #endif
269
270                         if ( !silent && mlt_properties_get_int( properties, "stats_off" ) == 0 )
271                         {
272                                 if ( progress )
273                                 {
274                                         int current_position = mlt_producer_position( producer );
275                                         if ( current_position > last_position )
276                                         {
277                                                 fprintf( stderr, "Current Frame: %10d, percentage: %10d\r",
278                                                         current_position, 100 * current_position / total_length );
279                                                 last_position = current_position;
280                                         }
281                                 }
282                                 else
283                                 {
284                                         fprintf( stderr, "Current Position: %10d\r", (int)mlt_consumer_position( consumer ) );
285                                 }
286                         }
287
288                         if ( silent || progress )
289                                 nanosleep( &tm, NULL );
290                 }
291
292                 if ( !silent )
293                         fprintf( stderr, "\n" );
294         }
295 }
296
297 static void show_usage( char *program_name )
298 {
299         fprintf( stderr,
300 "Usage: %s [options] [producer [name=value]* ]+\n"
301 "Options:\n"
302 "  -attach filter[:arg] [name=value]*       Attach a filter to the output\n"
303 "  -attach-cut filter[:arg] [name=value]*   Attach a filter to a cut\n"
304 "  -attach-track filter[:arg] [name=value]* Attach a filter to a track\n"
305 "  -attach-clip filter[:arg] [name=value]*  Attach a filter to a producer\n"
306 "  -audio-track | -hide-video               Add an audio-only track\n"
307 "  -blank frames                            Add blank silence to a track\n"
308 "  -consumer id[:arg] [name=value]*         Set the consumer (sink)\n"
309 "  -debug                                   Set the logging level to debug\n"
310 "  -filter filter[:arg] [name=value]*       Add a filter to the current track\n"
311 "  -group [name=value]*                     Apply properties repeatedly\n"
312 "  -help                                    Show this message\n"
313 "  -join clips                              Join multiple clips into one cut\n"
314 "  -mix length                              Add a mix between the last two cuts\n"
315 "  -mixer transition                        Add a transition to the mix\n"
316 "  -null-track | -hide-track                Add a hidden track\n"
317 "  -profile name                            Set the processing settings\n"
318 "  -progress                                Display progress along with position\n"
319 "  -remove                                  Remove the most recent cut\n"
320 "  -repeat times                            Repeat the last cut\n"
321 "  -query                                   List all of the registered services\n"
322 "  -query \"consumers\" | \"consumer\"=id       List consumers or show info about one\n"
323 "  -query \"filters\" | \"filter\"=id           List filters or show info about one\n"
324 "  -query \"producers\" | \"producer\"=id       List producers or show info about one\n"
325 "  -query \"transitions\" | \"transition\"=id   List transitions, show info about one\n"
326 "  -serialise [filename]                    Write the commands to a text file\n"
327 "  -silent                                  Do not display position/transport\n"
328 "  -split relative-frame                    Split the last cut into two cuts\n"
329 "  -swap                                    Rearrange the last two cuts\n"
330 "  -track                                   Add a track\n"
331 "  -transition id[:arg] [name=value]*       Add a transition\n"
332 "  -verbose                                 Set the logging level to verbose\n"
333 "  -version                                 Show the version and copyright\n"
334 "  -video-track | -hide-audio               Add a video-only track\n"
335 "For more help: <http://www.mltframework.org/>\n",
336         basename( program_name ) );
337 }
338
339 static void guess_profile( mlt_producer melt, mlt_profile profile )
340 {
341         mlt_frame fr = NULL;
342         uint8_t *buffer;
343         mlt_image_format fmt = mlt_image_yuv422;
344         mlt_properties p;
345         int w = profile->width;
346         int h = profile->height;
347
348         if ( ! mlt_service_get_frame( MLT_PRODUCER_SERVICE(melt), &fr, 0 ) && fr )
349         {
350                 mlt_properties_set_double( MLT_FRAME_PROPERTIES( fr ), "consumer_aspect_ratio", mlt_profile_sar( profile ) );
351                 if ( ! mlt_frame_get_image( fr, &buffer, &fmt, &w, &h, 0 ) )
352                 {
353                         // Some source properties are not exposed until after the first get_image call.
354                         mlt_frame_close( fr );
355                         mlt_service_get_frame( MLT_PRODUCER_SERVICE(melt), &fr, 0 );
356                         p = MLT_FRAME_PROPERTIES( fr );
357 //                      mlt_properties_dump(p, stderr);
358                         if ( mlt_properties_get_int( p, "meta.media.frame_rate_den" ) && mlt_properties_get_int( p, "meta.media.sample_aspect_den" ) )
359                         {
360                                 profile->width = mlt_properties_get_int( p, "meta.media.width" );
361                                 profile->height = mlt_properties_get_int( p, "meta.media.height" );
362                                 profile->progressive = mlt_properties_get_int( p, "meta.media.progressive" );
363                                 profile->frame_rate_num = mlt_properties_get_int( p, "meta.media.frame_rate_num" );
364                                 profile->frame_rate_den = mlt_properties_get_int( p, "meta.media.frame_rate_den" );
365                                 // AVCHD is mis-reported as double frame rate.
366                                 if ( profile->progressive == 0 && (
367                                      profile->frame_rate_num / profile->frame_rate_den == 50 ||
368                                      profile->frame_rate_num / profile->frame_rate_den == 59 ) )
369                                         profile->frame_rate_num /= 2;
370                                 profile->sample_aspect_num = mlt_properties_get_int( p, "meta.media.sample_aspect_num" );
371                                 profile->sample_aspect_den = mlt_properties_get_int( p, "meta.media.sample_aspect_den" );
372                                 profile->colorspace = mlt_properties_get_int( p, "meta.media.colorspace" );
373                                 profile->display_aspect_num = (int) ( (double) profile->sample_aspect_num * profile->width / profile->sample_aspect_den + 0.5 );
374                                 profile->display_aspect_den = profile->height;
375                                 free( profile->description );
376                                 profile->description = strdup( "automatic" );
377                                 profile->is_explicit = 0;
378                         }
379                 }
380         }
381         mlt_frame_close( fr );
382         mlt_producer_seek( melt, 0 );
383 }
384
385 static void query_metadata( mlt_repository repo, mlt_service_type type, const char *typestr, char *id )
386 {
387         mlt_properties metadata = mlt_repository_metadata( repo, type, id );
388         if ( metadata )
389         {
390                 char *s = mlt_properties_serialise_yaml( metadata );
391                 fprintf( stderr, "%s", s );
392                 free( s );
393         }
394         else
395         {
396                 fprintf( stderr, "# No metadata for %s \"%s\"\n", typestr, id );
397         }
398 }
399
400 static void query_services( mlt_repository repo, mlt_service_type type )
401 {
402         mlt_properties services = NULL;
403         const char *typestr = NULL;
404         switch ( type )
405         {
406                 case consumer_type:
407                         services = mlt_repository_consumers( repo );
408                         typestr = "consumers";
409                         break;
410                 case filter_type:
411                         services = mlt_repository_filters( repo );
412                         typestr = "filters";
413                         break;
414                 case producer_type:
415                         services = mlt_repository_producers( repo );
416                         typestr = "producers";
417                         break;
418                 case transition_type:
419                         services = mlt_repository_transitions( repo );
420                         typestr = "transitions";
421                         break;
422                 default:
423                         return;
424         }
425         fprintf( stderr, "---\n%s:\n", typestr );
426         if ( services )
427         {
428                 int j;
429                 for ( j = 0; j < mlt_properties_count( services ); j++ )
430                         fprintf( stderr, "  - %s\n", mlt_properties_get_name( services, j ) );
431         }
432         fprintf( stderr, "...\n" );
433 }
434
435 static void on_fatal_error( mlt_properties owner, mlt_consumer consumer )
436 {
437         mlt_consumer_stop( consumer );
438         exit( EXIT_FAILURE );
439 }
440
441 int main( int argc, char **argv )
442 {
443         int i;
444         mlt_consumer consumer = NULL;
445         mlt_producer melt = NULL;
446         FILE *store = NULL;
447         char *name = NULL;
448         mlt_profile profile = NULL;
449         int is_progress = 0;
450         int is_silent = 0;
451         mlt_profile backup_profile;
452
453         // Construct the factory
454         mlt_repository repo = mlt_factory_init( NULL );
455
456 #ifdef WIN32
457         is_silent = 1;
458 #endif
459         
460         for ( i = 1; i < argc; i ++ )
461         {
462                 // Check for serialisation switch
463                 if ( !strcmp( argv[ i ], "-serialise" ) )
464                 {
465                         name = argv[ ++ i ];
466                         if ( name != NULL && strstr( name, ".melt" ) )
467                                 store = fopen( name, "w" );
468                         else
469                         {
470                                 if ( name == NULL || name[0] == '-' )
471                                         store = stdout;
472                                 name = NULL;
473                         }
474                 }
475                 // Look for the profile option
476                 else if ( !strcmp( argv[ i ], "-profile" ) )
477                 {
478                         const char *pname = argv[ ++ i ];
479                         if ( pname && pname[0] != '-' )
480                                 profile = mlt_profile_init( pname );
481                 }
482                 else if ( !strcmp( argv[ i ], "-progress" ) )
483                 {
484                         is_progress = 1;
485                 }
486                 // Look for the query option
487                 else if ( !strcmp( argv[ i ], "-query" ) )
488                 {
489                         const char *pname = argv[ ++ i ];
490                         if ( pname && pname[0] != '-' )
491                         {
492                                 if ( !strcmp( pname, "consumers" ) || !strcmp( pname, "consumer" ) )
493                                         query_services( repo, consumer_type );
494                                 else if ( !strcmp( pname, "filters" ) || !strcmp( pname, "filter" ) )
495                                         query_services( repo, filter_type );
496                                 else if ( !strcmp( pname, "producers" ) || !strcmp( pname, "producer" ) )
497                                         query_services( repo, producer_type );
498                                 else if ( !strcmp( pname, "transitions" ) || !strcmp( pname, "transition" ) )
499                                         query_services( repo, transition_type );
500                                 
501                                 else if ( !strncmp( pname, "consumer=", 9 ) )
502                                         query_metadata( repo, consumer_type, "consumer", strchr( pname, '=' ) + 1 );
503                                 else if ( !strncmp( pname, "filter=", 7 ) )
504                                         query_metadata( repo, filter_type, "filter", strchr( pname, '=' ) + 1 );
505                                 else if ( !strncmp( pname, "producer=", 9 ) )
506                                         query_metadata( repo, producer_type, "producer", strchr( pname, '=' ) + 1 );
507                                 else if ( !strncmp( pname, "transition=", 11 ) )
508                                         query_metadata( repo, transition_type, "transition", strchr( pname, '=' ) + 1 );
509                                 else
510                                         goto query_all;
511                         }
512                         else
513                         {
514 query_all:
515                                 query_services( repo, consumer_type );
516                                 query_services( repo, filter_type );
517                                 query_services( repo, producer_type );
518                                 query_services( repo, transition_type );
519                                 fprintf( stderr, "# You can query the metadata for a specific service using:\n"
520                                         "# -query <type>=<identifer>\n"
521                                         "# where <type> is one of: consumer, filter, producer, or transition.\n" );
522                         }
523                         goto exit_factory;
524                 }
525                 else if ( !strcmp( argv[ i ], "-silent" ) )
526                 {
527                         is_silent = 1;
528                 }
529                 else if ( !strcmp( argv[ i ], "-verbose" ) )
530                 {
531                         mlt_log_set_level( MLT_LOG_VERBOSE );
532                 }
533                 else if ( !strcmp( argv[ i ], "-version" ) || !strcmp( argv[ i ], "--version" ) )
534                 {
535                         fprintf( stderr, "MLT %s " VERSION "\n"
536                                 "Copyright (C) 2002-2011 Ushodaya Enterprises Limited\n"
537                                 "<http://www.mltframework.org/>\n"
538                                 "This is free software; see the source for copying conditions.  There is NO\n"
539                                 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
540                                 basename( argv[0] ) );
541                         goto exit_factory;
542                 }
543                 else if ( !strcmp( argv[ i ], "-debug" ) )
544                 {
545                         mlt_log_set_level( MLT_LOG_DEBUG );
546                 }
547         }
548
549         // Create profile if not set explicitly
550         if ( getenv( "MLT_PROFILE" ) )
551                 profile = mlt_profile_init( NULL );
552         if ( profile == NULL )
553                 profile = mlt_profile_init( NULL );
554         else
555                 profile->is_explicit = 1;
556
557         // Look for the consumer option to load profile settings from consumer properties
558         backup_profile = mlt_profile_clone( profile );
559         load_consumer( &consumer, profile, argc, argv );
560
561         // If the consumer changed the profile, then it is explicit.
562         if ( backup_profile && !profile->is_explicit && (
563              profile->width != backup_profile->width ||
564              profile->height != backup_profile->height ||
565              profile->sample_aspect_num != backup_profile->sample_aspect_num ||
566              profile->sample_aspect_den != backup_profile->sample_aspect_den ||
567              profile->frame_rate_den != backup_profile->frame_rate_den ||
568              profile->frame_rate_num != backup_profile->frame_rate_num ||
569              profile->colorspace != backup_profile->colorspace ) )
570                 profile->is_explicit = 1;
571         mlt_profile_close( backup_profile );
572
573         // Get melt producer
574         if ( argc > 1 )
575                 melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] );
576
577         if ( melt )
578         {
579                 // Generate an automatic profile if needed.
580                 if ( ! profile->is_explicit )
581                 {
582                         guess_profile( melt, profile );
583                         mlt_producer_close( melt );
584                         melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] );
585                 }
586                 
587                 // Reload the consumer with the fully qualified profile.
588                 // The producer or guess_profile could have changed the profile.
589                 load_consumer( &consumer, profile, argc, argv );
590
591                 // If we have no consumer, default to sdl
592                 if ( store == NULL && consumer == NULL )
593                         consumer = create_consumer( profile, NULL );
594         }
595         
596         // Set transport properties on consumer and produder
597         if ( consumer != NULL && melt != NULL )
598         {
599                 mlt_properties_set_data( MLT_CONSUMER_PROPERTIES( consumer ), "transport_producer", melt, 0, NULL, NULL );
600                 mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( melt ), "transport_consumer", consumer, 0, NULL, NULL );
601                 if ( is_progress )
602                         mlt_properties_set_int(  MLT_CONSUMER_PROPERTIES( consumer ), "progress", is_progress );
603                 if ( is_silent )
604                         mlt_properties_set_int(  MLT_CONSUMER_PROPERTIES( consumer ), "silent", is_silent );
605         }
606
607         if ( argc > 1 && melt != NULL && mlt_producer_get_length( melt ) > 0 )
608         {
609                 // Parse the arguments
610                 for ( i = 1; i < argc; i ++ )
611                 {
612                         if ( !strcmp( argv[ i ], "-serialise" ) )
613                         {
614                                 if ( store != stdout )
615                                         i ++;
616                         }
617                         else
618                         {
619                                 if ( store != NULL )
620                                         fprintf( store, "%s\n", argv[ i ] );
621
622                                 i ++;
623
624                                 while ( argv[ i ] != NULL && argv[ i ][ 0 ] != '-' )
625                                 {
626                                         if ( store != NULL )
627                                                 fprintf( store, "%s\n", argv[ i ] );
628                                         i += 1;
629                                 }
630
631                                 i --;
632                         }
633                 }
634
635                 if ( consumer != NULL && store == NULL )
636                 {
637                         // Get melt's properties
638                         mlt_properties melt_props = MLT_PRODUCER_PROPERTIES( melt );
639         
640                         // Get the last group
641                         mlt_properties group = mlt_properties_get_data( melt_props, "group", 0 );
642         
643                         // Apply group settings
644                         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
645                         mlt_properties_inherit( properties, group );
646
647                         // Connect consumer to melt
648                         mlt_consumer_connect( consumer, MLT_PRODUCER_SERVICE( melt ) );
649
650                         // Start the consumer
651                         mlt_events_listen( properties, consumer, "consumer-fatal-error", ( mlt_listener )on_fatal_error );
652                         if ( mlt_consumer_start( consumer ) == 0 )
653                         {
654                                 // Transport functionality
655                                 transport( melt, consumer );
656                                 
657                                 // Stop the consumer
658                                 mlt_consumer_stop( consumer );
659                         }       
660                 }
661                 else if ( store != NULL && store != stdout && name != NULL )
662                 {
663                         fprintf( stderr, "Project saved as %s.\n", name );
664                         fclose( store );
665                 }
666         }
667         else
668         {
669                 show_usage( argv[0] );
670         }
671
672         // Close the producer
673         if ( melt != NULL )
674                 mlt_producer_close( melt );
675
676         // Close the consumer
677         if ( consumer != NULL )
678                 mlt_consumer_close( consumer );
679
680         // Close the factory
681         mlt_profile_close( profile );
682
683 exit_factory:
684                 
685         mlt_factory_close( );
686
687         return 0;
688 }