+static void on_end_track( deserialise_context context, const xmlChar *name )
+{
+ // Get the track from the stack
+ enum service_type track_type;
+ mlt_service track = context_pop_service( context, &track_type );
+
+ if ( track != NULL && track_type == mlt_entry_type )
+ {
+ mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
+ enum service_type parent_type;
+ mlt_service parent = context_pop_service( context, &parent_type );
+ mlt_multitrack multitrack = NULL;
+
+ mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
+ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+ if ( parent_type == mlt_tractor_type )
+ multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
+ else if ( parent_type == mlt_multitrack_type )
+ multitrack = MLT_MULTITRACK( parent );
+ else
+ fprintf( stderr, "track contained in an invalid container\n" );
+
+ if ( multitrack != NULL )
+ {
+ // Set producer i/o if specified
+ if ( mlt_properties_get( track_props, "in" ) != NULL ||
+ mlt_properties_get( track_props, "out" ) != NULL )
+ {
+ mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ),
+ mlt_properties_get_position( track_props, "in" ),
+ mlt_properties_get_position( track_props, "out" ) );
+ mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
+ mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( cut ), track_props );
+ track_props = MLT_PRODUCER_PROPERTIES( cut );
+ mlt_producer_close( cut );
+ }
+ else
+ {
+ mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
+ }
+
+ // Set the hide state of the track producer
+ char *hide_s = mlt_properties_get( track_props, "hide" );
+ if ( hide_s != NULL )
+ {
+ if ( strcmp( hide_s, "video" ) == 0 )
+ mlt_properties_set_int( producer_props, "hide", 1 );
+ else if ( strcmp( hide_s, "audio" ) == 0 )
+ mlt_properties_set_int( producer_props, "hide", 2 );
+ else if ( strcmp( hide_s, "both" ) == 0 )
+ mlt_properties_set_int( producer_props, "hide", 3 );
+ }
+ }
+
+ if ( parent != NULL )
+ context_push_service( context, parent, parent_type );
+
+ mlt_service_close( track );
+ }
+ else
+ {
+ fprintf( stderr, "Invalid state at end of track\n" );
+ }
+}
+
+static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ // use a dummy service to hold properties to allow arbitrary nesting
+ mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+ mlt_service_init( service, NULL );
+
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ context_push_service( context, service, mlt_dummy_filter_type );
+
+ // Set the properties
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
+}
+
+static void on_end_filter( deserialise_context context, const xmlChar *name )
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ enum service_type parent_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( mlt_properties_get( properties, "mlt_service" ), NULL ) );
+ mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
+
+ track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
+
+ // Propogate the properties
+ qualify_property( context, properties, "resource" );
+ qualify_property( context, properties, "luma" );
+ qualify_property( context, properties, "luma.resource" );
+ qualify_property( context, properties, "composite.luma" );
+ qualify_property( context, properties, "producer.resource" );
+ mlt_properties_inherit( filter_props, properties );
+
+ // Attach all filters from service onto filter
+ attach_filters( filter, service );
+
+ // Associate the filter with the parent
+ if ( parent != NULL )
+ {
+ if ( parent_type == mlt_tractor_type )
+ {
+ mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
+ mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
+ mlt_filter_set_in_and_out( MLT_FILTER( filter ),
+ mlt_properties_get_int( properties, "in" ),
+ mlt_properties_get_int( properties, "out" ) );
+ }
+ else
+ {
+ mlt_service_attach( parent, MLT_FILTER( filter ) );
+ }
+
+ // Put the parent back on the stack
+ context_push_service( context, parent, parent_type );
+ }
+ else
+ {
+ fprintf( stderr, "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" );
+ }
+}
+
+static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ // use a dummy service to hold properties to allow arbitrary nesting
+ mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+ mlt_service_init( service, NULL );
+
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ context_push_service( context, service, mlt_dummy_transition_type );
+
+ // Set the properties
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
+}
+
+static void on_end_transition( deserialise_context context, const xmlChar *name )
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ enum service_type parent_type;
+ mlt_service parent = context_pop_service( context, &parent_type );
+
+ if ( service != NULL && type == mlt_dummy_transition_type )
+ {
+ mlt_service effect = MLT_SERVICE( mlt_factory_transition(mlt_properties_get(properties,"mlt_service"), NULL ) );
+ mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
+
+ track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
+
+ // Propogate the properties
+ qualify_property( context, properties, "resource" );
+ qualify_property( context, properties, "luma" );
+ qualify_property( context, properties, "luma.resource" );
+ qualify_property( context, properties, "composite.luma" );
+ qualify_property( context, properties, "producer.resource" );
+ mlt_properties_inherit( effect_props, properties );
+
+ // Attach all filters from service onto effect
+ attach_filters( effect, service );
+
+ // Associate the filter with the parent
+ if ( parent != NULL )
+ {
+ if ( parent_type == mlt_tractor_type )
+ {
+ 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_properties_get_int( properties, "a_track" ),
+ mlt_properties_get_int( properties, "b_track" ) );
+ 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" );
+ }
+
+ // Put the parent back on the stack
+ context_push_service( context, parent, parent_type );
+ }
+ else
+ {
+ fprintf( stderr, "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" );
+ }
+}
+
+static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ char *value = NULL;
+
+ if ( service != NULL )
+ {
+ // Set the properties
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ {
+ if ( strcmp( atts[ 0 ], "name" ) == 0 )
+ context->property = strdup( atts[ 1 ] );
+ else if ( strcmp( atts[ 0 ], "value" ) == 0 )
+ value = (char*) atts[ 1 ];
+ }
+
+ if ( context->property != NULL )
+ mlt_properties_set( properties, context->property, value == NULL ? "" : value );
+
+ // Tell parser to collect any further nodes for serialisation
+ context->is_value = 1;
+
+ context_push_service( context, service, type );
+ }
+ else
+ {
+ fprintf( stderr, "Property without a service '%s'?\n", ( char * )name );
+ }
+}
+
+static void on_end_property( deserialise_context context, const xmlChar *name )
+{
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ if ( service != NULL )
+ {
+ // Tell parser to stop building a tree
+ context->is_value = 0;
+
+ // See if there is a xml tree for the value
+ if ( context->property != NULL && context->value_doc != NULL )
+ {
+ xmlChar *value;
+ int size;
+
+ // Serialise the tree to get value
+ xmlDocDumpMemory( context->value_doc, &value, &size );
+ mlt_properties_set( properties, context->property, value );
+ xmlFree( value );
+ xmlFreeDoc( context->value_doc );
+ context->value_doc = NULL;
+ }
+
+ // Close this property handling
+ free( context->property );
+ context->property = NULL;
+
+ context_push_service( context, service, type );
+ }
+ else
+ {
+ fprintf( stderr, "Property without a service '%s'??\n", (char *)name );
+ }
+}
+
+static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+
+//printf("on_start_element: %s\n", name );
+ context->branch[ context->depth ] ++;
+ context->depth ++;
+
+ // Build a tree from nodes within a property value
+ if ( context->is_value == 1 )
+ {
+ xmlNodePtr node = xmlNewNode( NULL, name );
+
+ if ( context->value_doc == NULL )
+ {
+ // Start a new tree
+ context->value_doc = xmlNewDoc( "1.0" );
+ xmlDocSetRootElement( context->value_doc, node );
+ }
+ else
+ {
+ // Append child to tree
+ xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
+ }
+ context_push_node( context, node );
+
+ // Set the attributes
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
+ }
+ else if ( strcmp( name, "tractor" ) == 0 )
+ on_start_tractor( context, name, atts );
+ else if ( strcmp( name, "multitrack" ) == 0 )
+ on_start_multitrack( context, name, atts );
+ else if ( strcmp( name, "playlist" ) == 0 || strcmp( name, "seq" ) == 0 || strcmp( name, "smil" ) == 0 )
+ on_start_playlist( context, name, atts );
+ else if ( strcmp( name, "producer" ) == 0 || strcmp( name, "video" ) == 0 )
+ on_start_producer( context, name, atts );
+ else if ( strcmp( name, "blank" ) == 0 )
+ on_start_blank( context, name, atts );
+ else if ( strcmp( name, "entry" ) == 0 )
+ on_start_entry( context, name, atts );
+ else if ( strcmp( name, "track" ) == 0 )
+ on_start_track( context, name, atts );
+ else if ( strcmp( name, "filter" ) == 0 )
+ on_start_filter( context, name, atts );
+ else if ( strcmp( name, "transition" ) == 0 )
+ on_start_transition( context, name, atts );
+ else if ( strcmp( name, "property" ) == 0 )
+ on_start_property( context, name, atts );
+ else if ( strcmp( name, "westley" ) == 0 )
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( context->producer_map, ( char * )atts[ 0 ], ( char * )atts[ 1 ] );
+}
+
+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 && strcmp( name, "property" ) != 0 )
+ context_pop_node( context );
+ else if ( strcmp( name, "multitrack" ) == 0 )
+ on_end_multitrack( context, name );
+ else if ( strcmp( name, "playlist" ) == 0 || strcmp( name, "seq" ) == 0 || strcmp( name, "smil" ) == 0 )
+ on_end_playlist( context, name );
+ else if ( strcmp( name, "track" ) == 0 )
+ on_end_track( context, name );
+ else if ( strcmp( name, "entry" ) == 0 )
+ on_end_entry( context, name );
+ else if ( strcmp( name, "tractor" ) == 0 )
+ on_end_tractor( context, name );
+ else if ( strcmp( name, "property" ) == 0 )
+ on_end_property( context, name );
+ else if ( strcmp( name, "producer" ) == 0 || strcmp( name, "video" ) == 0 )
+ on_end_producer( context, name );
+ else if ( strcmp( name, "filter" ) == 0 )
+ on_end_filter( context, name );
+ else if ( strcmp( name, "transition" ) == 0 )
+ on_end_transition( context, name );
+
+ context->branch[ context->depth ] = 0;
+ context->depth --;
+}
+
+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 );
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+ if ( service != NULL )
+ context_push_service( context, service, type );
+
+ value[ len ] = 0;
+ strncpy( value, (const char*) ch, len );
+
+ if ( context->stack_node_size > 0 )
+ xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
+
+ // libxml2 generates an on_characters immediately after a get_entity within
+ // an element value, and we ignore it because it is called again during
+ // actual substitution.
+ else if ( context->property != NULL && context->entity_is_replace == 0 )
+ {
+ char *s = mlt_properties_get( properties, context->property );
+ if ( s != NULL )
+ {
+ // Append new text to existing content
+ char *new = calloc( strlen( s ) + len + 1, 1 );
+ strcat( new, s );
+ strcat( new, value );
+ mlt_properties_set( properties, context->property, new );
+ free( new );
+ }
+ else
+ mlt_properties_set( properties, context->property, value );
+ }
+ context->entity_is_replace = 0;
+
+ free( value);
+}
+
+/** Convert parameters parsed from resource into entity declarations.
+*/
+static void params_to_entities( deserialise_context context )
+{
+ if ( context->params != NULL )
+ {
+ int i;
+
+ // Add our params as entitiy declarations
+ for ( i = 0; i < mlt_properties_count( context->params ); i++ )
+ {
+ xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
+ xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
+ context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, name ) );
+ }
+
+ // Flag completion
+ mlt_properties_close( context->params );
+ context->params = NULL;
+ }
+}
+
+// The following 3 facilitate entity substitution in the SAX parser
+static void on_internal_subset( void *ctx, const xmlChar* name,
+ const xmlChar* publicId, const xmlChar* systemId )
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+
+ context->publicId = publicId;
+ context->systemId = systemId;
+ xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
+
+ // Override default entities with our parameters
+ params_to_entities( context );
+}
+
+// TODO: Check this with Dan... I think this is for westley parameterisation
+// but it's breaking standard escaped entities (like < etc).
+static void on_entity_declaration( void *ctx, const xmlChar* name, int type,
+ const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+
+ xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
+}
+
+// TODO: Check this functionality (see on_entity_declaration)
+static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
+{
+ struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+ deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+ xmlEntityPtr e = NULL;
+
+ // Setup for entity declarations if not ready
+ if ( xmlGetIntSubset( context->entity_doc ) == NULL )
+ {
+ xmlCreateIntSubset( context->entity_doc, "westley", "", "" );
+ context->publicId = "";
+ context->systemId = "";
+ }
+
+ // Add our parameters if not already
+ params_to_entities( context );
+
+ e = xmlGetPredefinedEntity( name );
+
+ // Send signal to on_characters that an entity substitutin is pending
+ if ( e == NULL )
+ {
+ e = xmlGetDocEntity( context->entity_doc, name );
+ if ( e != NULL )
+ context->entity_is_replace = 1;
+ }
+
+ return e;
+}
+
+/** Convert a hexadecimal character to its value.
+*/
+static int tohex( char p )
+{
+ return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
+}
+
+/** Decode a url-encoded string containing hexadecimal character sequences.
+*/
+static char *url_decode( char *dest, char *src )
+{
+ char *p = dest;
+
+ while ( *src )
+ {
+ if ( *src == '%' )
+ {
+ *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
+ src += 3;
+ }
+ else
+ {
+ *p ++ = *src ++;
+ }
+ }
+
+ *p = *src;
+ return dest;
+}
+
+/** Extract the filename from a URL attaching parameters to a properties list.
+*/
+static void parse_url( mlt_properties properties, char *url )
+{
+ int i;
+ int n = strlen( url );
+ char *name = NULL;
+ char *value = NULL;
+
+ for ( i = 0; i < n; i++ )
+ {
+ switch ( url[ i ] )
+ {
+ case '?':
+ url[ i++ ] = '\0';
+ name = &url[ i ];
+ break;
+
+ case ':':
+ case '=':
+ url[ i++ ] = '\0';
+ value = &url[ i ];
+ break;
+
+ case '&':
+ url[ i++ ] = '\0';
+ if ( name != NULL && value != NULL )
+ mlt_properties_set( properties, name, value );
+ name = &url[ i ];
+ value = NULL;
+ break;
+ }
+ }
+ if ( name != NULL && value != NULL )
+ mlt_properties_set( properties, name, value );
+}
+
+// Quick workaround to avoid unecessary libxml2 warnings
+static int file_exists( char *file )
+{
+ char *name = strdup( file );
+ int exists = 0;
+ if ( name != NULL && strchr( name, '?' ) )
+ *( strchr( name, '?' ) ) = '\0';
+ if ( name != NULL )
+ {
+ FILE *f = fopen( name, "r" );
+ exists = f != NULL;
+ if ( exists ) fclose( f );
+ }
+ free( name );
+ return exists;
+}
+
+mlt_producer producer_westley_init( int info, char *data )
+{
+ xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
+ struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
+ mlt_properties properties = NULL;
+ int i = 0;
+ struct _xmlParserCtxt *xmlcontext;
+ int well_formed = 0;
+ char *filename = NULL;
+
+ if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) )
+ return NULL;
+
+ context = calloc( 1, sizeof( struct deserialise_context_s ) );
+ if ( context == NULL )
+ return NULL;
+
+ context->producer_map = mlt_properties_new();
+ context->destructors = mlt_properties_new();
+ context->params = mlt_properties_new();
+
+ // Decode URL and parse parameters
+ mlt_properties_set( context->producer_map, "root", "" );
+ if ( info == 0 )
+ {
+ filename = strdup( data );
+ parse_url( context->params, url_decode( filename, data ) );
+
+ // We need the directory prefix which was used for the westley
+ if ( strchr( filename, '/' ) )
+ {
+ char *root = NULL;
+ mlt_properties_set( context->producer_map, "root", filename );
+ root = mlt_properties_get( context->producer_map, "root" );
+ *( strrchr( root, '/' ) ) = '\0';
+
+ // If we don't have an absolute path here, we're heading for disaster...
+ if ( root[ 0 ] != '/' )
+ {
+ char *cwd = getcwd( NULL, 0 );
+ char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
+ sprintf( real, "%s/%s", cwd, root );
+ mlt_properties_set( context->producer_map, "root", real );
+ free( real );
+ free( cwd );
+ }
+ }
+ }
+
+ // We need to track the number of registered filters
+ mlt_properties_set_int( context->destructors, "registered", 0 );
+
+ // Setup SAX callbacks
+ sax->startElement = on_start_element;
+ 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;
+
+ // Setup libxml2 SAX parsing
+ xmlInitParser();
+ xmlSubstituteEntitiesDefault( 1 );
+ // This is used to facilitate entity substitution in the SAX parser
+ context->entity_doc = xmlNewDoc( "1.0" );
+ if ( info == 0 )
+ xmlcontext = xmlCreateFileParserCtxt( filename );
+ else
+ xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
+
+ // Invalid context - clean up and return NULL
+ if ( xmlcontext == NULL )
+ {
+ mlt_properties_close( context->producer_map );
+ mlt_properties_close( context->destructors );
+ mlt_properties_close( context->params );
+ free( context );
+ free( sax );
+ free( filename );
+ return NULL;
+ }
+
+ xmlcontext->sax = sax;
+ xmlcontext->_private = ( void* )context;
+
+ // Parse
+ xmlParseDocument( xmlcontext );
+ well_formed = xmlcontext->wellFormed;
+
+ // Cleanup after parsing
+ xmlFreeDoc( context->entity_doc );
+ free( sax );
+ xmlcontext->sax = NULL;
+ xmlcontext->_private = NULL;
+ xmlFreeParserCtxt( xmlcontext );
+ xmlMemoryDump( ); // for debugging
+
+ // Get the last producer on the stack
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+ if ( well_formed && service != NULL )
+ {
+ // Verify it is a producer service (mlt_type="mlt_producer")
+ // (producer, playlist, multitrack)
+ char *type = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "mlt_type" );
+ if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
+ service = NULL;
+ }
+
+#ifdef DEBUG
+ xmlDocPtr doc = westley_make_doc( service );
+ xmlDocFormatDump( stdout, doc, 1 );
+ xmlFreeDoc( doc );
+ service = NULL;
+#endif
+
+ if ( well_formed && service != NULL )
+ {
+ char *title = mlt_properties_get( context->producer_map, "title" );
+
+ // Need the complete producer list for various reasons
+ properties = context->destructors;
+
+ // Now make sure we don't have a reference to the service in the properties
+ 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 )
+ {
+ mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
+ break;
+ }
+ }
+
+ // We are done referencing destructor property list
+ // Set this var to service properties for convenience
+ properties = MLT_SERVICE_PROPERTIES( service );
+
+ // Assign the title
+ mlt_properties_set( properties, "title", title );
+
+ // Optimise for overlapping producers
+ mlt_producer_optimise( MLT_PRODUCER( service ) );
+
+ // Handle deep copies
+ if ( getenv( "MLT_WESTLEY_DEEP" ) == NULL )
+ {
+ // Now assign additional properties
+ if ( info == 0 )
+ mlt_properties_set( properties, "resource", data );
+
+ // This tells consumer_westley not to deep copy
+ mlt_properties_set( properties, "westley", "was here" );
+ }
+ else
+ {
+ // Allow the project to be edited
+ mlt_properties_set( properties, "_westley", "was here" );
+ mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
+ }
+ }
+ else
+ {
+ // Return null if not well formed
+ service = NULL;
+ }
+
+ // Clean up
+ mlt_properties_close( context->producer_map );
+ if ( context->params != NULL )
+ mlt_properties_close( context->params );
+ mlt_properties_close( context->destructors );
+ free( context );
+ free( filename );
+
+ return MLT_PRODUCER( service );
+}