]> git.sesse.net Git - mlt/blob - src/modules/xml/producer_xml.c
add support for more than one consumer element in xml
[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 this, char *s )
97 {
98         int i;
99         
100         s[0] = 0;
101         for ( i = 0; i < this->depth; i++ )
102         {
103                 int len = strlen( s );
104                 snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", this->branch[ i ] );
105         }
106         return s;
107 }
108
109 /** Push a service.
110 */
111
112 static int context_push_service( deserialise_context this, mlt_service that, enum service_type type )
113 {
114         int ret = this->stack_service_size >= STACK_SIZE - 1;
115         if ( ret == 0 )
116         {
117                 this->stack_service[ this->stack_service_size ] = that;
118                 this->stack_types[ this->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( this, s ) );
125                 }
126         }
127         return ret;
128 }
129
130 /** Pop a service.
131 */
132
133 static mlt_service context_pop_service( deserialise_context this, enum service_type *type )
134 {
135         mlt_service result = NULL;
136         
137         *type = invalid_type;
138         if ( this->stack_service_size > 0 )
139         {
140                 result = this->stack_service[ -- this->stack_service_size ];
141                 if ( type != NULL )
142                         *type = this->stack_types[ this->stack_service_size ];
143         }
144         return result;
145 }
146
147 /** Push a node.
148 */
149
150 static int context_push_node( deserialise_context this, xmlNodePtr node )
151 {
152         int ret = this->stack_node_size >= STACK_SIZE - 1;
153         if ( ret == 0 )
154                 this->stack_node[ this->stack_node_size ++ ] = node;
155         return ret;
156 }
157
158 /** Pop a node.
159 */
160
161 static xmlNodePtr context_pop_node( deserialise_context this )
162 {
163         xmlNodePtr result = NULL;
164         if ( this->stack_node_size > 0 )
165                 result = this->stack_node[ -- this->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 this, 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( this, 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 == NULL && resource != NULL )
588                         producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) );
589         
590                 if ( producer == NULL )
591                         producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "+INVALID.txt" ) );
592
593                 if ( producer == NULL )
594                         producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "colour:red" ) );
595
596                 // Track this producer
597                 track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
598                 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( producer ), context->lc_numeric );
599
600                 // Propogate the properties
601                 qualify_property( context, properties, "resource" );
602                 qualify_property( context, properties, "luma" );
603                 qualify_property( context, properties, "luma.resource" );
604                 qualify_property( context, properties, "composite.luma" );
605                 qualify_property( context, properties, "producer.resource" );
606
607                 // Handle in/out properties separately
608                 mlt_position in = -1;
609                 mlt_position out = -1;
610         
611                 // Get in
612                 if ( mlt_properties_get( properties, "in" ) != NULL )
613                         in = mlt_properties_get_position( properties, "in" );
614                 // Let Kino-SMIL clipBegin be a synonym for in
615                 if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
616                 {
617                         if ( strchr( mlt_properties_get( properties, "clipBegin" ), ':' ) )
618                                 // Parse clock value
619                                 in = parse_clock_value( mlt_properties_get( properties, "clipBegin" ),
620                                         mlt_producer_get_fps( MLT_PRODUCER(  producer ) ) );
621                         else
622                                 // Parse frames value
623                                 in = mlt_properties_get_position( properties, "clipBegin" );
624                 }
625                 // Get out
626                 if ( mlt_properties_get( properties, "out" ) != NULL )
627                         out = mlt_properties_get_position( properties, "out" );
628                 // Let Kino-SMIL clipEnd be a synonym for out
629                 if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
630                 {
631                         if ( strchr( mlt_properties_get( properties, "clipEnd" ), ':' ) )
632                                 // Parse clock value
633                                 out = parse_clock_value( mlt_properties_get( properties, "clipEnd" ),
634                                         mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
635                         else
636                                 // Parse frames value
637                                 out = mlt_properties_get_position( properties, "clipEnd" );
638                 }
639                 // Remove in and out
640                 mlt_properties_set( properties, "in", NULL );
641                 mlt_properties_set( properties, "out", NULL );
642
643                 // Inherit the properties
644                 mlt_properties_inherit( MLT_SERVICE_PROPERTIES( producer ), properties );
645
646                 // Attach all filters from service onto producer
647                 attach_filters( producer, service );
648
649                 // Add the producer to the producer map
650                 if ( mlt_properties_get( properties, "id" ) != NULL )
651                         mlt_properties_set_data( context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL );
652
653                 // See if the producer should be added to a playlist or multitrack
654                 if ( add_producer( context, producer, in, out ) == 0 )
655                 {
656                         // Otherwise, set in and out on...
657                         if ( in != -1 || out != -1 )
658                         {
659                                 // Get the parent service
660                                 enum service_type type;
661                                 mlt_service parent = context_pop_service( context, &type );
662                                 if ( parent != NULL )
663                                 {
664                                         // Get the parent properties
665                                         properties = MLT_SERVICE_PROPERTIES( parent );
666                                 
667                                         char *resource = mlt_properties_get( properties, "resource" );
668                                 
669                                         // Put the parent producer back
670                                         context_push_service( context, parent, type );
671                                         
672                                         // If the parent is a track or entry
673                                         if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
674                                         {
675                                                 if ( in > -1 ) mlt_properties_set_position( properties, "in", in );
676                                                 if ( out > -1 ) mlt_properties_set_position( properties, "out", out );
677                                         }
678                                         else
679                                         {
680                                                 // Otherwise, set in and out on producer directly
681                                                 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
682                                         }
683                                 }
684                                 else
685                                 {
686                                         // Otherwise, set in and out on producer directly
687                                         mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
688                                 }
689                         }
690
691                         // Push the producer onto the stack
692                         context_push_service( context, producer, mlt_producer_type );
693                 }
694
695                 mlt_service_close( service );
696         }
697 }
698
699 static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
700 {
701         // Get the playlist from the stack
702         enum service_type type;
703         mlt_service service = context_pop_service( context, &type );
704         mlt_position length = 0;
705         
706         if ( type == mlt_playlist_type && service != NULL )
707         {
708                 // Look for the length attribute
709                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
710                 {
711                         if ( xmlStrcmp( atts[0], _x("length") ) == 0 )
712                         {
713                                 length = atoll( _s(atts[1]) );
714                                 break;
715                         }
716                 }
717
718                 // Append a blank to the playlist
719                 mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
720
721                 // Push the playlist back onto the stack
722                 context_push_service( context, service, type );
723         }
724         else
725         {
726                 fprintf( stderr, "blank without a playlist - a definite no no\n" );
727         }
728 }
729
730 static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
731 {
732         mlt_producer entry = NULL;
733         mlt_properties temp = mlt_properties_new( );
734
735         for ( ; atts != NULL && *atts != NULL; atts += 2 )
736         {
737                 mlt_properties_set( temp, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
738                 
739                 // Look for the producer attribute
740                 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
741                 {
742                         mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
743                         if ( producer !=  NULL )
744                                 mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
745                 }
746         }
747
748         // If we have a valid entry
749         if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
750         {
751                 mlt_playlist_clip_info info;
752                 enum service_type parent_type = invalid_type;
753                 mlt_service parent = context_pop_service( context, &parent_type );
754                 mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
755
756                 if ( parent_type == mlt_playlist_type )
757                 {
758                         // Append the producer to the playlist
759                         mlt_position in = -1;
760                         mlt_position out = -1;
761                         if ( mlt_properties_get( temp, "in" ) )
762                                 in = mlt_properties_get_position( temp, "in" );
763                         if ( mlt_properties_get( temp, "out" ) )
764                                 out = mlt_properties_get_position( temp, "out" );
765                         mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer, in, out );
766
767                         // Handle the repeat property
768                         if ( mlt_properties_get_int( temp, "repeat" ) > 0 )
769                         {
770                                 mlt_playlist_repeat_clip( MLT_PLAYLIST( parent ),
771                                                                                   mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1,
772                                                                                   mlt_properties_get_int( temp, "repeat" ) );
773                         }
774
775                         mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
776                         entry = info.cut;
777                 }
778                 else
779                 {
780                         fprintf( stderr, "Entry not part of a playlist...\n" );
781                 }
782
783                 context_push_service( context, parent, parent_type );
784         }
785
786         // Push the cut onto the stack
787         context_push_service( context, MLT_PRODUCER_SERVICE( entry ), mlt_entry_type );
788
789         mlt_properties_close( temp );
790 }
791
792 static void on_end_entry( deserialise_context context, const xmlChar *name )
793 {
794         // Get the entry from the stack
795         enum service_type entry_type = invalid_type;
796         mlt_service entry = context_pop_service( context, &entry_type );
797
798         if ( entry == NULL && entry_type != mlt_entry_type )
799         {
800                 fprintf( stderr, "Invalid state at end of entry\n" );
801         }
802 }
803
804 static void on_start_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
805 {
806         // use a dummy service to hold properties to allow arbitrary nesting
807         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
808         mlt_service_init( service, NULL );
809
810         // Push the dummy service onto the stack
811         context_push_service( context, service, mlt_entry_type );
812         
813         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "resource", "<track>" );
814         
815         for ( ; atts != NULL && *atts != NULL; atts += 2 )
816         {
817                 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
818                 
819                 // Look for the producer attribute
820                 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
821                 {
822                         mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
823                         if ( producer !=  NULL )
824                                 mlt_properties_set_data( MLT_SERVICE_PROPERTIES( service ), "producer", producer, 0, NULL, NULL );
825                 }
826         }
827 }
828
829 static void on_end_track( deserialise_context context, const xmlChar *name )
830 {
831         // Get the track from the stack
832         enum service_type track_type;
833         mlt_service track = context_pop_service( context, &track_type );
834
835         if ( track != NULL && track_type == mlt_entry_type )
836         {
837                 mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
838                 enum service_type parent_type = invalid_type;
839                 mlt_service parent = context_pop_service( context, &parent_type );
840                 mlt_multitrack multitrack = NULL;
841
842                 mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
843                 mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
844
845                 if ( parent_type == mlt_tractor_type )
846                         multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
847                 else if ( parent_type == mlt_multitrack_type )
848                         multitrack = MLT_MULTITRACK( parent );
849                 else
850                         fprintf( stderr, "track contained in an invalid container\n" );
851
852                 if ( multitrack != NULL )
853                 {
854                         // Set producer i/o if specified
855                         if ( mlt_properties_get( track_props, "in" ) != NULL ||
856                                  mlt_properties_get( track_props, "out" ) != NULL )
857                         {
858                                 mlt_position in = -1;
859                                 mlt_position out = -1;
860                                 if ( mlt_properties_get( track_props, "in" ) )
861                                         in = mlt_properties_get_position( track_props, "in" );
862                                 if ( mlt_properties_get( track_props, "out" ) )
863                                         out = mlt_properties_get_position( track_props, "out" );
864                                 mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ), in, out );
865                                 mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
866                                 mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( cut ), track_props );
867                                 track_props = MLT_PRODUCER_PROPERTIES( cut );
868                                 mlt_producer_close( cut );
869                         }
870                         else
871                         {
872                                 mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
873                         }
874
875                         // Set the hide state of the track producer
876                         char *hide_s = mlt_properties_get( track_props, "hide" );
877                         if ( hide_s != NULL )
878                         {
879                                 if ( strcmp( hide_s, "video" ) == 0 )
880                                         mlt_properties_set_int( producer_props, "hide", 1 );
881                                 else if ( strcmp( hide_s, "audio" ) == 0 )
882                                         mlt_properties_set_int( producer_props, "hide", 2 );
883                                 else if ( strcmp( hide_s, "both" ) == 0 )
884                                         mlt_properties_set_int( producer_props, "hide", 3 );
885                         }
886                 }
887
888                 if ( parent != NULL )
889                         context_push_service( context, parent, parent_type );
890
891                 mlt_service_close( track );
892         }
893         else
894         {
895                 fprintf( stderr, "Invalid state at end of track\n" );
896         }
897 }
898
899 static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
900 {
901         // use a dummy service to hold properties to allow arbitrary nesting
902         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
903         mlt_service_init( service, NULL );
904
905         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
906
907         context_push_service( context, service, mlt_dummy_filter_type );
908
909         // Set the properties
910         for ( ; atts != NULL && *atts != NULL; atts += 2 )
911                 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
912 }
913
914 static void on_end_filter( deserialise_context context, const xmlChar *name )
915 {
916         enum service_type type;
917         mlt_service service = context_pop_service( context, &type );
918         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
919
920         enum service_type parent_type = invalid_type;
921         mlt_service parent = context_pop_service( context, &parent_type );
922
923         if ( service != NULL && type == mlt_dummy_filter_type )
924         {
925                 mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, mlt_properties_get( properties, "mlt_service" ), NULL ) );
926                 mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
927
928                 track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
929                 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( filter ), context->lc_numeric );
930
931                 // Propogate the properties
932                 qualify_property( context, properties, "resource" );
933                 qualify_property( context, properties, "luma" );
934                 qualify_property( context, properties, "luma.resource" );
935                 qualify_property( context, properties, "composite.luma" );
936                 qualify_property( context, properties, "producer.resource" );
937                 mlt_properties_inherit( filter_props, properties );
938
939                 // Attach all filters from service onto filter
940                 attach_filters( filter, service );
941
942                 // Associate the filter with the parent
943                 if ( parent != NULL )
944                 {
945                         if ( parent_type == mlt_tractor_type )
946                         {
947                                 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
948                                 mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
949                                 mlt_filter_set_in_and_out( MLT_FILTER( filter ), 
950                                                                                    mlt_properties_get_int( properties, "in" ),
951                                                                                    mlt_properties_get_int( properties, "out" ) );
952                         }
953                         else
954                         {
955                                 mlt_service_attach( parent, MLT_FILTER( filter ) );
956                         }
957
958                         // Put the parent back on the stack
959                         context_push_service( context, parent, parent_type );
960                 }
961                 else
962                 {
963                         fprintf( stderr, "filter closed with invalid parent...\n" );
964                 }
965
966                 // Close the dummy filter service
967                 mlt_service_close( service );
968         }
969         else
970         {
971                 fprintf( stderr, "Invalid top of stack on filter close\n" );
972         }
973 }
974
975 static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
976 {
977         // use a dummy service to hold properties to allow arbitrary nesting
978         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
979         mlt_service_init( service, NULL );
980
981         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
982
983         context_push_service( context, service, mlt_dummy_transition_type );
984
985         // Set the properties
986         for ( ; atts != NULL && *atts != NULL; atts += 2 )
987                 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
988 }
989
990 static void on_end_transition( deserialise_context context, const xmlChar *name )
991 {
992         enum service_type type;
993         mlt_service service = context_pop_service( context, &type );
994         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
995
996         enum service_type parent_type = invalid_type;
997         mlt_service parent = context_pop_service( context, &parent_type );
998
999         if ( service != NULL && type == mlt_dummy_transition_type )
1000         {
1001                 char *id = mlt_properties_get( properties, "mlt_service" );
1002                 mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) );
1003                 mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
1004
1005                 track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
1006                 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( effect ), context->lc_numeric );
1007
1008                 // Propogate the properties
1009                 qualify_property( context, properties, "resource" );
1010                 qualify_property( context, properties, "luma" );
1011                 qualify_property( context, properties, "luma.resource" );
1012                 qualify_property( context, properties, "composite.luma" );
1013                 qualify_property( context, properties, "producer.resource" );
1014                 mlt_properties_inherit( effect_props, properties );
1015
1016                 // Attach all filters from service onto effect
1017                 attach_filters( effect, service );
1018
1019                 // Associate the filter with the parent
1020                 if ( parent != NULL )
1021                 {
1022                         if ( parent_type == mlt_tractor_type )
1023                         {
1024                                 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
1025                                 if ( mlt_properties_get_int( properties, "a_track" ) == mlt_properties_get_int( properties, "b_track" ) )
1026                                         mlt_properties_set_int( properties, "b_track", mlt_properties_get_int( properties, "a_track" ) + 1 );
1027                                 mlt_field_plant_transition( field, MLT_TRANSITION( effect ), 
1028                                                                                         mlt_properties_get_int( properties, "a_track" ),
1029                                                                                         mlt_properties_get_int( properties, "b_track" ) );
1030                                 mlt_transition_set_in_and_out( MLT_TRANSITION( effect ), 
1031                                                                                    mlt_properties_get_int( properties, "in" ),
1032                                                                                    mlt_properties_get_int( properties, "out" ) );
1033                         }
1034                         else
1035                         {
1036                                 fprintf( stderr, "Misplaced transition - ignoring\n" );
1037                         }
1038
1039                         // Put the parent back on the stack
1040                         context_push_service( context, parent, parent_type );
1041                 }
1042                 else
1043                 {
1044                         fprintf( stderr, "transition closed with invalid parent...\n" );
1045                 }
1046
1047                 // Close the dummy filter service
1048                 mlt_service_close( service );
1049         }
1050         else
1051         {
1052                 fprintf( stderr, "Invalid top of stack on transition close\n" );
1053         }
1054 }
1055
1056 static void on_start_consumer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1057 {
1058         if ( context->pass == 1 )
1059         {
1060                 mlt_consumer consumer = mlt_consumer_new( context->profile );
1061                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
1062
1063                 mlt_properties_set_lcnumeric( properties, context->lc_numeric );
1064                 context_push_service( context, MLT_CONSUMER_SERVICE(consumer), mlt_dummy_consumer_type );
1065
1066                 // Set the properties from attributes
1067                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1068                         mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
1069         }
1070 }
1071
1072 static void on_end_consumer( deserialise_context context, const xmlChar *name )
1073 {
1074         if ( context->pass == 1 )
1075         {
1076                 // Get the consumer from the stack
1077                 enum service_type type;
1078                 mlt_service service = context_pop_service( context, &type );
1079
1080                 if ( service && type == mlt_dummy_consumer_type )
1081                 {
1082                         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1083                         qualify_property( context, properties, "resource" );
1084                         qualify_property( context, properties, "target" );
1085                         char *resource = mlt_properties_get( properties, "resource" );
1086
1087                         if ( context->multi_consumer > 1 )
1088                         {
1089                                 // Instantiate the multi consumer
1090                                 if ( !context->consumer )
1091                                 {
1092                                         context->consumer = mlt_factory_consumer( context->profile, "multi", NULL );
1093                                         if ( context->consumer )
1094                                         {
1095                                                 // Track this consumer
1096                                                 track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
1097                                                 mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
1098                                         }
1099                                 }
1100                                 if ( context->consumer )
1101                                 {
1102                                         // Set this service instance on multi consumer
1103                                         char key[20];
1104                                         snprintf( key, sizeof(key), "%d", context->consumer_count++ );
1105                                         mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(context->consumer), key, service, 0,
1106                                                 (mlt_destructor) mlt_service_close, NULL );
1107                                 }
1108                         }
1109                         else
1110                         {
1111                                 // Instantiate the consumer
1112                                 context->consumer = mlt_factory_consumer( context->profile, mlt_properties_get( properties, "mlt_service" ), resource );
1113                                 if ( context->consumer )
1114                                 {
1115                                         // Track this consumer
1116                                         track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
1117                                         mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
1118
1119                                         // Inherit the properties
1120                                         mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties );
1121                                 }
1122                                 // Close the dummy
1123                                 mlt_service_close( service );
1124                         }
1125                 }
1126         }
1127 }
1128
1129 static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1130 {
1131         enum service_type type;
1132         mlt_service service = context_pop_service( context, &type );
1133         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1134         const char *value = NULL;
1135
1136         if ( service != NULL )
1137         {
1138                 // Set the properties
1139                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1140                 {
1141                         if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 )
1142                                 context->property = strdup( _s(atts[ 1 ]) );
1143                         else if ( xmlStrcmp( atts[ 0 ], _x("value") ) == 0 )
1144                                 value = _s(atts[ 1 ]);
1145                 }
1146
1147                 if ( context->property != NULL )
1148                         mlt_properties_set( properties, context->property, value == NULL ? "" : value );
1149
1150                 // Tell parser to collect any further nodes for serialisation
1151                 context->is_value = 1;
1152
1153                 context_push_service( context, service, type );
1154         }
1155         else
1156         {
1157                 fprintf( stderr, "Property without a service '%s'?\n", ( const char * )name );
1158         }
1159 }
1160
1161 static void on_end_property( deserialise_context context, const xmlChar *name )
1162 {
1163         enum service_type type;
1164         mlt_service service = context_pop_service( context, &type );
1165         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1166
1167         if ( service != NULL )
1168         {
1169                 // Tell parser to stop building a tree
1170                 context->is_value = 0;
1171         
1172                 // See if there is a xml tree for the value
1173                 if ( context->property != NULL && context->value_doc != NULL )
1174                 {
1175                         xmlChar *value;
1176                         int size;
1177                 
1178                         // Serialise the tree to get value
1179                         xmlDocDumpMemory( context->value_doc, &value, &size );
1180                         mlt_properties_set( properties, context->property, _s(value) );
1181 #ifdef WIN32
1182                         xmlFreeFunc xmlFree = NULL;
1183                         xmlMemGet( &xmlFree, NULL, NULL, NULL);
1184 #endif
1185                         xmlFree( value );
1186                         xmlFreeDoc( context->value_doc );
1187                         context->value_doc = NULL;
1188                 }
1189
1190                 // Close this property handling
1191                 free( context->property );
1192                 context->property = NULL;
1193
1194                 context_push_service( context, service, type );
1195         }
1196         else
1197         {
1198                 fprintf( stderr, "Property without a service '%s'??\n", (const char *)name );
1199         }
1200 }
1201
1202 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
1203 {
1204         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1205         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1206         
1207 //printf("on_start_element: %s\n", name );
1208         if ( context->pass == 0 )
1209         {
1210                 if ( xmlStrcmp( name, _x("mlt") ) == 0 ||
1211                      xmlStrcmp( name, _x("profile") ) == 0 ||
1212                      xmlStrcmp( name, _x("profileinfo") ) == 0 )
1213                         on_start_profile( context, name, atts );
1214                 if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1215                         context->multi_consumer++;
1216                 return;
1217         }
1218         context->branch[ context->depth ] ++;
1219         context->depth ++;
1220         
1221         // Build a tree from nodes within a property value
1222         if ( context->is_value == 1 && context->pass == 1 )
1223         {
1224                 xmlNodePtr node = xmlNewNode( NULL, name );
1225                 
1226                 if ( context->value_doc == NULL )
1227                 {
1228                         // Start a new tree
1229                         context->value_doc = xmlNewDoc( _x("1.0") );
1230                         xmlDocSetRootElement( context->value_doc, node );
1231                 }
1232                 else
1233                 {
1234                         // Append child to tree
1235                         xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
1236                 }
1237                 context_push_node( context, node );
1238                 
1239                 // Set the attributes
1240                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1241                         xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
1242         }
1243         else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1244                 on_start_tractor( context, name, atts );
1245         else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1246                 on_start_multitrack( context, name, atts );
1247         else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1248                 on_start_playlist( context, name, atts );
1249         else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1250                 on_start_producer( context, name, atts );
1251         else if ( xmlStrcmp( name, _x("blank") ) == 0 )
1252                 on_start_blank( context, name, atts );
1253         else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1254                 on_start_entry( context, name, atts );
1255         else if ( xmlStrcmp( name, _x("track") ) == 0 )
1256                 on_start_track( context, name, atts );
1257         else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1258                 on_start_filter( context, name, atts );
1259         else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1260                 on_start_transition( context, name, atts );
1261         else if ( xmlStrcmp( name, _x("property") ) == 0 )
1262                 on_start_property( context, name, atts );
1263         else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1264                 on_start_consumer( context, name, atts );
1265         else if ( xmlStrcmp( name, _x("westley") ) == 0 || xmlStrcmp( name, _x("mlt") ) == 0 )
1266         {
1267                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1268                 {
1269                         if ( xmlStrcmp( atts[0], _x("LC_NUMERIC") ) )
1270                                 mlt_properties_set( context->producer_map, _s( atts[0] ), _s(atts[1] ) );
1271                         else if ( !context->lc_numeric )
1272                                 context->lc_numeric = strdup( _s( atts[1] ) );
1273                 }
1274         }
1275 }
1276
1277 static void on_end_element( void *ctx, const xmlChar *name )
1278 {
1279         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1280         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1281         
1282 //printf("on_end_element: %s\n", name );
1283         if ( context->is_value == 1 && context->pass == 1 && xmlStrcmp( name, _x("property") ) != 0 )
1284                 context_pop_node( context );
1285         else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1286                 on_end_multitrack( context, name );
1287         else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1288                 on_end_playlist( context, name );
1289         else if ( xmlStrcmp( name, _x("track") ) == 0 )
1290                 on_end_track( context, name );
1291         else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1292                 on_end_entry( context, name );
1293         else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1294                 on_end_tractor( context, name );
1295         else if ( xmlStrcmp( name, _x("property") ) == 0 )
1296                 on_end_property( context, name );
1297         else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1298                 on_end_producer( context, name );
1299         else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1300                 on_end_filter( context, name );
1301         else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1302                 on_end_transition( context, name );
1303         else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1304                 on_end_consumer( context, name );
1305
1306         context->branch[ context->depth ] = 0;
1307         context->depth --;
1308 }
1309
1310 static void on_characters( void *ctx, const xmlChar *ch, int len )
1311 {
1312         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1313         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1314         char *value = calloc( len + 1, 1 );
1315         enum service_type type;
1316         mlt_service service = context_pop_service( context, &type );
1317         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1318
1319         if ( service != NULL )
1320                 context_push_service( context, service, type );
1321
1322         value[ len ] = 0;
1323         strncpy( value, (const char*) ch, len );
1324
1325         if ( context->stack_node_size > 0 )
1326                 xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
1327
1328         // libxml2 generates an on_characters immediately after a get_entity within
1329         // an element value, and we ignore it because it is called again during
1330         // actual substitution.
1331         else if ( context->property != NULL && context->entity_is_replace == 0 )
1332         {
1333                 char *s = mlt_properties_get( properties, context->property );
1334                 if ( s != NULL )
1335                 {
1336                         // Append new text to existing content
1337                         char *new = calloc( strlen( s ) + len + 1, 1 );
1338                         strcat( new, s );
1339                         strcat( new, value );
1340                         mlt_properties_set( properties, context->property, new );
1341                         free( new );
1342                 }
1343                 else
1344                         mlt_properties_set( properties, context->property, value );
1345         }
1346         context->entity_is_replace = 0;
1347         
1348         free( value);
1349 }
1350
1351 /** Convert parameters parsed from resource into entity declarations.
1352 */
1353 static void params_to_entities( deserialise_context context )
1354 {
1355         if ( context->params != NULL )
1356         {       
1357                 int i;
1358                 
1359                 // Add our params as entitiy declarations
1360                 for ( i = 0; i < mlt_properties_count( context->params ); i++ )
1361                 {
1362                         xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
1363                         xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
1364                                 context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, _s(name) ) );
1365                 }
1366
1367                 // Flag completion
1368                 mlt_properties_close( context->params );
1369                 context->params = NULL;
1370         }
1371 }
1372
1373 // The following 3 facilitate entity substitution in the SAX parser
1374 static void on_internal_subset( void *ctx, const xmlChar* name,
1375         const xmlChar* publicId, const xmlChar* systemId )
1376 {
1377         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1378         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1379         
1380         context->publicId = publicId;
1381         context->systemId = systemId;
1382         xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
1383         
1384         // Override default entities with our parameters
1385         params_to_entities( context );
1386 }
1387
1388 // TODO: Check this with Dan... I think this is for parameterisation
1389 // but it's breaking standard escaped entities (like &lt; etc).
1390 static void on_entity_declaration( void *ctx, const xmlChar* name, int type, 
1391         const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
1392 {
1393         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1394         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1395         
1396         xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
1397 }
1398
1399 // TODO: Check this functionality (see on_entity_declaration)
1400 static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
1401 {
1402         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1403         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1404         xmlEntityPtr e = NULL;
1405
1406         // Setup for entity declarations if not ready
1407         if ( xmlGetIntSubset( context->entity_doc ) == NULL )
1408         {
1409                 xmlCreateIntSubset( context->entity_doc, _x("mlt"), _x(""), _x("") );
1410                 context->publicId = _x("");
1411                 context->systemId = _x("");
1412         }
1413
1414         // Add our parameters if not already
1415         params_to_entities( context );
1416         
1417         e = xmlGetPredefinedEntity( name );
1418         
1419         // Send signal to on_characters that an entity substitutin is pending
1420         if ( e == NULL )
1421         {
1422                 e = xmlGetDocEntity( context->entity_doc, name );
1423                 if ( e != NULL )
1424                         context->entity_is_replace = 1;
1425         }
1426         
1427         return e;
1428 }
1429
1430 /** Convert a hexadecimal character to its value.
1431 */
1432 static int tohex( char p )
1433 {
1434         return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
1435 }
1436
1437 /** Decode a url-encoded string containing hexadecimal character sequences.
1438 */
1439 static char *url_decode( char *dest, char *src )
1440 {
1441         char *p = dest;
1442         
1443         while ( *src )
1444         {
1445                 if ( *src == '%' )
1446                 {
1447                         *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
1448                         src += 3;
1449                 }
1450                 else
1451                 {
1452                         *p ++ = *src ++;
1453                 }
1454         }
1455
1456         *p = *src;
1457         return dest;
1458 }
1459
1460 /** Extract the filename from a URL attaching parameters to a properties list.
1461 */
1462 static void parse_url( mlt_properties properties, char *url )
1463 {
1464         int i;
1465         int n = strlen( url );
1466         char *name = NULL;
1467         char *value = NULL;
1468         
1469         for ( i = 0; i < n; i++ )
1470         {
1471                 switch ( url[ i ] )
1472                 {
1473                         case '?':
1474                                 url[ i++ ] = '\0';
1475                                 name = &url[ i ];
1476                                 break;
1477                         
1478                         case ':':
1479                         case '=':
1480                                 url[ i++ ] = '\0';
1481                                 value = &url[ i ];
1482                                 break;
1483                         
1484                         case '&':
1485                                 url[ i++ ] = '\0';
1486                                 if ( name != NULL && value != NULL )
1487                                         mlt_properties_set( properties, name, value );
1488                                 name = &url[ i ];
1489                                 value = NULL;
1490                                 break;
1491                 }
1492         }
1493         if ( name != NULL && value != NULL )
1494                 mlt_properties_set( properties, name, value );
1495 }
1496
1497 // Quick workaround to avoid unecessary libxml2 warnings
1498 static int file_exists( char *file )
1499 {
1500         char *name = strdup( file );
1501         int exists = 0;
1502         if ( name != NULL && strchr( name, '?' ) )
1503                 *( strchr( name, '?' ) ) = '\0';
1504         if ( name != NULL )
1505         {
1506                 FILE *f = fopen( name, "r" );
1507                 exists = f != NULL;
1508                 if ( exists ) fclose( f );
1509         }
1510         free( name );
1511         return exists;
1512 }
1513
1514 mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data )
1515 {
1516         xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
1517         struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
1518         mlt_properties properties = NULL;
1519         int i = 0;
1520         struct _xmlParserCtxt *xmlcontext;
1521         int well_formed = 0;
1522         char *filename = NULL;
1523         int info = strcmp( id, "xml-string" ) ? 0 : 1;
1524
1525         if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) )
1526                 return NULL;
1527
1528         context = calloc( 1, sizeof( struct deserialise_context_s ) );
1529         if ( context == NULL )
1530                 return NULL;
1531
1532         context->producer_map = mlt_properties_new();
1533         context->destructors = mlt_properties_new();
1534         context->params = mlt_properties_new();
1535         context->profile = profile;
1536
1537         // Decode URL and parse parameters
1538         mlt_properties_set( context->producer_map, "root", "" );
1539         if ( info == 0 )
1540         {
1541                 filename = strdup( data );
1542                 parse_url( context->params, url_decode( filename, data ) );
1543
1544                 // We need the directory prefix which was used for the xml
1545                 if ( strchr( filename, '/' ) )
1546                 {
1547                         char *root = NULL;
1548                         mlt_properties_set( context->producer_map, "root", filename );
1549                         root = mlt_properties_get( context->producer_map, "root" );
1550                         *( strrchr( root, '/' ) ) = '\0';
1551
1552                         // If we don't have an absolute path here, we're heading for disaster...
1553                         if ( root[ 0 ] != '/' )
1554                         {
1555                                 char *cwd = getcwd( NULL, 0 );
1556                                 char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
1557                                 sprintf( real, "%s/%s", cwd, root );
1558                                 mlt_properties_set( context->producer_map, "root", real );
1559                                 free( real );
1560                                 free( cwd );
1561                         }
1562                 }
1563         }
1564
1565         // We need to track the number of registered filters
1566         mlt_properties_set_int( context->destructors, "registered", 0 );
1567
1568         // Setup SAX callbacks
1569         sax->startElement = on_start_element;
1570
1571         // Setup libxml2 SAX parsing
1572         xmlInitParser(); 
1573         xmlSubstituteEntitiesDefault( 1 );
1574         // This is used to facilitate entity substitution in the SAX parser
1575         context->entity_doc = xmlNewDoc( _x("1.0") );
1576         if ( info == 0 )
1577                 xmlcontext = xmlCreateFileParserCtxt( filename );
1578         else
1579                 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1580
1581         // Invalid context - clean up and return NULL
1582         if ( xmlcontext == NULL )
1583         {
1584                 mlt_properties_close( context->producer_map );
1585                 mlt_properties_close( context->destructors );
1586                 mlt_properties_close( context->params );
1587                 free( context );
1588                 free( sax );
1589                 free( filename );
1590                 return NULL;
1591         }
1592
1593         // Parse
1594         xmlcontext->sax = sax;
1595         xmlcontext->_private = ( void* )context;        
1596         xmlParseDocument( xmlcontext );
1597         
1598         // Cleanup after parsing
1599         xmlcontext->sax = NULL;
1600         xmlcontext->_private = NULL;
1601         xmlFreeParserCtxt( xmlcontext );
1602         context->stack_node_size = 0;
1603         context->stack_service_size = 0;
1604
1605         // Setup the second pass
1606         context->pass ++;
1607         if ( info == 0 )
1608                 xmlcontext = xmlCreateFileParserCtxt( filename );
1609         else
1610                 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1611
1612         // Invalid context - clean up and return NULL
1613         if ( xmlcontext == NULL )
1614         {
1615                 mlt_properties_close( context->producer_map );
1616                 mlt_properties_close( context->destructors );
1617                 mlt_properties_close( context->params );
1618                 xmlFreeDoc( context->entity_doc );
1619                 free( context );
1620                 free( sax );
1621                 free( filename );
1622                 return NULL;
1623         }
1624
1625         // Setup SAX callbacks
1626         sax->endElement = on_end_element;
1627         sax->characters = on_characters;
1628         sax->cdataBlock = on_characters;
1629         sax->internalSubset = on_internal_subset;
1630         sax->entityDecl = on_entity_declaration;
1631         sax->getEntity = on_get_entity;
1632
1633         // Parse
1634         xmlcontext->sax = sax;
1635         xmlcontext->_private = ( void* )context;
1636         xmlParseDocument( xmlcontext );
1637         well_formed = xmlcontext->wellFormed;
1638
1639         // Cleanup after parsing
1640         xmlFreeDoc( context->entity_doc );
1641         free( sax );
1642         xmlMemoryDump( ); // for debugging
1643
1644         // Get the last producer on the stack
1645         enum service_type type;
1646         mlt_service service = context_pop_service( context, &type );
1647         if ( well_formed && service != NULL )
1648         {
1649                 // Verify it is a producer service (mlt_type="mlt_producer")
1650                 // (producer, playlist, multitrack)
1651                 char *type = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "mlt_type" );
1652                 if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
1653                         service = NULL;
1654         }
1655
1656 #ifdef DEBUG
1657         xmlDocPtr doc = xml_make_doc( service );
1658         xmlDocFormatDump( stdout, doc, 1 );
1659         xmlFreeDoc( doc );
1660         service = NULL;
1661 #endif
1662         
1663         if ( well_formed && service != NULL )
1664         {
1665                 char *title = mlt_properties_get( context->producer_map, "title" );
1666                 
1667                 // Need the complete producer list for various reasons
1668                 properties = context->destructors;
1669
1670                 // Now make sure we don't have a reference to the service in the properties
1671                 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
1672                 {
1673                         char *name = mlt_properties_get_name( properties, i );
1674                         if ( mlt_properties_get_data_at( properties, i, NULL ) == service )
1675                         {
1676                                 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
1677                                 break;
1678                         }
1679                 }
1680
1681                 // We are done referencing destructor property list
1682                 // Set this var to service properties for convenience
1683                 properties = MLT_SERVICE_PROPERTIES( service );
1684         
1685                 // Assign the title
1686                 mlt_properties_set( properties, "title", title );
1687
1688                 // Optimise for overlapping producers
1689                 mlt_producer_optimise( MLT_PRODUCER( service ) );
1690
1691                 // Handle deep copies
1692                 if ( getenv( "MLT_XML_DEEP" ) == NULL )
1693                 {
1694                         // Now assign additional properties
1695                         if ( info == 0 )
1696                                 mlt_properties_set( properties, "resource", data );
1697
1698                         // This tells consumer_xml not to deep copy
1699                         mlt_properties_set( properties, "xml", "was here" );
1700                 }
1701                 else
1702                 {
1703                         // Allow the project to be edited
1704                         mlt_properties_set( properties, "_xml", "was here" );
1705                         mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
1706                 }
1707
1708                 // Make consumer available
1709                 mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES( context->consumer ) );
1710                 mlt_properties_set_data( properties, "consumer", context->consumer, 0,
1711                         (mlt_destructor) mlt_consumer_close, NULL );
1712         }
1713         else
1714         {
1715                 // Return null if not well formed
1716                 service = NULL;
1717         }
1718
1719         // Clean up
1720         mlt_properties_close( context->producer_map );
1721         if ( context->params != NULL )
1722                 mlt_properties_close( context->params );
1723         mlt_properties_close( context->destructors );
1724         if ( context->lc_numeric )
1725                 free( context->lc_numeric );
1726         free( context );
1727         free( filename );
1728
1729         return MLT_PRODUCER( service );
1730 }