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