]> git.sesse.net Git - mlt/blobdiff - src/modules/xml/producer_xml.c
Add xml_retain property support to xml module.
[mlt] / src / modules / xml / producer_xml.c
index 082dd9cedb4952ce3b78ad9c38dcb169eb329453..086657f4263d6ab9f7363c7a25c7ed8f24dae35e 100644 (file)
@@ -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;
 }
@@ -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 );
        }
 }
 
@@ -722,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 );
 
@@ -765,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 )
@@ -808,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;
 
@@ -860,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)
@@ -890,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 )
@@ -905,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;
                }
 
@@ -945,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)
@@ -976,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 )
@@ -991,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 );
@@ -1035,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 )
@@ -1066,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
@@ -1090,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
@@ -1111,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 );
        }
 }
 
@@ -1204,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 ] ++;
@@ -1301,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 );
@@ -1324,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 );
@@ -1334,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);
 }
 
@@ -1530,22 +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 ) );
+       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 ) );
@@ -1560,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 ) );
@@ -1590,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;
@@ -1600,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 ) );
@@ -1618,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;
@@ -1645,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 ) );
@@ -1663,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 );
@@ -1681,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;
@@ -1733,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" );
@@ -1752,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
        {
@@ -1760,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 );