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