]> git.sesse.net Git - mlt/blob - src/melt/melt.c
a60e80f426b3692c36fb27c60441cd72abf99293
[mlt] / src / melt / melt.c
1 /*
2  * melt.c -- MLT command line utility
3  * Copyright (C) 2002-2010 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sched.h>
28 #include <libgen.h>
29
30 #include <framework/mlt.h>
31
32 #ifdef __DARWIN__
33 #include <SDL.h>
34 #endif
35
36 #include "io.h"
37
38 static void transport_action( mlt_producer producer, char *value )
39 {
40         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
41         mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
42         mlt_consumer consumer = mlt_properties_get_data( properties, "transport_consumer", NULL );
43
44         mlt_properties_set_int( properties, "stats_off", 1 );
45
46         if ( strlen( value ) == 1 )
47         {
48                 switch( value[ 0 ] )
49                 {
50                         case 'q':
51                                 mlt_properties_set_int( properties, "done", 1 );
52                                 break;
53                         case '0':
54                                 mlt_producer_set_speed( producer, 1 );
55                                 mlt_producer_seek( producer, 0 );
56                                 break;
57                         case '1':
58                                 mlt_producer_set_speed( producer, -10 );
59                                 break;
60                         case '2':
61                                 mlt_producer_set_speed( producer, -5 );
62                                 break;
63                         case '3':
64                                 mlt_producer_set_speed( producer, -2 );
65                                 break;
66                         case '4':
67                                 mlt_producer_set_speed( producer, -1 );
68                                 break;
69                         case '5':
70                                 mlt_producer_set_speed( producer, 0 );
71                                 break;
72                         case '6':
73                         case ' ':
74                                 mlt_producer_set_speed( producer, 1 );
75                                 break;
76                         case '7':
77                                 mlt_producer_set_speed( producer, 2 );
78                                 break;
79                         case '8':
80                                 mlt_producer_set_speed( producer, 5 );
81                                 break;
82                         case '9':
83                                 mlt_producer_set_speed( producer, 10 );
84                                 break;
85                         case 'd':
86                                 if ( multitrack != NULL )
87                                 {
88                                         int i = 0;
89                                         mlt_position last = -1;
90                                         fprintf( stderr, "\n" );
91                                         for ( i = 0; 1; i ++ )
92                                         {
93                                                 mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_start, i );
94                                                 if ( time == last )
95                                                         break;
96                                                 last = time;
97                                                 fprintf( stderr, "%d: %d\n", i, (int)time );
98                                         }
99                                 }
100                                 break;
101
102                         case 'g':
103                                 if ( multitrack != NULL )
104                                 {
105                                         mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 );
106                                         mlt_producer_seek( producer, time );
107                                 }
108                                 break;
109                         case 'H':
110                                 if ( producer != NULL )
111                                 {
112                                         mlt_position position = mlt_producer_position( producer );
113                                         mlt_producer_seek( producer, position - ( mlt_producer_get_fps( producer ) * 60 ) );
114                                 }
115                                 break;
116                         case 'h':
117                                 if ( producer != NULL )
118                                 {
119                                         mlt_position position = mlt_producer_position( producer );
120                                         mlt_producer_set_speed( producer, 0 );
121                                         mlt_producer_seek( producer, position - 1 );
122                                 }
123                                 break;
124                         case 'j':
125                                 if ( multitrack != NULL )
126                                 {
127                                         mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 );
128                                         mlt_producer_seek( producer, time );
129                                 }
130                                 break;
131                         case 'k':
132                                 if ( multitrack != NULL )
133                                 {
134                                         mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 );
135                                         mlt_producer_seek( producer, time );
136                                 }
137                                 break;
138                         case 'l':
139                                 if ( producer != NULL )
140                                 {
141                                         mlt_position position = mlt_producer_position( producer );
142                                         if ( mlt_producer_get_speed( producer ) != 0 )
143                                                 mlt_producer_set_speed( producer, 0 );
144                                         else
145                                                 mlt_producer_seek( producer, position + 1 );
146                                 }
147                                 break;
148                         case 'L':
149                                 if ( producer != NULL )
150                                 {
151                                         mlt_position position = mlt_producer_position( producer );
152                                         mlt_producer_seek( producer, position + ( mlt_producer_get_fps( producer ) * 60 ) );
153                                 }
154                                 break;
155                 }
156
157                 mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 );
158         }
159
160         mlt_properties_set_int( properties, "stats_off", 0 );
161 }
162
163 static mlt_consumer create_consumer( mlt_profile profile, char *id )
164 {
165         char *arg = id != NULL ? strchr( id, ':' ) : NULL;
166         if ( arg != NULL )
167                 *arg ++ = '\0';
168         mlt_consumer consumer = mlt_factory_consumer( profile, id, arg );
169         if ( consumer != NULL )
170         {
171                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
172                 mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL );
173         }
174         return consumer;
175 }
176
177 #ifdef __DARWIN__
178
179 static void event_handling( mlt_producer producer, mlt_consumer consumer )
180 {
181         SDL_Event event;
182
183         while ( SDL_PollEvent( &event ) )
184         {
185                 switch( event.type )
186                 {
187                         case SDL_QUIT:
188                                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( consumer ), "done", 1 );
189                                 break;
190
191                         case SDL_KEYDOWN:
192                                 if ( event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
193                                 {
194                                         char keyboard[ 2 ] = { event.key.keysym.unicode, 0 };
195                                         transport_action( producer, keyboard );
196                                 }
197                                 break;
198                 }
199         }
200 }
201
202 #endif
203
204 static void transport( mlt_producer producer, mlt_consumer consumer )
205 {
206         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
207         int silent = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "silent" );
208         int progress = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "progress" );
209         struct timespec tm = { 0, 40000 };
210         int total_length = mlt_producer_get_length( producer );
211         int last_position = 0;
212
213         if ( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
214         {
215                 if ( !silent && !progress )
216                 {
217                         term_init( );
218
219                         fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
220                         fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5=  0| |6=  1| |7=  2| |8=  5| |9= 10|\n" );
221                         fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
222
223                         fprintf( stderr, "+---------------------------------------------------------------------+\n" );
224                         fprintf( stderr, "|               H = back 1 minute,  L = forward 1 minute              |\n" );
225                         fprintf( stderr, "|                 h = previous frame,  l = next frame                 |\n" );
226                         fprintf( stderr, "|           g = start of clip, j = next clip, k = previous clip       |\n" );
227                         fprintf( stderr, "|                0 = restart, q = quit, space = play                  |\n" );
228                         fprintf( stderr, "+---------------------------------------------------------------------+\n" );
229                 }
230
231                 while( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
232                 {
233                         int value = ( silent || progress )? -1 : term_read( );
234
235                         if ( value != -1 )
236                         {
237                                 char string[ 2 ] = { value, 0 };
238                                 transport_action( producer, string );
239                         }
240
241 #ifdef __DARWIN__
242                         event_handling( producer, consumer );
243 #endif
244
245                         if ( !silent && mlt_properties_get_int( properties, "stats_off" ) == 0 )
246                         {
247                                 if ( progress )
248                                 {
249                                         int current_position = mlt_producer_position( producer );
250                                         if ( current_position > last_position )
251                                         {
252                                                 fprintf( stderr, "Current Frame: %10d, percentage: %10d\r",
253                                                         current_position, 100 * current_position / total_length );
254                                                 last_position = current_position;
255                                         }
256                                 }
257                                 else
258                                 {
259                                         fprintf( stderr, "Current Position: %10d\r", (int)mlt_producer_position( producer ) );
260                                 }
261                         }
262
263                         if ( silent || progress )
264                                 nanosleep( &tm, NULL );
265                 }
266
267                 if ( !silent )
268                         fprintf( stderr, "\n" );
269         }
270 }
271
272 static void query_metadata( mlt_repository repo, mlt_service_type type, const char *typestr, char *id )
273 {
274         mlt_properties metadata = mlt_repository_metadata( repo, type, id );
275         if ( metadata )
276         {
277                 char *s = mlt_properties_serialise_yaml( metadata );
278                 fprintf( stderr, "%s", s );
279                 free( s );
280         }
281         else
282         {
283                 fprintf( stderr, "# No metadata for %s \"%s\"\n", typestr, id );
284         }
285 }
286
287 static void query_services( mlt_repository repo, mlt_service_type type )
288 {
289         mlt_properties services = NULL;
290         const char *typestr = NULL;
291         switch ( type )
292         {
293                 case consumer_type:
294                         services = mlt_repository_consumers( repo );
295                         typestr = "consumers";
296                         break;
297                 case filter_type:
298                         services = mlt_repository_filters( repo );
299                         typestr = "filters";
300                         break;
301                 case producer_type:
302                         services = mlt_repository_producers( repo );
303                         typestr = "producers";
304                         break;
305                 case transition_type:
306                         services = mlt_repository_transitions( repo );
307                         typestr = "transitions";
308                         break;
309                 default:
310                         return;
311         }
312         fprintf( stderr, "---\n%s:\n", typestr );
313         if ( services )
314         {
315                 int j;
316                 for ( j = 0; j < mlt_properties_count( services ); j++ )
317                         fprintf( stderr, "  - %s\n", mlt_properties_get_name( services, j ) );
318         }
319         fprintf( stderr, "...\n" );
320 }
321
322 int main( int argc, char **argv )
323 {
324         int i;
325         mlt_consumer consumer = NULL;
326         mlt_producer melt = NULL;
327         FILE *store = NULL;
328         char *name = NULL;
329         mlt_profile profile = NULL;
330         int is_progress = 0;
331         int is_silent = 0;
332
333         // Construct the factory
334         mlt_repository repo = mlt_factory_init( NULL );
335
336         for ( i = 1; i < argc; i ++ )
337         {
338                 // Check for serialisation switch
339                 if ( !strcmp( argv[ i ], "-serialise" ) )
340                 {
341                         name = argv[ ++ i ];
342                         if ( name != NULL && strstr( name, ".melt" ) )
343                                 store = fopen( name, "w" );
344                         else
345                         {
346                                 if ( name == NULL || name[0] == '-' )
347                                         store = stdout;
348                                 name = NULL;
349                         }
350                 }
351                 // Look for the profile option
352                 else if ( !strcmp( argv[ i ], "-profile" ) )
353                 {
354                         const char *pname = argv[ ++ i ];
355                         if ( pname && pname[0] != '-' )
356                                 profile = mlt_profile_init( pname );
357                 }
358                 else if ( !strcmp( argv[ i ], "-progress" ) )
359                 {
360                         is_progress = 1;
361                 }
362                 // Look for the query option
363                 else if ( !strcmp( argv[ i ], "-query" ) )
364                 {
365                         const char *pname = argv[ ++ i ];
366                         if ( pname && pname[0] != '-' )
367                         {
368                                 if ( !strcmp( pname, "consumers" ) || !strcmp( pname, "consumer" ) )
369                                         query_services( repo, consumer_type );
370                                 else if ( !strcmp( pname, "filters" ) || !strcmp( pname, "filter" ) )
371                                         query_services( repo, filter_type );
372                                 else if ( !strcmp( pname, "producers" ) || !strcmp( pname, "producer" ) )
373                                         query_services( repo, producer_type );
374                                 else if ( !strcmp( pname, "transitions" ) || !strcmp( pname, "transition" ) )
375                                         query_services( repo, transition_type );
376                                 
377                                 else if ( !strncmp( pname, "consumer=", 9 ) )
378                                         query_metadata( repo, consumer_type, "consumer", strchr( pname, '=' ) + 1 );
379                                 else if ( !strncmp( pname, "filter=", 7 ) )
380                                         query_metadata( repo, filter_type, "filter", strchr( pname, '=' ) + 1 );
381                                 else if ( !strncmp( pname, "producer=", 9 ) )
382                                         query_metadata( repo, producer_type, "producer", strchr( pname, '=' ) + 1 );
383                                 else if ( !strncmp( pname, "transition=", 11 ) )
384                                         query_metadata( repo, transition_type, "transition", strchr( pname, '=' ) + 1 );
385                                 else
386                                         goto query_all;
387                         }
388                         else
389                         {
390 query_all:
391                                 query_services( repo, consumer_type );
392                                 query_services( repo, filter_type );
393                                 query_services( repo, producer_type );
394                                 query_services( repo, transition_type );
395                                 fprintf( stderr, "# You can query the metadata for a specific service using:\n"
396                                         "# -query <type>=<identifer>\n"
397                                         "# where <type> is one of: consumer, filter, producer, or transition.\n" );
398                         }
399                         goto exit_factory;
400                 }
401                 else if ( !strcmp( argv[ i ], "-silent" ) )
402                 {
403                         is_silent = 1;
404                 }
405                 else if ( !strcmp( argv[ i ], "-verbose" ) )
406                 {
407                         mlt_log_set_level( MLT_LOG_VERBOSE );
408                 }
409                 else if ( !strcmp( argv[ i ], "-version" ) || !strcmp( argv[ i ], "--version" ) )
410                 {
411                         fprintf( stderr, "MLT %s " VERSION "\n"
412                                 "Copyright (C) 2002-2010 Ushodaya Enterprises Limited\n"
413                                 "<http://www.mltframework.org/>\n"
414                                 "This is free software; see the source for copying conditions.  There is NO\n"
415                                 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
416                                 basename( argv[0] ) );
417                         goto exit_factory;
418                 }
419                 else if ( !strcmp( argv[ i ], "-debug" ) )
420                 {
421                         mlt_log_set_level( MLT_LOG_DEBUG );
422                 }
423         }
424
425         // Create profile if not set explicitly
426         if ( profile == NULL )
427                 profile = mlt_profile_init( NULL );
428
429         // Look for the consumer option
430         for ( i = 1; i < argc; i ++ )
431         {
432                 if ( !strcmp( argv[ i ], "-consumer" ) )
433                 {
434                         consumer = create_consumer( profile, argv[ ++ i ] );
435                         if ( consumer )
436                         {
437                                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
438                                 while ( argv[ i + 1 ] != NULL && strstr( argv[ i + 1 ], "=" ) )
439                                         mlt_properties_parse( properties, argv[ ++ i ] );
440                         }
441                 }
442         }
443
444         // If we have no consumer, default to sdl
445         if ( store == NULL && consumer == NULL )
446                 consumer = create_consumer( profile, NULL );
447
448         // Get melt producer
449         if ( argc > 1 )
450                 melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] );
451
452         // Set transport properties on consumer and produder
453         if ( consumer != NULL && melt != NULL )
454         {
455                 mlt_properties_set_data( MLT_CONSUMER_PROPERTIES( consumer ), "transport_producer", melt, 0, NULL, NULL );
456                 mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( melt ), "transport_consumer", consumer, 0, NULL, NULL );
457                 if ( is_progress )
458                         mlt_properties_set_int(  MLT_CONSUMER_PROPERTIES( consumer ), "progress", is_progress );
459                 if ( is_silent )
460                         mlt_properties_set_int(  MLT_CONSUMER_PROPERTIES( consumer ), "silent", is_silent );
461         }
462
463         if ( argc > 1 && melt != NULL && mlt_producer_get_length( melt ) > 0 )
464         {
465                 // Parse the arguments
466                 for ( i = 1; i < argc; i ++ )
467                 {
468                         if ( !strcmp( argv[ i ], "-serialise" ) )
469                         {
470                                 if ( store != stdout )
471                                         i ++;
472                         }
473                         else
474                         {
475                                 if ( store != NULL )
476                                         fprintf( store, "%s\n", argv[ i ] );
477
478                                 i ++;
479
480                                 while ( argv[ i ] != NULL && argv[ i ][ 0 ] != '-' )
481                                 {
482                                         if ( store != NULL )
483                                                 fprintf( store, "%s\n", argv[ i ] );
484                                         i += 1;
485                                 }
486
487                                 i --;
488                         }
489                 }
490
491                 if ( consumer != NULL && store == NULL )
492                 {
493                         // Get melt's properties
494                         mlt_properties melt_props = MLT_PRODUCER_PROPERTIES( melt );
495         
496                         // Get the last group
497                         mlt_properties group = mlt_properties_get_data( melt_props, "group", 0 );
498         
499                         // Apply group settings
500                         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
501                         mlt_properties_inherit( properties, group );
502
503                         // Connect consumer to melt
504                         mlt_consumer_connect( consumer, MLT_PRODUCER_SERVICE( melt ) );
505
506                         // Start the consumer
507                         mlt_consumer_start( consumer );
508
509                         // Transport functionality
510                         transport( melt, consumer );
511
512                         // Stop the consumer
513                         mlt_consumer_stop( consumer );
514                 }
515                 else if ( store != NULL && store != stdout && name != NULL )
516                 {
517                         fprintf( stderr, "Project saved as %s.\n", name );
518                         fclose( store );
519                 }
520         }
521         else
522         {
523                 fprintf( stderr,
524 "Usage: %s [options] [producer [name=value]* ]+\n"
525 "Options:\n"
526 "  -attach filter[:arg] [name=value]*       Attach a filter to the output\n"
527 "  -attach-cut filter[:arg] [name=value]*   Attach a filter to a cut\n"
528 "  -attach-track filter[:arg] [name=value]* Attach a filter to a track\n"
529 "  -attach-clip filter[:arg] [name=value]*  Attach a filter to a producer\n"
530 "  -audio-track | -hide-video               Add an audio-only track\n"
531 "  -blank frames                            Add blank silence to a track\n"
532 "  -consumer id[:arg] [name=value]*         Set the consumer (sink)\n"
533 "  -debug                                   Set the logging level to debug\n"
534 "  -filter filter[:arg] [name=value]*       Add a filter to the current track\n"
535 "  -group [name=value]*                     Apply properties repeatedly\n"
536 "  -help                                    Show this message\n"
537 "  -join clips                              Join multiple clips into one cut\n"
538 "  -mix length                              Add a mix between the last two cuts\n"
539 "  -mixer transition                        Add a transition to the mix\n"
540 "  -null-track | -hide-track                Add a hidden track\n"
541 "  -profile name                            Set the processing settings\n"
542 "  -progress                                Display progress along with position\n"
543 "  -remove                                  Remove the most recent cut\n"
544 "  -repeat times                            Repeat the last cut\n"
545 "  -query                                   List all of the registered services\n"
546 "  -query \"consumers\" | \"consumer\"=id       List consumers or show info about one\n"
547 "  -query \"filters\" | \"filter\"=id           List filters or show info about one\n"
548 "  -query \"producers\" | \"producer\"=id       List producers or show info about one\n"
549 "  -query \"transitions\" | \"transition\"=id   List transitions, show info about one\n"
550 "  -serialise [filename]                    Write the commands to a text file\n"
551 "  -silent                                  Do not display position/transport\n"
552 "  -split relative-frame                    Split the last cut into two cuts\n"
553 "  -swap                                    Rearrange the last two cuts\n"
554 "  -track                                   Add a track\n"
555 "  -transition id[:arg] [name=value]*       Add a transition\n"
556 "  -verbose                                 Set the logging level to verbose\n"
557 "  -version                                 Show the version and copyright\n"
558 "  -video-track | -hide-audio               Add a video-only track\n"
559 "For more help: <http://www.mltframework.org/>\n",
560                 basename( argv[0] ) );
561         }
562
563         // Close the producer
564         if ( melt != NULL )
565                 mlt_producer_close( melt );
566
567         // Close the consumer
568         if ( consumer != NULL )
569                 mlt_consumer_close( consumer );
570
571         // Close the factory
572         mlt_profile_close( profile );
573
574 exit_factory:
575                 
576         mlt_factory_close( );
577
578         return 0;
579 }