X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fxml%2Fproducer_xml.c;h=086657f4263d6ab9f7363c7a25c7ed8f24dae35e;hb=236717e4ecc23e458e191b6c9a2ba70915b80937;hp=2b41e7a16ebd2063a03174a502766b90a4c0011c;hpb=4051d04ebd446241dbea27e0ea90e0dfced21a9f;p=mlt diff --git a/src/modules/xml/producer_xml.c b/src/modules/xml/producer_xml.c index 2b41e7a1..086657f4 100644 --- a/src/modules/xml/producer_xml.c +++ b/src/modules/xml/producer_xml.c @@ -89,6 +89,7 @@ struct deserialise_context_s int multi_consumer; int consumer_count; int seekable; + mlt_consumer qglsl; }; typedef struct deserialise_context_s *deserialise_context; @@ -153,15 +154,18 @@ static mlt_service context_pop_service( deserialise_context context, enum servic { mlt_service result = NULL; - *type = invalid_type; + if ( type ) *type = mlt_invalid_type; if ( context->stack_service_size > 0 ) { result = context->stack_service[ -- context->stack_service_size ]; if ( type != NULL ) *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 - 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 ); + 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; } @@ -466,7 +470,7 @@ static void on_end_multitrack( deserialise_context context, const xmlChar *name 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 ); @@ -539,13 +543,13 @@ static void on_end_producer( deserialise_context context, const xmlChar *name ) mlt_service producer = NULL; qualify_property( context, properties, "resource" ); - char *resource = trim( mlt_properties_get( properties, "resource" ) ); + char *resource = mlt_properties_get( properties, "resource" ); // Let Kino-SMIL src be a synonym for resource if ( resource == NULL ) { qualify_property( context, properties, "src" ); - resource = trim( mlt_properties_get( properties, "src" ) ); + resource = mlt_properties_get( properties, "src" ); } // Instantiate the producer @@ -579,13 +583,15 @@ static void on_end_producer( deserialise_context context, const xmlChar *name ) 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 ); - context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" ); + if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( producer ), "seekable" ) ) + context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" ); // Propagate the properties qualify_property( context, properties, "resource" ); @@ -638,7 +644,7 @@ static void on_end_producer( deserialise_context context, const xmlChar *name ) // Get the parent properties properties = MLT_SERVICE_PROPERTIES( parent ); - char *resource = trim( mlt_properties_get( properties, "resource" ) ); + char *resource = mlt_properties_get( properties, "resource" ); // Put the parent producer back context_push_service( context, parent, type ); @@ -665,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 ); } } @@ -675,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 ) { @@ -684,14 +693,12 @@ 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 ); } @@ -725,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 ); @@ -768,7 +775,7 @@ 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 ) @@ -811,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; @@ -863,13 +870,17 @@ 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 { mlt_log_error( NULL, "[producer_xml] Invalid state at end of track\n" ); } + + if ( track ) + { + mlt_service_close( track ); + free( track ); + } } static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts) @@ -893,7 +904,7 @@ 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 ) @@ -908,6 +919,7 @@ static void on_end_filter( deserialise_context context, const xmlChar *name ) if ( parent ) context_push_service( context, parent, parent_type ); mlt_service_close( service ); + free( service ); return; } @@ -948,14 +960,17 @@ static void on_end_filter( deserialise_context context, const xmlChar *name ) { mlt_log_error( NULL, "[producer_xml] filter closed with invalid parent...\n" ); } - - // Close the dummy filter service - mlt_service_close( service ); } else { mlt_log_error( NULL, "[producer_xml] Invalid top of stack on filter close\n" ); } + + if ( service ) + { + mlt_service_close( service ); + free(service); + } } static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts) @@ -979,7 +994,7 @@ 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 ) @@ -994,6 +1009,7 @@ static void on_end_transition( deserialise_context context, const xmlChar *name 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 ); @@ -1038,24 +1054,27 @@ static void on_end_transition( deserialise_context context, const xmlChar *name mlt_log_error( NULL, "[producer_xml] transition closed with invalid parent...\n" ); } - // Close the dummy filter service - mlt_service_close( service ); } else { 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_consumer consumer = mlt_consumer_new( context->profile ); - mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + mlt_properties properties = mlt_properties_new(); mlt_properties_set_lcnumeric( properties, context->lc_numeric ); - context_push_service( context, MLT_CONSUMER_SERVICE(consumer), mlt_dummy_consumer_type ); + context_push_service( context, (mlt_service) properties, mlt_dummy_consumer_type ); // Set the properties from attributes for ( ; atts != NULL && *atts != NULL; atts += 2 ) @@ -1069,21 +1088,23 @@ static void on_end_consumer( deserialise_context context, const xmlChar *name ) { // Get the consumer from the stack enum service_type type; - mlt_service service = context_pop_service( context, &type ); + mlt_properties properties = (mlt_properties) context_pop_service( context, &type ); - if ( service && type == mlt_dummy_consumer_type ) + if ( properties && type == mlt_dummy_consumer_type ) { - mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); qualify_property( context, properties, "resource" ); qualify_property( context, properties, "target" ); - char *resource = trim( mlt_properties_get( properties, "resource" ) ); + char *resource = mlt_properties_get( properties, "resource" ); - if ( context->multi_consumer > 1 ) + if ( context->multi_consumer > 1 || context->qglsl ) { // Instantiate the multi consumer if ( !context->consumer ) { - context->consumer = mlt_factory_consumer( context->profile, "multi", NULL ); + if ( context->qglsl ) + context->consumer = context->qglsl; + else + context->consumer = mlt_factory_consumer( context->profile, "multi", NULL ); if ( context->consumer ) { // Track this consumer @@ -1093,11 +1114,12 @@ static void on_end_consumer( deserialise_context context, const xmlChar *name ) } if ( context->consumer ) { - // Set this service instance on multi consumer + // Set this properties object on multi consumer char key[20]; snprintf( key, sizeof(key), "%d", context->consumer_count++ ); - mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(context->consumer), key, service, 0, - (mlt_destructor) mlt_service_close, NULL ); + mlt_properties_inc_ref( properties ); + mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(context->consumer), key, properties, 0, + (mlt_destructor) mlt_properties_close, NULL ); } } else @@ -1114,10 +1136,11 @@ static void on_end_consumer( deserialise_context context, const xmlChar *name ) // Inherit the properties mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties ); } - // Close the dummy - mlt_service_close( service ); } } + // Close the dummy + if ( properties ) + mlt_properties_close( properties ); } } @@ -1207,6 +1230,14 @@ static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **at 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 ] ++; @@ -1304,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 ); @@ -1327,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 ); @@ -1337,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); } @@ -1533,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" ); + + // Strip file:// prefix + if ( data && strlen( data ) >= 7 && strncmp( data, "file://", 7 ) == 0 ) + data += 7; - if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) ) + if ( data == NULL || !strcmp( data, "" ) || ( is_filename && !file_exists( data ) ) ) return NULL; context = calloc( 1, sizeof( struct deserialise_context_s ) ); @@ -1559,7 +1639,7 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, // 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 ) ); @@ -1589,7 +1669,9 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, mlt_properties_set_int( context->destructors, "registered", 0 ); // 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; @@ -1599,7 +1681,7 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, 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 ) ); @@ -1617,14 +1699,17 @@ 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; @@ -1644,7 +1729,7 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, // Setup the second pass context->pass ++; - if ( info == 0 ) + if ( is_filename ) xmlcontext = xmlCreateFileParserCtxt( filename ); else xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) ); @@ -1662,15 +1747,22 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, return NULL; } + // 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 ); @@ -1680,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; @@ -1732,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 && !mlt_properties_get( properties, "resource" ) ) + 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" ); @@ -1751,6 +1857,8 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, (mlt_destructor) mlt_consumer_close, NULL ); mlt_properties_set_int( properties, "seekable", context->seekable ); + + retain_services( context, service ); } else { @@ -1759,6 +1867,8 @@ 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 );