]> git.sesse.net Git - mlt/blobdiff - src/modules/westley/producer_westley.c
Cloning optimisations and introduction of the service parser
[mlt] / src / modules / westley / producer_westley.c
index 8705035a21245b96c4495afe818e430d0b35def5..dc6baa5cdfea5009e164a316163b74b88c95dce9 100644 (file)
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <unistd.h>
 
 #include <libxml/parser.h>
 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
@@ -53,7 +54,7 @@ enum service_type
        mlt_transition_type,
        mlt_consumer_type,
        mlt_field_type,
-       mlt_service_type,
+       mlt_services_type,
        mlt_dummy_filter_type,
        mlt_dummy_transition_type,
        mlt_dummy_producer_type,
@@ -172,13 +173,14 @@ static inline void qualify_property( deserialise_context context, mlt_properties
        if ( resource != NULL )
        {
                // Qualify file name properties 
-               char *root = mlt_properties_get( context->producer_map, "_root" );
-               if ( root != NULL )
+               char *root = mlt_properties_get( context->producer_map, "root" );
+               if ( root != NULL && strcmp( root, "" ) )
                {
-                       char *full_resource = malloc( strlen( root ) + strlen( resource ) + 1 );
-                       if ( resource[ 0 ] != '/' )
+                       char *full_resource = malloc( strlen( root ) + strlen( resource ) + 2 );
+                       if ( resource[ 0 ] != '/' && strchr( resource, ':' ) == NULL )
                        {
                                strcpy( full_resource, root );
+                               strcat( full_resource, "/" );
                                strcat( full_resource, resource );
                        }
                        else
@@ -597,7 +599,84 @@ static void on_start_blank( deserialise_context context, const xmlChar *name, co
        }
 }
 
-static void on_start_entry_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       mlt_producer entry = NULL;
+       mlt_properties temp = mlt_properties_new( );
+
+       for ( ; atts != NULL && *atts != NULL; atts += 2 )
+       {
+               mlt_properties_set( temp, (char*) atts[0], (char*) atts[1] );
+               
+               // Look for the producer attribute
+               if ( strcmp( atts[ 0 ], "producer" ) == 0 )
+               {
+                       mlt_producer producer = mlt_properties_get_data( context->producer_map, (char*) atts[1], NULL );
+                       if ( producer !=  NULL )
+                               mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
+               }
+       }
+
+       // If we have a valid entry
+       if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
+       {
+               mlt_playlist_clip_info info;
+               enum service_type parent_type;
+               mlt_service parent = context_pop_service( context, &parent_type );
+               mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
+
+               if ( parent_type == mlt_playlist_type )
+               {
+                       // Append the producer to the playlist
+                       if ( mlt_properties_get( temp, "in" ) != NULL || mlt_properties_get( temp, "out" ) != NULL )
+                       {
+                               mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer,
+                                       mlt_properties_get_position( temp, "in" ), 
+                                       mlt_properties_get_position( temp, "out" ) );
+                       }
+                       else
+                       {
+                               mlt_playlist_append( MLT_PLAYLIST( parent ), producer );
+                       }
+
+                       // Handle the repeat property
+                       if ( mlt_properties_get_int( temp, "repeat" ) > 0 )
+                       {
+                               mlt_playlist_repeat_clip( MLT_PLAYLIST( parent ),
+                                                                                 mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1,
+                                                                                 mlt_properties_get_int( temp, "repeat" ) );
+                       }
+
+                       mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
+                       entry = info.cut;
+               }
+               else
+               {
+                       fprintf( stderr, "Entry not part of a playlist...\n" );
+               }
+
+               context_push_service( context, parent, parent_type );
+       }
+
+       // Push the cut onto the stack
+       context_push_service( context, mlt_producer_service( entry ), mlt_entry_type );
+
+       mlt_properties_close( temp );
+}
+
+static void on_end_entry( deserialise_context context, const xmlChar *name )
+{
+       // Get the entry from the stack
+       enum service_type entry_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" );
+       }
+}
+
+static void on_start_track( 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 ) );
@@ -606,10 +685,7 @@ static void on_start_entry_track( deserialise_context context, const xmlChar *na
        // Push the dummy service onto the stack
        context_push_service( context, service, mlt_entry_type );
        
-       if ( strcmp( name, "entry" ) == 0 )
-               mlt_properties_set( mlt_service_properties( service ), "resource", "<entry>" );
-       else
-               mlt_properties_set( mlt_service_properties( service ), "resource", "<track>" );
+       mlt_properties_set( mlt_service_properties( service ), "resource", "<track>" );
        
        for ( ; atts != NULL && *atts != NULL; atts += 2 )
        {
@@ -651,17 +727,23 @@ static void on_end_track( deserialise_context context, const xmlChar *name )
                if ( multitrack != NULL )
                {
                        // Set the track on the multitrack
-                       mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
 
                        // Set producer i/o if specified
-                       if ( mlt_properties_get( track_props, "in" ) != NULL ||
-                               mlt_properties_get( track_props, "out" ) != NULL )
+                       if ( mlt_properties_get( track_props, "in" ) != NULL &&
+                                mlt_properties_get( track_props, "out" ) != NULL )
                        {
-                               mlt_producer_set_in_and_out( MLT_PRODUCER( producer ),
+                               mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ),
                                        mlt_properties_get_position( track_props, "in" ),
                                        mlt_properties_get_position( track_props, "out" ) );
+                               mlt_properties_set_int( mlt_producer_properties( cut ), "cut", 1 );
+                               mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
+                               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 )
@@ -686,50 +768,6 @@ static void on_end_track( deserialise_context context, const xmlChar *name )
        }
 }
 
