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