]> git.sesse.net Git - mlt/blob - src/modules/core/consumer_multi.c
default multi consumer to real_time=-1
[mlt] / src / modules / core / consumer_multi.c
1 /*
2  * Copyright (C) 2011 Ushodaya Enterprises Limited
3  * Author: Dan Dennedy <dan@dennedy.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <framework/mlt.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <pthread.h>
25
26 // Forward references
27 static int start( mlt_consumer consumer );
28 static int stop( mlt_consumer consumer );
29 static int is_stopped( mlt_consumer consumer );
30 static void *consumer_thread( void *arg );
31 static void consumer_close( mlt_consumer consumer );
32
33 static mlt_properties normalisers = NULL;
34
35 /** Initialise the consumer.
36 */
37
38 mlt_consumer consumer_multi_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
39 {
40         mlt_consumer consumer = mlt_consumer_new( profile );
41
42         if ( consumer )
43         {
44                 // Assign callbacks
45                 consumer->close = consumer_close;
46                 consumer->start = start;
47                 consumer->stop = stop;
48                 consumer->is_stopped = is_stopped;
49
50                 mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "resource", arg );
51                 mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(consumer), "real_time", -1 );
52         }
53
54         return consumer;
55 }
56
57 static mlt_consumer create_consumer( mlt_profile profile, char *id )
58 {
59         char *myid = id ? strdup( id ) : NULL;
60         char *arg = myid ? strchr( myid, ':' ) : NULL;
61         if ( arg != NULL )
62                 *arg ++ = '\0';
63         mlt_consumer consumer = mlt_factory_consumer( profile, myid, arg );
64         if ( myid )
65                 free( myid );
66         return consumer;
67 }
68
69 static void create_filter( mlt_profile profile, mlt_service service, char *effect, int *created )
70 {
71         char *id = strdup( effect );
72         char *arg = strchr( id, ':' );
73         if ( arg != NULL )
74                 *arg ++ = '\0';
75
76         // The swscale and avcolor_space filters require resolution as arg to test compatibility
77         if ( strncmp( effect, "swscale", 7 ) == 0 || strncmp( effect, "avcolo", 6 ) == 0 )
78                 arg = (char*) mlt_properties_get_int( MLT_SERVICE_PROPERTIES( service ), "_real_width" );
79
80         mlt_filter filter = mlt_factory_filter( profile, id, arg );
81         if ( filter != NULL )
82         {
83                 mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 );
84                 mlt_service_attach( service, filter );
85                 mlt_filter_close( filter );
86                 *created = 1;
87         }
88         free( id );
89 }
90
91 static void attach_normalisers( mlt_profile profile, mlt_service service )
92 {
93         // Loop variable
94         int i;
95
96         // Tokeniser
97         mlt_tokeniser tokeniser = mlt_tokeniser_init( );
98
99         // We only need to load the normalising properties once
100         if ( normalisers == NULL )
101         {
102                 char temp[ 1024 ];
103                 snprintf( temp, sizeof(temp), "%s/core/loader.ini", mlt_environment( "MLT_DATA" ) );
104                 normalisers = mlt_properties_load( temp );
105                 mlt_factory_register_for_clean_up( normalisers, ( mlt_destructor )mlt_properties_close );
106         }
107
108         // Apply normalisers
109         for ( i = 0; i < mlt_properties_count( normalisers ); i ++ )
110         {
111                 int j = 0;
112                 int created = 0;
113                 char *value = mlt_properties_get_value( normalisers, i );
114                 mlt_tokeniser_parse_new( tokeniser, value, "," );
115                 for ( j = 0; !created && j < mlt_tokeniser_count( tokeniser ); j ++ )
116                         create_filter( profile, service, mlt_tokeniser_get_string( tokeniser, j ), &created );
117         }
118
119         // Close the tokeniser
120         mlt_tokeniser_close( tokeniser );
121
122         // Attach the audio and video format converters
123         int created = 0;
124         create_filter( profile, service, "avcolor_space", &created );
125         if ( !created )
126                 create_filter( profile, service, "imageconvert", &created );
127         create_filter( profile, service, "audioconvert", &created );
128 }
129
130 static mlt_consumer generate_consumer( mlt_consumer consumer, mlt_properties props, int index )
131 {
132         mlt_profile profile = NULL;
133         if ( mlt_properties_get( props, "mlt_profile" ) )
134                 profile = mlt_profile_init( mlt_properties_get( props, "mlt_profile" ) );
135         if ( !profile )
136                 profile = mlt_profile_clone( mlt_service_profile( MLT_CONSUMER_SERVICE(consumer) ) );
137         mlt_consumer nested = create_consumer( profile, mlt_properties_get( props, "mlt_service" ) );
138
139         if ( nested )
140         {
141                 mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer);
142                 mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested);
143                 char key[30];
144
145                 snprintf( key, sizeof(key), "%d.consumer", index );
146                 mlt_properties_set_data( properties, key, nested, 0, (mlt_destructor) mlt_consumer_close, NULL );
147                 snprintf( key, sizeof(key), "%d.profile", index );
148                 mlt_properties_set_data( properties, key, profile, 0, (mlt_destructor) mlt_profile_close, NULL );
149
150                 mlt_properties_set_int( nested_props, "put_mode", 1 );
151                 mlt_properties_pass_list( nested_props, properties, "terminate_on_pause" );
152                 mlt_properties_set( props, "consumer", NULL );
153                 // set mlt_profile before other properties to facilitate presets
154                 mlt_properties_pass_list( nested_props, props, "mlt_profile" );
155                 mlt_properties_inherit( nested_props, props );
156
157                 attach_normalisers( profile, MLT_CONSUMER_SERVICE(nested) );
158         }
159         else
160         {
161                 mlt_profile_close( profile );
162         }
163         return nested;
164 }
165
166 static void foreach_consumer_init( mlt_consumer consumer )
167 {
168         const char *resource = mlt_properties_get( MLT_CONSUMER_PROPERTIES(consumer), "resource" );
169         mlt_properties properties = mlt_properties_parse_yaml( resource );
170         char key[20];
171         int index = 0;
172
173         if ( mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "0", NULL ) )
174         {
175                 // Properties set directly by application
176                 mlt_properties p;
177
178                 if ( properties )
179                         mlt_properties_close( properties );
180                 properties = MLT_CONSUMER_PROPERTIES(consumer);
181                 do {
182                         snprintf( key, sizeof(key), "%d", index );
183                         if ( ( p = mlt_properties_get_data( properties, key, NULL ) ) )
184                                 generate_consumer( consumer, p, index++ );
185                 } while ( p );
186         }
187         else if ( properties && mlt_properties_get_data( properties, "0", NULL ) )
188         {
189                 // YAML file supplied
190                 mlt_properties p;
191
192                 do {
193                         snprintf( key, sizeof(key), "%d", index );
194                         if ( ( p = mlt_properties_get_data( properties, key, NULL ) ) )
195                                 generate_consumer( consumer, p, index++ );
196                 } while ( p );
197                 mlt_properties_close( properties );
198         }
199         else
200         {
201                 // properties file supplied or properties on this consumer
202                 const char *s;
203
204                 if ( properties )
205                         mlt_properties_close( properties );
206                 if ( resource )
207                         properties = mlt_properties_load( resource );
208                 else
209                         properties = MLT_CONSUMER_PROPERTIES( consumer );
210
211                 do {
212                         snprintf( key, sizeof(key), "%d", index );
213                         if ( ( s = mlt_properties_get( properties, key ) ) )
214                         {
215                                 mlt_properties p = mlt_properties_new();
216                                 int i, count;
217
218                                 if ( !p ) break;
219                                 mlt_properties_set( p, "mlt_service", mlt_properties_get( properties, key ) );
220                                 snprintf( key, sizeof(key), "%d.", index );
221
222                                 count = mlt_properties_count( properties );
223                                 for ( i = 0; i < count; i++ )
224                                 {
225                                         char *name = mlt_properties_get_name( properties, i );
226                                         if ( !strncmp( name, key, strlen(key) ) )
227                                                 mlt_properties_set( p, name + strlen(key),
228                                                         mlt_properties_get_value( properties, i ) );
229                                 }
230                                 generate_consumer( consumer, p, index++ );
231                                 mlt_properties_close( p );
232                         }
233                 } while ( s );
234                 if ( resource )
235                         mlt_properties_close( properties );
236         }
237 }
238
239 static void foreach_consumer_start( mlt_consumer consumer )
240 {
241         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
242         mlt_consumer nested = NULL;
243         char key[30];
244         int index = 0;
245
246         do {
247                 snprintf( key, sizeof(key), "%d.consumer", index++ );
248                 nested = mlt_properties_get_data( properties, key, NULL );
249                 if ( nested )
250                 {
251                         mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested);
252                         mlt_properties_set_position( nested_props, "_multi_position", 0 );
253                         mlt_properties_set_data( nested_props, "_multi_audio", NULL, 0, NULL, NULL );
254                         mlt_properties_set_int( nested_props, "_multi_samples", 0 );
255                         mlt_consumer_start( nested );
256                 }
257         } while ( nested );
258 }
259
260 static void foreach_consumer_refresh( mlt_consumer consumer )
261 {
262         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
263         mlt_consumer nested = NULL;
264         char key[30];
265         int index = 0;
266
267         do {
268                 snprintf( key, sizeof(key), "%d.consumer", index++ );
269                 nested = mlt_properties_get_data( properties, key, NULL );
270                 if ( nested ) mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(nested), "refresh", 1 );
271         } while ( nested );
272 }
273
274 static void foreach_consumer_put( mlt_consumer consumer, mlt_frame frame )
275 {
276         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
277         mlt_consumer nested = NULL;
278         char key[30];
279         int index = 0;
280
281         do {
282                 snprintf( key, sizeof(key), "%d.consumer", index++ );
283                 nested = mlt_properties_get_data( properties, key, NULL );
284                 if ( nested )
285                 {
286                         mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested);
287                         double self_fps = mlt_properties_get_double( properties, "fps" );
288                         double nested_fps = mlt_properties_get_double( nested_props, "fps" );
289                         mlt_position nested_pos = mlt_properties_get_position( nested_props, "_multi_position" );
290                         mlt_position self_pos = mlt_frame_get_position( frame );
291                         double self_time = self_pos / self_fps;
292                         double nested_time = nested_pos / nested_fps;
293
294                         // get the audio for the current frame
295                         uint8_t *buffer = NULL;
296                         mlt_audio_format format = mlt_audio_s16;
297                         int channels = mlt_properties_get_int( properties, "channels" );
298                         int frequency = mlt_properties_get_int( properties, "frequency" );
299                         int current_samples = mlt_sample_calculator( self_fps, frequency, self_pos );
300                         mlt_frame_get_audio( frame, (void**) &buffer, &format, &frequency, &channels, &current_samples );
301                         int current_size = mlt_audio_format_size( format, current_samples, channels );
302
303                         // get any leftover audio
304                         int prev_size = 0;
305                         uint8_t *prev_buffer = mlt_properties_get_data( nested_props, "_multi_audio", &prev_size );
306                         uint8_t *new_buffer = NULL;
307                         if ( prev_size > 0 )
308                         {
309                                 new_buffer = mlt_pool_alloc( prev_size + current_size );
310                                 memcpy( new_buffer, prev_buffer, prev_size );
311                                 memcpy( new_buffer + prev_size, buffer, current_size );
312                                 buffer = new_buffer;
313                         }
314                         current_size += prev_size;
315                         current_samples += mlt_properties_get_int( nested_props, "_multi_samples" );
316
317                         while ( nested_time <= self_time )
318                         {
319                                 // put ideal number of samples into cloned frame
320                                 mlt_frame clone_frame = mlt_frame_clone( frame, 0 );
321                                 int nested_samples = mlt_sample_calculator( nested_fps, frequency, nested_pos );
322                                 // -10 is an optimization to avoid tiny amounts of leftover samples
323                                 nested_samples = nested_samples > current_samples - 10 ? current_samples : nested_samples;
324                                 int nested_size = mlt_audio_format_size( format, nested_samples, channels );
325                                 if ( nested_size > 0 )
326                                 {
327                                         prev_buffer = mlt_pool_alloc( nested_size );
328                                         memcpy( prev_buffer, buffer, nested_size );
329                                 }
330                                 else
331                                 {
332                                         prev_buffer = NULL;
333                                         nested_size = 0;
334                                 }
335                                 mlt_frame_set_audio( clone_frame, prev_buffer, format, nested_size, mlt_pool_release );
336                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_samples", nested_samples );
337                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_frequency", frequency );
338                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_channels", channels );
339
340                                 // chomp the audio
341                                 current_samples -= nested_samples;
342                                 current_size -= nested_size;
343                                 buffer += nested_size;
344
345                                 // send frame to nested consumer
346                                 mlt_consumer_put_frame( nested, clone_frame );
347                                 mlt_properties_set_position( nested_props, "_multi_position", ++nested_pos );
348                                 nested_time = nested_pos / nested_fps;
349                         }
350
351                         // save any remaining audio
352                         if ( current_size > 0 )
353                         {
354                                 prev_buffer = mlt_pool_alloc( current_size );
355                                 memcpy( prev_buffer, buffer, current_size );
356                         }
357                         else
358                         {
359                                 prev_buffer = NULL;
360                                 current_size = 0;
361                         }
362                         mlt_pool_release( new_buffer );
363                         mlt_properties_set_data( nested_props, "_multi_audio", prev_buffer, current_size, mlt_pool_release, NULL );
364                         mlt_properties_set_int( nested_props, "_multi_samples", current_samples );
365                 }
366         } while ( nested );
367 }
368
369 static void foreach_consumer_stop( mlt_consumer consumer )
370 {
371         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
372         mlt_consumer nested = NULL;
373         char key[30];
374         int index = 0;
375
376         do {
377                 snprintf( key, sizeof(key), "%d.consumer", index++ );
378                 nested = mlt_properties_get_data( properties, key, NULL );
379                 if ( nested ) mlt_consumer_stop( nested );
380         } while ( nested );
381 }
382
383 /** Start the consumer.
384 */
385
386 static int start( mlt_consumer consumer )
387 {
388         // Check that we're not already running
389         if ( is_stopped( consumer ) )
390         {
391                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
392                 pthread_t *thread = calloc( 1, sizeof( pthread_t ) );
393
394                 // Assign the thread to properties with automatic dealloc
395                 mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL );
396
397                 // Set the running state
398                 mlt_properties_set_int( properties, "running", 1 );
399
400                 // Construct and start nested consumers
401                 if ( !mlt_properties_get_data( properties, "0.consumer", NULL ) )
402                         foreach_consumer_init( consumer );
403                 foreach_consumer_start( consumer );
404
405                 // Create the thread
406                 pthread_create( thread, NULL, consumer_thread, consumer );
407         }
408         return 0;
409 }
410
411 /** Stop the consumer.
412 */
413
414 static int stop( mlt_consumer consumer )
415 {
416         // Check that we're running
417         if ( !is_stopped( consumer ) )
418         {
419                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
420                 pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL );
421
422                 // Stop the thread
423                 mlt_properties_set_int( properties, "running", 0 );
424
425                 // Wait for termination
426                 if ( thread )
427                 {
428                         foreach_consumer_refresh( consumer );
429                         pthread_join( *thread, NULL );
430                 }
431
432                 // Stop nested consumers
433                 foreach_consumer_stop( consumer );
434         }
435
436         return 0;
437 }
438
439 /** Determine if the consumer is stopped.
440 */
441
442 static int is_stopped( mlt_consumer consumer )
443 {
444         return !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "running" );
445 }
446
447 /** The main thread - the argument is simply the consumer.
448 */
449
450 static void *consumer_thread( void *arg )
451 {
452         mlt_consumer consumer = arg;
453         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
454         mlt_frame frame = NULL;
455
456         // Determine whether to stop at end-of-media
457         int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
458         int terminated = 0;
459
460         // Loop while running
461         while ( !terminated && !is_stopped( consumer ) )
462         {
463                 // Get the next frame
464                 frame = mlt_consumer_rt_frame( consumer );
465
466                 // Check for termination
467                 if ( terminate_on_pause && frame )
468                         terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0;
469
470                 // Check that we have a frame to work with
471                 if ( frame && !terminated && !is_stopped( consumer ) )
472                 {
473                         if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered" ) )
474                         {
475                                 if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "_speed" ) == 0 )
476                                         foreach_consumer_refresh( consumer );
477                                 foreach_consumer_put( consumer, frame );
478                                 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
479                         }
480                         else
481                         {
482                                 int dropped = mlt_properties_get_int( properties, "_dropped" );
483                                 mlt_log_info( MLT_CONSUMER_SERVICE(consumer), "dropped frame %d\n", ++dropped );
484                                 mlt_properties_set_int( properties, "_dropped", dropped );
485                         }
486                         mlt_frame_close( frame );
487                 }
488                 else
489                 {
490                         if ( frame ) mlt_frame_close( frame );
491                         foreach_consumer_put( consumer, NULL );
492                         terminated = 1;
493                 }
494         }
495
496         // Indicate that the consumer is stopped
497         mlt_properties_set_int( properties, "running", 0 );
498         mlt_consumer_stopped( consumer );
499
500         return NULL;
501 }
502
503 /** Close the consumer.
504 */
505
506 static void consumer_close( mlt_consumer consumer )
507 {
508         mlt_consumer_stop( consumer );
509         // Close the parent
510         mlt_consumer_close( consumer );
511         free( consumer );
512 }