]> git.sesse.net Git - mlt/blob - src/modules/westley/producer_westley.c
Fix to compositing/watermark; miracle/mlt shutdown cleanup
[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_service_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         mlt_properties_set_position( properties, "length", 0 );
296
297         for ( ; atts != NULL && *atts != NULL; atts += 2 )
298                 mlt_properties_set( mlt_service_properties( service ), (char*) atts[0], (char*) atts[1] );
299
300         if ( mlt_properties_get_position( properties, "length" ) < mlt_properties_get_position( properties, "out" ) )
301         {
302                 mlt_position length = mlt_properties_get_position( properties, "out" ) + 1;
303                 mlt_properties_set_position( properties, "length", length );
304         }
305
306         if ( mlt_properties_get( properties, "id" ) != NULL )
307                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
308         
309         context_push_service( context, service, mlt_tractor_type );
310 }
311
312 static void on_end_tractor( deserialise_context context, const xmlChar *name )
313 {
314         // Get the tractor
315         enum service_type type;
316         mlt_service tractor = context_pop_service( context, &type );
317
318         if ( tractor != NULL && type == mlt_tractor_type )
319         {
320                 // See if the tractor should be added to a playlist or multitrack
321                 if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
322                         context_push_service( context, tractor, type );
323         }
324 }
325
326 static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
327 {
328         enum service_type type;
329         mlt_service parent = context_pop_service( context, &type );
330
331         // If we don't have a parent, then create one now, providing we're in a state where we can
332         if ( parent == NULL || ( type == mlt_playlist_type || type == mlt_multitrack_type ) )
333         {
334                 // Push the parent back
335                 if ( parent != NULL )
336                         context_push_service( context, parent, type );
337
338                 // Create a tractor to contain the multitrack
339                 parent = mlt_tractor_service( mlt_tractor_new( ) );
340                 track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close );
341                 type = mlt_tractor_type;
342
343                 // Flag it as a synthesised tractor for clean up later
344                 mlt_properties_set_int( mlt_service_properties( parent ), "fezzik_synth", 1 );
345         }
346
347         if ( type == mlt_tractor_type )
348         {
349                 mlt_service service = MLT_SERVICE( mlt_tractor_multitrack( MLT_TRACTOR( parent ) ) );
350                 mlt_properties properties = mlt_service_properties( service );
351                 mlt_properties_set_position( properties, "length", 0 );
352                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
353                         mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
354
355                 if ( mlt_properties_get( properties, "id" ) != NULL )
356                         mlt_properties_set_data( context->producer_map, mlt_properties_get( properties,"id" ), service, 0, NULL, NULL );
357
358                 context_push_service( context, parent, type );
359                 context_push_service( context, service, mlt_multitrack_type );
360         }
361         else
362         {
363                 fprintf( stderr, "Invalid multitrack position\n" );
364         }
365 }
366
367 static void on_end_multitrack( deserialise_context context, const xmlChar *name )
368 {
369         // Get the multitrack from the stack
370         enum service_type type;
371         mlt_service service = context_pop_service( context, &type );
372
373         if ( service == NULL || type != mlt_multitrack_type )
374                 fprintf( stderr, "End multitrack in the wrong state...\n" );
375 }
376
377 static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
378 {
379         mlt_service service = mlt_playlist_service( mlt_playlist_init() );
380         mlt_properties properties = mlt_service_properties( service );
381
382         track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
383
384         mlt_properties_set_position( properties, "length", 0 );
385
386         for ( ; atts != NULL && *atts != NULL; atts += 2 )
387         {
388                 mlt_properties_set( properties, ( char* )atts[0], ( char* )atts[1] );
389
390                 // Out will be overwritten later as we append, so we need to save it
391                 if ( strcmp( atts[ 0 ], "out" ) == 0 )
392                         mlt_properties_set( properties, "_westley.out", ( char* )atts[ 1 ] );
393         }
394
395         if ( mlt_properties_get( properties, "id" ) != NULL )
396                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
397
398         context_push_service( context, service, mlt_playlist_type );
399 }
400
401 static void on_end_playlist( deserialise_context context, const xmlChar *name )
402 {
403         // Get the playlist from the stack
404         enum service_type type;
405         mlt_service service = context_pop_service( context, &type );
406
407         if ( service != NULL && type == mlt_playlist_type )
408         {
409                 mlt_properties properties = mlt_service_properties( service );
410                 mlt_position in = mlt_properties_get_position( properties, "in" );
411                 mlt_position out;
412
413                 if ( mlt_properties_get( properties, "_westley.out" ) != NULL )
414                         out = mlt_properties_get_position( properties, "_westley.out" );
415                 else
416                         out = mlt_properties_get_position( properties, "length" ) - 1;
417
418                 if ( mlt_properties_get_position( properties, "length" ) < out )
419                         mlt_properties_set_position( properties, "length", out  + 1 );
420
421                 mlt_producer_set_in_and_out( MLT_PRODUCER( service ), in, out );
422         
423                 // See if the playlist should be added to a playlist or multitrack
424                 if ( add_producer( context, service, in, out ) == 0 )
425                         context_push_service( context, service, type );
426         }
427         else
428         {
429                 fprintf( stderr, "Invalid state of playlist end\n" );
430         }
431 }
432
433 static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
434 {
435         // use a dummy service to hold properties to allow arbitrary nesting
436         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
437         mlt_service_init( service, NULL );
438
439         mlt_properties properties = mlt_service_properties( service );
440
441         context_push_service( context, service, mlt_dummy_producer_type );
442
443         for ( ; atts != NULL && *atts != NULL; atts += 2 )
444                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
445 }
446
447 static void on_end_producer( deserialise_context context, const xmlChar *name )
448 {
449         enum service_type type;
450         mlt_service service = context_pop_service( context, &type );
451         mlt_properties properties = mlt_service_properties( service );
452
453         if ( service != NULL && type == mlt_dummy_producer_type )
454         {
455                 mlt_service producer = NULL;
456
457                 qualify_property( context, properties, "resource" );
458                 char *resource = mlt_properties_get( properties, "resource" );
459
460                 // Let Kino-SMIL src be a synonym for resource
461                 if ( resource == NULL )
462                 {
463                         qualify_property( context, properties, "src" );
464                         resource = mlt_properties_get( properties, "src" );
465                 }
466
467                 // Instantiate the producer
468                 if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
469                 {
470                         char temp[ 1024 ];
471                         strncpy( temp, mlt_properties_get( properties, "mlt_service" ), 1024 );
472                         if ( resource != NULL )
473                         {
474                                 strcat( temp, ":" );
475                                 strncat( temp, resource, 1023 - strlen( temp ) );
476                         }
477                         producer = MLT_SERVICE( mlt_factory_producer( "fezzik", temp ) );
478                 }
479
480                 // Just in case the plugin requested doesn't exist...
481                 if ( producer == NULL && resource != NULL )
482                         producer = MLT_SERVICE( mlt_factory_producer( "fezzik", resource ) );
483         
484                 // Track this producer
485                 track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
486
487                 // Propogate the properties
488                 qualify_property( context, properties, "resource" );
489                 qualify_property( context, properties, "luma" );
490                 qualify_property( context, properties, "luma.resource" );
491                 qualify_property( context, properties, "composite.luma" );
492                 qualify_property( context, properties, "producer.resource" );
493
494                 // Handle in/out properties separately
495                 mlt_position in = -1;
496                 mlt_position out = -1;
497         
498                 // Get in
499                 if ( mlt_properties_get( properties, "in" ) != NULL )
500                         in = mlt_properties_get_position( properties, "in" );
501                 // Let Kino-SMIL clipBegin be a synonym for in
502                 if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
503                         in = mlt_properties_get_position( properties, "clipBegin" );
504                 // Get out
505                 if ( mlt_properties_get( properties, "out" ) != NULL )
506                         out = mlt_properties_get_position( properties, "out" );
507                 // Let Kino-SMIL clipEnd be a synonym for out
508                 if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
509                         out = mlt_properties_get_position( properties, "clipEnd" );
510         
511                 // Remove in and out
512                 mlt_properties_set( properties, "in", NULL );
513                 mlt_properties_set( properties, "out", NULL );
514
515                 // Inherit the properties
516                 mlt_properties_inherit( mlt_service_properties( producer ), properties );
517
518                 // Attach all filters from service onto producer
519                 attach_filters( producer, service );
520
521                 // Add the producer to the producer map
522                 if ( mlt_properties_get( properties, "id" ) != NULL )
523                         mlt_properties_set_data( context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL );
524
525                 // See if the producer should be added to a playlist or multitrack
526                 if ( add_producer( context, producer, in, out ) == 0 )
527                 {
528                         // Otherwise, set in and out on...
529                         if ( in != -1 || out != -1 )
530                         {
531                                 // Get the parent service
532                                 enum service_type type;
533                                 mlt_service parent = context_pop_service( context, &type );
534                                 if ( parent != NULL )
535                                 {
536                                         // Get the parent properties
537                                         properties = mlt_service_properties( parent );
538                                 
539                                         char *resource = mlt_properties_get( properties, "resource" );
540                                 
541                                         // Put the parent producer back
542                                         context_push_service( context, parent, type );
543                                         
544                                         // If the parent is a track or entry
545                                         if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
546                                         {
547                                                 mlt_properties_set_position( properties, "in", in );
548                                                 mlt_properties_set_position( properties, "out", out );
549                                         }
550                                         else
551                                         {
552                                                 // Otherwise, set in and out on producer directly
553                                                 mlt_producer_set_in_and_out( MLT_PRODUCER( service ), in, out );
554                                         }
555                                 }
556                                 else
557                                 {
558                                         // Otherwise, set in and out on producer directly
559                                         mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
560                                 }
561                         }
562         
563                         // Push the producer onto the stack
564                         context_push_service( context, producer, mlt_producer_type );
565                 }
566
567                 mlt_service_close( service );
568         }
569 }
570
571 static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
572 {
573         // Get the playlist from the stack
574         enum service_type type;
575         mlt_service service = context_pop_service( context, &type );
576         mlt_position length = 0;
577         
578         if ( type == mlt_playlist_type && service != NULL )
579         {
580                 // Look for the length attribute
581                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
582                 {
583                         if ( strcmp( atts[0], "length" ) == 0 )
584                         {
585                                 length = atoll( atts[1] );
586                                 break;
587                         }
588                 }
589
590                 // Append a blank to the playlist
591                 mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
592
593                 // Push the playlist back onto the stack
594                 context_push_service( context, service, type );
595         }
596         else
597         {
598                 fprintf( stderr, "blank without a playlist - a definite no no\n" );
599         }
600 }
601
602 static void on_start_entry_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
603 {
604         // use a dummy service to hold properties to allow arbitrary nesting
605         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
606         mlt_service_init( service, NULL );
607
608         // Push the dummy service onto the stack
609         context_push_service( context, service, mlt_entry_type );
610         
611         if ( strcmp( name, "entry" ) == 0 )
612                 mlt_properties_set( mlt_service_properties( service ), "resource", "<entry>" );
613         else
614                 mlt_properties_set( mlt_service_properties( service ), "resource", "<track>" );
615         
616         for ( ; atts != NULL && *atts != NULL; atts += 2 )
617         {
618                 mlt_properties_set( mlt_service_properties( service ), (char*) atts[0], (char*) atts[1] );
619                 
620                 // Look for the producer attribute
621                 if ( strcmp( atts[ 0 ], "producer" ) == 0 )
622                 {
623                         mlt_producer producer = mlt_properties_get_data( context->producer_map, (char*) atts[1], NULL );
624                         if ( producer !=  NULL )
625                                 mlt_properties_set_data( mlt_service_properties( service ), "producer", producer, 0, NULL, NULL );
626                 }
627         }
628 }
629
630 static void on_end_track( deserialise_context context, const xmlChar *name )
631 {
632         // Get the track from the stack
633         enum service_type track_type;
634         mlt_service track = context_pop_service( context, &track_type );
635
636         if ( track != NULL && track_type == mlt_entry_type )
637         {
638                 mlt_properties track_props = mlt_service_properties( track );
639                 enum service_type parent_type;
640                 mlt_service parent = context_pop_service( context, &parent_type );
641                 mlt_multitrack multitrack = NULL;
642
643                 mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
644                 mlt_properties producer_props = mlt_producer_properties( producer );
645
646                 if ( parent_type == mlt_tractor_type )
647                         multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
648                 else if ( parent_type == mlt_multitrack_type )
649                         multitrack = MLT_MULTITRACK( parent );
650                 else
651                         fprintf( stderr, "track contained in an invalid container\n" );
652
653                 if ( multitrack != NULL )
654                 {
655                         // Set the track on the multitrack
656                         mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
657
658                         // Set producer i/o if specified
659                         if ( mlt_properties_get( track_props, "in" ) != NULL ||
660                                 mlt_properties_get( track_props, "out" ) != NULL )
661                         {
662                                 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ),
663                                         mlt_properties_get_position( track_props, "in" ),
664                                         mlt_properties_get_position( track_props, "out" ) );
665                         }
666         
667                         // Set the hide state of the track producer
668                         char *hide_s = mlt_properties_get( track_props, "hide" );
669                         if ( hide_s != NULL )
670                         {
671                                 if ( strcmp( hide_s, "video" ) == 0 )
672                                         mlt_properties_set_int( producer_props, "hide", 1 );
673                                 else if ( strcmp( hide_s, "audio" ) == 0 )
674                                         mlt_properties_set_int( producer_props, "hide", 2 );
675                                 else if ( strcmp( hide_s, "both" ) == 0 )
676                                         mlt_properties_set_int( producer_props, "hide", 3 );
677                         }
678                 }
679
680                 if ( parent != NULL )
681                         context_push_service( context, parent, parent_type );
682
683                 mlt_service_close( track );
684         }
685         else
686         {
687                 fprintf( stderr, "Invalid state at end of track\n" );
688         }
689 }
690
691 static void on_end_entry( deserialise_context context, const xmlChar *name )
692 {
693         // Get the entry from the stack
694         enum service_type entry_type;
695         mlt_service entry = context_pop_service( context, &entry_type );
696
697         if ( entry != NULL && entry_type == mlt_entry_type )
698         {
699                 mlt_properties entry_props = mlt_service_properties( entry );
700                 enum service_type parent_type;
701                 mlt_service parent = context_pop_service( context, &parent_type );
702                 mlt_producer producer = mlt_properties_get_data( entry_props, "producer", NULL );
703
704                 if ( parent_type == mlt_playlist_type )
705                 {
706                         // Append the producer to the playlist
707                         if ( mlt_properties_get( mlt_service_properties( entry ), "in" ) != NULL ||
708                                 mlt_properties_get( mlt_service_properties( entry ), "out" ) != NULL )
709                         {
710                                 mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer,
711                                         mlt_properties_get_position( mlt_service_properties( entry ), "in" ), 
712                                         mlt_properties_get_position( mlt_service_properties( entry ), "out" ) );
713                         }
714                         else
715                         {
716                                 mlt_playlist_append( MLT_PLAYLIST( parent ), producer );
717                         }
718                 }
719                 else
720                 {
721                         fprintf( stderr, "Invalid position for an entry...\n" );
722                 }
723
724                 if ( parent != NULL )
725                         context_push_service( context, parent, parent_type );
726
727                 mlt_service_close( entry );
728         }
729         else
730         {
731                 fprintf( stderr, "Invalid state at end of entry\n" );
732         }
733 }
734
735 static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
736 {
737         // use a dummy service to hold properties to allow arbitrary nesting
738         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
739         mlt_service_init( service, NULL );
740
741         mlt_properties properties = mlt_service_properties( service );
742
743         context_push_service( context, service, mlt_dummy_filter_type );
744
745         // Set the properties
746         for ( ; atts != NULL && *atts != NULL; atts += 2 )
747                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
748 }
749
750 static void on_end_filter( deserialise_context context, const xmlChar *name )
751 {
752         enum service_type type;
753         mlt_service service = context_pop_service( context, &type );
754         mlt_properties properties = mlt_service_properties( service );
755
756         enum service_type parent_type;
757         mlt_service parent = context_pop_service( context, &parent_type );
758
759         if ( service != NULL && type == mlt_dummy_filter_type )
760         {
761                 mlt_service filter = MLT_SERVICE( mlt_factory_filter( mlt_properties_get( properties, "mlt_service" ), NULL ) );
762                 mlt_properties filter_props = mlt_service_properties( filter );
763
764                 track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
765
766                 // Propogate the properties
767                 qualify_property( context, properties, "resource" );
768                 qualify_property( context, properties, "luma" );
769                 qualify_property( context, properties, "luma.resource" );
770                 qualify_property( context, properties, "composite.luma" );
771                 qualify_property( context, properties, "producer.resource" );
772                 mlt_properties_inherit( filter_props, properties );
773
774                 // Attach all filters from service onto filter
775                 attach_filters( filter, service );
776
777                 // Associate the filter with the parent
778                 if ( parent != NULL )
779                 {
780                         if ( parent_type == mlt_tractor_type )
781                         {
782                                 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
783                                 mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
784                                 mlt_filter_set_in_and_out( MLT_FILTER( filter ), 
785                                                                                    mlt_properties_get_int( properties, "in" ),
786                                                                                    mlt_properties_get_int( properties, "out" ) );
787                         }
788                         else
789                         {
790                                 mlt_service_attach( parent, MLT_FILTER( filter ) );
791                         }
792
793                         // Put the parent back on the stack
794                         context_push_service( context, parent, parent_type );
795                 }
796                 else
797                 {
798                         fprintf( stderr, "filter closed with invalid parent...\n" );
799                 }
800
801                 // Close the dummy filter service
802                 mlt_service_close( service );
803         }
804         else
805         {
806                 fprintf( stderr, "Invalid top of stack on filter close\n" );
807         }
808 }
809
810 static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
811 {
812         // use a dummy service to hold properties to allow arbitrary nesting
813         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
814         mlt_service_init( service, NULL );
815
816         mlt_properties properties = mlt_service_properties( service );
817
818         context_push_service( context, service, mlt_dummy_transition_type );
819
820         // Set the properties
821         for ( ; atts != NULL && *atts != NULL; atts += 2 )
822                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
823 }
824
825 static void on_end_transition( deserialise_context context, const xmlChar *name )
826 {
827         enum service_type type;
828         mlt_service service = context_pop_service( context, &type );
829         mlt_properties properties = mlt_service_properties( service );
830
831         enum service_type parent_type;
832         mlt_service parent = context_pop_service( context, &parent_type );
833
834         if ( service != NULL && type == mlt_dummy_transition_type )
835         {
836                 mlt_service effect = MLT_SERVICE( mlt_factory_transition(mlt_properties_get(properties,"mlt_service"), NULL ) );
837                 mlt_properties effect_props = mlt_service_properties( effect );
838
839                 track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
840
841                 // Propogate the properties
842                 qualify_property( context, properties, "resource" );
843                 qualify_property( context, properties, "luma" );
844                 qualify_property( context, properties, "luma.resource" );
845                 qualify_property( context, properties, "composite.luma" );
846                 qualify_property( context, properties, "producer.resource" );
847                 mlt_properties_inherit( effect_props, properties );
848
849                 // Attach all filters from service onto effect
850                 attach_filters( effect, service );
851
852                 // Associate the filter with the parent
853                 if ( parent != NULL )
854                 {
855                         if ( parent_type == mlt_tractor_type )
856                         {
857                                 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
858                                 mlt_field_plant_transition( field, MLT_TRANSITION( effect ), 
859                                                                                         mlt_properties_get_int( properties, "a_track" ),
860                                                                                         mlt_properties_get_int( properties, "b_track" ) );
861                                 mlt_transition_set_in_and_out( MLT_TRANSITION( effect ), 
862                                                                                    mlt_properties_get_int( properties, "in" ),
863                                                                                    mlt_properties_get_int( properties, "out" ) );
864                         }
865                         else
866                         {
867                                 fprintf( stderr, "Misplaced transition - ignoring\n" );
868                         }
869
870                         // Put the parent back on the stack
871                         context_push_service( context, parent, parent_type );
872                 }
873                 else
874                 {
875                         fprintf( stderr, "transition closed with invalid parent...\n" );
876                 }
877
878                 // Close the dummy filter service
879                 mlt_service_close( service );
880         }
881         else
882         {
883                 fprintf( stderr, "Invalid top of stack on transition close\n" );
884         }
885 }
886
887 static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
888 {
889         enum service_type type;
890         mlt_service service = context_pop_service( context, &type );
891         mlt_properties properties = mlt_service_properties( service );
892         char *value = NULL;
893
894         if ( service != NULL )
895         {
896                 // Set the properties
897                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
898                 {
899                         if ( strcmp( atts[ 0 ], "name" ) == 0 )
900                                 context->property = strdup( atts[ 1 ] );
901                         else if ( strcmp( atts[ 0 ], "value" ) == 0 )
902                                 value = (char*) atts[ 1 ];
903                 }
904
905                 if ( context->property != NULL && value != NULL )
906                         mlt_properties_set( properties, context->property, value );
907         
908                 // Tell parser to collect any further nodes for serialisation
909                 context->is_value = 1;
910
911                 context_push_service( context, service, type );
912         }
913         else
914         {
915                 fprintf( stderr, "Property without a service '%s'?\n", ( char * )name );
916         }
917 }
918
919 static void on_end_property( deserialise_context context, const xmlChar *name )
920 {
921         enum service_type type;
922         mlt_service service = context_pop_service( context, &type );
923         mlt_properties properties = mlt_service_properties( service );
924
925         if ( service != NULL )
926         {
927                 // Tell parser to stop building a tree
928                 context->is_value = 0;
929         
930                 // See if there is a xml tree for the value
931                 if ( context->property != NULL && context->value_doc != NULL )
932                 {
933                         xmlChar *value;
934                         int size;
935                 
936                         // Serialise the tree to get value
937                         xmlDocDumpMemory( context->value_doc, &value, &size );
938                         mlt_properties_set( properties, context->property, value );
939                         xmlFree( value );
940                         xmlFreeDoc( context->value_doc );
941                         context->value_doc = NULL;
942                 }
943         
944                 // Close this property handling
945                 free( context->property );
946                 context->property = NULL;
947
948                 context_push_service( context, service, type );
949         }
950         else
951         {
952                 fprintf( stderr, "Property without a service '%s'??\n", (char *)name );
953         }
954 }
955
956 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
957 {
958         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
959         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
960         
961 //printf("on_start_element: %s\n", name );
962         context->branch[ context->depth ] ++;
963         context->depth ++;
964         
965         // Build a tree from nodes within a property value
966         if ( context->is_value == 1 )
967         {
968                 xmlNodePtr node = xmlNewNode( NULL, name );
969                 
970                 if ( context->value_doc == NULL )
971                 {
972                         // Start a new tree
973                         context->value_doc = xmlNewDoc( "1.0" );
974                         xmlDocSetRootElement( context->value_doc, node );
975                 }
976                 else
977                 {
978                         // Append child to tree
979                         xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
980                 }
981                 context_push_node( context, node );
982                 
983                 // Set the attributes
984                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
985                         xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
986         }
987         else if ( strcmp( name, "tractor" ) == 0 )
988                 on_start_tractor( context, name, atts );
989         else if ( strcmp( name, "multitrack" ) == 0 )
990                 on_start_multitrack( context, name, atts );
991         else if ( strcmp( name, "playlist" ) == 0 || strcmp( name, "seq" ) == 0 || strcmp( name, "smil" ) == 0 )
992                 on_start_playlist( context, name, atts );
993         else if ( strcmp( name, "producer" ) == 0 || strcmp( name, "video" ) == 0 )
994                 on_start_producer( context, name, atts );
995         else if ( strcmp( name, "blank" ) == 0 )
996                 on_start_blank( context, name, atts );
997         else if ( strcmp( name, "entry" ) == 0 || strcmp( name, "track" ) == 0 )
998                 on_start_entry_track( context, name, atts );
999         else if ( strcmp( name, "filter" ) == 0 )
1000                 on_start_filter( context, name, atts );
1001         else if ( strcmp( name, "transition" ) == 0 )
1002                 on_start_transition( context, name, atts );
1003         else if ( strcmp( name, "property" ) == 0 )
1004                 on_start_property( context, name, atts );
1005         else if ( strcmp( name, "westley" ) == 0 )
1006                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1007                         mlt_properties_set( context->producer_map, ( char * )atts[ 0 ], ( char * )atts[ 1 ] );
1008 }
1009
1010 static void on_end_element( void *ctx, const xmlChar *name )
1011 {
1012         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1013         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1014         
1015 //printf("on_end_element: %s\n", name );
1016         if ( context->is_value == 1 && strcmp( name, "property" ) != 0 )
1017                 context_pop_node( context );
1018         else if ( strcmp( name, "multitrack" ) == 0 )
1019                 on_end_multitrack( context, name );
1020         else if ( strcmp( name, "playlist" ) == 0 || strcmp( name, "seq" ) == 0 || strcmp( name, "smil" ) == 0 )
1021                 on_end_playlist( context, name );
1022         else if ( strcmp( name, "track" ) == 0 )
1023                 on_end_track( context, name );
1024         else if ( strcmp( name, "entry" ) == 0 )
1025                 on_end_entry( context, name );
1026         else if ( strcmp( name, "tractor" ) == 0 )
1027                 on_end_tractor( context, name );
1028         else if ( strcmp( name, "property" ) == 0 )
1029                 on_end_property( context, name );
1030         else if ( strcmp( name, "producer" ) == 0 || strcmp( name, "video" ) == 0 )
1031                 on_end_producer( context, name );
1032         else if ( strcmp( name, "filter" ) == 0 )
1033                 on_end_filter( context, name );
1034         else if ( strcmp( name, "transition" ) == 0 )
1035                 on_end_transition( context, name );
1036
1037         context->branch[ context->depth ] = 0;
1038         context->depth --;
1039 }
1040
1041 static void on_characters( void *ctx, const xmlChar *ch, int len )
1042 {
1043         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1044         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1045         char *value = calloc( len + 1, 1 );
1046         enum service_type type;
1047         mlt_service service = context_pop_service( context, &type );
1048         mlt_properties properties = mlt_service_properties( service );
1049
1050         if ( service != NULL )
1051                 context_push_service( context, service, type );
1052
1053         value[ len ] = 0;
1054         strncpy( value, (const char*) ch, len );
1055
1056         if ( context->stack_node_size > 0 )
1057                 xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
1058
1059         // libxml2 generates an on_characters immediately after a get_entity within
1060         // an element value, and we ignore it because it is called again during
1061         // actual substitution.
1062         else if ( context->property != NULL && context->entity_is_replace == 0 )
1063         {
1064                 char *s = mlt_properties_get( properties, context->property );
1065                 if ( s != NULL )
1066                 {
1067                         // Append new text to existing content
1068                         char *new = calloc( strlen( s ) + len + 1, 1 );
1069                         strcat( new, s );
1070                         strcat( new, value );
1071                         mlt_properties_set( properties, context->property, new );
1072                         free( new );
1073                 }
1074                 else
1075                         mlt_properties_set( properties, context->property, value );
1076         }
1077         context->entity_is_replace = 0;
1078         
1079         free( value);
1080 }
1081
1082 /** Convert parameters parsed from resource into entity declarations.
1083 */
1084 static void params_to_entities( deserialise_context context )
1085 {
1086         if ( context->params != NULL )
1087         {       
1088                 int i;
1089                 
1090                 // Add our params as entitiy declarations
1091                 for ( i = 0; i < mlt_properties_count( context->params ); i++ )
1092                 {
1093                         xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
1094                         xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
1095                                 context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, name ) );
1096                 }
1097
1098                 // Flag completion
1099                 mlt_properties_close( context->params );
1100                 context->params = NULL;
1101         }
1102 }
1103
1104 // The following 3 facilitate entity substitution in the SAX parser
1105 static void on_internal_subset( void *ctx, const xmlChar* name,
1106         const xmlChar* publicId, const xmlChar* systemId )
1107 {
1108         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1109         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1110         
1111         context->publicId = publicId;
1112         context->systemId = systemId;
1113         xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
1114         
1115         // Override default entities with our parameters
1116         params_to_entities( context );
1117 }
1118
1119 static void on_entity_declaration( void *ctx, const xmlChar* name, int type, 
1120         const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
1121 {
1122         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1123         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1124         
1125         xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
1126 }
1127
1128 xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
1129 {
1130         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1131         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1132         xmlEntityPtr e = NULL;
1133
1134         // Setup for entity declarations if not ready
1135         if ( xmlGetIntSubset( context->entity_doc ) == NULL )
1136         {
1137                 xmlCreateIntSubset( context->entity_doc, "westley", "", "" );
1138                 context->publicId = "";
1139                 context->systemId = "";
1140         }
1141
1142         // Add our parameters if not already
1143         params_to_entities( context );
1144         
1145         e = xmlGetDocEntity( context->entity_doc, name );
1146         
1147         // Send signal to on_characters that an entity substitutin is pending
1148         if ( e != NULL )
1149                 context->entity_is_replace = 1;
1150         
1151         return e;
1152 }
1153
1154 /** Convert a hexadecimal character to its value.
1155 */
1156 static int tohex( char p )
1157 {
1158         return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
1159 }
1160
1161 /** Decode a url-encoded string containing hexadecimal character sequences.
1162 */
1163 static char *url_decode( char *dest, char *src )
1164 {
1165         char *p = dest;
1166         
1167         while ( *src )
1168         {
1169                 if ( *src == '%' )
1170                 {
1171                         *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
1172                         src += 3;
1173                 }
1174                 else
1175                 {
1176                         *p ++ = *src ++;
1177                 }
1178         }
1179
1180         *p = *src;
1181         return dest;
1182 }
1183
1184 /** Extract the filename from a URL attaching parameters to a properties list.
1185 */
1186 static void parse_url( mlt_properties properties, char *url )
1187 {
1188         int i;
1189         int n = strlen( url );
1190         char *name = NULL;
1191         char *value = NULL;
1192         
1193         for ( i = 0; i < n; i++ )
1194         {
1195                 switch ( url[ i ] )
1196                 {
1197                         case '?':
1198                                 url[ i++ ] = '\0';
1199                                 name = &url[ i ];
1200                                 break;
1201                         
1202                         case ':':
1203                         case '=':
1204                                 url[ i++ ] = '\0';
1205                                 value = &url[ i ];
1206                                 break;
1207                         
1208                         case '&':
1209                                 url[ i++ ] = '\0';
1210                                 if ( name != NULL && value != NULL )
1211                                         mlt_properties_set( properties, name, value );
1212                                 name = &url[ i ];
1213                                 value = NULL;
1214                                 break;
1215                 }
1216         }
1217         if ( name != NULL && value != NULL )
1218                 mlt_properties_set( properties, name, value );
1219 }
1220
1221 mlt_producer producer_westley_init( int info, char *data )
1222 {
1223         if ( data == NULL )
1224                 return NULL;
1225         xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
1226         struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
1227         mlt_properties properties = NULL;
1228         int i = 0;
1229         struct _xmlParserCtxt *xmlcontext;
1230         int well_formed = 0;
1231         char *filename = NULL;
1232         
1233         context->producer_map = mlt_properties_new();
1234         context->destructors = mlt_properties_new();
1235         context->params = mlt_properties_new();
1236
1237         // Decode URL and parse parameters
1238         mlt_properties_set( context->producer_map, "root", "" );
1239         if ( info == 0 )
1240         {
1241                 filename = strdup( data );
1242                 parse_url( context->params, url_decode( filename, data ) );
1243
1244                 // We need the directory prefix which was used for the westley
1245                 if ( strchr( filename, '/' ) )
1246                 {
1247                         char *root = NULL;
1248                         mlt_properties_set( context->producer_map, "root", filename );
1249                         root = mlt_properties_get( context->producer_map, "root" );
1250                         *( strrchr( root, '/' ) ) = '\0';
1251
1252                         // If we don't have an absolute path here, we're heading for disaster...
1253                         if ( root[ 0 ] != '/' )
1254                         {
1255                                 char *cwd = getcwd( NULL, 0 );
1256                                 char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
1257                                 sprintf( real, "%s/%s", cwd, root );
1258                                 mlt_properties_set( context->producer_map, "root", real );
1259                                 free( real );
1260                                 free( cwd );
1261                         }
1262                 }
1263         }
1264
1265         // We need to track the number of registered filters
1266         mlt_properties_set_int( context->destructors, "registered", 0 );
1267
1268         // Setup SAX callbacks
1269         sax->startElement = on_start_element;
1270         sax->endElement = on_end_element;
1271         sax->characters = on_characters;
1272         sax->cdataBlock = on_characters;
1273         sax->internalSubset = on_internal_subset;
1274         sax->entityDecl = on_entity_declaration;
1275         sax->getEntity = on_get_entity;
1276
1277         // Setup libxml2 SAX parsing
1278         xmlInitParser(); 
1279         xmlSubstituteEntitiesDefault( 1 );
1280         // This is used to facilitate entity substitution in the SAX parser
1281         context->entity_doc = xmlNewDoc( "1.0" );
1282         if ( info == 0 )
1283                 xmlcontext = xmlCreateFileParserCtxt( filename );
1284         else
1285                 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1286
1287         xmlcontext->sax = sax;
1288         xmlcontext->_private = ( void* )context;
1289         
1290         // Parse
1291         xmlParseDocument( xmlcontext );
1292         well_formed = xmlcontext->wellFormed;
1293         
1294         // Cleanup after parsing
1295         xmlFreeDoc( context->entity_doc );
1296         free( sax );
1297         xmlcontext->sax = NULL;
1298         xmlcontext->_private = NULL;
1299         xmlFreeParserCtxt( xmlcontext );
1300         xmlMemoryDump( ); // for debugging
1301
1302         // Get the last producer on the stack
1303         enum service_type type;
1304         mlt_service service = context_pop_service( context, &type );
1305         if ( well_formed && service != NULL )
1306         {
1307                 // Verify it is a producer service (mlt_type="mlt_producer")
1308                 // (producer, playlist, multitrack)
1309                 char *type = mlt_properties_get( mlt_service_properties( service ), "mlt_type" );
1310                 if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
1311                         service = NULL;
1312         }
1313
1314 #ifdef DEBUG
1315         xmlDocPtr doc = westley_make_doc( service );
1316         xmlDocFormatDump( stdout, doc, 1 );
1317         xmlFreeDoc( doc );
1318         service = NULL;
1319 #endif
1320         
1321         if ( well_formed && service != NULL )
1322         {
1323                 char *title = mlt_properties_get( context->producer_map, "title" );
1324                 
1325                 // Need the complete producer list for various reasons
1326                 properties = context->destructors;
1327
1328                 // Now make sure we don't have a reference to the service in the properties
1329                 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
1330                 {
1331                         char *name = mlt_properties_get_name( properties, i );
1332                         if ( mlt_properties_get_data( properties, name, NULL ) == service )
1333                         {
1334                                 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
1335                                 break;
1336                         }
1337                 }
1338
1339                 // We are done referencing destructor property list
1340                 // Set this var to service properties for convenience
1341                 properties = mlt_service_properties( service );
1342         
1343                 // make the returned service destroy the connected services
1344                 mlt_properties_set_data( properties, "__destructors__", context->destructors, 0, (mlt_destructor) mlt_properties_close, NULL );
1345
1346                 // Assign the title
1347                 mlt_properties_set( properties, "title", title );
1348
1349                 // Handle deep copies
1350                 if ( getenv( "MLT_WESTLEY_DEEP" ) == NULL )
1351                 {
1352                         // Now assign additional properties
1353                         if ( info == 0 )
1354                                 mlt_properties_set( properties, "resource", data );
1355
1356                         // This tells consumer_westley not to deep copy
1357                         mlt_properties_set( properties, "westley", "was here" );
1358                 }
1359                 else
1360                 {
1361                         // Allow the project to be edited
1362                         mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
1363                 }
1364         }
1365         else
1366         {
1367                 // Return null if not well formed
1368                 service = NULL;
1369                 
1370                 // Clean up
1371                 mlt_properties_close( context->destructors );
1372         }
1373
1374         // Clean up
1375         mlt_properties_close( context->producer_map );
1376         if ( context->params != NULL )
1377                 mlt_properties_close( context->params );
1378         free( context );
1379         free( filename );
1380
1381         return MLT_PRODUCER( service );
1382 }