]> git.sesse.net Git - mlt/blob - src/modules/westley/producer_westley.c
Minor fixes to westley and mlt_consumer; first draft westley docs
[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 // TODO: try using XmlReader interface to avoid global context issues in sax.
24  
25 #include "producer_westley.h"
26 #include <framework/mlt.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 #include <libxml/parser.h>
32
33 #define STACK_SIZE 1000
34
35 struct deserialise_context_s
36 {
37         mlt_service stack_service[ STACK_SIZE ];
38         int stack_service_size;
39         mlt_properties producer_map;
40         mlt_properties destructors;
41         char *property;
42         mlt_properties producer_properties;
43 };
44 typedef struct deserialise_context_s *deserialise_context;
45
46
47 /** Push a service.
48 */
49
50 static int context_push_service( deserialise_context this, mlt_service that )
51 {
52         int ret = this->stack_service_size >= STACK_SIZE;
53         if ( ret == 0 )
54                 this->stack_service[ this->stack_service_size ++ ] = that;
55         return ret;
56 }
57
58 /** Pop a service.
59 */
60
61 static mlt_service context_pop_service( deserialise_context this )
62 {
63         mlt_service result = NULL;
64         if ( this->stack_service_size > 0 )
65                 result = this->stack_service[ -- this->stack_service_size ];
66         return result;
67 }
68
69 // Set the destructor on a new service
70 static void track_service( mlt_properties properties, void *service, mlt_destructor destructor )
71 {
72         int registered = mlt_properties_get_int( properties, "registered" );
73         char *key = mlt_properties_get( properties, "registered" );
74         mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
75         mlt_properties_set_int( properties, "registered", ++ registered );
76 }
77
78 static void on_start_tractor( deserialise_context context, const xmlChar *name, const xmlChar **atts)
79 {
80         mlt_service service = mlt_tractor_service( mlt_tractor_init() );
81         mlt_properties properties = mlt_service_properties( service );
82
83         track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
84
85         mlt_properties_set_position( properties, "length", 0 );
86
87         for ( ; atts != NULL && *atts != NULL; atts += 2 )
88                 mlt_properties_set( mlt_service_properties( service ), (char*) atts[0], (char*) atts[1] );
89
90         if ( mlt_properties_get_position( properties, "length" ) < mlt_properties_get_position( properties, "out" ) )
91         {
92                 mlt_position length = mlt_properties_get_position( properties, "out" ) + 1;
93                 mlt_properties_set_position( properties, "length", length );
94         }
95
96         if ( mlt_properties_get( properties, "id" ) != NULL )
97                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
98         
99         context_push_service( context, service );
100 }
101
102 static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
103 {
104         mlt_service service = mlt_multitrack_service( mlt_multitrack_init() );
105         mlt_properties properties = mlt_service_properties( service );
106
107         track_service( context->destructors, service, (mlt_destructor) mlt_multitrack_close );
108
109         mlt_properties_set_position( properties, "length", 0 );
110
111         for ( ; atts != NULL && *atts != NULL; atts += 2 )
112                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
113
114         context_push_service( context, service );
115 }
116
117 static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
118 {
119         mlt_service service = mlt_playlist_service( mlt_playlist_init() );
120         mlt_properties properties = mlt_service_properties( service );
121
122         track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
123
124         mlt_properties_set_position( properties, "length", 0 );
125
126         for ( ; atts != NULL && *atts != NULL; atts += 2 )
127         {
128                 mlt_properties_set( properties, ( char* )atts[0], ( char* )atts[1] );
129
130                 // Out will be overwritten later as we append, so we need to save it
131                 if ( strcmp( atts[ 0 ], "out" ) == 0 )
132                         mlt_properties_set( properties, "_westley.out", ( char* )atts[ 1 ] );
133         }
134
135         if ( mlt_properties_get( properties, "id" ) != NULL )
136                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
137
138         context_push_service( context, service );
139 }
140
141 static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
142 {
143         mlt_properties properties = context->producer_properties = mlt_properties_new();
144
145         for ( ; atts != NULL && *atts != NULL; atts += 2 )
146         {
147                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
148         }
149 }
150
151 static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
152 {
153         // Get the playlist from the stack
154         mlt_service service = context_pop_service( context );
155         mlt_position length = 0;
156
157         // Look for the length attribute
158         for ( ; atts != NULL && *atts != NULL; atts += 2 )
159         {
160                 if ( strcmp( atts[0], "length" ) == 0 )
161                 {
162                         length = atoll( atts[1] );
163                         break;
164                 }
165         }
166
167         // Append a blank to the playlist
168         mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
169
170         // Push the playlist back onto the stack
171         context_push_service( context, service );
172 }
173
174 static void on_start_entry_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
175 {
176         // Use a dummy service to hold properties to allow arbitratry nesting
177         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
178         mlt_service_init( service, NULL );
179
180         // Push the dummy service onto the stack
181         context_push_service( context, service );
182         
183         for ( ; atts != NULL && *atts != NULL; atts += 2 )
184         {
185                 mlt_properties_set( mlt_service_properties( service ), (char*) atts[0], (char*) atts[1] );
186                 
187                 // Look for the producer attribute
188                 if ( strcmp( atts[ 0 ], "producer" ) == 0 )
189                 {
190                         if ( mlt_properties_get_data( context->producer_map, (char*) atts[1], NULL ) !=  NULL )
191                                 // Push the referenced producer onto the stack
192                                 context_push_service( context, MLT_SERVICE( mlt_properties_get_data( context->producer_map, (char*) atts[1], NULL ) ) );
193                 }
194         }
195 }
196
197 static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
198 {
199         mlt_properties properties = context->producer_properties = mlt_properties_new();
200
201         // Set the properties
202         for ( ; atts != NULL && *atts != NULL; atts += 2 )
203                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
204 }
205
206 static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
207 {
208         mlt_properties properties = context->producer_properties = mlt_properties_new();
209
210         // Set the properties
211         for ( ; atts != NULL && *atts != NULL; atts += 2 )
212                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
213 }
214
215 static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
216 {
217         mlt_properties properties = context->producer_properties;
218         char *value = NULL;
219
220         if ( properties == NULL )
221                 return;
222         
223         // Set the properties
224         for ( ; atts != NULL && *atts != NULL; atts += 2 )
225         {
226                 if ( strcmp( atts[ 0 ], "name" ) == 0 )
227                 {
228                         context->property = strdup( atts[ 1 ] );
229                 }
230                 else if ( strcmp( atts[ 0 ], "value" ) == 0 )
231                 {
232                         value = (char*) atts[ 1 ];
233                 }
234         }
235
236         if ( context->property != NULL && value != NULL )
237                 mlt_properties_set( properties, context->property, value );
238 }
239
240 static void on_end_multitrack( deserialise_context context, const xmlChar *name )
241 {
242         // Get the producer (multitrack) from the stack
243         mlt_service producer = context_pop_service( context );
244
245         // Get the tractor from the stack
246         mlt_service service = context_pop_service( context );
247
248         // Connect the tractor to the producer
249         mlt_tractor_connect( MLT_TRACTOR( service ), producer );
250         mlt_properties_set_data( mlt_service_properties( service ), "multitrack",
251                 MLT_MULTITRACK( producer ), 0, NULL, NULL );
252
253         // Push the tractor back onto the stack
254         context_push_service( context, service );
255
256         // Push the producer back onto the stack
257         context_push_service( context, producer );
258 }
259
260 static void on_end_playlist( deserialise_context context, const xmlChar *name )
261 {
262         // Get the playlist from the stack
263         mlt_service service = context_pop_service( context );
264         mlt_properties properties = mlt_service_properties( service );
265
266         mlt_position in = mlt_properties_get_position( properties, "in" );
267         mlt_position out;
268
269         if ( mlt_properties_get( properties, "_westley.out" ) != NULL )
270                 out = mlt_properties_get_position( properties, "_westley.out" );
271         else
272                 out = mlt_properties_get_position( properties, "length" ) - 1;
273
274         if ( mlt_properties_get_position( properties, "length" ) < out )
275                 mlt_properties_set_position( properties, "length", out  + 1 );
276
277         mlt_producer_set_in_and_out( MLT_PRODUCER( service ), in, out );
278
279         // Push the playlist back onto the stack
280         context_push_service( context, service );
281 }
282
283 static void on_end_track( deserialise_context context, const xmlChar *name )
284 {
285         // Get the producer from the stack
286         mlt_service producer = context_pop_service( context );
287
288         // Get the dummy track service from the stack
289         mlt_service track = context_pop_service( context );
290
291         // Get the multitrack from the stack
292         mlt_service service = context_pop_service( context );
293
294         // Set the track on the multitrack
295         mlt_multitrack_connect( MLT_MULTITRACK( service ),
296                 MLT_PRODUCER( producer ),
297                 mlt_multitrack_count( MLT_MULTITRACK( service ) ) );
298
299         // Set producer i/o if specified
300         if ( mlt_properties_get( mlt_service_properties( track ), "in" ) != NULL ||
301                 mlt_properties_get( mlt_service_properties( track ), "out" ) != NULL )
302         {
303                 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ),
304                         mlt_properties_get_position( mlt_service_properties( track ), "in" ),
305                         mlt_properties_get_position( mlt_service_properties( track ), "out" ) );
306         }
307
308         // Push the multitrack back onto the stack
309         context_push_service( context, service );
310
311         mlt_service_close( track );
312 }
313
314 static void on_end_entry( deserialise_context context, const xmlChar *name )
315 {
316         // Get the producer from the stack
317         mlt_service producer = context_pop_service( context );
318
319         // Get the dummy entry service from the stack
320         mlt_service entry = context_pop_service( context );
321
322         // Get the playlist from the stack
323         mlt_service service = context_pop_service( context );
324
325         // Append the producer to the playlist
326         if ( mlt_properties_get( mlt_service_properties( entry ), "in" ) != NULL ||
327                 mlt_properties_get( mlt_service_properties( entry ), "out" ) != NULL )
328         {
329                 mlt_playlist_append_io( MLT_PLAYLIST( service ),
330                         MLT_PRODUCER( producer ),
331                         mlt_properties_get_position( mlt_service_properties( entry ), "in" ), 
332                         mlt_properties_get_position( mlt_service_properties( entry ), "out" ) );
333         }
334         else
335         {
336                 mlt_playlist_append( MLT_PLAYLIST( service ), MLT_PRODUCER( producer ) );
337         }
338
339         // Push the playlist back onto the stack
340         context_push_service( context, service );
341
342         mlt_service_close( entry );
343 }
344
345 static void on_end_tractor( deserialise_context context, const xmlChar *name )
346 {
347         // Get and discard the last producer
348         mlt_producer multitrack = MLT_PRODUCER( context_pop_service( context ) );
349
350         // Get the tractor
351         mlt_service tractor = context_pop_service( context );
352         multitrack = mlt_properties_get_data( mlt_service_properties( tractor ), "multitrack", NULL );
353
354         // Inherit the producer's properties
355         mlt_properties properties = mlt_producer_properties( multitrack );
356         mlt_properties_set_position( properties, "length", mlt_producer_get_out( multitrack ) + 1 );
357         mlt_producer_set_in_and_out( multitrack, 0, mlt_producer_get_out( multitrack ) );
358         mlt_properties_set_double( properties, "fps", mlt_producer_get_fps( multitrack ) );
359
360         // Push the playlist back onto the stack
361         context_push_service( context, tractor );
362 }
363
364 static void on_end_property( deserialise_context context, const xmlChar *name )
365 {
366         // Close this property handling
367         free( context->property );
368         context->property = NULL;
369 }
370
371 static void on_end_producer( deserialise_context context, const xmlChar *name )
372 {
373         mlt_properties properties = context->producer_properties;
374         mlt_service service = NULL;
375         
376         if ( properties == NULL )
377                 return;
378                 
379         // Instantiate the producer
380         if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
381         {
382                 service = MLT_SERVICE( mlt_factory_producer( "fezzik", mlt_properties_get( properties, "mlt_service" ) ) );
383         }
384         if ( service == NULL && mlt_properties_get( properties, "resource" ) != NULL )
385         {
386                 char *root = mlt_properties_get( context->producer_map, "_root" );
387                 char *resource = mlt_properties_get( properties, "resource" );
388                 char *full_resource = malloc( strlen( root ) + strlen( resource ) + 1 );
389                 if ( resource[ 0 ] != '/' )
390                 {
391                         strcpy( full_resource, root );
392                         strcat( full_resource, resource );
393                 }
394                 else
395                 {
396                         strcpy( full_resource, resource );
397                 }
398                 service = MLT_SERVICE( mlt_factory_producer( "fezzik", full_resource ) );
399                 free( full_resource );
400         }
401
402         track_service( context->destructors, service, (mlt_destructor) mlt_producer_close );
403
404         // Add the producer to the producer map
405         if ( mlt_properties_get( properties, "id" ) != NULL )
406                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
407
408         mlt_properties_inherit( mlt_service_properties( service ), properties );
409         mlt_properties_close( properties );
410         context->producer_properties = NULL;
411         properties = mlt_service_properties( service );
412
413         // Set in and out
414         mlt_producer_set_in_and_out( MLT_PRODUCER( service ),
415                 mlt_properties_get_position( properties, "in" ),
416                 mlt_properties_get_position( properties, "out" ) );
417
418         // Push the new producer onto the stack
419         context_push_service( context, service );
420 }
421
422 static void on_end_filter( deserialise_context context, const xmlChar *name )
423 {
424         mlt_properties properties = context->producer_properties;
425         if ( properties == NULL )
426                 return;
427
428         char *id;
429         char key[11];
430         key[ 10 ] = '\0';
431
432         // Get the producer from the stack
433         mlt_service producer = context_pop_service( context );
434 //fprintf( stderr, "connecting filter to %s\n", mlt_properties_get( mlt_service_properties( producer ), "resource" ) );
435
436         // Create the filter
437         mlt_service service = MLT_SERVICE( mlt_factory_filter( mlt_properties_get( properties, "mlt_service" ), NULL ) );
438
439         track_service( context->destructors, service, (mlt_destructor) mlt_filter_close );
440
441         // Connect the filter to the producer
442         mlt_filter_connect( MLT_FILTER( service ), producer,
443                 mlt_properties_get_int( properties, "track" ) );
444
445         // Set in and out from producer if non existant
446         if ( mlt_properties_get( properties, "in" ) == NULL )
447                 mlt_properties_set_position( properties, "in", mlt_producer_get_in( MLT_PRODUCER( producer ) ) );
448         if ( mlt_properties_get( properties, "out" ) == NULL )
449                 mlt_properties_set_position( properties, "out", mlt_producer_get_out( MLT_PRODUCER( producer ) ) );
450
451         // Propogate the properties
452         mlt_properties_inherit( mlt_service_properties( service ), properties );
453         mlt_properties_close( properties );
454         context->producer_properties = NULL;
455         properties = mlt_service_properties( service );
456
457         // Set in and out
458 //fprintf( stderr, "setting filter in %d out %d\n", mlt_properties_get_position( properties, "in" ), mlt_properties_get_position( properties, "out" ) );
459         mlt_filter_set_in_and_out( MLT_FILTER( service ), 
460                 mlt_properties_get_position( properties, "in" ),
461                 mlt_properties_get_position( properties, "out" ) );
462
463         // Get the parent producer from the stack
464         mlt_service tractor = context_pop_service( context );
465
466         if ( tractor != NULL )
467         {
468 //fprintf( stderr, "connecting tractor %s to filter\n", mlt_properties_get( mlt_service_properties( tractor ), "resource" ) );
469                 // Connect the tractor to the filter
470                 if ( strcmp( mlt_properties_get( mlt_service_properties( tractor ), "resource" ), "<tractor>" ) == 0 )
471                         mlt_tractor_connect( MLT_TRACTOR( tractor ), service );
472
473                 // Push the parent producer back onto the stack
474                 context_push_service( context, tractor );
475         }
476
477 //fprintf( stderr, "setting filter in %d out %d\n", mlt_properties_get_position( properties, "in" ), mlt_properties_get_position( properties, "out" ) );
478         // If a producer alias is in the producer_map, get it
479         snprintf( key, 10, "%p", producer );
480         if ( mlt_properties_get_data( context->producer_map, key, NULL ) != NULL )
481                 producer = mlt_properties_get_data( context->producer_map, key, NULL );
482
483         // Put the producer in the producer map
484         id = mlt_properties_get( mlt_service_properties( producer ), "id" );
485         if ( id != NULL )
486                 mlt_properties_set_data( context->producer_map, id, service, 0, NULL, NULL );
487
488         // For filter chain support, add an alias to the producer map
489         snprintf( key, 10, "%p", service );
490         mlt_properties_set_data( context->producer_map, key, producer, 0, NULL, NULL );
491
492         // Push the filter onto the stack
493         context_push_service( context, service );
494
495 }
496
497 static void on_end_transition( deserialise_context context, const xmlChar *name )
498 {
499         mlt_properties properties = context->producer_properties;
500         if ( properties == NULL )
501                 return;
502
503         // Get the producer from the stack
504         mlt_service producer = context_pop_service( context );
505
506         // Create the transition
507         mlt_service service = MLT_SERVICE( mlt_factory_transition( mlt_properties_get( properties, "mlt_service" ), NULL ) );
508
509         track_service( context->destructors, service, (mlt_destructor) mlt_transition_close );
510
511         // Propogate the properties
512         mlt_properties_inherit( mlt_service_properties( service ), properties );
513         mlt_properties_close( properties );
514         context->producer_properties = NULL;
515         properties = mlt_service_properties( service );
516
517         // Set in and out
518         mlt_transition_set_in_and_out( MLT_TRANSITION( service ),
519                 mlt_properties_get_position( properties, "in" ),
520                 mlt_properties_get_position( properties, "out" ) );
521
522         // Connect the filter to the producer
523         mlt_transition_connect( MLT_TRANSITION( service ), producer,
524                 mlt_properties_get_int( properties, "a_track" ),
525                 mlt_properties_get_int( properties, "b_track" ) );
526
527         // Get the tractor from the stack
528         mlt_service tractor = context_pop_service( context );
529
530         // Connect the tractor to the transition
531         mlt_tractor_connect( MLT_TRACTOR( tractor ), service );
532
533         // Push the tractor back onto the stack
534         context_push_service( context, tractor );
535
536         // Push the transition onto the stack
537         context_push_service( context, service );
538 }
539
540 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
541 {
542         deserialise_context context = ( deserialise_context ) ctx;
543         
544         if ( strcmp( name, "tractor" ) == 0 )
545                 on_start_tractor( context, name, atts );
546         else if ( strcmp( name, "multitrack" ) == 0 )
547                 on_start_multitrack( context, name, atts );
548         else if ( strcmp( name, "playlist" ) == 0 )
549                 on_start_playlist( context, name, atts );
550         else if ( strcmp( name, "producer" ) == 0 )
551                 on_start_producer( context, name, atts );
552         else if ( strcmp( name, "blank" ) == 0 )
553                 on_start_blank( context, name, atts );
554         else if ( strcmp( name, "entry" ) == 0 || strcmp( name, "track" ) == 0 )
555                 on_start_entry_track( context, name, atts );
556         else if ( strcmp( name, "filter" ) == 0 )
557                 on_start_filter( context, name, atts );
558         else if ( strcmp( name, "transition" ) == 0 )
559                 on_start_transition( context, name, atts );
560         else if ( strcmp( name, "property" ) == 0 )
561                 on_start_property( context, name, atts );
562 }
563
564 static void on_end_element( void *ctx, const xmlChar *name )
565 {
566         deserialise_context context = ( deserialise_context ) ctx;
567         
568         if ( strcmp( name, "multitrack" ) == 0 )
569                 on_end_multitrack( context, name );
570         else if ( strcmp( name, "playlist" ) == 0 )
571                 on_end_playlist( context, name );
572         else if ( strcmp( name, "track" ) == 0 )
573                 on_end_track( context, name );
574         else if ( strcmp( name, "entry" ) == 0 )
575                 on_end_entry( context, name );
576         else if ( strcmp( name, "tractor" ) == 0 )
577                 on_end_tractor( context, name );
578         else if ( strcmp( name, "property" ) == 0 )
579                 on_end_property( context, name );
580         else if ( strcmp( name, "producer" ) == 0 )
581                 on_end_producer( context, name );
582         else if ( strcmp( name, "filter" ) == 0 )
583                 on_end_filter( context, name );
584         else if ( strcmp( name, "transition" ) == 0 )
585                 on_end_transition( context, name );
586 }
587
588 static void on_characters( void *ctx, const xmlChar *ch, int len )
589 {
590         deserialise_context context = ( deserialise_context ) ctx;
591         char *value = calloc( len + 1, 1 );
592
593         value[ len ] = 0;
594         strncpy( value, (const char*) ch, len );
595
596         if ( context->property != NULL && context->producer_properties != NULL )
597                 mlt_properties_set( context->producer_properties, context->property, value );
598                 
599         free( value);
600 }
601
602 mlt_producer producer_westley_init( char *filename )
603 {
604         xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
605         struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
606         mlt_properties properties = NULL;
607         int i = 0;
608
609         context->producer_map = mlt_properties_new();
610         context->destructors = mlt_properties_new();
611
612         // We need to track the number of registered filters
613         mlt_properties_set_int( context->destructors, "registered", 0 );
614
615         // We need the directory prefix which was used for the westley
616         mlt_properties_set( context->producer_map, "_root", "" );
617         if ( strchr( filename, '/' ) )
618         {
619                 char *root = NULL;
620                 mlt_properties_set( context->producer_map, "_root", filename );
621                 root = mlt_properties_get( context->producer_map, "_root" );
622                 *( strrchr( root, '/' ) + 1 ) = '\0';
623         }
624
625         sax->startElement = on_start_element;
626         sax->endElement = on_end_element;
627         sax->characters = on_characters;
628         sax->cdataBlock = on_characters;
629
630         // I REALLY DON'T GET THIS - HOW THE HELL CAN YOU REFERENCE A WESTLEY IN A WESTLEY???
631         xmlInitParser();
632
633         xmlSAXUserParseFile( sax, context, filename );
634
635         // Need the complete producer list for various reasons
636         properties = context->destructors;
637
638         // Get the last producer on the stack
639         mlt_service service = context_pop_service( context );
640
641         // Do we actually have a producer here?
642         if ( service != NULL )
643         {
644                 // Now make sure we don't have a reference to the service in the properties
645                 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
646                 {
647                         char *name = mlt_properties_get_name( properties, i );
648                         if ( mlt_properties_get_data( properties, name, NULL ) == service )
649                         {
650                                 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
651                                 break;
652                         }
653                 }
654
655                 // We are done referencing destructor property list
656                 // Set this var to service properties for convenience
657                 properties = mlt_service_properties( service );
658         
659                 // make the returned service destroy the connected services
660                 mlt_properties_set_data( properties, "__destructors__", context->destructors, 0, (mlt_destructor) mlt_properties_close, NULL );
661
662                 // Now assign additional properties
663                 mlt_properties_set( properties, "resource", filename );
664
665                 // This tells consumer_westley not to deep copy
666                 mlt_properties_set( properties, "westley", "was here" );
667         }
668         else
669         {
670                 // Clean up
671                 mlt_properties_close( properties );
672         }
673
674         free( context->stack_service );
675         mlt_properties_close( context->producer_map );
676         //free( context );
677         free( sax );
678         //xmlCleanupParser();
679         xmlMemoryDump( );
680
681
682         return MLT_PRODUCER( service );
683 }