]> git.sesse.net Git - mlt/blob - src/modules/xml/producer_xml.c
fix memory leak (coverity-710865)
[mlt] / src / modules / xml / producer_xml.c
1 /*
2  * producer_xml.c -- a libxml2 parser of mlt service networks
3  * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 // TODO: destroy unreferenced producers (they are currently destroyed
22 //       when the returned producer is closed).
23
24 #include <framework/mlt.h>
25 #include <framework/mlt_log.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <unistd.h>
30
31 #include <libxml/parser.h>
32 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
33 #include <libxml/tree.h>
34
35 #define STACK_SIZE 1000
36 #define BRANCH_SIG_LEN 4000
37
38 #define _x (const xmlChar*)
39 #define _s (const char*)
40
41 #undef DEBUG
42 #ifdef DEBUG
43 extern xmlDocPtr xml_make_doc( mlt_service service );
44 #endif
45
46 enum service_type
47 {
48         mlt_invalid_type,
49         mlt_unknown_type,
50         mlt_producer_type,
51         mlt_playlist_type,
52         mlt_entry_type,
53         mlt_tractor_type,
54         mlt_multitrack_type,
55         mlt_filter_type,
56         mlt_transition_type,
57         mlt_consumer_type,
58         mlt_field_type,
59         mlt_services_type,
60         mlt_dummy_filter_type,
61         mlt_dummy_transition_type,
62         mlt_dummy_producer_type,
63         mlt_dummy_consumer_type
64 };
65
66 struct deserialise_context_s
67 {
68         enum service_type stack_types[ STACK_SIZE ];
69         mlt_service stack_service[ STACK_SIZE ];
70         int stack_service_size;
71         mlt_properties producer_map;
72         mlt_properties destructors;
73         char *property;
74         int is_value;
75         xmlDocPtr value_doc;
76         xmlNodePtr stack_node[ STACK_SIZE ];
77         int stack_node_size;
78         xmlDocPtr entity_doc;
79         int entity_is_replace;
80         int depth;
81         int branch[ STACK_SIZE ];
82         const xmlChar *publicId;
83         const xmlChar *systemId;
84         mlt_properties params;
85         mlt_profile profile;
86         int pass;
87         char *lc_numeric;
88         mlt_consumer consumer;
89         int multi_consumer;
90         int consumer_count;
91         int seekable;
92 };
93 typedef struct deserialise_context_s *deserialise_context;
94
95 /** Trim the leading and trailing whitespace from a string in-place.
96 */
97 static char* trim( char *s )
98 {
99         int n;
100         if ( s && ( n = strlen( s ) ) )
101         {
102                 int i = 0;
103                 while ( i < n && isspace( s[i] ) ) i++;
104                 while ( --n && isspace( s[n] ) );
105                 n = n - i + 1;
106                 if ( n > 0 )
107                         memmove( s, s + i, n );
108                 s[ n ] = 0;
109         }
110         return s;
111 }
112
113 /** Convert the numerical current branch address to a dot-delimited string.
114 */
115 static char *serialise_branch( deserialise_context context, char *s )
116 {
117         int i;
118         
119         s[0] = 0;
120         for ( i = 0; i < context->depth; i++ )
121         {
122                 int len = strlen( s );
123                 snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", context->branch[ i ] );
124         }
125         return s;
126 }
127
128 /** Push a service.
129 */
130
131 static int context_push_service( deserialise_context context, mlt_service that, enum service_type type )
132 {
133         int ret = context->stack_service_size >= STACK_SIZE - 1;
134         if ( ret == 0 )
135         {
136                 context->stack_service[ context->stack_service_size ] = that;
137                 context->stack_types[ context->stack_service_size++ ] = type;
138                 
139                 // Record the tree branch on which this service lives
140                 if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_xml_branch" ) == NULL )
141                 {
142                         char s[ BRANCH_SIG_LEN ];
143                         mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( context, s ) );
144                 }
145         }
146         return ret;
147 }
148
149 /** Pop a service.
150 */
151
152 static mlt_service context_pop_service( deserialise_context context, enum service_type *type )
153 {
154         mlt_service result = NULL;
155         
156         *type = invalid_type;
157         if ( context->stack_service_size > 0 )
158         {
159                 result = context->stack_service[ -- context->stack_service_size ];
160                 if ( type != NULL )
161                         *type = context->stack_types[ context->stack_service_size ];
162                 // Set the service's profile and locale so mlt_property time-to-position conversions can get fps
163                 mlt_properties_set_data( MLT_SERVICE_PROPERTIES( result ), "_profile", context->profile, 0, NULL, NULL );
164                 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( result ), context->lc_numeric );
165         }
166         return result;
167 }
168
169 /** Push a node.
170 */
171
172 static int context_push_node( deserialise_context context, xmlNodePtr node )
173 {
174         int ret = context->stack_node_size >= STACK_SIZE - 1;
175         if ( ret == 0 )
176                 context->stack_node[ context->stack_node_size ++ ] = node;
177         return ret;
178 }
179
180 /** Pop a node.
181 */
182
183 static xmlNodePtr context_pop_node( deserialise_context context )
184 {
185         xmlNodePtr result = NULL;
186         if ( context->stack_node_size > 0 )
187                 result = context->stack_node[ -- context->stack_node_size ];
188         return result;
189 }
190
191
192 // Set the destructor on a new service
193 static void track_service( mlt_properties properties, void *service, mlt_destructor destructor )
194 {
195         int registered = mlt_properties_get_int( properties, "registered" );
196         char *key = mlt_properties_get( properties, "registered" );
197         mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
198         mlt_properties_set_int( properties, "registered", ++ registered );
199 }
200
201
202 // Prepend the property value with the document root
203 static inline void qualify_property( deserialise_context context, mlt_properties properties, const char *name )
204 {
205         char *resource = mlt_properties_get( properties, name );
206         if ( resource != NULL && resource[0] )
207         {
208                 // Qualify file name properties 
209                 char *root = mlt_properties_get( context->producer_map, "root" );
210                 if ( root != NULL && strcmp( root, "" ) )
211                 {
212                         char *full_resource = malloc( strlen( root ) + strlen( resource ) + 2 );
213                         if ( resource[ 0 ] != '/' && strchr( resource, ':' ) == NULL )
214                         {
215                                 strcpy( full_resource, root );
216                                 strcat( full_resource, "/" );
217                                 strcat( full_resource, resource );
218                         }
219                         else
220                         {
221                                 strcpy( full_resource, resource );
222                         }
223                         mlt_properties_set( properties, name, full_resource );
224                         free( full_resource );
225                 }
226         }
227 }
228
229
230 /** This function adds a producer to a playlist or multitrack when
231     there is no entry or track element.
232 */
233
234 static int add_producer( deserialise_context context, mlt_service service, mlt_position in, mlt_position out )
235 {
236         // Return value (0 = service remains top of stack, 1 means it can be removed)
237         int result = 0;
238
239         // Get the parent producer
240         enum service_type type = mlt_invalid_type;
241         mlt_service container = context_pop_service( context, &type );
242         int contained = 0;
243
244         if ( service != NULL && container != NULL )
245         {
246                 char *container_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( container ), "_xml_branch" );
247                 char *service_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_xml_branch" );
248                 contained = !strncmp( container_branch, service_branch, strlen( container_branch ) );
249         }
250
251         if ( contained )
252         {
253                 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
254                 char *hide_s = mlt_properties_get( properties, "hide" );
255
256                 // Indicate that this service is no longer top of stack
257                 result = 1;
258
259                 switch( type )
260                 {
261                         case mlt_tractor_type: 
262                                 {
263                                         mlt_multitrack multitrack = mlt_tractor_multitrack( MLT_TRACTOR( container ) );
264                                         mlt_multitrack_connect( multitrack, MLT_PRODUCER( service ), mlt_multitrack_count( multitrack ) );
265                                 }
266                                 break;
267                         case mlt_multitrack_type:
268                                 {
269                                         mlt_multitrack_connect( MLT_MULTITRACK( container ),
270                                                 MLT_PRODUCER( service ),
271                                                 mlt_multitrack_count( MLT_MULTITRACK( container ) ) );
272                                 }
273                                 break;
274                         case mlt_playlist_type:
275                                 {
276                                         mlt_playlist_append_io( MLT_PLAYLIST( container ), MLT_PRODUCER( service ), in, out );
277                                 }
278                                 break;
279                         default:
280                                 result = 0;
281                                 mlt_log_warning( NULL, "[producer_xml] Producer defined inside something that isn't a container\n" );
282                                 break;
283                 };
284
285                 // Set the hide state of the track producer
286                 if ( hide_s != NULL )
287                 {
288                         if ( strcmp( hide_s, "video" ) == 0 )
289                                 mlt_properties_set_int( properties, "hide", 1 );
290                         else if ( strcmp( hide_s, "audio" ) == 0 )
291                                 mlt_properties_set_int( properties, "hide", 2 );
292                         else if ( strcmp( hide_s, "both" ) == 0 )
293                                 mlt_properties_set_int( properties, "hide", 3 );
294                 }
295         }
296
297         // Put the parent producer back
298         if ( container != NULL )
299                 context_push_service( context, container, type );
300
301         return result;
302 }
303
304 /** Attach filters defined on that to this.
305 */
306
307 static void attach_filters( mlt_service service, mlt_service that )
308 {
309         if ( that != NULL )
310         {
311                 int i = 0;
312                 mlt_filter filter = NULL;
313                 for ( i = 0; ( filter = mlt_service_filter( that, i ) ) != NULL; i ++ )
314                 {
315                         mlt_service_attach( service, filter );
316                         attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) );
317                 }
318         }
319 }
320
321 static void on_start_profile( deserialise_context context, const xmlChar *name, const xmlChar **atts)
322 {
323         mlt_profile p = context->profile;
324         for ( ; atts != NULL && *atts != NULL; atts += 2 )
325         {
326                 if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 || xmlStrcmp( atts[ 0 ], _x("profile") ) == 0 )
327                 {
328                         mlt_profile my_profile = mlt_profile_init( _s(atts[ 1 ]) );
329                         if ( my_profile )
330                         {
331                                 p->description = strdup( my_profile->description );
332                                 p->display_aspect_den = my_profile->display_aspect_den;
333                                 p->display_aspect_num = my_profile->display_aspect_num;
334                                 p->frame_rate_den = my_profile->frame_rate_den;
335                                 p->frame_rate_num = my_profile->frame_rate_num;
336                                 p->width = my_profile->width;
337                                 p->height = my_profile->height;
338                                 p->progressive = my_profile->progressive;
339                                 p->sample_aspect_den = my_profile->sample_aspect_den;
340                                 p->sample_aspect_num = my_profile->sample_aspect_num;
341                                 p->colorspace = my_profile->colorspace;
342                                 p->is_explicit = 1;
343                                 mlt_profile_close( my_profile );
344                         }
345                 }
346                 else if ( xmlStrcmp( atts[ 0 ], _x("description") ) == 0 )
347                 {
348                         if ( p->description )
349                                 free( p->description );
350                         p->description = strdup( _s(atts[ 1 ]) );
351                         p->is_explicit = 1;
352                 }
353                 else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_den") ) == 0 )
354                         p->display_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 );
355                 else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_num") ) == 0 )
356                         p->display_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 );
357                 else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_num") ) == 0 )
358                         p->sample_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 );
359                 else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_den") ) == 0 )
360                         p->sample_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 );
361                 else if ( xmlStrcmp( atts[ 0 ], _x("width") ) == 0 )
362                         p->width = strtol( _s(atts[ 1 ]), NULL, 0 );
363                 else if ( xmlStrcmp( atts[ 0 ], _x("height") ) == 0 )
364                         p->height = strtol( _s(atts[ 1 ]), NULL, 0 );
365                 else if ( xmlStrcmp( atts[ 0 ], _x("progressive") ) == 0 )
366                         p->progressive = strtol( _s(atts[ 1 ]), NULL, 0 );
367                 else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_num") ) == 0 )
368                         p->frame_rate_num = strtol( _s(atts[ 1 ]), NULL, 0 );
369                 else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_den") ) == 0 )
370                         p->frame_rate_den = strtol( _s(atts[ 1 ]), NULL, 0 );
371                 else if ( xmlStrcmp( atts[ 0 ], _x("colorspace") ) == 0 )
372                         p->colorspace = strtol( _s(atts[ 1 ]), NULL, 0 );
373         }
374 }
375
376 static void on_start_tractor( deserialise_context context, const xmlChar *name, const xmlChar **atts)
377 {
378         mlt_tractor tractor = mlt_tractor_new( );
379         mlt_service service = MLT_TRACTOR_SERVICE( tractor );
380         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
381
382         track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
383         mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( service ), context->lc_numeric );
384
385         for ( ; atts != NULL && *atts != NULL; atts += 2 )
386                 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
387
388         mlt_properties_set_int( MLT_TRACTOR_PROPERTIES( tractor ), "global_feed", 1 );
389
390         if ( mlt_properties_get( properties, "id" ) != NULL )
391                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
392         
393         context_push_service( context, service, mlt_tractor_type );
394 }
395
396 static void on_end_tractor( deserialise_context context, const xmlChar *name )
397 {
398         // Get the tractor
399         enum service_type type;
400         mlt_service tractor = context_pop_service( context, &type );
401
402         if ( tractor != NULL && type == mlt_tractor_type )
403         {
404                 // See if the tractor should be added to a playlist or multitrack
405                 if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
406                         context_push_service( context, tractor, type );
407         }
408         else
409         {
410                 mlt_log_error( NULL, "[producer_xml] Invalid state for tractor\n" );
411         }
412 }
413
414 static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
415 {
416         enum service_type type;
417         mlt_service parent = context_pop_service( context, &type );
418
419         // If we don't have a parent, then create one now, providing we're in a state where we can
420         if ( parent == NULL || ( type == mlt_playlist_type || type == mlt_multitrack_type ) )
421         {
422                 mlt_tractor tractor = NULL;
423                 // Push the parent back
424                 if ( parent != NULL )
425                         context_push_service( context, parent, type );
426
427                 // Create a tractor to contain the multitrack
428                 tractor = mlt_tractor_new( );
429                 parent = MLT_TRACTOR_SERVICE( tractor );
430                 track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close );
431                 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( parent ), context->lc_numeric );
432                 type = mlt_tractor_type;
433
434                 // Flag it as a synthesised tractor for clean up later
435                 mlt_properties_set_int( MLT_SERVICE_PROPERTIES( parent ), "loader_synth", 1 );
436         }
437
438         if ( type == mlt_tractor_type )
439         {
440                 mlt_service service = MLT_SERVICE( mlt_tractor_multitrack( MLT_TRACTOR( parent ) ) );
441                 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
442                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
443                         mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
444
445                 if ( mlt_properties_get( properties, "id" ) != NULL )
446                         mlt_properties_set_data( context->producer_map, mlt_properties_get( properties,"id" ), service, 0, NULL, NULL );
447
448                 context_push_service( context, parent, type );
449                 context_push_service( context, service, mlt_multitrack_type );
450         }
451         else
452         {
453                 mlt_log_error( NULL, "[producer_xml] Invalid multitrack position\n" );
454         }
455 }
456
457 static void on_end_multitrack( deserialise_context context, const xmlChar *name )
458 {
459         // Get the multitrack from the stack
460         enum service_type type;
461         mlt_service service = context_pop_service( context, &type );
462
463         if ( service == NULL || type != mlt_multitrack_type )
464                 mlt_log_error( NULL, "[producer_xml] End multitrack in the wrong state...\n" );
465 }
466
467 static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
468 {
469         mlt_playlist playlist = mlt_playlist_new( context->profile );
470         mlt_service service = MLT_PLAYLIST_SERVICE( playlist );
471         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
472
473         track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
474
475         for ( ; atts != NULL && *atts != NULL; atts += 2 )
476         {
477                 mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
478
479                 // Out will be overwritten later as we append, so we need to save it
480                 if ( xmlStrcmp( atts[ 0 ], _x("out") ) == 0 )
481                         mlt_properties_set( properties, "_xml.out", ( const char* )atts[ 1 ] );
482         }
483
484         if ( mlt_properties_get( properties, "id" ) != NULL )
485                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
486
487         context_push_service( context, service, mlt_playlist_type );
488 }
489
490 static void on_end_playlist( deserialise_context context, const xmlChar *name )
491 {
492         // Get the playlist from the stack
493         enum service_type type;
494         mlt_service service = context_pop_service( context, &type );
495
496         if ( service != NULL && type == mlt_playlist_type )
497         {
498                 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
499                 mlt_position in = -1;
500                 mlt_position out = -1;
501
502                 if ( mlt_properties_get( properties, "in" ) )
503                         in = mlt_properties_get_position( properties, "in" );
504                 if ( mlt_properties_get( properties, "out" ) )
505                         out = mlt_properties_get_position( properties, "out" );
506
507                 // See if the playlist should be added to a playlist or multitrack
508                 if ( add_producer( context, service, in, out ) == 0 )
509                         context_push_service( context, service, type );
510         }
511         else
512         {
513                 mlt_log_error( NULL, "[producer_xml] Invalid state of playlist end %d\n", type );
514         }
515 }
516
517 static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
518 {
519         // use a dummy service to hold properties to allow arbitrary nesting
520         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
521         mlt_service_init( service, NULL );
522
523         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
524
525         context_push_service( context, service, mlt_dummy_producer_type );
526
527         for ( ; atts != NULL && *atts != NULL; atts += 2 )
528                 mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
529 }
530
531 static void on_end_producer( deserialise_context context, const xmlChar *name )
532 {
533         enum service_type type;
534         mlt_service service = context_pop_service( context, &type );
535         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
536
537         if ( service != NULL && type == mlt_dummy_producer_type )
538         {
539                 mlt_service producer = NULL;
540
541                 qualify_property( context, properties, "resource" );
542                 char *resource = trim( mlt_properties_get( properties, "resource" ) );
543
544                 // Let Kino-SMIL src be a synonym for resource
545                 if ( resource == NULL )
546                 {
547                         qualify_property( context, properties, "src" );
548                         resource = trim( mlt_properties_get( properties, "src" ) );
549                 }
550
551                 // Instantiate the producer
552                 if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
553                 {
554                         char *service_name = trim( mlt_properties_get( properties, "mlt_service" ) );
555                         if ( resource )
556                         {
557                                 char *temp = calloc( 1, strlen( service_name ) + strlen( resource ) + 2 );
558                                 strcat( temp, service_name );
559                                 strcat( temp, ":" );
560                                 strcat( temp, resource );
561                                 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, temp ) );
562                                 free( temp );
563                         }
564                         else
565                         {
566                                 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, service_name ) );
567                         }
568                 }
569
570                 // Just in case the plugin requested doesn't exist...
571                 if ( !producer && resource )
572                         producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) );
573                 if ( !producer )
574                         mlt_log_error( NULL, "[producer_xml] failed to load producer \"%s\"\n", resource );
575                 if ( !producer )
576                         producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "+INVALID.txt" ) );
577                 if ( !producer )
578                         producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "colour:red" ) );
579                 if ( !producer )
580                 {
581                         mlt_service_close( service );
582                         return;
583                 }
584
585                 // Track this producer
586                 track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
587                 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( producer ), context->lc_numeric );
588                 context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" );
589
590                 // Propagate the properties
591                 qualify_property( context, properties, "resource" );
592                 qualify_property( context, properties, "luma" );
593                 qualify_property( context, properties, "luma.resource" );
594                 qualify_property( context, properties, "composite.luma" );
595                 qualify_property( context, properties, "producer.resource" );
596
597                 // Handle in/out properties separately
598                 mlt_position in = -1;
599                 mlt_position out = -1;
600         
601                 // Get in
602                 if ( mlt_properties_get( properties, "in" ) )
603                         in = mlt_properties_get_position( properties, "in" );
604                 // Let Kino-SMIL clipBegin be a synonym for in
605                 else if ( mlt_properties_get( properties, "clipBegin" ) )
606                         in = mlt_properties_get_position( properties, "clipBegin" );
607                 // Get out
608                 if ( mlt_properties_get( properties, "out" ) )
609                         out = mlt_properties_get_position( properties, "out" );
610                 // Let Kino-SMIL clipEnd be a synonym for out
611                 else if ( mlt_properties_get( properties, "clipEnd" ) )
612                         out = mlt_properties_get_position( properties, "clipEnd" );
613                 // Remove in and out
614                 mlt_properties_set( properties, "in", NULL );
615                 mlt_properties_set( properties, "out", NULL );
616
617                 // Inherit the properties
618                 mlt_properties_inherit( MLT_SERVICE_PROPERTIES( producer ), properties );
619
620                 // Attach all filters from service onto producer
621                 attach_filters( producer, service );
622
623                 // Add the producer to the producer map
624                 if ( mlt_properties_get( properties, "id" ) != NULL )
625                         mlt_properties_set_data( context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL );
626
627                 // See if the producer should be added to a playlist or multitrack
628                 if ( add_producer( context, producer, in, out ) == 0 )
629                 {
630                         // Otherwise, set in and out on...
631                         if ( in != -1 || out != -1 )
632                         {
633                                 // Get the parent service
634                                 enum service_type type;
635                                 mlt_service parent = context_pop_service( context, &type );
636                                 if ( parent != NULL )
637                                 {
638                                         // Get the parent properties
639                                         properties = MLT_SERVICE_PROPERTIES( parent );
640                                 
641                                         char *resource = trim( mlt_properties_get( properties, "resource" ) );
642                                 
643                                         // Put the parent producer back
644                                         context_push_service( context, parent, type );
645                                         
646                                         // If the parent is a track or entry
647                                         if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
648                                         {
649                                                 if ( in > -1 ) mlt_properties_set_position( properties, "in", in );
650                                                 if ( out > -1 ) mlt_properties_set_position( properties, "out", out );
651                                         }
652                                         else
653                                         {
654                                                 // Otherwise, set in and out on producer directly
655                                                 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
656                                         }
657                                 }
658                                 else
659                                 {
660                                         // Otherwise, set in and out on producer directly
661                                         mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
662                                 }
663                         }
664
665                         // Push the producer onto the stack
666                         context_push_service( context, producer, mlt_producer_type );
667                 }
668
669                 mlt_service_close( service );
670         }
671 }
672
673 static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
674 {
675         // Get the playlist from the stack
676         enum service_type type;
677         mlt_service service = context_pop_service( context, &type );
678         
679         if ( type == mlt_playlist_type && service != NULL )
680         {
681                 // Look for the length attribute
682                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
683                 {
684                         if ( xmlStrcmp( atts[0], _x("length") ) == 0 )
685                         {
686                                 // Append a blank to the playlist
687                                 mlt_playlist_blank_time( MLT_PLAYLIST( service ), _s(atts[1]) );
688                                 break;
689                         }
690                 }
691
692                 // Push the playlist back onto the stack
693                 context_push_service( context, service, type );
694         }
695         else
696         {
697                 mlt_log_error( NULL, "[producer_xml] blank without a playlist - a definite no no\n" );
698         }
699 }
700
701 static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
702 {
703         mlt_producer entry = NULL;
704         mlt_properties temp = mlt_properties_new( );
705         mlt_properties_set_data( temp, "_profile", context->profile, 0, NULL, NULL );
706         mlt_properties_set_lcnumeric( temp, context->lc_numeric );
707
708         for ( ; atts != NULL && *atts != NULL; atts += 2 )
709         {
710                 mlt_properties_set( temp, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
711                 
712                 // Look for the producer attribute
713                 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
714                 {
715                         mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
716                         if ( producer !=  NULL )
717                                 mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
718                 }
719         }
720
721         // If we have a valid entry
722         if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
723         {
724                 mlt_playlist_clip_info info;
725                 enum service_type parent_type = invalid_type;
726                 mlt_service parent = context_pop_service( context, &parent_type );
727                 mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
728
729                 if ( parent_type == mlt_playlist_type )
730                 {
731                         // Append the producer to the playlist
732                         mlt_position in = -1;
733                         mlt_position out = -1;
734                         if ( mlt_properties_get( temp, "in" ) )
735                                 in = mlt_properties_get_position( temp, "in" );
736                         if ( mlt_properties_get( temp, "out" ) )
737                                 out = mlt_properties_get_position( temp, "out" );
738                         mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer, in, out );
739
740                         // Handle the repeat property
741                         if ( mlt_properties_get_int( temp, "repeat" ) > 0 )
742                         {
743                                 mlt_playlist_repeat_clip( MLT_PLAYLIST( parent ),
744                                                                                   mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1,
745                                                                                   mlt_properties_get_int( temp, "repeat" ) );
746                         }
747
748                         mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
749                         entry = info.cut;
750                 }
751                 else
752                 {
753                         mlt_log_error( NULL, "[producer_xml] Entry not part of a playlist...\n" );
754                 }
755
756                 context_push_service( context, parent, parent_type );
757         }
758
759         // Push the cut onto the stack
760         context_push_service( context, MLT_PRODUCER_SERVICE( entry ), mlt_entry_type );
761
762         mlt_properties_close( temp );
763 }
764
765 static void on_end_entry( deserialise_context context, const xmlChar *name )
766 {
767         // Get the entry from the stack
768         enum service_type entry_type = invalid_type;
769         mlt_service entry = context_pop_service( context, &entry_type );
770
771         if ( entry == NULL && entry_type != mlt_entry_type )
772         {
773                 mlt_log_error( NULL, "[producer_xml] Invalid state at end of entry\n" );
774         }
775 }
776
777 static void on_start_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
778 {
779         // use a dummy service to hold properties to allow arbitrary nesting
780         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
781         mlt_service_init( service, NULL );
782
783         // Push the dummy service onto the stack
784         context_push_service( context, service, mlt_entry_type );
785         
786         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "resource", "<track>" );
787         
788         for ( ; atts != NULL && *atts != NULL; atts += 2 )
789         {
790                 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
791                 
792                 // Look for the producer attribute
793                 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
794                 {
795                         mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
796                         if ( producer !=  NULL )
797                                 mlt_properties_set_data( MLT_SERVICE_PROPERTIES( service ), "producer", producer, 0, NULL, NULL );
798                 }
799         }
800 }
801
802 static void on_end_track( deserialise_context context, const xmlChar *name )
803 {
804         // Get the track from the stack
805         enum service_type track_type;
806         mlt_service track = context_pop_service( context, &track_type );
807
808         if ( track != NULL && track_type == mlt_entry_type )
809         {
810                 mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
811                 enum service_type parent_type = invalid_type;
812                 mlt_service parent = context_pop_service( context, &parent_type );
813                 mlt_multitrack multitrack = NULL;
814
815                 mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
816                 mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
817
818                 if ( parent_type == mlt_tractor_type )
819                         multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
820                 else if ( parent_type == mlt_multitrack_type )
821                         multitrack = MLT_MULTITRACK( parent );
822                 else
823                         mlt_log_error( NULL, "[producer_xml] track contained in an invalid container\n" );
824
825                 if ( multitrack != NULL )
826                 {
827                         // Set producer i/o if specified
828                         if ( mlt_properties_get( track_props, "in" ) != NULL ||
829                                  mlt_properties_get( track_props, "out" ) != NULL )
830                         {
831                                 mlt_position in = -1;
832                                 mlt_position out = -1;
833                                 if ( mlt_properties_get( track_props, "in" ) )
834                                         in = mlt_properties_get_position( track_props, "in" );
835                                 if ( mlt_properties_get( track_props, "out" ) )
836                                         out = mlt_properties_get_position( track_props, "out" );
837                                 mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ), in, out );
838                                 mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
839                                 mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( cut ), track_props );
840                                 track_props = MLT_PRODUCER_PROPERTIES( cut );
841                                 mlt_producer_close( cut );
842                         }
843                         else
844                         {
845                                 mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
846                         }
847
848                         // Set the hide state of the track producer
849                         char *hide_s = mlt_properties_get( track_props, "hide" );
850                         if ( hide_s != NULL )
851                         {
852                                 if ( strcmp( hide_s, "video" ) == 0 )
853                                         mlt_properties_set_int( producer_props, "hide", 1 );
854                                 else if ( strcmp( hide_s, "audio" ) == 0 )
855                                         mlt_properties_set_int( producer_props, "hide", 2 );
856                                 else if ( strcmp( hide_s, "both" ) == 0 )
857                                         mlt_properties_set_int( producer_props, "hide", 3 );
858                         }
859                 }
860
861                 if ( parent != NULL )
862                         context_push_service( context, parent, parent_type );
863
864                 mlt_service_close( track );
865         }
866         else
867         {
868                 mlt_log_error( NULL, "[producer_xml] Invalid state at end of track\n" );
869         }
870 }
871
872 static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
873 {
874         // use a dummy service to hold properties to allow arbitrary nesting
875         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
876         mlt_service_init( service, NULL );
877
878         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
879
880         context_push_service( context, service, mlt_dummy_filter_type );
881
882         // Set the properties
883         for ( ; atts != NULL && *atts != NULL; atts += 2 )
884                 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
885 }
886
887 static void on_end_filter( deserialise_context context, const xmlChar *name )
888 {
889         enum service_type type;
890         mlt_service service = context_pop_service( context, &type );
891         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
892
893         enum service_type parent_type = invalid_type;
894         mlt_service parent = context_pop_service( context, &parent_type );
895
896         if ( service != NULL && type == mlt_dummy_filter_type )
897         {
898                 char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
899                 mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, id, NULL ) );
900                 mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
901
902                 if ( !filter )
903                 {
904                         mlt_log_error( NULL, "[producer_xml] failed to load filter \"%s\"\n", id );
905                         if ( parent )
906                                 context_push_service( context, parent, parent_type );
907                         mlt_service_close( service );
908                         return;
909                 }
910
911                 track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
912                 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( filter ), context->lc_numeric );
913
914                 // Propogate the properties
915                 qualify_property( context, properties, "resource" );
916                 qualify_property( context, properties, "luma" );
917                 qualify_property( context, properties, "luma.resource" );
918                 qualify_property( context, properties, "composite.luma" );
919                 qualify_property( context, properties, "producer.resource" );
920                 mlt_properties_inherit( filter_props, properties );
921
922                 // Attach all filters from service onto filter
923                 attach_filters( filter, service );
924
925                 // Associate the filter with the parent
926                 if ( parent != NULL )
927                 {
928                         if ( parent_type == mlt_tractor_type )
929                         {
930                                 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
931                                 mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
932                                 mlt_filter_set_in_and_out( MLT_FILTER( filter ), 
933                                                                                    mlt_properties_get_int( properties, "in" ),
934                                                                                    mlt_properties_get_int( properties, "out" ) );
935                         }
936                         else
937                         {
938                                 mlt_service_attach( parent, MLT_FILTER( filter ) );
939                         }
940
941                         // Put the parent back on the stack
942                         context_push_service( context, parent, parent_type );
943                 }
944                 else
945                 {
946                         mlt_log_error( NULL, "[producer_xml] filter closed with invalid parent...\n" );
947                 }
948
949                 // Close the dummy filter service
950                 mlt_service_close( service );
951         }
952         else
953         {
954                 mlt_log_error( NULL, "[producer_xml] Invalid top of stack on filter close\n" );
955         }
956 }
957
958 static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
959 {
960         // use a dummy service to hold properties to allow arbitrary nesting
961         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
962         mlt_service_init( service, NULL );
963
964         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
965
966         context_push_service( context, service, mlt_dummy_transition_type );
967
968         // Set the properties
969         for ( ; atts != NULL && *atts != NULL; atts += 2 )
970                 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
971 }
972
973 static void on_end_transition( deserialise_context context, const xmlChar *name )
974 {
975         enum service_type type;
976         mlt_service service = context_pop_service( context, &type );
977         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
978
979         enum service_type parent_type = invalid_type;
980         mlt_service parent = context_pop_service( context, &parent_type );
981
982         if ( service != NULL && type == mlt_dummy_transition_type )
983         {
984                 char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
985                 mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) );
986                 mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
987
988                 if ( !effect )
989                 {
990                         mlt_log_error( NULL, "[producer_xml] failed to load transition \"%s\"\n", id );
991                         if ( parent )
992                                 context_push_service( context, parent, parent_type );
993                         mlt_service_close( service );
994                         return;
995                 }
996                 track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
997                 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( effect ), context->lc_numeric );
998
999                 // Propogate the properties
1000                 qualify_property( context, properties, "resource" );
1001                 qualify_property( context, properties, "luma" );
1002                 qualify_property( context, properties, "luma.resource" );
1003                 qualify_property( context, properties, "composite.luma" );
1004                 qualify_property( context, properties, "producer.resource" );
1005                 mlt_properties_inherit( effect_props, properties );
1006
1007                 // Attach all filters from service onto effect
1008                 attach_filters( effect, service );
1009
1010                 // Associate the filter with the parent
1011                 if ( parent != NULL )
1012                 {
1013                         if ( parent_type == mlt_tractor_type )
1014                         {
1015                                 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
1016                                 if ( mlt_properties_get_int( properties, "a_track" ) == mlt_properties_get_int( properties, "b_track" ) )
1017                                         mlt_properties_set_int( properties, "b_track", mlt_properties_get_int( properties, "a_track" ) + 1 );
1018                                 mlt_field_plant_transition( field, MLT_TRANSITION( effect ),
1019                                                                                         mlt_properties_get_int( properties, "a_track" ),
1020                                                                                         mlt_properties_get_int( properties, "b_track" ) );
1021                                 mlt_transition_set_in_and_out( MLT_TRANSITION( effect ),
1022                                                                                    mlt_properties_get_int( properties, "in" ),
1023                                                                                    mlt_properties_get_int( properties, "out" ) );
1024                         }
1025                         else
1026                         {
1027                                 mlt_log_warning( NULL, "[producer_xml] Misplaced transition - ignoring\n" );
1028                         }
1029
1030                         // Put the parent back on the stack
1031                         context_push_service( context, parent, parent_type );
1032                 }
1033                 else
1034                 {
1035                         mlt_log_error( NULL, "[producer_xml] transition closed with invalid parent...\n" );
1036                 }
1037
1038                 // Close the dummy filter service
1039                 mlt_service_close( service );
1040         }
1041         else
1042         {
1043                 mlt_log_error( NULL, "[producer_xml] Invalid top of stack on transition close\n" );
1044         }
1045 }
1046
1047 static void on_start_consumer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1048 {
1049         if ( context->pass == 1 )
1050         {
1051                 mlt_consumer consumer = mlt_consumer_new( context->profile );
1052                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
1053
1054                 mlt_properties_set_lcnumeric( properties, context->lc_numeric );
1055                 context_push_service( context, MLT_CONSUMER_SERVICE(consumer), mlt_dummy_consumer_type );
1056
1057                 // Set the properties from attributes
1058                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1059                         mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
1060         }
1061 }
1062
1063 static void on_end_consumer( deserialise_context context, const xmlChar *name )
1064 {
1065         if ( context->pass == 1 )
1066         {
1067                 // Get the consumer from the stack
1068                 enum service_type type;
1069                 mlt_service service = context_pop_service( context, &type );
1070
1071                 if ( service && type == mlt_dummy_consumer_type )
1072                 {
1073                         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1074                         qualify_property( context, properties, "resource" );
1075                         qualify_property( context, properties, "target" );
1076                         char *resource = trim( mlt_properties_get( properties, "resource" ) );
1077
1078                         if ( context->multi_consumer > 1 )
1079                         {
1080                                 // Instantiate the multi consumer
1081                                 if ( !context->consumer )
1082                                 {
1083                                         context->consumer = mlt_factory_consumer( context->profile, "multi", NULL );
1084                                         if ( context->consumer )
1085                                         {
1086                                                 // Track this consumer
1087                                                 track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
1088                                                 mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
1089                                         }
1090                                 }
1091                                 if ( context->consumer )
1092                                 {
1093                                         // Set this service instance on multi consumer
1094                                         char key[20];
1095                                         snprintf( key, sizeof(key), "%d", context->consumer_count++ );
1096                                         mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(context->consumer), key, service, 0,
1097                                                 (mlt_destructor) mlt_service_close, NULL );
1098                                 }
1099                         }
1100                         else
1101                         {
1102                                 // Instantiate the consumer
1103                                 char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
1104                                 context->consumer = mlt_factory_consumer( context->profile, id, resource );
1105                                 if ( context->consumer )
1106                                 {
1107                                         // Track this consumer
1108                                         track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
1109                                         mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
1110
1111                                         // Inherit the properties
1112                                         mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties );
1113                                 }
1114                                 // Close the dummy
1115                                 mlt_service_close( service );
1116                         }
1117                 }
1118         }
1119 }
1120
1121 static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1122 {
1123         enum service_type type;
1124         mlt_service service = context_pop_service( context, &type );
1125         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1126         const char *value = NULL;
1127
1128         if ( service != NULL )
1129         {
1130                 // Set the properties
1131                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1132                 {
1133                         if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 )
1134                                 context->property = strdup( _s(atts[ 1 ]) );
1135                         else if ( xmlStrcmp( atts[ 0 ], _x("value") ) == 0 )
1136                                 value = _s(atts[ 1 ]);
1137                 }
1138
1139                 if ( context->property != NULL )
1140                         mlt_properties_set( properties, context->property, value == NULL ? "" : value );
1141
1142                 // Tell parser to collect any further nodes for serialisation
1143                 context->is_value = 1;
1144
1145                 context_push_service( context, service, type );
1146         }
1147         else
1148         {
1149                 mlt_log_error( NULL, "[producer_xml] Property without a service '%s'?\n", ( const char * )name );
1150         }
1151 }
1152
1153 static void on_end_property( deserialise_context context, const xmlChar *name )
1154 {
1155         enum service_type type;
1156         mlt_service service = context_pop_service( context, &type );
1157         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1158
1159         if ( service != NULL )
1160         {
1161                 // Tell parser to stop building a tree
1162                 context->is_value = 0;
1163         
1164                 // See if there is a xml tree for the value
1165                 if ( context->property != NULL && context->value_doc != NULL )
1166                 {
1167                         xmlChar *value;
1168                         int size;
1169                 
1170                         // Serialise the tree to get value
1171                         xmlDocDumpMemory( context->value_doc, &value, &size );
1172                         mlt_properties_set( properties, context->property, _s(value) );
1173 #ifdef WIN32
1174                         xmlFreeFunc xmlFree = NULL;
1175                         xmlMemGet( &xmlFree, NULL, NULL, NULL);
1176 #endif
1177                         xmlFree( value );
1178                         xmlFreeDoc( context->value_doc );
1179                         context->value_doc = NULL;
1180                 }
1181
1182                 // Close this property handling
1183                 free( context->property );
1184                 context->property = NULL;
1185
1186                 context_push_service( context, service, type );
1187         }
1188         else
1189         {
1190                 mlt_log_error( NULL, "[producer_xml] Property without a service '%s'??\n", (const char *)name );
1191         }
1192 }
1193
1194 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
1195 {
1196         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1197         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1198         
1199         if ( context->pass == 0 )
1200         {
1201                 if ( xmlStrcmp( name, _x("mlt") ) == 0 ||
1202                      xmlStrcmp( name, _x("profile") ) == 0 ||
1203                      xmlStrcmp( name, _x("profileinfo") ) == 0 )
1204                         on_start_profile( context, name, atts );
1205                 if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1206                         context->multi_consumer++;
1207                 return;
1208         }
1209         context->branch[ context->depth ] ++;
1210         context->depth ++;
1211         
1212         // Build a tree from nodes within a property value
1213         if ( context->is_value == 1 && context->pass == 1 )
1214         {
1215                 xmlNodePtr node = xmlNewNode( NULL, name );
1216                 
1217                 if ( context->value_doc == NULL )
1218                 {
1219                         // Start a new tree
1220                         context->value_doc = xmlNewDoc( _x("1.0") );
1221                         xmlDocSetRootElement( context->value_doc, node );
1222                 }
1223                 else
1224                 {
1225                         // Append child to tree
1226                         xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
1227                 }
1228                 context_push_node( context, node );
1229                 
1230                 // Set the attributes
1231                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1232                         xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
1233         }
1234         else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1235                 on_start_tractor( context, name, atts );
1236         else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1237                 on_start_multitrack( context, name, atts );
1238         else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1239                 on_start_playlist( context, name, atts );
1240         else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1241                 on_start_producer( context, name, atts );
1242         else if ( xmlStrcmp( name, _x("blank") ) == 0 )
1243                 on_start_blank( context, name, atts );
1244         else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1245                 on_start_entry( context, name, atts );
1246         else if ( xmlStrcmp( name, _x("track") ) == 0 )
1247                 on_start_track( context, name, atts );
1248         else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1249                 on_start_filter( context, name, atts );
1250         else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1251                 on_start_transition( context, name, atts );
1252         else if ( xmlStrcmp( name, _x("property") ) == 0 )
1253                 on_start_property( context, name, atts );
1254         else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1255                 on_start_consumer( context, name, atts );
1256         else if ( xmlStrcmp( name, _x("westley") ) == 0 || xmlStrcmp( name, _x("mlt") ) == 0 )
1257         {
1258                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1259                 {
1260                         if ( xmlStrcmp( atts[0], _x("LC_NUMERIC") ) )
1261                                 mlt_properties_set( context->producer_map, _s( atts[0] ), _s(atts[1] ) );
1262                         else if ( !context->lc_numeric )
1263                                 context->lc_numeric = strdup( _s( atts[1] ) );
1264                 }
1265         }
1266 }
1267
1268 static void on_end_element( void *ctx, const xmlChar *name )
1269 {
1270         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1271         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1272         
1273         if ( context->is_value == 1 && context->pass == 1 && xmlStrcmp( name, _x("property") ) != 0 )
1274                 context_pop_node( context );
1275         else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1276                 on_end_multitrack( context, name );
1277         else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1278                 on_end_playlist( context, name );
1279         else if ( xmlStrcmp( name, _x("track") ) == 0 )
1280                 on_end_track( context, name );
1281         else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1282                 on_end_entry( context, name );
1283         else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1284                 on_end_tractor( context, name );
1285         else if ( xmlStrcmp( name, _x("property") ) == 0 )
1286                 on_end_property( context, name );
1287         else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1288                 on_end_producer( context, name );
1289         else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1290                 on_end_filter( context, name );
1291         else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1292                 on_end_transition( context, name );
1293         else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1294                 on_end_consumer( context, name );
1295
1296         context->branch[ context->depth ] = 0;
1297         context->depth --;
1298 }
1299
1300 static void on_characters( void *ctx, const xmlChar *ch, int len )
1301 {
1302         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1303         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1304         char *value = calloc( len + 1, 1 );
1305         enum service_type type;
1306         mlt_service service = context_pop_service( context, &type );
1307         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1308
1309         if ( service != NULL )
1310                 context_push_service( context, service, type );
1311
1312         value[ len ] = 0;
1313         strncpy( value, (const char*) ch, len );
1314
1315         if ( context->stack_node_size > 0 )
1316                 xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
1317
1318         // libxml2 generates an on_characters immediately after a get_entity within
1319         // an element value, and we ignore it because it is called again during
1320         // actual substitution.
1321         else if ( context->property != NULL && context->entity_is_replace == 0 )
1322         {
1323                 char *s = mlt_properties_get( properties, context->property );
1324                 if ( s != NULL )
1325                 {
1326                         // Append new text to existing content
1327                         char *new = calloc( strlen( s ) + len + 1, 1 );
1328                         strcat( new, s );
1329                         strcat( new, value );
1330                         mlt_properties_set( properties, context->property, new );
1331                         free( new );
1332                 }
1333                 else
1334                         mlt_properties_set( properties, context->property, value );
1335         }
1336         context->entity_is_replace = 0;
1337         
1338         free( value);
1339 }
1340
1341 /** Convert parameters parsed from resource into entity declarations.
1342 */
1343 static void params_to_entities( deserialise_context context )
1344 {
1345         if ( context->params != NULL )
1346         {       
1347                 int i;
1348                 
1349                 // Add our params as entitiy declarations
1350                 for ( i = 0; i < mlt_properties_count( context->params ); i++ )
1351                 {
1352                         xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
1353                         xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
1354                                 context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, _s(name) ) );
1355                 }
1356
1357                 // Flag completion
1358                 mlt_properties_close( context->params );
1359                 context->params = NULL;
1360         }
1361 }
1362
1363 // The following 3 facilitate entity substitution in the SAX parser
1364 static void on_internal_subset( void *ctx, const xmlChar* name,
1365         const xmlChar* publicId, const xmlChar* systemId )
1366 {
1367         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1368         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1369         
1370         context->publicId = publicId;
1371         context->systemId = systemId;
1372         xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
1373         
1374         // Override default entities with our parameters
1375         params_to_entities( context );
1376 }
1377
1378 // TODO: Check this with Dan... I think this is for parameterisation
1379 // but it's breaking standard escaped entities (like &lt; etc).
1380 static void on_entity_declaration( void *ctx, const xmlChar* name, int type, 
1381         const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
1382 {
1383         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1384         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1385         
1386         xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
1387 }
1388
1389 // TODO: Check this functionality (see on_entity_declaration)
1390 static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
1391 {
1392         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1393         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1394         xmlEntityPtr e = NULL;
1395
1396         // Setup for entity declarations if not ready
1397         if ( xmlGetIntSubset( context->entity_doc ) == NULL )
1398         {
1399                 xmlCreateIntSubset( context->entity_doc, _x("mlt"), _x(""), _x("") );
1400                 context->publicId = _x("");
1401                 context->systemId = _x("");
1402         }
1403
1404         // Add our parameters if not already
1405         params_to_entities( context );
1406         
1407         e = xmlGetPredefinedEntity( name );
1408         
1409         // Send signal to on_characters that an entity substitutin is pending
1410         if ( e == NULL )
1411         {
1412                 e = xmlGetDocEntity( context->entity_doc, name );
1413                 if ( e != NULL )
1414                         context->entity_is_replace = 1;
1415         }
1416         
1417         return e;
1418 }
1419
1420 static void     on_error( void * ctx, const char * msg, ... )
1421 {
1422         struct _xmlError* err_ptr = xmlCtxtGetLastError( ctx );
1423
1424         switch( err_ptr->level )
1425         {
1426         case XML_ERR_WARNING:
1427                 mlt_log_warning( NULL, "[producer_xml] parse warning: %s\trow: %d\tcol: %d\n",
1428                                          err_ptr->message, err_ptr->line, err_ptr->int2 );
1429                 break;
1430         case XML_ERR_ERROR:
1431                 mlt_log_error( NULL, "[producer_xml] parse error: %s\trow: %d\tcol: %d\n",
1432                                        err_ptr->message, err_ptr->line, err_ptr->int2 );
1433                 break;
1434         default:
1435         case XML_ERR_FATAL:
1436                 mlt_log_fatal( NULL, "[producer_xml] parse fatal: %s\trow: %d\tcol: %d\n",
1437                                        err_ptr->message, err_ptr->line, err_ptr->int2 );
1438                 break;
1439         }
1440 }
1441
1442 /** Convert a hexadecimal character to its value.
1443 */
1444 static int tohex( char p )
1445 {
1446         return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
1447 }
1448
1449 /** Decode a url-encoded string containing hexadecimal character sequences.
1450 */
1451 static char *url_decode( char *dest, char *src )
1452 {
1453         char *p = dest;
1454         
1455         while ( *src )
1456         {
1457                 if ( *src == '%' )
1458                 {
1459                         *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
1460                         src += 3;
1461                 }
1462                 else
1463                 {
1464                         *p ++ = *src ++;
1465                 }
1466         }
1467
1468         *p = *src;
1469         return dest;
1470 }
1471
1472 /** Extract the filename from a URL attaching parameters to a properties list.
1473 */
1474 static void parse_url( mlt_properties properties, char *url )
1475 {
1476         int i;
1477         int n = strlen( url );
1478         char *name = NULL;
1479         char *value = NULL;
1480         
1481         for ( i = 0; i < n; i++ )
1482         {
1483                 switch ( url[ i ] )
1484                 {
1485                         case '?':
1486                                 url[ i++ ] = '\0';
1487                                 name = &url[ i ];
1488                                 break;
1489                         
1490                         case ':':
1491                         case '=':
1492 #ifdef WIN32
1493                                 if ( url[i] == ':' && url[i + 1] != '/' )
1494                                 {
1495 #endif
1496                                         url[ i++ ] = '\0';
1497                                         value = &url[ i ];
1498 #ifdef WIN32
1499                                 }
1500 #endif
1501                                 break;
1502                         
1503                         case '&':
1504                                 url[ i++ ] = '\0';
1505                                 if ( name != NULL && value != NULL )
1506                                         mlt_properties_set( properties, name, value );
1507                                 name = &url[ i ];
1508                                 value = NULL;
1509                                 break;
1510                 }
1511         }
1512         if ( name != NULL && value != NULL )
1513                 mlt_properties_set( properties, name, value );
1514 }
1515
1516 // Quick workaround to avoid unecessary libxml2 warnings
1517 static int file_exists( char *file )
1518 {
1519         char *name = strdup( file );
1520         int exists = 0;
1521         if ( name != NULL && strchr( name, '?' ) )
1522                 *( strchr( name, '?' ) ) = '\0';
1523         if ( name != NULL )
1524         {
1525                 FILE *f = fopen( name, "r" );
1526                 exists = f != NULL;
1527                 if ( exists ) fclose( f );
1528         }
1529         free( name );
1530         return exists;
1531 }
1532
1533 mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data )
1534 {
1535         xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
1536         struct deserialise_context_s *context;
1537         mlt_properties properties = NULL;
1538         int i = 0;
1539         struct _xmlParserCtxt *xmlcontext;
1540         int well_formed = 0;
1541         char *filename = NULL;
1542         int info = strcmp( id, "xml-string" ) ? 0 : 1;
1543
1544         // Strip file:// prefix
1545         if ( data && strlen( data ) >= 7 && strncmp( data, "file://", 7 ) == 0 )
1546                 data += 7;
1547
1548         if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) )
1549                 return NULL;
1550
1551         context = calloc( 1, sizeof( struct deserialise_context_s ) );
1552         if ( context == NULL )
1553                 return NULL;
1554
1555         context->producer_map = mlt_properties_new();
1556         context->destructors = mlt_properties_new();
1557         context->params = mlt_properties_new();
1558         context->profile = profile;
1559         context->seekable = 1;
1560
1561         // Decode URL and parse parameters
1562         mlt_properties_set( context->producer_map, "root", "" );
1563         if ( info == 0 )
1564         {
1565                 filename = strdup( data );
1566                 parse_url( context->params, url_decode( filename, data ) );
1567
1568                 // We need the directory prefix which was used for the xml
1569                 if ( strchr( filename, '/' ) )
1570                 {
1571                         char *root = NULL;
1572                         mlt_properties_set( context->producer_map, "root", filename );
1573                         root = mlt_properties_get( context->producer_map, "root" );
1574                         *( strrchr( root, '/' ) ) = '\0';
1575
1576                         // If we don't have an absolute path here, we're heading for disaster...
1577                         if ( root[ 0 ] != '/' )
1578                         {
1579                                 char *cwd = getcwd( NULL, 0 );
1580                                 char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
1581                                 sprintf( real, "%s/%s", cwd, root );
1582                                 mlt_properties_set( context->producer_map, "root", real );
1583                                 free( real );
1584                                 free( cwd );
1585                         }
1586                 }
1587         }
1588
1589         // We need to track the number of registered filters
1590         mlt_properties_set_int( context->destructors, "registered", 0 );
1591
1592         // Setup SAX callbacks for first pass
1593         sax->startElement = on_start_element;
1594         sax->warning = on_error;
1595         sax->error = on_error;
1596         sax->fatalError = on_error;
1597
1598         // Setup libxml2 SAX parsing
1599         xmlInitParser(); 
1600         xmlSubstituteEntitiesDefault( 1 );
1601         // This is used to facilitate entity substitution in the SAX parser
1602         context->entity_doc = xmlNewDoc( _x("1.0") );
1603         if ( info == 0 )
1604                 xmlcontext = xmlCreateFileParserCtxt( filename );
1605         else
1606                 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1607
1608         // Invalid context - clean up and return NULL
1609         if ( xmlcontext == NULL )
1610         {
1611                 mlt_properties_close( context->producer_map );
1612                 mlt_properties_close( context->destructors );
1613                 mlt_properties_close( context->params );
1614                 free( context );
1615                 free( sax );
1616                 free( filename );
1617                 return NULL;
1618         }
1619
1620         // Parse
1621         xmlcontext->sax = sax;
1622         xmlcontext->_private = ( void* )context;        
1623         xmlParseDocument( xmlcontext );
1624         well_formed = xmlcontext->wellFormed;
1625         
1626         // Cleanup after parsing
1627         xmlcontext->sax = NULL;
1628         xmlcontext->_private = NULL;
1629         xmlFreeParserCtxt( xmlcontext );
1630         context->stack_node_size = 0;
1631         context->stack_service_size = 0;
1632
1633         // Bad xml - clean up and return NULL
1634         if ( !well_formed )
1635         {
1636                 mlt_properties_close( context->producer_map );
1637                 mlt_properties_close( context->destructors );
1638                 mlt_properties_close( context->params );
1639                 xmlFreeDoc( context->entity_doc );
1640                 free( context );
1641                 free( sax );
1642                 free( filename );
1643                 return NULL;
1644         }
1645
1646         // Setup the second pass
1647         context->pass ++;
1648         if ( info == 0 )
1649                 xmlcontext = xmlCreateFileParserCtxt( filename );
1650         else
1651                 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1652
1653         // Invalid context - clean up and return NULL
1654         if ( xmlcontext == NULL )
1655         {
1656                 mlt_properties_close( context->producer_map );
1657                 mlt_properties_close( context->destructors );
1658                 mlt_properties_close( context->params );
1659                 xmlFreeDoc( context->entity_doc );
1660                 free( context );
1661                 free( sax );
1662                 free( filename );
1663                 return NULL;
1664         }
1665
1666         // Setup SAX callbacks for second pass
1667         sax->endElement = on_end_element;
1668         sax->characters = on_characters;
1669         sax->cdataBlock = on_characters;
1670         sax->internalSubset = on_internal_subset;
1671         sax->entityDecl = on_entity_declaration;
1672         sax->getEntity = on_get_entity;
1673
1674         // Parse
1675         xmlcontext->sax = sax;
1676         xmlcontext->_private = ( void* )context;
1677         xmlParseDocument( xmlcontext );
1678         well_formed = xmlcontext->wellFormed;
1679
1680         // Cleanup after parsing
1681         xmlFreeDoc( context->entity_doc );
1682         free( sax );
1683         xmlMemoryDump( ); // for debugging
1684
1685         // Get the last producer on the stack
1686         enum service_type type;
1687         mlt_service service = context_pop_service( context, &type );
1688         if ( well_formed && service != NULL )
1689         {
1690                 // Verify it is a producer service (mlt_type="mlt_producer")
1691                 // (producer, playlist, multitrack)
1692                 char *type = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "mlt_type" );
1693                 if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
1694                         service = NULL;
1695         }
1696
1697 #ifdef DEBUG
1698         xmlDocPtr doc = xml_make_doc( service );
1699         xmlDocFormatDump( stdout, doc, 1 );
1700         xmlFreeDoc( doc );
1701         service = NULL;
1702 #endif
1703         
1704         if ( well_formed && service != NULL )
1705         {
1706                 char *title = mlt_properties_get( context->producer_map, "title" );
1707                 
1708                 // Need the complete producer list for various reasons
1709                 properties = context->destructors;
1710
1711                 // Now make sure we don't have a reference to the service in the properties
1712                 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
1713                 {
1714                         char *name = mlt_properties_get_name( properties, i );
1715                         if ( mlt_properties_get_data_at( properties, i, NULL ) == service )
1716                         {
1717                                 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
1718                                 break;
1719                         }
1720                 }
1721
1722                 // We are done referencing destructor property list
1723                 // Set this var to service properties for convenience
1724                 properties = MLT_SERVICE_PROPERTIES( service );
1725         
1726                 // Assign the title
1727                 mlt_properties_set( properties, "title", title );
1728
1729                 // Optimise for overlapping producers
1730                 mlt_producer_optimise( MLT_PRODUCER( service ) );
1731
1732                 // Handle deep copies
1733                 if ( getenv( "MLT_XML_DEEP" ) == NULL )
1734                 {
1735                         // Now assign additional properties
1736                         if ( info == 0 && !mlt_properties_get( properties, "resource" ) )
1737                                 mlt_properties_set( properties, "resource", data );
1738
1739                         // This tells consumer_xml not to deep copy
1740                         mlt_properties_set( properties, "xml", "was here" );
1741                 }
1742                 else
1743                 {
1744                         // Allow the project to be edited
1745                         mlt_properties_set( properties, "_xml", "was here" );
1746                         mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
1747                 }
1748
1749                 // Make consumer available
1750                 mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES( context->consumer ) );
1751                 mlt_properties_set_data( properties, "consumer", context->consumer, 0,
1752                         (mlt_destructor) mlt_consumer_close, NULL );
1753
1754                 mlt_properties_set_int( properties, "seekable", context->seekable );
1755         }
1756         else
1757         {
1758                 // Return null if not well formed
1759                 service = NULL;
1760         }
1761
1762         // Clean up
1763         mlt_properties_close( context->producer_map );
1764         if ( context->params != NULL )
1765                 mlt_properties_close( context->params );
1766         mlt_properties_close( context->destructors );
1767         if ( context->lc_numeric )
1768                 free( context->lc_numeric );
1769         free( context );
1770         free( filename );
1771
1772         return MLT_PRODUCER( service );
1773 }