X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fcore%2Fconsumer_multi.c;h=8d0b66eb71a17514e9d21bf60747aa0dba5f3afc;hb=cd57591f30d646a5a650d8e880ed24130eddc4b6;hp=05372009964813406400e6634f5d24dc6b84fd22;hpb=5e6855440288fd16dcbed190a49d9fb9e0341ecb;p=mlt diff --git a/src/modules/core/consumer_multi.c b/src/modules/core/consumer_multi.c index 05372009..8d0b66eb 100644 --- a/src/modules/core/consumer_multi.c +++ b/src/modules/core/consumer_multi.c @@ -22,6 +22,7 @@ #include #include #include +#include // Forward references static int start( mlt_consumer consumer ); @@ -29,6 +30,7 @@ static int stop( mlt_consumer consumer ); static int is_stopped( mlt_consumer consumer ); static void *consumer_thread( void *arg ); static void consumer_close( mlt_consumer consumer ); +static void purge( mlt_consumer consumer ); static mlt_properties normalisers = NULL; @@ -41,25 +43,36 @@ mlt_consumer consumer_multi_init( mlt_profile profile, mlt_service_type type, co if ( consumer ) { + mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); + + // Set defaults + mlt_properties_set( properties, "resource", arg ); + mlt_properties_set_int( properties, "real_time", -1 ); + mlt_properties_set_int( properties, "terminate_on_pause", 1 ); + + // Init state + mlt_properties_set_int( properties, "joined", 1 ); + // Assign callbacks consumer->close = consumer_close; consumer->start = start; consumer->stop = stop; consumer->is_stopped = is_stopped; - - mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "resource", arg ); + consumer->purge = purge; } return consumer; } -static mlt_consumer create_consumer( mlt_profile profile, char *id ) +static mlt_consumer create_consumer( mlt_profile profile, char *id, char *arg ) { char *myid = id ? strdup( id ) : NULL; - char *arg = myid ? strchr( myid, ':' ) : NULL; - if ( arg != NULL ) - *arg ++ = '\0'; - mlt_consumer consumer = mlt_factory_consumer( profile, myid, arg ); + char *myarg = ( myid && !arg ) ? strchr( myid, ':' ) : NULL; + if ( myarg ) + *myarg ++ = '\0'; + else + myarg = arg; + mlt_consumer consumer = mlt_factory_consumer( profile, myid, myarg ); if ( myid ) free( myid ); return consumer; @@ -72,17 +85,27 @@ static void create_filter( mlt_profile profile, mlt_service service, char *effec if ( arg != NULL ) *arg ++ = '\0'; - // The swscale and avcolor_space filters require resolution as arg to test compatibility - if ( strncmp( effect, "swscale", 7 ) == 0 || strncmp( effect, "avcolo", 6 ) == 0 ) - arg = (char*) mlt_properties_get_int( MLT_SERVICE_PROPERTIES( service ), "_real_width" ); - - mlt_filter filter = mlt_factory_filter( profile, id, arg ); - if ( filter != NULL ) + // We cannot use GLSL-based filters here. + if ( strncmp( effect, "movit.", 6 ) && strncmp( effect, "glsl.", 5 ) ) { - mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 ); - mlt_service_attach( service, filter ); - mlt_filter_close( filter ); - *created = 1; + mlt_filter filter; + // The swscale and avcolor_space filters require resolution as arg to test compatibility + if ( strncmp( effect, "swscale", 7 ) == 0 || strncmp( effect, "avcolo", 6 ) == 0 ) + { + int width = mlt_properties_get_int( MLT_SERVICE_PROPERTIES( service ), "meta.media.width" ); + filter = mlt_factory_filter( profile, id, &width ); + } + else + { + filter = mlt_factory_filter( profile, id, arg ); + } + if ( filter ) + { + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 ); + mlt_service_attach( service, filter ); + mlt_filter_close( filter ); + *created = 1; + } } free( id ); } @@ -120,12 +143,27 @@ static void attach_normalisers( mlt_profile profile, mlt_service service ) // Attach the audio and video format converters int created = 0; + // movit.convert skips setting the frame->convert_image pointer if GLSL cannot be used. + mlt_filter filter = mlt_factory_filter( profile, "movit.convert", NULL ); + if ( filter != NULL ) + { + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 ); + mlt_service_attach( service, filter ); + mlt_filter_close( filter ); + created = 1; + } + // avcolor_space and imageconvert only set frame->convert_image if it has not been set. create_filter( profile, service, "avcolor_space", &created ); if ( !created ) create_filter( profile, service, "imageconvert", &created ); create_filter( profile, service, "audioconvert", &created ); } +static void on_frame_show( void *dummy, mlt_properties properties, mlt_frame frame ) +{ + mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); +} + static mlt_consumer generate_consumer( mlt_consumer consumer, mlt_properties props, int index ) { mlt_profile profile = NULL; @@ -133,7 +171,8 @@ static mlt_consumer generate_consumer( mlt_consumer consumer, mlt_properties pro profile = mlt_profile_init( mlt_properties_get( props, "mlt_profile" ) ); if ( !profile ) profile = mlt_profile_clone( mlt_service_profile( MLT_CONSUMER_SERVICE(consumer) ) ); - mlt_consumer nested = create_consumer( profile, mlt_properties_get( props, "mlt_service" ) ); + mlt_consumer nested = create_consumer( profile, mlt_properties_get( props, "mlt_service" ), + mlt_properties_get( props, "target" ) ); if ( nested ) { @@ -154,6 +193,14 @@ static mlt_consumer generate_consumer( mlt_consumer consumer, mlt_properties pro mlt_properties_inherit( nested_props, props ); attach_normalisers( profile, MLT_CONSUMER_SERVICE(nested) ); + + // Relay the first available consumer-frame-show event + mlt_event event = mlt_properties_get_data( properties, "frame-show-event", NULL ); + if ( !event ) + { + event = mlt_events_listen( nested_props, properties, "consumer-frame-show", (mlt_listener) on_frame_show ); + mlt_properties_set_data( properties, "frame-show-event", event, 0, /*mlt_event_close*/ NULL, NULL ); + } } else { @@ -212,12 +259,23 @@ static void foreach_consumer_init( mlt_consumer consumer ) if ( ( s = mlt_properties_get( properties, key ) ) ) { mlt_properties p = mlt_properties_new(); - int i, count; - if ( !p ) break; - mlt_properties_set( p, "mlt_service", mlt_properties_get( properties, key ) ); + + // Terminate mlt_service value at the argument delimiter if supplied. + // Needed here instead of just relying upon create_consumer() so that + // a properties preset is picked up correctly. + char *service = strdup( mlt_properties_get( properties, key ) ); + char *arg = strchr( service, ':' ); + if ( arg ) { + *arg ++ = '\0'; + mlt_properties_set( p, "target", arg ); + } + mlt_properties_set( p, "mlt_service", service ); + free( service ); + snprintf( key, sizeof(key), "%d.", index ); + int i, count; count = mlt_properties_count( properties ); for ( i = 0; i < count; i++ ) { @@ -316,7 +374,9 @@ static void foreach_consumer_put( mlt_consumer consumer, mlt_frame frame ) while ( nested_time <= self_time ) { // put ideal number of samples into cloned frame - mlt_frame clone_frame = mlt_frame_clone( frame, 0 ); + int deeply = index > 1 ? 1 : 0; + mlt_frame clone_frame = mlt_frame_clone( frame, deeply ); + mlt_properties clone_props = MLT_FRAME_PROPERTIES( clone_frame ); int nested_samples = mlt_sample_calculator( nested_fps, frequency, nested_pos ); // -10 is an optimization to avoid tiny amounts of leftover samples nested_samples = nested_samples > current_samples - 10 ? current_samples : nested_samples; @@ -332,15 +392,21 @@ static void foreach_consumer_put( mlt_consumer consumer, mlt_frame frame ) nested_size = 0; } mlt_frame_set_audio( clone_frame, prev_buffer, format, nested_size, mlt_pool_release ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_samples", nested_samples ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_frequency", frequency ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_channels", channels ); + mlt_properties_set_int( clone_props, "audio_samples", nested_samples ); + mlt_properties_set_int( clone_props, "audio_frequency", frequency ); + mlt_properties_set_int( clone_props, "audio_channels", channels ); // chomp the audio current_samples -= nested_samples; current_size -= nested_size; buffer += nested_size; + // Fix some things + mlt_properties_set_int( clone_props, "meta.media.width", + mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "width" ) ); + mlt_properties_set_int( clone_props, "meta.media.height", + mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "height" ) ); + // send frame to nested consumer mlt_consumer_put_frame( nested, clone_frame ); mlt_properties_set_position( nested_props, "_multi_position", ++nested_pos ); @@ -371,11 +437,27 @@ static void foreach_consumer_stop( mlt_consumer consumer ) mlt_consumer nested = NULL; char key[30]; int index = 0; + struct timespec tm = { 0, 1000 * 1000 }; do { snprintf( key, sizeof(key), "%d.consumer", index++ ); nested = mlt_properties_get_data( properties, key, NULL ); - if ( nested ) mlt_consumer_stop( nested ); + if ( nested ) + { + // Let consumer with terminate_on_pause stop on their own + if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES(nested), "terminate_on_pause" ) ) + { + // Send additional dummy frame to unlatch nested consumer's threads + mlt_consumer_put_frame( nested, mlt_frame_init( MLT_CONSUMER_SERVICE(consumer) ) ); + // wait for stop + while ( !mlt_consumer_is_stopped( nested ) ) + nanosleep( &tm, NULL ); + } + else + { + mlt_consumer_stop( nested ); + } + } } while ( nested ); } @@ -395,6 +477,7 @@ static int start( mlt_consumer consumer ) // Set the running state mlt_properties_set_int( properties, "running", 1 ); + mlt_properties_set_int( properties, "joined", 0 ); // Construct and start nested consumers if ( !mlt_properties_get_data( properties, "0.consumer", NULL ) ) @@ -413,7 +496,7 @@ static int start( mlt_consumer consumer ) static int stop( mlt_consumer consumer ) { // Check that we're running - if ( !is_stopped( consumer ) ) + if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES(consumer), "joined" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL ); @@ -427,6 +510,7 @@ static int stop( mlt_consumer consumer ) foreach_consumer_refresh( consumer ); pthread_join( *thread, NULL ); } + mlt_properties_set_int( properties, "joined", 1 ); // Stop nested consumers foreach_consumer_stop( consumer ); @@ -443,6 +527,27 @@ static int is_stopped( mlt_consumer consumer ) return !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "running" ); } +/** Purge each of the child consumers. +*/ + +static void purge( mlt_consumer consumer ) +{ + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + if ( mlt_properties_get_int( properties, "running" ) ) + { + mlt_consumer nested = NULL; + char key[30]; + int index = 0; + + do { + snprintf( key, sizeof(key), "%d.consumer", index++ ); + nested = mlt_properties_get_data( properties, key, NULL ); + if ( nested ) + mlt_consumer_purge( nested ); + } while ( nested ); + } +} + /** The main thread - the argument is simply the consumer. */ @@ -474,7 +579,6 @@ static void *consumer_thread( void *arg ) if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "_speed" ) == 0 ) foreach_consumer_refresh( consumer ); foreach_consumer_put( consumer, frame ); - mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } else { @@ -486,14 +590,18 @@ static void *consumer_thread( void *arg ) } else { - if ( frame ) mlt_frame_close( frame ); - foreach_consumer_put( consumer, NULL ); + if ( frame && terminated ) + { + // Send this termination frame to nested consumers for their cancellation + foreach_consumer_put( consumer, frame ); + } + if ( frame ) + mlt_frame_close( frame ); terminated = 1; } } // Indicate that the consumer is stopped - mlt_properties_set_int( properties, "running", 0 ); mlt_consumer_stopped( consumer ); return NULL; @@ -505,6 +613,7 @@ static void *consumer_thread( void *arg ) static void consumer_close( mlt_consumer consumer ) { mlt_consumer_stop( consumer ); + // Close the parent mlt_consumer_close( consumer ); free( consumer );