Minor fixes to westley and mlt_consumer; first draft westley docs
[melted] / 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 }