X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fxml%2Fproducer_xml.c;h=086657f4263d6ab9f7363c7a25c7ed8f24dae35e;hb=236717e4ecc23e458e191b6c9a2ba70915b80937;hp=b7bfed8195770f4091af434f59c6bc19ebbb566c;hpb=2027a87d227ee80413745b5ac3ccb39fd86d6fdc;p=mlt diff --git a/src/modules/xml/producer_xml.c b/src/modules/xml/producer_xml.c index b7bfed81..086657f4 100644 --- a/src/modules/xml/producer_xml.c +++ b/src/modules/xml/producer_xml.c @@ -22,9 +22,9 @@ // when the returned producer is closed). #include +#include #include #include -#include #include #include @@ -60,6 +60,7 @@ enum service_type mlt_dummy_filter_type, mlt_dummy_transition_type, mlt_dummy_producer_type, + mlt_dummy_consumer_type }; struct deserialise_context_s @@ -83,20 +84,44 @@ struct deserialise_context_s mlt_properties params; mlt_profile profile; int pass; + char *lc_numeric; + mlt_consumer consumer; + int multi_consumer; + int consumer_count; + int seekable; + mlt_consumer qglsl; }; typedef struct deserialise_context_s *deserialise_context; +/** Trim the leading and trailing whitespace from a string in-place. +*/ +static char* trim( char *s ) +{ + int n; + if ( s && ( n = strlen( s ) ) ) + { + int i = 0; + while ( i < n && isspace( s[i] ) ) i++; + while ( --n && isspace( s[n] ) ); + n = n - i + 1; + if ( n > 0 ) + memmove( s, s + i, n ); + s[ n ] = 0; + } + return s; +} + /** Convert the numerical current branch address to a dot-delimited string. */ -static char *serialise_branch( deserialise_context this, char *s ) +static char *serialise_branch( deserialise_context context, char *s ) { int i; s[0] = 0; - for ( i = 0; i < this->depth; i++ ) + for ( i = 0; i < context->depth; i++ ) { int len = strlen( s ); - snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", this->branch[ i ] ); + snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", context->branch[ i ] ); } return s; } @@ -104,19 +129,19 @@ static char *serialise_branch( deserialise_context this, char *s ) /** Push a service. */ -static int context_push_service( deserialise_context this, mlt_service that, enum service_type type ) +static int context_push_service( deserialise_context context, mlt_service that, enum service_type type ) { - int ret = this->stack_service_size >= STACK_SIZE - 1; + int ret = context->stack_service_size >= STACK_SIZE - 1; if ( ret == 0 ) { - this->stack_service[ this->stack_service_size ] = that; - this->stack_types[ this->stack_service_size++ ] = type; + context->stack_service[ context->stack_service_size ] = that; + context->stack_types[ context->stack_service_size++ ] = type; // Record the tree branch on which this service lives if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_xml_branch" ) == NULL ) { char s[ BRANCH_SIG_LEN ]; - mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( this, s ) ); + mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( context, s ) ); } } return ret; @@ -125,16 +150,22 @@ static int context_push_service( deserialise_context this, mlt_service that, enu /** Pop a service. */ -static mlt_service context_pop_service( deserialise_context this, enum service_type *type ) +static mlt_service context_pop_service( deserialise_context context, enum service_type *type ) { mlt_service result = NULL; - *type = invalid_type; - if ( this->stack_service_size > 0 ) + if ( type ) *type = mlt_invalid_type; + if ( context->stack_service_size > 0 ) { - result = this->stack_service[ -- this->stack_service_size ]; + result = context->stack_service[ -- context->stack_service_size ]; if ( type != NULL ) - *type = this->stack_types[ this->stack_service_size ]; + *type = context->stack_types[ context->stack_service_size ]; + // Set the service's profile and locale so mlt_property time-to-position conversions can get fps + if ( result ) + { + mlt_properties_set_data( MLT_SERVICE_PROPERTIES( result ), "_profile", context->profile, 0, NULL, NULL ); + mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( result ), context->lc_numeric ); + } } return result; } @@ -142,22 +173,22 @@ static mlt_service context_pop_service( deserialise_context this, enum service_t /** Push a node. */ -static int context_push_node( deserialise_context this, xmlNodePtr node ) +static int context_push_node( deserialise_context context, xmlNodePtr node ) { - int ret = this->stack_node_size >= STACK_SIZE - 1; + int ret = context->stack_node_size >= STACK_SIZE - 1; if ( ret == 0 ) - this->stack_node[ this->stack_node_size ++ ] = node; + context->stack_node[ context->stack_node_size ++ ] = node; return ret; } /** Pop a node. */ -static xmlNodePtr context_pop_node( deserialise_context this ) +static xmlNodePtr context_pop_node( deserialise_context context ) { xmlNodePtr result = NULL; - if ( this->stack_node_size > 0 ) - result = this->stack_node[ -- this->stack_node_size ]; + if ( context->stack_node_size > 0 ) + result = context->stack_node[ -- context->stack_node_size ]; return result; } @@ -251,7 +282,7 @@ static int add_producer( deserialise_context context, mlt_service service, mlt_p break; default: result = 0; - fprintf( stderr, "Producer defined inside something that isn't a container\n" ); + mlt_log_warning( NULL, "[producer_xml] Producer defined inside something that isn't a container\n" ); break; }; @@ -277,7 +308,7 @@ static int add_producer( deserialise_context context, mlt_service service, mlt_p /** Attach filters defined on that to this. */ -static void attach_filters( mlt_service this, mlt_service that ) +static void attach_filters( mlt_service service, mlt_service that ) { if ( that != NULL ) { @@ -285,7 +316,7 @@ static void attach_filters( mlt_service this, mlt_service that ) mlt_filter filter = NULL; for ( i = 0; ( filter = mlt_service_filter( that, i ) ) != NULL; i ++ ) { - mlt_service_attach( this, filter ); + mlt_service_attach( service, filter ); attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) ); } } @@ -353,6 +384,7 @@ static void on_start_tractor( deserialise_context context, const xmlChar *name, mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close ); + mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( service ), context->lc_numeric ); for ( ; atts != NULL && *atts != NULL; atts += 2 ) mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] ); @@ -379,7 +411,7 @@ static void on_end_tractor( deserialise_context context, const xmlChar *name ) } else { - fprintf( stderr, "Invalid state for tractor\n" ); + mlt_log_error( NULL, "[producer_xml] Invalid state for tractor\n" ); } } @@ -400,6 +432,7 @@ static void on_start_multitrack( deserialise_context context, const xmlChar *nam tractor = mlt_tractor_new( ); parent = MLT_TRACTOR_SERVICE( tractor ); track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close ); + mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( parent ), context->lc_numeric ); type = mlt_tractor_type; // Flag it as a synthesised tractor for clean up later @@ -421,7 +454,7 @@ static void on_start_multitrack( deserialise_context context, const xmlChar *nam } else { - fprintf( stderr, "Invalid multitrack position\n" ); + mlt_log_error( NULL, "[producer_xml] Invalid multitrack position\n" ); } } @@ -432,12 +465,12 @@ static void on_end_multitrack( deserialise_context context, const xmlChar *name mlt_service service = context_pop_service( context, &type ); if ( service == NULL || type != mlt_multitrack_type ) - fprintf( stderr, "End multitrack in the wrong state...\n" ); + mlt_log_error( NULL, "[producer_xml] End multitrack in the wrong state...\n" ); } static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts) { - mlt_playlist playlist = mlt_playlist_init( ); + mlt_playlist playlist = mlt_playlist_new( context->profile ); mlt_service service = MLT_PLAYLIST_SERVICE( playlist ); mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); @@ -481,7 +514,7 @@ static void on_end_playlist( deserialise_context context, const xmlChar *name ) } else { - fprintf( stderr, "Invalid state of playlist end %d\n", type ); + mlt_log_error( NULL, "[producer_xml] Invalid state of playlist end %d\n", type ); } } @@ -499,44 +532,6 @@ static void on_start_producer( deserialise_context context, const xmlChar *name, mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] ); } -// Parse a SMIL clock value (as produced by Kino 0.9.1) and return position in frames -static mlt_position parse_clock_value( char *value, double fps ) -{ - // This implementation expects a fully specified clock value - no optional - // parts (e.g. 1:05) - char *pos, *copy = strdup( value ); - int hh, mm, ss, ms; - mlt_position result = -1; - - value = copy; - pos = strchr( value, ':' ); - if ( !pos ) - return result; - *pos = '\0'; - hh = atoi( value ); - value = pos + 1; - - pos = strchr( value, ':' ); - if ( !pos ) - return result; - *pos = '\0'; - mm = atoi( value ); - value = pos + 1; - - pos = strchr( value, '.' ); - if ( !pos ) - return result; - *pos = '\0'; - ss = atoi( value ); - value = pos + 1; - - ms = atoi( value ); - free( copy ); - result = ( fps * ( ( (hh * 3600) + (mm * 60) + ss ) * 1000 + ms ) / 1000 + 0.5 ); - - return result; -} - static void on_end_producer( deserialise_context context, const xmlChar *name ) { enum service_type type; @@ -560,7 +555,7 @@ static void on_end_producer( deserialise_context context, const xmlChar *name ) // Instantiate the producer if ( mlt_properties_get( properties, "mlt_service" ) != NULL ) { - char *service_name = mlt_properties_get( properties, "mlt_service" ); + char *service_name = trim( mlt_properties_get( properties, "mlt_service" ) ); if ( resource ) { char *temp = calloc( 1, strlen( service_name ) + strlen( resource ) + 2 ); @@ -577,19 +572,28 @@ static void on_end_producer( deserialise_context context, const xmlChar *name ) } // Just in case the plugin requested doesn't exist... - if ( producer == NULL && resource != NULL ) + if ( !producer && resource ) producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) ); - - if ( producer == NULL ) + if ( !producer ) + mlt_log_error( NULL, "[producer_xml] failed to load producer \"%s\"\n", resource ); + if ( !producer ) producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "+INVALID.txt" ) ); - - if ( producer == NULL ) + if ( !producer ) producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "colour:red" ) ); + if ( !producer ) + { + mlt_service_close( service ); + free( service ); + return; + } // Track this producer track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close ); + mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( producer ), context->lc_numeric ); + if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( producer ), "seekable" ) ) + context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" ); - // Propogate the properties + // Propagate the properties qualify_property( context, properties, "resource" ); qualify_property( context, properties, "luma" ); qualify_property( context, properties, "luma.resource" ); @@ -601,33 +605,17 @@ static void on_end_producer( deserialise_context context, const xmlChar *name ) mlt_position out = -1; // Get in - if ( mlt_properties_get( properties, "in" ) != NULL ) + if ( mlt_properties_get( properties, "in" ) ) in = mlt_properties_get_position( properties, "in" ); // Let Kino-SMIL clipBegin be a synonym for in - if ( mlt_properties_get( properties, "clipBegin" ) != NULL ) - { - if ( strchr( mlt_properties_get( properties, "clipBegin" ), ':' ) ) - // Parse clock value - in = parse_clock_value( mlt_properties_get( properties, "clipBegin" ), - mlt_producer_get_fps( MLT_PRODUCER( producer ) ) ); - else - // Parse frames value - in = mlt_properties_get_position( properties, "clipBegin" ); - } + else if ( mlt_properties_get( properties, "clipBegin" ) ) + in = mlt_properties_get_position( properties, "clipBegin" ); // Get out - if ( mlt_properties_get( properties, "out" ) != NULL ) + if ( mlt_properties_get( properties, "out" ) ) out = mlt_properties_get_position( properties, "out" ); // Let Kino-SMIL clipEnd be a synonym for out - if ( mlt_properties_get( properties, "clipEnd" ) != NULL ) - { - if ( strchr( mlt_properties_get( properties, "clipEnd" ), ':' ) ) - // Parse clock value - out = parse_clock_value( mlt_properties_get( properties, "clipEnd" ), - mlt_producer_get_fps( MLT_PRODUCER( producer ) ) ); - else - // Parse frames value - out = mlt_properties_get_position( properties, "clipEnd" ); - } + else if ( mlt_properties_get( properties, "clipEnd" ) ) + out = mlt_properties_get_position( properties, "clipEnd" ); // Remove in and out mlt_properties_set( properties, "in", NULL ); mlt_properties_set( properties, "out", NULL ); @@ -683,8 +671,12 @@ static void on_end_producer( deserialise_context context, const xmlChar *name ) // Push the producer onto the stack context_push_service( context, producer, mlt_producer_type ); } + } + if ( service ) + { mlt_service_close( service ); + free( service ); } } @@ -693,7 +685,6 @@ static void on_start_blank( deserialise_context context, const xmlChar *name, co // Get the playlist from the stack enum service_type type; mlt_service service = context_pop_service( context, &type ); - mlt_position length = 0; if ( type == mlt_playlist_type && service != NULL ) { @@ -702,20 +693,18 @@ static void on_start_blank( deserialise_context context, const xmlChar *name, co { if ( xmlStrcmp( atts[0], _x("length") ) == 0 ) { - length = atoll( _s(atts[1]) ); + // Append a blank to the playlist + mlt_playlist_blank_time( MLT_PLAYLIST( service ), _s(atts[1]) ); break; } } - // Append a blank to the playlist - mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 ); - // Push the playlist back onto the stack context_push_service( context, service, type ); } else { - fprintf( stderr, "blank without a playlist - a definite no no\n" ); + mlt_log_error( NULL, "[producer_xml] blank without a playlist - a definite no no\n" ); } } @@ -723,6 +712,8 @@ static void on_start_entry( deserialise_context context, const xmlChar *name, co { mlt_producer entry = NULL; mlt_properties temp = mlt_properties_new( ); + mlt_properties_set_data( temp, "_profile", context->profile, 0, NULL, NULL ); + mlt_properties_set_lcnumeric( temp, context->lc_numeric ); for ( ; atts != NULL && *atts != NULL; atts += 2 ) { @@ -741,7 +732,7 @@ static void on_start_entry( deserialise_context context, const xmlChar *name, co if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL ) { mlt_playlist_clip_info info; - enum service_type parent_type = invalid_type; + enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service( context, &parent_type ); mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL ); @@ -769,7 +760,7 @@ static void on_start_entry( deserialise_context context, const xmlChar *name, co } else { - fprintf( stderr, "Entry not part of a playlist...\n" ); + mlt_log_error( NULL, "[producer_xml] Entry not part of a playlist...\n" ); } context_push_service( context, parent, parent_type ); @@ -784,12 +775,12 @@ static void on_start_entry( deserialise_context context, const xmlChar *name, co static void on_end_entry( deserialise_context context, const xmlChar *name ) { // Get the entry from the stack - enum service_type entry_type = invalid_type; + enum service_type entry_type = mlt_invalid_type; mlt_service entry = context_pop_service( context, &entry_type ); if ( entry == NULL && entry_type != mlt_entry_type ) { - fprintf( stderr, "Invalid state at end of entry\n" ); + mlt_log_error( NULL, "[producer_xml] Invalid state at end of entry\n" ); } } @@ -827,7 +818,7 @@ static void on_end_track( deserialise_context context, const xmlChar *name ) if ( track != NULL && track_type == mlt_entry_type ) { mlt_properties track_props = MLT_SERVICE_PROPERTIES( track ); - enum service_type parent_type = invalid_type; + enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service( context, &parent_type ); mlt_multitrack multitrack = NULL; @@ -839,7 +830,7 @@ static void on_end_track( deserialise_context context, const xmlChar *name ) else if ( parent_type == mlt_multitrack_type ) multitrack = MLT_MULTITRACK( parent ); else - fprintf( stderr, "track contained in an invalid container\n" ); + mlt_log_error( NULL, "[producer_xml] track contained in an invalid container\n" ); if ( multitrack != NULL ) { @@ -879,12 +870,16 @@ static void on_end_track( deserialise_context context, const xmlChar *name ) if ( parent != NULL ) context_push_service( context, parent, parent_type ); - - mlt_service_close( track ); } else { - fprintf( stderr, "Invalid state at end of track\n" ); + mlt_log_error( NULL, "[producer_xml] Invalid state at end of track\n" ); + } + + if ( track ) + { + mlt_service_close( track ); + free( track ); } } @@ -909,15 +904,27 @@ static void on_end_filter( deserialise_context context, const xmlChar *name ) mlt_service service = context_pop_service( context, &type ); mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); - enum service_type parent_type = invalid_type; + enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service( context, &parent_type ); if ( service != NULL && type == mlt_dummy_filter_type ) { - mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, mlt_properties_get( properties, "mlt_service" ), NULL ) ); + char *id = trim( mlt_properties_get( properties, "mlt_service" ) ); + mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, id, NULL ) ); mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter ); + if ( !filter ) + { + mlt_log_error( NULL, "[producer_xml] failed to load filter \"%s\"\n", id ); + if ( parent ) + context_push_service( context, parent, parent_type ); + mlt_service_close( service ); + free( service ); + return; + } + track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close ); + mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( filter ), context->lc_numeric ); // Propogate the properties qualify_property( context, properties, "resource" ); @@ -951,15 +958,18 @@ static void on_end_filter( deserialise_context context, const xmlChar *name ) } else { - fprintf( stderr, "filter closed with invalid parent...\n" ); + mlt_log_error( NULL, "[producer_xml] filter closed with invalid parent...\n" ); } - - // Close the dummy filter service - mlt_service_close( service ); } else { - fprintf( stderr, "Invalid top of stack on filter close\n" ); + mlt_log_error( NULL, "[producer_xml] Invalid top of stack on filter close\n" ); + } + + if ( service ) + { + mlt_service_close( service ); + free(service); } } @@ -984,16 +994,26 @@ static void on_end_transition( deserialise_context context, const xmlChar *name mlt_service service = context_pop_service( context, &type ); mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); - enum service_type parent_type = invalid_type; + enum service_type parent_type = mlt_invalid_type; mlt_service parent = context_pop_service( context, &parent_type ); if ( service != NULL && type == mlt_dummy_transition_type ) { - char *id = mlt_properties_get( properties, "mlt_service" ); + char *id = trim( mlt_properties_get( properties, "mlt_service" ) ); mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) ); mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect ); + if ( !effect ) + { + mlt_log_error( NULL, "[producer_xml] failed to load transition \"%s\"\n", id ); + if ( parent ) + context_push_service( context, parent, parent_type ); + mlt_service_close( service ); + free( service ); + return; + } track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close ); + mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( effect ), context->lc_numeric ); // Propogate the properties qualify_property( context, properties, "resource" ); @@ -1014,16 +1034,16 @@ static void on_end_transition( deserialise_context context, const xmlChar *name mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) ); if ( mlt_properties_get_int( properties, "a_track" ) == mlt_properties_get_int( properties, "b_track" ) ) mlt_properties_set_int( properties, "b_track", mlt_properties_get_int( properties, "a_track" ) + 1 ); - mlt_field_plant_transition( field, MLT_TRANSITION( effect ), + mlt_field_plant_transition( field, MLT_TRANSITION( effect ), mlt_properties_get_int( properties, "a_track" ), mlt_properties_get_int( properties, "b_track" ) ); - mlt_transition_set_in_and_out( MLT_TRANSITION( effect ), + mlt_transition_set_in_and_out( MLT_TRANSITION( effect ), mlt_properties_get_int( properties, "in" ), mlt_properties_get_int( properties, "out" ) ); } else { - fprintf( stderr, "Misplaced transition - ignoring\n" ); + mlt_log_warning( NULL, "[producer_xml] Misplaced transition - ignoring\n" ); } // Put the parent back on the stack @@ -1031,15 +1051,96 @@ static void on_end_transition( deserialise_context context, const xmlChar *name } else { - fprintf( stderr, "transition closed with invalid parent...\n" ); + mlt_log_error( NULL, "[producer_xml] transition closed with invalid parent...\n" ); } - // Close the dummy filter service - mlt_service_close( service ); } else { - fprintf( stderr, "Invalid top of stack on transition close\n" ); + mlt_log_error( NULL, "[producer_xml] Invalid top of stack on transition close\n" ); + } + + if ( service ) + { + mlt_service_close( service ); + free( service ); + } +} + +static void on_start_consumer( deserialise_context context, const xmlChar *name, const xmlChar **atts) +{ + if ( context->pass == 1 ) + { + mlt_properties properties = mlt_properties_new(); + + mlt_properties_set_lcnumeric( properties, context->lc_numeric ); + context_push_service( context, (mlt_service) properties, mlt_dummy_consumer_type ); + + // Set the properties from attributes + for ( ; atts != NULL && *atts != NULL; atts += 2 ) + mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] ); + } +} + +static void on_end_consumer( deserialise_context context, const xmlChar *name ) +{ + if ( context->pass == 1 ) + { + // Get the consumer from the stack + enum service_type type; + mlt_properties properties = (mlt_properties) context_pop_service( context, &type ); + + if ( properties && type == mlt_dummy_consumer_type ) + { + qualify_property( context, properties, "resource" ); + qualify_property( context, properties, "target" ); + char *resource = mlt_properties_get( properties, "resource" ); + + if ( context->multi_consumer > 1 || context->qglsl ) + { + // Instantiate the multi consumer + if ( !context->consumer ) + { + if ( context->qglsl ) + context->consumer = context->qglsl; + else + context->consumer = mlt_factory_consumer( context->profile, "multi", NULL ); + if ( context->consumer ) + { + // Track this consumer + track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close ); + mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric ); + } + } + if ( context->consumer ) + { + // Set this properties object on multi consumer + char key[20]; + snprintf( key, sizeof(key), "%d", context->consumer_count++ ); + mlt_properties_inc_ref( properties ); + mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(context->consumer), key, properties, 0, + (mlt_destructor) mlt_properties_close, NULL ); + } + } + else + { + // Instantiate the consumer + char *id = trim( mlt_properties_get( properties, "mlt_service" ) ); + context->consumer = mlt_factory_consumer( context->profile, id, resource ); + if ( context->consumer ) + { + // Track this consumer + track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close ); + mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric ); + + // Inherit the properties + mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties ); + } + } + } + // Close the dummy + if ( properties ) + mlt_properties_close( properties ); } } @@ -1071,7 +1172,7 @@ static void on_start_property( deserialise_context context, const xmlChar *name, } else { - fprintf( stderr, "Property without a service '%s'?\n", ( const char * )name ); + mlt_log_error( NULL, "[producer_xml] Property without a service '%s'?\n", ( const char * )name ); } } @@ -1112,7 +1213,7 @@ static void on_end_property( deserialise_context context, const xmlChar *name ) } else { - fprintf( stderr, "Property without a service '%s'??\n", (const char *)name ); + mlt_log_error( NULL, "[producer_xml] Property without a service '%s'??\n", (const char *)name ); } } @@ -1121,13 +1222,22 @@ static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **at struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx; deserialise_context context = ( deserialise_context )( xmlcontext->_private ); -//printf("on_start_element: %s\n", name ); if ( context->pass == 0 ) { if ( xmlStrcmp( name, _x("mlt") ) == 0 || xmlStrcmp( name, _x("profile") ) == 0 || xmlStrcmp( name, _x("profileinfo") ) == 0 ) on_start_profile( context, name, atts ); + if ( xmlStrcmp( name, _x("consumer") ) == 0 ) + context->multi_consumer++; + + // Check for a service beginning with glsl. or movit. + for ( ; atts != NULL && *atts != NULL; atts += 2 ) { + if ( !xmlStrncmp( atts[1], _x("glsl."), 5 ) || !xmlStrncmp( atts[1], _x("movit."), 6 ) ) { + mlt_properties_set_int( context->params, "qglsl", 1 ); + break; + } + } return; } context->branch[ context->depth ] ++; @@ -1175,9 +1285,18 @@ static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **at on_start_transition( context, name, atts ); else if ( xmlStrcmp( name, _x("property") ) == 0 ) on_start_property( context, name, atts ); + else if ( xmlStrcmp( name, _x("consumer") ) == 0 ) + on_start_consumer( context, name, atts ); else if ( xmlStrcmp( name, _x("westley") ) == 0 || xmlStrcmp( name, _x("mlt") ) == 0 ) + { for ( ; atts != NULL && *atts != NULL; atts += 2 ) - mlt_properties_set( context->producer_map, ( const char * )atts[ 0 ], ( const char * )atts[ 1 ] ); + { + if ( xmlStrcmp( atts[0], _x("LC_NUMERIC") ) ) + mlt_properties_set( context->producer_map, _s( atts[0] ), _s(atts[1] ) ); + else if ( !context->lc_numeric ) + context->lc_numeric = strdup( _s( atts[1] ) ); + } + } } static void on_end_element( void *ctx, const xmlChar *name ) @@ -1185,7 +1304,6 @@ static void on_end_element( void *ctx, const xmlChar *name ) struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx; deserialise_context context = ( deserialise_context )( xmlcontext->_private ); -//printf("on_end_element: %s\n", name ); if ( context->is_value == 1 && context->pass == 1 && xmlStrcmp( name, _x("property") ) != 0 ) context_pop_node( context ); else if ( xmlStrcmp( name, _x("multitrack") ) == 0 ) @@ -1206,6 +1324,8 @@ static void on_end_element( void *ctx, const xmlChar *name ) on_end_filter( context, name ); else if ( xmlStrcmp( name, _x("transition") ) == 0 ) on_end_transition( context, name ); + else if ( xmlStrcmp( name, _x("consumer") ) == 0 ) + on_end_consumer( context, name ); context->branch[ context->depth ] = 0; context->depth --; @@ -1215,7 +1335,7 @@ static void on_characters( void *ctx, const xmlChar *ch, int len ) { struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx; deserialise_context context = ( deserialise_context )( xmlcontext->_private ); - char *value = calloc( len + 1, 1 ); + char *value = calloc( 1, len + 1 ); enum service_type type; mlt_service service = context_pop_service( context, &type ); mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); @@ -1238,7 +1358,7 @@ static void on_characters( void *ctx, const xmlChar *ch, int len ) if ( s != NULL ) { // Append new text to existing content - char *new = calloc( strlen( s ) + len + 1, 1 ); + char *new = calloc( 1, strlen( s ) + len + 1 ); strcat( new, s ); strcat( new, value ); mlt_properties_set( properties, context->property, new ); @@ -1248,7 +1368,11 @@ static void on_characters( void *ctx, const xmlChar *ch, int len ) mlt_properties_set( properties, context->property, value ); } context->entity_is_replace = 0; - + + // Check for a service beginning with glsl. or movit. + if ( !strncmp( value, "glsl.", 5 ) || !strncmp( value, "movit.", 6 ) ) + mlt_properties_set_int( context->params, "qglsl", 1 ); + free( value); } @@ -1331,6 +1455,28 @@ static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name ) return e; } +static void on_error( void * ctx, const char * msg, ... ) +{ + struct _xmlError* err_ptr = xmlCtxtGetLastError( ctx ); + + switch( err_ptr->level ) + { + case XML_ERR_WARNING: + mlt_log_warning( NULL, "[producer_xml] parse warning: %s\trow: %d\tcol: %d\n", + err_ptr->message, err_ptr->line, err_ptr->int2 ); + break; + case XML_ERR_ERROR: + mlt_log_error( NULL, "[producer_xml] parse error: %s\trow: %d\tcol: %d\n", + err_ptr->message, err_ptr->line, err_ptr->int2 ); + break; + default: + case XML_ERR_FATAL: + mlt_log_fatal( NULL, "[producer_xml] parse fatal: %s\trow: %d\tcol: %d\n", + err_ptr->message, err_ptr->line, err_ptr->int2 ); + break; + } +} + /** Convert a hexadecimal character to its value. */ static int tohex( char p ) @@ -1381,8 +1527,15 @@ static void parse_url( mlt_properties properties, char *url ) case ':': case '=': - url[ i++ ] = '\0'; - value = &url[ i ]; +#ifdef WIN32 + if ( url[i] == ':' && url[i + 1] != '/' ) + { +#endif + url[ i++ ] = '\0'; + value = &url[ i ]; +#ifdef WIN32 + } +#endif break; case '&': @@ -1415,18 +1568,63 @@ static int file_exists( char *file ) return exists; } +// This function will add remaing services in the context service stack marked +// with a "xml_retain" property to a property named "xml_retain" on the returned +// service. The property is a mlt_properties data property. + +static void retain_services( struct deserialise_context_s *context, mlt_service service ) +{ + mlt_properties retain_list = mlt_properties_new(); + enum service_type type; + mlt_service retain_service = context_pop_service( context, &type ); + + while ( retain_service ) + { + mlt_properties retain_properties = MLT_SERVICE_PROPERTIES( retain_service ); + + if ( mlt_properties_get_int( retain_properties, "xml_retain" ) ) + { + // Remove the retained service from the destructors list. + int i; + for ( i = mlt_properties_count( context->destructors ) - 1; i >= 1; i -- ) + { + const char *name = mlt_properties_get_name( context->destructors, i ); + if ( mlt_properties_get_data_at( context->destructors, i, NULL ) == retain_service ) + { + mlt_properties_set_data( context->destructors, name, retain_service, 0, NULL, NULL ); + break; + } + } + const char *name = mlt_properties_get( retain_properties, "id" ); + if ( name ) + mlt_properties_set_data( retain_list, name, retain_service, 0, + (mlt_destructor) mlt_service_close, NULL ); + } + retain_service = context_pop_service( context, &type ); + } + if ( mlt_properties_count( retain_list ) > 0 ) + { + mlt_properties_set_data( MLT_SERVICE_PROPERTIES(service), "xml_retain", retain_list, 0, + (mlt_destructor) mlt_properties_close, NULL ); + } +} + mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data ) { - xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) ); - struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) ); + xmlSAXHandler *sax, *sax_orig; + struct deserialise_context_s *context; mlt_properties properties = NULL; int i = 0; struct _xmlParserCtxt *xmlcontext; int well_formed = 0; char *filename = NULL; - int info = strcmp( id, "xml-string" ) ? 0 : 1; + int is_filename = strcmp( id, "xml-string" ); - if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) ) + // Strip file:// prefix + if ( data && strlen( data ) >= 7 && strncmp( data, "file://", 7 ) == 0 ) + data += 7; + + if ( data == NULL || !strcmp( data, "" ) || ( is_filename && !file_exists( data ) ) ) return NULL; context = calloc( 1, sizeof( struct deserialise_context_s ) ); @@ -1437,10 +1635,11 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, context->destructors = mlt_properties_new(); context->params = mlt_properties_new(); context->profile = profile; + context->seekable = 1; // Decode URL and parse parameters mlt_properties_set( context->producer_map, "root", "" ); - if ( info == 0 ) + if ( is_filename ) { filename = strdup( data ); parse_url( context->params, url_decode( filename, data ) ); @@ -1469,15 +1668,20 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, // We need to track the number of registered filters mlt_properties_set_int( context->destructors, "registered", 0 ); - // Setup SAX callbacks + // Setup SAX callbacks for first pass + sax = calloc( 1, sizeof( xmlSAXHandler ) ); sax->startElement = on_start_element; + sax->characters = on_characters; + sax->warning = on_error; + sax->error = on_error; + sax->fatalError = on_error; // Setup libxml2 SAX parsing xmlInitParser(); xmlSubstituteEntitiesDefault( 1 ); // This is used to facilitate entity substitution in the SAX parser context->entity_doc = xmlNewDoc( _x("1.0") ); - if ( info == 0 ) + if ( is_filename ) xmlcontext = xmlCreateFileParserCtxt( filename ); else xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) ); @@ -1495,20 +1699,37 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, } // Parse + sax_orig = xmlcontext->sax; xmlcontext->sax = sax; xmlcontext->_private = ( void* )context; xmlParseDocument( xmlcontext ); + well_formed = xmlcontext->wellFormed; // Cleanup after parsing - xmlcontext->sax = NULL; + xmlcontext->sax = sax_orig; xmlcontext->_private = NULL; + if ( xmlcontext->myDoc ) + xmlFreeDoc( xmlcontext->myDoc ); xmlFreeParserCtxt( xmlcontext ); context->stack_node_size = 0; context->stack_service_size = 0; + // Bad xml - clean up and return NULL + if ( !well_formed ) + { + mlt_properties_close( context->producer_map ); + mlt_properties_close( context->destructors ); + mlt_properties_close( context->params ); + xmlFreeDoc( context->entity_doc ); + free( context ); + free( sax ); + free( filename ); + return NULL; + } + // Setup the second pass context->pass ++; - if ( info == 0 ) + if ( is_filename ) xmlcontext = xmlCreateFileParserCtxt( filename ); else xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) ); @@ -1526,15 +1747,22 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, return NULL; } - // Setup SAX callbacks + // Create the qglsl consumer now, if requested, so that glsl.manager + // may exist when trying to load glsl. or movit. services. + // The "if requested" part can come from query string qglsl=1 or when + // a service beginning with glsl. or movit. appears in the XML. + if ( mlt_properties_get_int( context->params, "qglsl" ) && strcmp( id, "xml-nogl" ) ) + context->qglsl = mlt_factory_consumer( profile, "qglsl", NULL ); + + // Setup SAX callbacks for second pass sax->endElement = on_end_element; - sax->characters = on_characters; sax->cdataBlock = on_characters; sax->internalSubset = on_internal_subset; sax->entityDecl = on_entity_declaration; sax->getEntity = on_get_entity; // Parse + sax_orig = xmlcontext->sax; xmlcontext->sax = sax; xmlcontext->_private = ( void* )context; xmlParseDocument( xmlcontext ); @@ -1544,6 +1772,11 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, xmlFreeDoc( context->entity_doc ); free( sax ); xmlMemoryDump( ); // for debugging + xmlcontext->sax = sax_orig; + xmlcontext->_private = NULL; + if ( xmlcontext->myDoc ) + xmlFreeDoc( xmlcontext->myDoc ); + xmlFreeParserCtxt( xmlcontext ); // Get the last producer on the stack enum service_type type; @@ -1575,7 +1808,7 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- ) { char *name = mlt_properties_get_name( properties, i ); - if ( mlt_properties_get_data( properties, name, NULL ) == service ) + if ( mlt_properties_get_data_at( properties, i, NULL ) == service ) { mlt_properties_set_data( properties, name, service, 0, NULL, NULL ); break; @@ -1596,8 +1829,17 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, if ( getenv( "MLT_XML_DEEP" ) == NULL ) { // Now assign additional properties - if ( info == 0 ) + if ( is_filename && ( + mlt_service_identify( service ) == tractor_type || + mlt_service_identify( service ) == playlist_type || + mlt_service_identify( service ) == multitrack_type ) ) + { + mlt_properties_set_int( properties, "_original_type", + mlt_service_identify( service ) ); + mlt_properties_set( properties, "_original_resource", + mlt_properties_get( properties, "resource" ) ); mlt_properties_set( properties, "resource", data ); + } // This tells consumer_xml not to deep copy mlt_properties_set( properties, "xml", "was here" ); @@ -1608,6 +1850,15 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, mlt_properties_set( properties, "_xml", "was here" ); mlt_properties_set_int( properties, "_mlt_service_hidden", 1 ); } + + // Make consumer available + mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES( context->consumer ) ); + mlt_properties_set_data( properties, "consumer", context->consumer, 0, + (mlt_destructor) mlt_consumer_close, NULL ); + + mlt_properties_set_int( properties, "seekable", context->seekable ); + + retain_services( context, service ); } else { @@ -1616,10 +1867,14 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, } // Clean up + if ( context->qglsl && context->consumer != context->qglsl ) + mlt_consumer_close( context->qglsl ); mlt_properties_close( context->producer_map ); if ( context->params != NULL ) mlt_properties_close( context->params ); mlt_properties_close( context->destructors ); + if ( context->lc_numeric ) + free( context->lc_numeric ); free( context ); free( filename );