]> git.sesse.net Git - mlt/blobdiff - src/modules/xml/producer_xml.c
fix leak on allocated sax context
[mlt] / src / modules / xml / producer_xml.c
index 68e034bc9c4e6a7bfa363c5e1220bd346747cb0c..19160a267b73fefd600ee0f6066f2dd84c1ebe77 100644 (file)
@@ -22,9 +22,9 @@
 //       when the returned producer is closed).
 
 #include <framework/mlt.h>
+#include <framework/mlt_log.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdio.h>
 #include <ctype.h>
 #include <unistd.h>
 
@@ -88,9 +88,28 @@ struct deserialise_context_s
        mlt_consumer consumer;
        int multi_consumer;
        int consumer_count;
+       int seekable;
 };
 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 context, char *s )
@@ -134,12 +153,18 @@ static mlt_service context_pop_service( deserialise_context context, enum servic
 {
        mlt_service result = NULL;
        
-       *type = invalid_type;
+       if ( type ) *type = 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
+               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;
 }
@@ -256,7 +281,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;
                };
 
@@ -385,7 +410,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" );
        }
 }
 
@@ -428,7 +453,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" );
        }
 }
 
@@ -439,12 +464,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 );
 
@@ -488,7 +513,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 );
        }
 }
 
@@ -506,44 +531,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;
@@ -555,19 +542,19 @@ static void on_end_producer( deserialise_context context, const xmlChar *name )
                mlt_service producer = NULL;
 
                qualify_property( context, properties, "resource" );
-               char *resource = mlt_properties_get( properties, "resource" );
+               char *resource = trim( mlt_properties_get( properties, "resource" ) );
 
                // Let Kino-SMIL src be a synonym for resource
                if ( resource == NULL )
                {
                        qualify_property( context, properties, "src" );
-                       resource = mlt_properties_get( properties, "src" );
+                       resource = trim( mlt_properties_get( properties, "src" ) );
                }
 
                // 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 );
@@ -587,7 +574,7 @@ static void on_end_producer( deserialise_context context, const xmlChar *name )
                if ( !producer && resource )
                        producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) );
                if ( !producer )
-                       fprintf( stderr, "failed to load producer \"%s\"\n", resource );
+                       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 )
@@ -601,8 +588,9 @@ static void on_end_producer( deserialise_context context, const xmlChar *name )
                // 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" );
 
-               // Propogate the properties
+               // Propagate the properties
                qualify_property( context, properties, "resource" );
                qualify_property( context, properties, "luma" );
                qualify_property( context, properties, "luma.resource" );
@@ -614,33 +602,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 );
@@ -669,7 +641,7 @@ static void on_end_producer( deserialise_context context, const xmlChar *name )
                                        // Get the parent properties
                                        properties = MLT_SERVICE_PROPERTIES( parent );
                                
-                                       char *resource = mlt_properties_get( properties, "resource" );
+                                       char *resource = trim( mlt_properties_get( properties, "resource" ) );
                                
                                        // Put the parent producer back
                                        context_push_service( context, parent, type );
@@ -706,7 +678,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 )
        {
@@ -715,20 +686,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" );
        }
 }
 
@@ -736,6 +705,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 )
        {
@@ -782,7 +753,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 );
@@ -802,7 +773,7 @@ static void on_end_entry( deserialise_context context, const xmlChar *name )
 
        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" );
        }
 }
 
@@ -852,7 +823,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 )
                {
@@ -897,7 +868,7 @@ static void on_end_track( deserialise_context context, const xmlChar *name )
        }
        else
        {
-               fprintf( stderr, "Invalid state at end of track\n" );
+               mlt_log_error( NULL, "[producer_xml] Invalid state at end of track\n" );
        }
 }
 
@@ -927,13 +898,13 @@ static void on_end_filter( deserialise_context context, const xmlChar *name )
 
        if ( service != NULL && type == mlt_dummy_filter_type )
        {
-               char *id = mlt_properties_get( properties, "mlt_service" );
+               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 )
                {
-                       fprintf( stderr, "failed to load filter \"%s\"\n", id );
+                       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 );
@@ -975,7 +946,7 @@ 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
@@ -983,7 +954,7 @@ static void on_end_filter( deserialise_context context, const xmlChar *name )
        }
        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" );
        }
 }
 
@@ -1013,13 +984,13 @@ static void on_end_transition( deserialise_context context, const xmlChar *name
 
        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 )
                {
-                       fprintf( stderr, "failed to load transition \"%s\"\n", id );
+                       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 );
@@ -1056,7 +1027,7 @@ static void on_end_transition( deserialise_context context, const xmlChar *name
                        }
                        else
                        {
-                               fprintf( stderr, "Misplaced transition - ignoring\n" );
+                               mlt_log_warning( NULL, "[producer_xml] Misplaced transition - ignoring\n" );
                        }
 
                        // Put the parent back on the stack
@@ -1064,7 +1035,7 @@ 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
@@ -1072,7 +1043,7 @@ static void on_end_transition( deserialise_context context, const xmlChar *name
        }
        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" );
        }
 }
 
@@ -1105,7 +1076,7 @@ static void on_end_consumer( deserialise_context context, const xmlChar *name )
                        mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
                        qualify_property( context, properties, "resource" );
                        qualify_property( context, properties, "target" );
-                       char *resource = mlt_properties_get( properties, "resource" );
+                       char *resource = trim( mlt_properties_get( properties, "resource" ) );
 
                        if ( context->multi_consumer > 1 )
                        {
@@ -1132,7 +1103,8 @@ static void on_end_consumer( deserialise_context context, const xmlChar *name )
                        else
                        {
                                // Instantiate the consumer
-                               context->consumer = mlt_factory_consumer( context->profile, mlt_properties_get( properties, "mlt_service" ), resource );
+                               char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
+                               context->consumer = mlt_factory_consumer( context->profile, id, resource );
                                if ( context->consumer )
                                {
                                        // Track this consumer
@@ -1177,7 +1149,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 );
        }
 }
 
@@ -1218,7 +1190,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 );
        }
 }
 
@@ -1227,7 +1199,6 @@ 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 ||
@@ -1302,7 +1273,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 )
@@ -1334,7 +1304,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 );
@@ -1357,7 +1327,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 );
@@ -1450,6 +1420,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 )
@@ -1500,8 +1492,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 '&':
@@ -1536,16 +1535,20 @@ static int file_exists( char *file )
 
 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 ) );
@@ -1556,10 +1559,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 ) );
@@ -1588,15 +1592,19 @@ 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->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 ) );
@@ -1614,20 +1622,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 ) );
@@ -1645,7 +1670,7 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype,
                return NULL;
        }
 
-       // Setup SAX callbacks
+       // Setup SAX callbacks for second pass
        sax->endElement = on_end_element;
        sax->characters = on_characters;
        sax->cdataBlock = on_characters;
@@ -1654,6 +1679,7 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype,
        sax->getEntity = on_get_entity;
 
        // Parse
+       sax_orig = xmlcontext->sax;
        xmlcontext->sax = sax;
        xmlcontext->_private = ( void* )context;
        xmlParseDocument( xmlcontext );
@@ -1663,6 +1689,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;
@@ -1715,8 +1746,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" );
@@ -1732,6 +1772,8 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype,
                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 );
        }
        else
        {