-static void on_end_entry( deserialise_context context, const xmlChar *name )
-{
-       // Get the entry from the stack
-       enum service_type entry_type;
-       mlt_service entry = context_pop_service( context, &entry_type );
-
-       if ( entry != NULL && entry_type == mlt_entry_type )
-       {
-               mlt_properties entry_props = mlt_service_properties( entry );
-               enum service_type parent_type;
-               mlt_service parent = context_pop_service( context, &parent_type );
-               mlt_producer producer = mlt_properties_get_data( entry_props, "producer", NULL );
-
-               if ( parent_type == mlt_playlist_type )
-               {
-                       // Append the producer to the playlist
-                       if ( mlt_properties_get( mlt_service_properties( entry ), "in" ) != NULL ||
-                               mlt_properties_get( mlt_service_properties( entry ), "out" ) != NULL )
-                       {
-                               mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer,
-                                       mlt_properties_get_position( mlt_service_properties( entry ), "in" ), 
-                                       mlt_properties_get_position( mlt_service_properties( entry ), "out" ) );
-                       }
-                       else
-                       {
-                               mlt_playlist_append( MLT_PLAYLIST( parent ), producer );
-                       }
-               }
-               else
-               {
-                       fprintf( stderr, "Invalid position for an entry...\n" );
-               }
-
-               if ( parent != NULL )
-                       context_push_service( context, parent, parent_type );
-
-               mlt_service_close( entry );
-       }
-       else
-       {
-               fprintf( stderr, "Invalid state at end of entry\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
@@ -992,14 +1030,19 @@ static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **at
                on_start_producer( context, name, atts );
        else if ( strcmp( name, "blank" ) == 0 )
                on_start_blank( context, name, atts );
-       else if ( strcmp( name, "entry" ) == 0 || strcmp( name, "track" ) == 0 )
-               on_start_entry_track( 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 )
@@ -1213,9 +1256,9 @@ static void parse_url( mlt_properties properties, char *url )
                mlt_properties_set( properties, name, value );
 }
 
-mlt_producer producer_westley_init( char *url )
+mlt_producer producer_westley_init( int info, char *data )
 {
-       if ( url == NULL )
+       if ( data == NULL )
                return NULL;
        xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
        struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
@@ -1223,28 +1266,43 @@ mlt_producer producer_westley_init( char *url )
        int i = 0;
        struct _xmlParserCtxt *xmlcontext;
        int well_formed = 0;
-       char *filename = strdup( url );
+       char *filename = NULL;
        
        context->producer_map = mlt_properties_new();
        context->destructors = mlt_properties_new();
        context->params = mlt_properties_new();
 
-       // Decode URL and parse parameters      
-       parse_url( context->params, url_decode( filename, url ) );
+       // 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 to track the number of registered filters
-       mlt_properties_set_int( context->destructors, "registered", 0 );
+               // 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';
 
-       // We need the directory prefix which was used for the westley
-       mlt_properties_set( context->producer_map, "_root", "" );
-       if ( strchr( filename, '/' ) )
-       {
-               char *root = NULL;
-               mlt_properties_set( context->producer_map, "_root", filename );
-               root = mlt_properties_get( context->producer_map, "_root" );
-               *( strrchr( root, '/' ) + 1 ) = '\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;
@@ -1259,7 +1317,11 @@ mlt_producer producer_westley_init( char *url )
        xmlSubstituteEntitiesDefault( 1 );
        // This is used to facilitate entity substitution in the SAX parser
        context->entity_doc = xmlNewDoc( "1.0" );
-       xmlcontext = xmlCreateFileParserCtxt( filename );
+       if ( info == 0 )
+               xmlcontext = xmlCreateFileParserCtxt( filename );
+       else
+               xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
+
        xmlcontext->sax = sax;
        xmlcontext->_private = ( void* )context;
        
@@ -1296,6 +1358,7 @@ mlt_producer producer_westley_init( char *url )
        
        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;
@@ -1318,10 +1381,18 @@ mlt_producer producer_westley_init( char *url )
                // make the returned service destroy the connected services
                mlt_properties_set_data( properties, "__destructors__", context->destructors, 0, (mlt_destructor) mlt_properties_close, NULL );
 
+               // 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
-                       mlt_properties_set( properties, "resource", url );
+                       if ( info == 0 )
+                               mlt_properties_set( properties, "resource", data );
 
                        // This tells consumer_westley not to deep copy
                        mlt_properties_set( properties, "westley", "was here" );