]> git.sesse.net Git - mlt/blob - src/modules/westley/producer_westley.c
06c6cfe38bb9480a728dc9c71a99d21f9cea3788
[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: determine why deserialise_context can not be released.
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 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
33 #include <libxml/tree.h>
34
35 #define STACK_SIZE 1000
36
37 struct deserialise_context_s
38 {
39         mlt_service stack_service[ STACK_SIZE ];
40         int stack_service_size;
41         mlt_properties producer_map;
42         mlt_properties destructors;
43         char *property;
44         mlt_properties producer_properties;
45         int is_value;
46         xmlDocPtr value_doc;
47         xmlNodePtr stack_node[ STACK_SIZE ];
48         int stack_node_size;
49         xmlDocPtr entity_doc;
50 };
51 typedef struct deserialise_context_s *deserialise_context;
52
53
54 /** Push a service.
55 */
56
57 static int context_push_service( deserialise_context this, mlt_service that )
58 {
59         int ret = this->stack_service_size >= STACK_SIZE - 1;
60         if ( ret == 0 )
61                 this->stack_service[ this->stack_service_size ++ ] = that;
62         return ret;
63 }
64
65 /** Pop a service.
66 */
67
68 static mlt_service context_pop_service( deserialise_context this )
69 {
70         mlt_service result = NULL;
71         if ( this->stack_service_size > 0 )
72                 result = this->stack_service[ -- this->stack_service_size ];
73         return result;
74 }
75
76 /** Push a node.
77 */
78
79 static int context_push_node( deserialise_context this, xmlNodePtr node )
80 {
81         int ret = this->stack_node_size >= STACK_SIZE - 1;
82         if ( ret == 0 )
83                 this->stack_node[ this->stack_node_size ++ ] = node;
84         return ret;
85 }
86
87 /** Pop a node.
88 */
89
90 static xmlNodePtr context_pop_node( deserialise_context this )
91 {
92         xmlNodePtr result = NULL;
93         if ( this->stack_node_size > 0 )
94                 result = this->stack_node[ -- this->stack_node_size ];
95         return result;
96 }
97
98
99 // Set the destructor on a new service
100 static void track_service( mlt_properties properties, void *service, mlt_destructor destructor )
101 {
102         int registered = mlt_properties_get_int( properties, "registered" );
103         char *key = mlt_properties_get( properties, "registered" );
104         mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
105         mlt_properties_set_int( properties, "registered", ++ registered );
106 }
107
108
109 // Forward declarations
110 static void on_end_track( deserialise_context context, const xmlChar *name );
111 static void on_end_entry( deserialise_context context, const xmlChar *name );
112
113 static void on_start_tractor( deserialise_context context, const xmlChar *name, const xmlChar **atts)
114 {
115         mlt_service service = mlt_tractor_service( mlt_tractor_init() );
116         mlt_properties properties = mlt_service_properties( service );
117
118         track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
119
120         mlt_properties_set_position( properties, "length", 0 );
121
122         for ( ; atts != NULL && *atts != NULL; atts += 2 )
123                 mlt_properties_set( mlt_service_properties( service ), (char*) atts[0], (char*) atts[1] );
124
125         if ( mlt_properties_get_position( properties, "length" ) < mlt_properties_get_position( properties, "out" ) )
126         {
127                 mlt_position length = mlt_properties_get_position( properties, "out" ) + 1;
128                 mlt_properties_set_position( properties, "length", length );
129         }
130
131         if ( mlt_properties_get( properties, "id" ) != NULL )
132                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
133         
134         context_push_service( context, service );
135 }
136
137 static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
138 {
139         mlt_service service = mlt_multitrack_service( mlt_multitrack_init() );
140         mlt_properties properties = mlt_service_properties( service );
141
142         track_service( context->destructors, service, (mlt_destructor) mlt_multitrack_close );
143
144         mlt_properties_set_position( properties, "length", 0 );
145
146         for ( ; atts != NULL && *atts != NULL; atts += 2 )
147                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
148
149         if ( mlt_properties_get( properties, "id" ) != NULL )
150                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
151
152         context_push_service( context, service );
153 }
154
155 static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
156 {
157         mlt_service service = mlt_playlist_service( mlt_playlist_init() );
158         mlt_properties properties = mlt_service_properties( service );
159
160         track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
161
162         mlt_properties_set_position( properties, "length", 0 );
163
164         for ( ; atts != NULL && *atts != NULL; atts += 2 )
165         {
166                 mlt_properties_set( properties, ( char* )atts[0], ( char* )atts[1] );
167
168                 // Out will be overwritten later as we append, so we need to save it
169                 if ( strcmp( atts[ 0 ], "out" ) == 0 )
170                         mlt_properties_set( properties, "_westley.out", ( char* )atts[ 1 ] );
171         }
172
173         if ( mlt_properties_get( properties, "id" ) != NULL )
174                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
175
176         context_push_service( context, service );
177 }
178
179 static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
180 {
181         mlt_properties properties = context->producer_properties = mlt_properties_new();
182
183         for ( ; atts != NULL && *atts != NULL; atts += 2 )
184         {
185                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
186         }
187 }
188
189 static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
190 {
191         // Get the playlist from the stack
192         mlt_service service = context_pop_service( context );
193         mlt_position length = 0;
194         
195         if ( service == NULL )
196                 return;
197         
198         // Look for the length attribute
199         for ( ; atts != NULL && *atts != NULL; atts += 2 )
200         {
201                 if ( strcmp( atts[0], "length" ) == 0 )
202                 {
203                         length = atoll( atts[1] );
204                         break;
205                 }
206         }
207
208         // Append a blank to the playlist
209         mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
210
211         // Push the playlist back onto the stack
212         context_push_service( context, service );
213 }
214
215 static void on_start_entry_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
216 {
217         // Use a dummy service to hold properties to allow arbitrary nesting
218         mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
219         mlt_service_init( service, NULL );
220
221         // Push the dummy service onto the stack
222         context_push_service( context, service );
223         
224         if ( strcmp( name, "entry" ) == 0 )
225                 mlt_properties_set( mlt_service_properties( service ), "resource", "<entry>" );
226         
227         for ( ; atts != NULL && *atts != NULL; atts += 2 )
228         {
229                 mlt_properties_set( mlt_service_properties( service ), (char*) atts[0], (char*) atts[1] );
230                 
231                 // Look for the producer attribute
232                 if ( strcmp( atts[ 0 ], "producer" ) == 0 )
233                 {
234                         if ( mlt_properties_get_data( context->producer_map, (char*) atts[1], NULL ) !=  NULL )
235                                 // Push the referenced producer onto the stack
236                                 context_push_service( context, MLT_SERVICE( mlt_properties_get_data( context->producer_map, (char*) atts[1], NULL ) ) );
237                 }
238         }
239 }
240
241 static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
242 {
243         mlt_properties properties = context->producer_properties = mlt_properties_new();
244
245         // Set the properties
246         for ( ; atts != NULL && *atts != NULL; atts += 2 )
247                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
248 }
249
250 static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
251 {
252         mlt_properties properties = context->producer_properties = mlt_properties_new();
253
254         // Set the properties
255         for ( ; atts != NULL && *atts != NULL; atts += 2 )
256                 mlt_properties_set( properties, (char*) atts[0], (char*) atts[1] );
257 }
258
259 static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
260 {
261         mlt_properties properties = context->producer_properties;
262         char *value = NULL;
263
264         if ( properties == NULL )
265                 return;
266         
267         // Set the properties
268         for ( ; atts != NULL && *atts != NULL; atts += 2 )
269         {
270                 if ( strcmp( atts[ 0 ], "name" ) == 0 )
271                 {
272                         context->property = strdup( atts[ 1 ] );
273                 }
274                 else if ( strcmp( atts[ 0 ], "value" ) == 0 )
275                 {
276                         value = (char*) atts[ 1 ];
277                 }
278         }
279
280         if ( context->property != NULL && value != NULL )
281                 mlt_properties_set( properties, context->property, value );
282         
283         // Tell parser to collect any further nodes for serialisation
284         context->is_value = 1;
285 }
286
287
288 /** This function adds a producer to a playlist or multitrack when
289     there is no entry or track element.
290 */
291
292 static int add_producer( deserialise_context context, mlt_service service, mlt_position in, mlt_position out )
293 {
294         // Get the parent producer
295         mlt_service producer = context_pop_service( context );
296         if ( producer != NULL )
297         {
298                 char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
299                 
300                 // Put the parent producer back
301                 context_push_service( context, producer );
302                         
303                 // If the parent producer is a multitrack or playlist (not a track or entry)
304                 if ( resource && ( strcmp( resource, "<playlist>" ) == 0 ||
305                         strcmp( resource, "<multitrack>" ) == 0 ) )
306                 {
307                         if ( strcmp( resource, "<playlist>" ) == 0 )
308                         {
309                                 // Append this producer to the playlist
310                                 mlt_playlist_append_io( MLT_PLAYLIST( producer ), 
311                                         MLT_PRODUCER( service ), in, out );
312                         }
313                         else
314                         {
315                                 // Set this producer on the multitrack
316                                 mlt_multitrack_connect( MLT_MULTITRACK( producer ),
317                                         MLT_PRODUCER( service ),
318                                         mlt_multitrack_count( MLT_MULTITRACK( producer ) ) );
319                         }
320                         // Normally, the enclosing entry or track will pop this service off
321                         // In its absence we do not push it on.
322                         return 1;
323                 }
324         }
325         return 0;
326 }
327
328 static void on_end_multitrack( deserialise_context context, const xmlChar *name )
329 {
330         // Get the multitrack from the stack
331         mlt_service producer = context_pop_service( context );
332         if ( producer == NULL )
333                 return;
334
335         // Get the tractor from the stack
336         mlt_service service = context_pop_service( context );
337         
338         // Create a tractor if one does not exist
339         char *resource = NULL;
340         if ( service != NULL )
341                 resource = mlt_properties_get( mlt_service_properties( service ), "resource" );
342         if ( service == NULL || resource == NULL || strcmp( resource, "<tractor>" ) )
343         {
344                 // Put the anonymous service back onto the stack!
345                 context_push_service( context, service );
346                 
347                 service = mlt_tractor_service( mlt_tractor_init() );
348                 track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
349                 
350                 // Inherit the producer's properties
351                 mlt_properties properties = mlt_service_properties( service );
352                 mlt_properties_set_position( properties, "length", mlt_producer_get_out( MLT_PRODUCER( producer ) ) + 1 );
353                 mlt_producer_set_in_and_out( MLT_PRODUCER( service ), 0, mlt_producer_get_out( MLT_PRODUCER( producer ) ) );
354                 mlt_properties_set_double( properties, "fps", mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
355         }
356         
357         // Connect the tractor to the multitrack
358         mlt_tractor_connect( MLT_TRACTOR( service ), producer );
359         mlt_properties_set_data( mlt_service_properties( service ), "multitrack",
360                 MLT_MULTITRACK( producer ), 0, NULL, NULL );
361
362         // See if the producer should be added to a playlist or multitrack
363         add_producer( context, service, 0, mlt_producer_get_out( MLT_PRODUCER( producer ) ) );
364         
365         // Always push the multitrack back onto the stack for filters and transitions
366         context_push_service( context, producer );
367         
368         // Always push the tractor back onto the stack for filters and transitions
369         context_push_service( context, service );
370 }
371
372 static void on_end_playlist( deserialise_context context, const xmlChar *name )
373 {
374         // Get the playlist from the stack
375         mlt_service producer = context_pop_service( context );
376         if ( producer == NULL )
377                 return;
378         mlt_properties properties = mlt_service_properties( producer );
379
380         mlt_position in = mlt_properties_get_position( properties, "in" );
381         mlt_position out;
382
383         if ( mlt_properties_get( properties, "_westley.out" ) != NULL )
384                 out = mlt_properties_get_position( properties, "_westley.out" );
385         else
386                 out = mlt_properties_get_position( properties, "length" ) - 1;
387
388         if ( mlt_properties_get_position( properties, "length" ) < out )
389                 mlt_properties_set_position( properties, "length", out  + 1 );
390
391         mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
392         
393         // See if the playlist should be added to a playlist or multitrack
394         if ( add_producer( context, producer, in, out ) == 0 )
395                 
396                 // Otherwise, push the playlist back onto the stack
397                 context_push_service( context, producer );
398 }
399
400 static void on_end_track( deserialise_context context, const xmlChar *name )
401 {
402         // Get the producer from the stack
403         mlt_service producer = context_pop_service( context );
404         if ( producer == NULL )
405                 return;
406
407         // See if the producer is a tractor
408         char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
409         if ( resource && strcmp( resource, "<tractor>" ) == 0 )
410                 // If so chomp its producer
411                 context_pop_service( context );
412
413         // Get the dummy track service from the stack
414         mlt_service track = context_pop_service( context );
415         if ( track == NULL )
416         {
417                 context_push_service( context, producer );
418                 return;
419         }
420
421         // Get the multitrack from the stack
422         mlt_service service = context_pop_service( context );
423         if ( service == NULL )
424         {
425                 context_push_service( context, producer );
426                 return;
427         }
428
429         // Set the track on the multitrack
430         mlt_multitrack_connect( MLT_MULTITRACK( service ),
431                 MLT_PRODUCER( producer ),
432                 mlt_multitrack_count( MLT_MULTITRACK( service ) ) );
433
434         // Set producer i/o if specified
435         if ( mlt_properties_get( mlt_service_properties( track ), "in" ) != NULL ||
436                 mlt_properties_get( mlt_service_properties( track ), "out" ) != NULL )
437         {
438                 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ),
439                         mlt_properties_get_position( mlt_service_properties( track ), "in" ),
440                         mlt_properties_get_position( mlt_service_properties( track ), "out" ) );
441         }
442
443         // Push the multitrack back onto the stack
444         context_push_service( context, service );
445
446         mlt_service_close( track );
447 }
448
449 static void on_end_entry( deserialise_context context, const xmlChar *name )
450 {
451         // Get the producer from the stack
452         mlt_service producer = context_pop_service( context );
453         if ( producer == NULL )
454                 return;
455         
456         // See if the producer is a tractor
457         char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
458         if ( resource && strcmp( resource, "<tractor>" ) == 0 )
459                 // If so chomp its producer
460                 context_pop_service( context );
461
462         // Get the dummy entry service from the stack
463         mlt_service entry = context_pop_service( context );
464         if ( entry == NULL )
465         {
466                 context_push_service( context, producer );
467                 return;
468         }
469
470         // Get the playlist from the stack
471         mlt_service service = context_pop_service( context );
472         if ( service == NULL )
473         {
474                 context_push_service( context, producer );
475                 return;
476         }
477
478         // Append the producer to the playlist
479         if ( mlt_properties_get( mlt_service_properties( entry ), "in" ) != NULL ||
480                 mlt_properties_get( mlt_service_properties( entry ), "out" ) != NULL )
481         {
482                 mlt_playlist_append_io( MLT_PLAYLIST( service ),
483                         MLT_PRODUCER( producer ),
484                         mlt_properties_get_position( mlt_service_properties( entry ), "in" ), 
485                         mlt_properties_get_position( mlt_service_properties( entry ), "out" ) );
486         }
487         else
488         {
489                 mlt_playlist_append( MLT_PLAYLIST( service ), MLT_PRODUCER( producer ) );
490         }
491
492         // Push the playlist back onto the stack
493         context_push_service( context, service );
494
495         mlt_service_close( entry );
496 }
497
498 static void on_end_tractor( deserialise_context context, const xmlChar *name )
499 {
500         // Get the tractor
501         mlt_service tractor = context_pop_service( context );
502         if ( tractor == NULL )
503                 return;
504         
505         // Get the tractor's multitrack
506         mlt_producer multitrack = mlt_properties_get_data( mlt_service_properties( tractor ), "multitrack", NULL );
507         if ( multitrack != NULL )
508         {
509                 // Inherit the producer's properties
510                 mlt_properties properties = mlt_producer_properties( MLT_PRODUCER( tractor ) );
511                 mlt_properties_set_position( properties, "length", mlt_producer_get_out( multitrack ) + 1 );
512                 mlt_producer_set_in_and_out( multitrack, 0, mlt_producer_get_out( multitrack ) );
513                 mlt_properties_set_double( properties, "fps", mlt_producer_get_fps( multitrack ) );
514         }
515
516         // See if the tractor should be added to a playlist or multitrack
517         if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
518                 
519                 // Otherwise, push the tractor back onto the stack
520                 context_push_service( context, tractor );
521 }
522
523 static void on_end_property( deserialise_context context, const xmlChar *name )
524 {
525         // Tell parser to stop building a tree
526         context->is_value = 0;
527         
528         // See if there is a xml tree for the value
529         if ( context->property != NULL && context->value_doc != NULL )
530         {
531                 xmlChar *value;
532                 int size;
533                 
534                 // Serialise the tree to get value
535                 xmlDocDumpMemory( context->value_doc, &value, &size );
536                 mlt_properties_set( context->producer_properties, context->property, value );
537                 xmlFree( value );
538                 xmlFreeDoc( context->value_doc );
539                 context->value_doc = NULL;
540         }
541         
542         // Close this property handling
543         free( context->property );
544         context->property = NULL;
545 }
546
547 static void on_end_producer( deserialise_context context, const xmlChar *name )
548 {
549         mlt_properties properties = context->producer_properties;
550         mlt_service service = NULL;
551         
552         if ( properties == NULL )
553                 return;
554                 
555         char *resource = mlt_properties_get( properties, "resource" );
556         // Let Kino-SMIL src be a synonym for resource
557         if ( resource == NULL )
558                 resource = mlt_properties_get( properties, "src" );
559         
560         // Instantiate the producer
561         if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
562         {
563                 service = MLT_SERVICE( mlt_factory_producer( "fezzik", mlt_properties_get( properties, "mlt_service" ) ) );
564         }
565         if ( service == NULL && resource != NULL )
566         {
567                 char *root = mlt_properties_get( context->producer_map, "_root" );
568                 char *full_resource = malloc( strlen( root ) + strlen( resource ) + 1 );
569                 if ( resource[ 0 ] != '/' )
570                 {
571                         strcpy( full_resource, root );
572                         strcat( full_resource, resource );
573                 }
574                 else
575                 {
576                         strcpy( full_resource, resource );
577                 }
578                 service = MLT_SERVICE( mlt_factory_producer( "fezzik", full_resource ) );
579                 free( full_resource );
580         }
581         if ( service == NULL )
582                 return;
583         track_service( context->destructors, service, (mlt_destructor) mlt_producer_close );
584
585         // Add the producer to the producer map
586         if ( mlt_properties_get( properties, "id" ) != NULL )
587                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
588
589         // Handle in/out properties separately
590         mlt_position in = -1;
591         mlt_position out = -1;
592         
593         // Get in
594         if ( mlt_properties_get( properties, "in" ) != NULL )
595                 in = mlt_properties_get_position( properties, "in" );
596         // Let Kino-SMIL clipBegin be a synonym for in
597         if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
598                 in = mlt_properties_get_position( properties, "clipBegin" );
599         // Get out
600         if ( mlt_properties_get( properties, "out" ) != NULL )
601                 out = mlt_properties_get_position( properties, "out" );
602         // Let Kino-SMIL clipEnd be a synonym for out
603         if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
604                 out = mlt_properties_get_position( properties, "clipEnd" );
605         
606         // Remove in and out
607         mlt_properties_set( properties, "in", NULL );
608         mlt_properties_set( properties, "out", NULL );
609         
610         mlt_properties_inherit( mlt_service_properties( service ), properties );
611         mlt_properties_close( properties );
612         context->producer_properties = NULL;
613
614         // See if the producer should be added to a playlist or multitrack
615         if ( add_producer( context, service, in, out ) == 0 )
616         {
617                 // Otherwise, set in and out on...
618                 if ( in != -1 || out != -1 )
619                 {
620                         // Get the parent service
621                         mlt_service parent = context_pop_service( context );
622                         if ( parent != NULL )
623                         {
624                                 // Get the parent properties
625                                 properties = mlt_service_properties( parent );
626                                 
627                                 char *resource = mlt_properties_get( properties, "resource" );
628                                 
629                                 // Put the parent producer back
630                                 context_push_service( context, parent );
631                                         
632                                 // If the parent is a track or entry
633                                 if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
634                                 {
635                                         mlt_properties_set_position( properties, "in", in );
636                                         mlt_properties_set_position( properties, "out", out );
637                                 }
638                                 else
639                                 {
640                                         // Otherwise, set in and out on producer directly
641                                         mlt_producer_set_in_and_out( MLT_PRODUCER( service ), in, out );
642                                 }
643                         }
644                         else
645                         {
646                                 // Otherwise, set in and out on producer directly
647                                 mlt_producer_set_in_and_out( MLT_PRODUCER( service ), in, out );
648                         }
649                 }
650         
651                 // Push the producer onto the stack
652                 context_push_service( context, service );
653         }
654 }
655
656 static void on_end_filter( deserialise_context context, const xmlChar *name )
657 {
658         mlt_properties properties = context->producer_properties;
659         if ( properties == NULL )
660                 return;
661
662         char *id;
663         char key[11];
664         key[ 10 ] = '\0';
665         mlt_service tractor = NULL;
666         
667         // Get the producer from the stack
668         mlt_service producer = context_pop_service( context );
669         if ( producer == NULL )
670                 return;
671         
672         // See if the producer is a tractor
673         char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
674         if ( resource != NULL && strcmp( resource, "<tractor>" ) == 0 )
675         {
676                 // If so, then get the next producer
677                 tractor = producer;
678                 producer = context_pop_service( context );
679         }
680         
681 //fprintf( stderr, "connecting filter to %s\n", mlt_properties_get( mlt_service_properties( producer ), "resource" ) );
682
683         // Create the filter
684         mlt_service service = MLT_SERVICE( mlt_factory_filter( mlt_properties_get( properties, "mlt_service" ), NULL ) );
685         if ( service == NULL )
686         {
687                 context_push_service( context, producer );
688                 return;
689         }
690         track_service( context->destructors, service, (mlt_destructor) mlt_filter_close );
691
692         // Connect the filter to the producer
693         mlt_filter_connect( MLT_FILTER( service ), producer,
694                 mlt_properties_get_int( properties, "track" ) );
695
696         // Set in and out from producer if non existant
697         if ( mlt_properties_get( properties, "in" ) == NULL )
698                 mlt_properties_set_position( properties, "in", mlt_producer_get_in( MLT_PRODUCER( producer ) ) );
699         if ( mlt_properties_get( properties, "out" ) == NULL )
700                 mlt_properties_set_position( properties, "out", mlt_producer_get_out( MLT_PRODUCER( producer ) ) );
701
702         // Propogate the properties
703         mlt_properties_inherit( mlt_service_properties( service ), properties );
704         mlt_properties_close( properties );
705         context->producer_properties = NULL;
706         properties = mlt_service_properties( service );
707
708         // Set in and out again due to inheritance
709         mlt_filter_set_in_and_out( MLT_FILTER( service ), 
710                 mlt_properties_get_position( properties, "in" ),
711                 mlt_properties_get_position( properties, "out" ) );
712
713         // If a producer alias is in the producer_map, get it
714         snprintf( key, 10, "%p", producer );
715         if ( mlt_properties_get_data( context->producer_map, key, NULL ) != NULL )
716                 producer = mlt_properties_get_data( context->producer_map, key, NULL );
717
718         // Put the producer in the producer map
719         id = mlt_properties_get( mlt_service_properties( producer ), "id" );
720         if ( id != NULL )
721                 mlt_properties_set_data( context->producer_map, id, service, 0, NULL, NULL );
722
723         // For filter chain support, add an alias to the producer map
724         snprintf( key, 10, "%p", service );
725         mlt_properties_set_data( context->producer_map, key, producer, 0, NULL, NULL );
726         
727         // Push the filter onto the stack
728         context_push_service( context, service );
729         
730         if ( tractor != NULL )
731         {
732                 // Connect the tractor to the filter
733                 mlt_tractor_connect( MLT_TRACTOR( tractor ), service );
734
735                 // Push the tractor back onto the stack
736                 context_push_service( context, tractor );
737         }
738 }
739
740 static void on_end_transition( deserialise_context context, const xmlChar *name )
741 {
742         mlt_service tractor = NULL;
743         mlt_properties properties = context->producer_properties;
744         if ( properties == NULL )
745                 return;
746
747         // Get the producer from the stack
748         mlt_service producer = context_pop_service( context );
749         if ( producer == NULL )
750                 return;
751
752         // See if the producer is a tractor
753         char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
754         if ( resource != NULL && strcmp( resource, "<tractor>" ) == 0 )
755         {
756                 // If so, then get the next producer
757                 tractor = producer;
758                 producer = context_pop_service( context );
759         }
760         
761         // Create the transition
762         mlt_service service = MLT_SERVICE( mlt_factory_transition( mlt_properties_get( properties, "mlt_service" ), NULL ) );
763         if ( service == NULL )
764         {
765                 context_push_service( context, producer );
766                 return;
767         }
768         track_service( context->destructors, service, (mlt_destructor) mlt_transition_close );
769
770         // Propogate the properties
771         mlt_properties_inherit( mlt_service_properties( service ), properties );
772         mlt_properties_close( properties );
773         context->producer_properties = NULL;
774         properties = mlt_service_properties( service );
775
776         // Set in and out again due to inheritance
777         mlt_transition_set_in_and_out( MLT_TRANSITION( service ),
778                 mlt_properties_get_position( properties, "in" ),
779                 mlt_properties_get_position( properties, "out" ) );
780
781         // Connect the transition to the producer
782         mlt_transition_connect( MLT_TRANSITION( service ), producer,
783                 mlt_properties_get_int( properties, "a_track" ),
784                 mlt_properties_get_int( properties, "b_track" ) );
785
786         // Push the transition onto the stack
787         context_push_service( context, service );
788         
789         if ( tractor != NULL )
790         {
791                 // Connect the tractor to the transition
792                 mlt_tractor_connect( MLT_TRACTOR( tractor ), service );
793
794                 // Push the tractor back onto the stack
795                 context_push_service( context, tractor );
796         }
797 }
798
799 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
800 {
801         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
802         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
803         
804         // Build a tree from nodes within a property value
805         if ( context->is_value == 1 )
806         {
807                 xmlNodePtr node = xmlNewNode( NULL, name );
808                 
809                 if ( context->value_doc == NULL )
810                 {
811                         // Start a new tree
812                         context->value_doc = xmlNewDoc( "1.0" );
813                         xmlDocSetRootElement( context->value_doc, node );
814                 }
815                 else
816                 {
817                         // Append child to tree
818                         xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
819                 }
820                 context_push_node( context, node );
821                 
822                 // Set the attributes
823                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
824                         xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
825         }
826         else if ( strcmp( name, "tractor" ) == 0 )
827                 on_start_tractor( context, name, atts );
828         else if ( strcmp( name, "multitrack" ) == 0 )
829                 on_start_multitrack( context, name, atts );
830         else if ( strcmp( name, "playlist" ) == 0 || strcmp( name, "seq" ) == 0 || strcmp( name, "smil" ) == 0 )
831                 on_start_playlist( context, name, atts );
832         else if ( strcmp( name, "producer" ) == 0 || strcmp( name, "video" ) == 0 )
833                 on_start_producer( context, name, atts );
834         else if ( strcmp( name, "blank" ) == 0 )
835                 on_start_blank( context, name, atts );
836         else if ( strcmp( name, "entry" ) == 0 || strcmp( name, "track" ) == 0 )
837                 on_start_entry_track( context, name, atts );
838         else if ( strcmp( name, "filter" ) == 0 )
839                 on_start_filter( context, name, atts );
840         else if ( strcmp( name, "transition" ) == 0 )
841                 on_start_transition( context, name, atts );
842         else if ( strcmp( name, "property" ) == 0 )
843                 on_start_property( context, name, atts );
844 }
845
846 static void on_end_element( void *ctx, const xmlChar *name )
847 {
848         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
849         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
850         
851         if ( context->is_value == 1 && strcmp( name, "property" ) != 0 )
852                 context_pop_node( context );
853         else if ( strcmp( name, "multitrack" ) == 0 )
854                 on_end_multitrack( context, name );
855         else if ( strcmp( name, "playlist" ) == 0 || strcmp( name, "seq" ) == 0 || strcmp( name, "smil" ) == 0 )
856                 on_end_playlist( context, name );
857         else if ( strcmp( name, "track" ) == 0 )
858                 on_end_track( context, name );
859         else if ( strcmp( name, "entry" ) == 0 )
860                 on_end_entry( context, name );
861         else if ( strcmp( name, "tractor" ) == 0 )
862                 on_end_tractor( context, name );
863         else if ( strcmp( name, "property" ) == 0 )
864                 on_end_property( context, name );
865         else if ( strcmp( name, "producer" ) == 0 || strcmp( name, "video" ) == 0 )
866                 on_end_producer( context, name );
867         else if ( strcmp( name, "filter" ) == 0 )
868                 on_end_filter( context, name );
869         else if ( strcmp( name, "transition" ) == 0 )
870                 on_end_transition( context, name );
871 }
872
873 static void on_characters( void *ctx, const xmlChar *ch, int len )
874 {
875         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
876         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
877         char *value = calloc( len + 1, 1 );
878
879         value[ len ] = 0;
880         strncpy( value, (const char*) ch, len );
881         
882         if ( context->stack_node_size > 0 )
883                 xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
884
885         else if ( context->property != NULL && context->producer_properties != NULL )
886                 mlt_properties_set( context->producer_properties, context->property, value );
887                 
888         free( value);
889 }
890
891 // The following 3 facilitate entity substitution in the SAX parser
892 static void on_internal_subset( void *ctx, const xmlChar* name,
893         const xmlChar* publicId, const xmlChar* systemId )
894 {
895         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
896         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
897         
898         xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
899 }
900
901 static void on_entity_declaration( void *ctx, const xmlChar* name, int type, 
902         const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
903 {
904         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
905         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
906         
907         xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
908 }
909
910 xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
911 {
912         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
913         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
914
915         return xmlGetDocEntity( context->entity_doc, name );
916 }
917
918
919 mlt_producer producer_westley_init( char *filename )
920 {
921         xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
922         struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
923         mlt_properties properties = NULL;
924         int i = 0;
925         struct _xmlParserCtxt *xmlcontext;
926         int well_formed = 0;
927         
928         context->producer_map = mlt_properties_new();
929         context->destructors = mlt_properties_new();
930
931         // We need to track the number of registered filters
932         mlt_properties_set_int( context->destructors, "registered", 0 );
933
934         // We need the directory prefix which was used for the westley
935         mlt_properties_set( context->producer_map, "_root", "" );
936         if ( strchr( filename, '/' ) )
937         {
938                 char *root = NULL;
939                 mlt_properties_set( context->producer_map, "_root", filename );
940                 root = mlt_properties_get( context->producer_map, "_root" );
941                 *( strrchr( root, '/' ) + 1 ) = '\0';
942         }
943
944         // Setup SAX callbacks
945         sax->startElement = on_start_element;
946         sax->endElement = on_end_element;
947         sax->characters = on_characters;
948         sax->cdataBlock = on_characters;
949         sax->internalSubset = on_internal_subset;
950         sax->entityDecl = on_entity_declaration;
951         sax->getEntity = on_get_entity;
952
953         // Setup libxml2 SAX parsing
954         xmlInitParser(); 
955         xmlSubstituteEntitiesDefault( 1 );
956         // This is used to facilitate entity substitution in the SAX parser
957         context->entity_doc = xmlNewDoc( "1.0" );
958         xmlcontext = xmlCreateFileParserCtxt( filename );
959         xmlcontext->sax = sax;
960         xmlcontext->_private = ( void* )context;
961         
962         // Parse
963         xmlParseDocument( xmlcontext );
964         well_formed = xmlcontext->wellFormed;
965         
966         // Cleanup after parsing
967         xmlFreeDoc( context->entity_doc );
968         free( sax );
969         xmlcontext->sax = NULL;
970         xmlcontext->_private = NULL;
971         xmlFreeParserCtxt( xmlcontext );
972         xmlMemoryDump( ); // for debugging
973
974         // Get the last producer on the stack
975         mlt_service service = context_pop_service( context );
976         if ( well_formed && service != NULL )
977         {
978                 // Verify it is a producer service (mlt_type="mlt_producer")
979                 // (producer, playlist, multitrack)
980                 char *type = mlt_properties_get( mlt_service_properties( service ), "mlt_type" );
981                 if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 ) )
982                         service = NULL;
983         }
984
985         if ( well_formed && service != NULL )
986         {
987                 // Need the complete producer list for various reasons
988                 properties = context->destructors;
989
990                 // Now make sure we don't have a reference to the service in the properties
991                 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
992                 {
993                         char *name = mlt_properties_get_name( properties, i );
994                         if ( mlt_properties_get_data( properties, name, NULL ) == service )
995                         {
996                                 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
997                                 break;
998                         }
999                 }
1000
1001                 // We are done referencing destructor property list
1002                 // Set this var to service properties for convenience
1003                 properties = mlt_service_properties( service );
1004         
1005                 // make the returned service destroy the connected services
1006                 mlt_properties_set_data( properties, "__destructors__", context->destructors, 0, (mlt_destructor) mlt_properties_close, NULL );
1007
1008                 // Now assign additional properties
1009                 mlt_properties_set( properties, "resource", filename );
1010
1011                 // This tells consumer_westley not to deep copy
1012                 mlt_properties_set( properties, "westley", "was here" );
1013         }
1014         else
1015         {
1016                 // Return null if not well formed
1017                 service = NULL;
1018                 
1019                 // Clean up
1020                 mlt_properties_close( context->destructors );
1021         }
1022
1023         free( context->stack_service );
1024         mlt_properties_close( context->producer_map );
1025         //free( context );
1026
1027         return MLT_PRODUCER( service );
1028 }