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