]> git.sesse.net Git - mlt/blob - src/modules/westley/producer_westley.c
touchup on the producer in/out applied to parent entry
[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         mlt_properties properties = mlt_service_properties( producer );
377
378         mlt_position in = mlt_properties_get_position( properties, "in" );
379         mlt_position out;
380
381         if ( mlt_properties_get( properties, "_westley.out" ) != NULL )
382                 out = mlt_properties_get_position( properties, "_westley.out" );
383         else
384                 out = mlt_properties_get_position( properties, "length" ) - 1;
385
386         if ( mlt_properties_get_position( properties, "length" ) < out )
387                 mlt_properties_set_position( properties, "length", out  + 1 );
388
389         mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
390         
391         // See if the playlist should be added to a playlist or multitrack
392         if ( add_producer( context, producer, in, out ) == 0 )
393                 
394                 // Otherwise, push the playlist back onto the stack
395                 context_push_service( context, producer );
396 }
397
398 static void on_end_track( deserialise_context context, const xmlChar *name )
399 {
400         // Get the producer from the stack
401         mlt_service producer = context_pop_service( context );
402         if ( producer == NULL )
403                 return;
404
405         // See if the producer is a tractor
406         char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
407         if ( resource && strcmp( resource, "<tractor>" ) == 0 )
408                 // If so chomp its producer
409                 context_pop_service( context );
410
411         // Get the dummy track service from the stack
412         mlt_service track = context_pop_service( context );
413         if ( track == NULL )
414         {
415                 context_push_service( context, producer );
416                 return;
417         }
418
419         // Get the multitrack from the stack
420         mlt_service service = context_pop_service( context );
421         if ( service == NULL )
422         {
423                 context_push_service( context, producer );
424                 return;
425         }
426
427         // Set the track on the multitrack
428         mlt_multitrack_connect( MLT_MULTITRACK( service ),
429                 MLT_PRODUCER( producer ),
430                 mlt_multitrack_count( MLT_MULTITRACK( service ) ) );
431
432         // Set producer i/o if specified
433         if ( mlt_properties_get( mlt_service_properties( track ), "in" ) != NULL ||
434                 mlt_properties_get( mlt_service_properties( track ), "out" ) != NULL )
435         {
436                 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ),
437                         mlt_properties_get_position( mlt_service_properties( track ), "in" ),
438                         mlt_properties_get_position( mlt_service_properties( track ), "out" ) );
439         }
440
441         // Push the multitrack back onto the stack
442         context_push_service( context, service );
443
444         mlt_service_close( track );
445 }
446
447 static void on_end_entry( deserialise_context context, const xmlChar *name )
448 {
449         // Get the producer from the stack
450         mlt_service producer = context_pop_service( context );
451         if ( producer == NULL )
452                 return;
453         
454         // See if the producer is a tractor
455         char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
456         if ( resource && strcmp( resource, "<tractor>" ) == 0 )
457                 // If so chomp its producer
458                 context_pop_service( context );
459
460         // Get the dummy entry service from the stack
461         mlt_service entry = context_pop_service( context );
462         if ( entry == NULL )
463         {
464                 context_push_service( context, producer );
465                 return;
466         }
467
468         // Get the playlist from the stack
469         mlt_service service = context_pop_service( context );
470         if ( service == NULL )
471         {
472                 context_push_service( context, producer );
473                 return;
474         }
475
476         // Append the producer to the playlist
477         if ( mlt_properties_get( mlt_service_properties( entry ), "in" ) != NULL ||
478                 mlt_properties_get( mlt_service_properties( entry ), "out" ) != NULL )
479         {
480                 mlt_playlist_append_io( MLT_PLAYLIST( service ),
481                         MLT_PRODUCER( producer ),
482                         mlt_properties_get_position( mlt_service_properties( entry ), "in" ), 
483                         mlt_properties_get_position( mlt_service_properties( entry ), "out" ) );
484         }
485         else
486         {
487                 mlt_playlist_append( MLT_PLAYLIST( service ), MLT_PRODUCER( producer ) );
488         }
489
490         // Push the playlist back onto the stack
491         context_push_service( context, service );
492
493         mlt_service_close( entry );
494 }
495
496 static void on_end_tractor( deserialise_context context, const xmlChar *name )
497 {
498         // Get the tractor
499         mlt_service tractor = context_pop_service( context );
500         if ( tractor == NULL )
501                 return;
502         
503         // Get the tractor's multitrack
504         mlt_producer multitrack = mlt_properties_get_data( mlt_service_properties( tractor ), "multitrack", NULL );
505         if ( multitrack != NULL )
506         {
507                 // Inherit the producer's properties
508                 mlt_properties properties = mlt_producer_properties( MLT_PRODUCER( tractor ) );
509                 mlt_properties_set_position( properties, "length", mlt_producer_get_out( multitrack ) + 1 );
510                 mlt_producer_set_in_and_out( multitrack, 0, mlt_producer_get_out( multitrack ) );
511                 mlt_properties_set_double( properties, "fps", mlt_producer_get_fps( multitrack ) );
512         }
513
514         // See if the tractor should be added to a playlist or multitrack
515         if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
516                 
517                 // Otherwise, push the tractor back onto the stack
518                 context_push_service( context, tractor );
519 }
520
521 static void on_end_property( deserialise_context context, const xmlChar *name )
522 {
523         // Tell parser to stop building a tree
524         context->is_value = 0;
525         
526         // See if there is a xml tree for the value
527         if ( context->property != NULL && context->value_doc != NULL )
528         {
529                 xmlChar *value;
530                 int size;
531                 
532                 // Serialise the tree to get value
533                 xmlDocDumpMemory( context->value_doc, &value, &size );
534                 mlt_properties_set( context->producer_properties, context->property, value );
535                 xmlFree( value );
536                 xmlFreeDoc( context->value_doc );
537                 context->value_doc = NULL;
538         }
539         
540         // Close this property handling
541         free( context->property );
542         context->property = NULL;
543 }
544
545 static void on_end_producer( deserialise_context context, const xmlChar *name )
546 {
547         mlt_properties properties = context->producer_properties;
548         mlt_service service = NULL;
549         
550         if ( properties == NULL )
551                 return;
552                 
553         char *resource = mlt_properties_get( properties, "resource" );
554         // Let Kino-SMIL src be a synonym for resource
555         if ( resource == NULL )
556                 resource = mlt_properties_get( properties, "src" );
557         
558         // Instantiate the producer
559         if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
560         {
561                 service = MLT_SERVICE( mlt_factory_producer( "fezzik", mlt_properties_get( properties, "mlt_service" ) ) );
562         }
563         if ( service == NULL && resource != NULL )
564         {
565                 char *root = mlt_properties_get( context->producer_map, "_root" );
566                 char *full_resource = malloc( strlen( root ) + strlen( resource ) + 1 );
567                 if ( resource[ 0 ] != '/' )
568                 {
569                         strcpy( full_resource, root );
570                         strcat( full_resource, resource );
571                 }
572                 else
573                 {
574                         strcpy( full_resource, resource );
575                 }
576                 service = MLT_SERVICE( mlt_factory_producer( "fezzik", full_resource ) );
577                 free( full_resource );
578         }
579         if ( service == NULL )
580                 return;
581         track_service( context->destructors, service, (mlt_destructor) mlt_producer_close );
582
583         // Add the producer to the producer map
584         if ( mlt_properties_get( properties, "id" ) != NULL )
585                 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
586
587         // Handle in/out properties separately
588         mlt_position in = -1;
589         mlt_position out = -1;
590         
591         // Get in
592         if ( mlt_properties_get( properties, "in" ) != NULL )
593                 in = mlt_properties_get_position( properties, "in" );
594         // Let Kino-SMIL clipBegin be a synonym for in
595         if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
596                 in = mlt_properties_get_position( properties, "clipBegin" );
597         // Get out
598         if ( mlt_properties_get( properties, "out" ) != NULL )
599                 out = mlt_properties_get_position( properties, "out" );
600         // Let Kino-SMIL clipEnd be a synonym for out
601         if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
602                 out = mlt_properties_get_position( properties, "clipEnd" );
603         
604         // Remove in and out
605         mlt_properties_set( properties, "in", NULL );
606         mlt_properties_set( properties, "out", NULL );
607         
608         mlt_properties_inherit( mlt_service_properties( service ), properties );
609         mlt_properties_close( properties );
610         context->producer_properties = NULL;
611
612         // See if the producer should be added to a playlist or multitrack
613         if ( add_producer( context, service, in, out ) == 0 )
614         {
615                 // Otherwise, set in and out on...
616                 if ( in != -1 || out != -1 )
617                 {
618                         // Get the parent service
619                         mlt_service parent = context_pop_service( context );
620                         if ( parent != NULL )
621                         {
622                                 // Get the parent properties
623                                 properties = mlt_service_properties( parent );
624                                 
625                                 char *resource = mlt_properties_get( properties, "resource" );
626                                 
627                                 // Put the parent producer back
628                                 context_push_service( context, parent );
629                                         
630                                 // If the parent is a track or entry
631                                 if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
632                                 {
633                                         mlt_properties_set_position( properties, "in", in );
634                                         mlt_properties_set_position( properties, "out", out );
635                                 }
636                                 else
637                                 {
638                                         // Otherwise, set in and out on producer directly
639                                         mlt_producer_set_in_and_out( MLT_PRODUCER( service ), in, out );
640                                 }
641                         }
642                         else
643                         {
644                                 // Otherwise, set in and out on producer directly
645                                 mlt_producer_set_in_and_out( MLT_PRODUCER( service ), in, out );
646                         }
647                 }
648         
649                 // Push the producer onto the stack
650                 context_push_service( context, service );
651         }
652 }
653
654 static void on_end_filter( deserialise_context context, const xmlChar *name )
655 {
656         mlt_properties properties = context->producer_properties;
657         if ( properties == NULL )
658                 return;
659
660         char *id;
661         char key[11];
662         key[ 10 ] = '\0';
663         mlt_service tractor = NULL;
664         
665         // Get the producer from the stack
666         mlt_service producer = context_pop_service( context );
667         if ( producer == NULL )
668                 return;
669         
670         // See if the producer is a tractor
671         char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
672         if ( resource != NULL && strcmp( resource, "<tractor>" ) == 0 )
673         {
674                 // If so, then get the next producer
675                 tractor = producer;
676                 producer = context_pop_service( context );
677         }
678         
679 //fprintf( stderr, "connecting filter to %s\n", mlt_properties_get( mlt_service_properties( producer ), "resource" ) );
680
681         // Create the filter
682         mlt_service service = MLT_SERVICE( mlt_factory_filter( mlt_properties_get( properties, "mlt_service" ), NULL ) );
683         if ( service == NULL )
684         {
685                 context_push_service( context, producer );
686                 return;
687         }
688         track_service( context->destructors, service, (mlt_destructor) mlt_filter_close );
689
690         // Connect the filter to the producer
691         mlt_filter_connect( MLT_FILTER( service ), producer,
692                 mlt_properties_get_int( properties, "track" ) );
693
694         // Set in and out from producer if non existant
695         if ( mlt_properties_get( properties, "in" ) == NULL )
696                 mlt_properties_set_position( properties, "in", mlt_producer_get_in( MLT_PRODUCER( producer ) ) );
697         if ( mlt_properties_get( properties, "out" ) == NULL )
698                 mlt_properties_set_position( properties, "out", mlt_producer_get_out( MLT_PRODUCER( producer ) ) );
699
700         // Propogate the properties
701         mlt_properties_inherit( mlt_service_properties( service ), properties );
702         mlt_properties_close( properties );
703         context->producer_properties = NULL;
704         properties = mlt_service_properties( service );
705
706         // Set in and out again due to inheritance
707         mlt_filter_set_in_and_out( MLT_FILTER( service ), 
708                 mlt_properties_get_position( properties, "in" ),
709                 mlt_properties_get_position( properties, "out" ) );
710
711         // If a producer alias is in the producer_map, get it
712         snprintf( key, 10, "%p", producer );
713         if ( mlt_properties_get_data( context->producer_map, key, NULL ) != NULL )
714                 producer = mlt_properties_get_data( context->producer_map, key, NULL );
715
716         // Put the producer in the producer map
717         id = mlt_properties_get( mlt_service_properties( producer ), "id" );
718         if ( id != NULL )
719                 mlt_properties_set_data( context->producer_map, id, service, 0, NULL, NULL );
720
721         // For filter chain support, add an alias to the producer map
722         snprintf( key, 10, "%p", service );
723         mlt_properties_set_data( context->producer_map, key, producer, 0, NULL, NULL );
724         
725         // Push the filter onto the stack
726         context_push_service( context, service );
727         
728         if ( tractor != NULL )
729         {
730                 // Connect the tractor to the filter
731                 mlt_tractor_connect( MLT_TRACTOR( tractor ), service );
732
733                 // Push the tractor back onto the stack
734                 context_push_service( context, tractor );
735         }
736 }
737
738 static void on_end_transition( deserialise_context context, const xmlChar *name )
739 {
740         mlt_service tractor = NULL;
741         mlt_properties properties = context->producer_properties;
742         if ( properties == NULL )
743                 return;
744
745         // Get the producer from the stack
746         mlt_service producer = context_pop_service( context );
747         if ( producer == NULL )
748                 return;
749
750         // See if the producer is a tractor
751         char *resource = mlt_properties_get( mlt_service_properties( producer ), "resource" );
752         if ( resource != NULL && strcmp( resource, "<tractor>" ) == 0 )
753         {
754                 // If so, then get the next producer
755                 tractor = producer;
756                 producer = context_pop_service( context );
757         }
758         
759         // Create the transition
760         mlt_service service = MLT_SERVICE( mlt_factory_transition( mlt_properties_get( properties, "mlt_service" ), NULL ) );
761         if ( service == NULL )
762         {
763                 context_push_service( context, producer );
764                 return;
765         }
766         track_service( context->destructors, service, (mlt_destructor) mlt_transition_close );
767
768         // Propogate the properties
769         mlt_properties_inherit( mlt_service_properties( service ), properties );
770         mlt_properties_close( properties );
771         context->producer_properties = NULL;
772         properties = mlt_service_properties( service );
773
774         // Set in and out again due to inheritance
775         mlt_transition_set_in_and_out( MLT_TRANSITION( service ),
776                 mlt_properties_get_position( properties, "in" ),
777                 mlt_properties_get_position( properties, "out" ) );
778
779         // Connect the transition to the producer
780         mlt_transition_connect( MLT_TRANSITION( service ), producer,
781                 mlt_properties_get_int( properties, "a_track" ),
782                 mlt_properties_get_int( properties, "b_track" ) );
783
784         // Push the transition onto the stack
785         context_push_service( context, service );
786         
787         if ( tractor != NULL )
788         {
789                 // Connect the tractor to the transition
790                 mlt_tractor_connect( MLT_TRACTOR( tractor ), service );
791
792                 // Push the tractor back onto the stack
793                 context_push_service( context, tractor );
794         }
795 }
796
797 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
798 {
799         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
800         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
801         
802         // Build a tree from nodes within a property value
803         if ( context->is_value == 1 )
804         {
805                 xmlNodePtr node = xmlNewNode( NULL, name );
806                 
807                 if ( context->value_doc == NULL )
808                 {
809                         // Start a new tree
810                         context->value_doc = xmlNewDoc( "1.0" );
811                         xmlDocSetRootElement( context->value_doc, node );
812                 }
813                 else
814                 {
815                         // Append child to tree
816                         xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
817                 }
818                 context_push_node( context, node );
819                 
820                 // Set the attributes
821                 for ( ; atts != NULL && *atts != NULL; atts += 2 )
822                         xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
823         }
824         else if ( strcmp( name, "tractor" ) == 0 )
825                 on_start_tractor( context, name, atts );
826         else if ( strcmp( name, "multitrack" ) == 0 )
827                 on_start_multitrack( context, name, atts );
828         else if ( strcmp( name, "playlist" ) == 0 || strcmp( name, "seq" ) == 0 || strcmp( name, "smil" ) == 0 )
829                 on_start_playlist( context, name, atts );
830         else if ( strcmp( name, "producer" ) == 0 || strcmp( name, "video" ) == 0 )
831                 on_start_producer( context, name, atts );
832         else if ( strcmp( name, "blank" ) == 0 )
833                 on_start_blank( context, name, atts );
834         else if ( strcmp( name, "entry" ) == 0 || strcmp( name, "track" ) == 0 )
835                 on_start_entry_track( context, name, atts );
836         else if ( strcmp( name, "filter" ) == 0 )
837                 on_start_filter( context, name, atts );
838         else if ( strcmp( name, "transition" ) == 0 )
839                 on_start_transition( context, name, atts );
840         else if ( strcmp( name, "property" ) == 0 )
841                 on_start_property( context, name, atts );
842 }
843
844 static void on_end_element( void *ctx, const xmlChar *name )
845 {
846         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
847         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
848         
849         if ( context->is_value == 1 && strcmp( name, "property" ) != 0 )
850                 context_pop_node( context );
851         else if ( strcmp( name, "multitrack" ) == 0 )
852                 on_end_multitrack( context, name );
853         else if ( strcmp( name, "playlist" ) == 0 || strcmp( name, "seq" ) == 0 || strcmp( name, "smil" ) == 0 )
854                 on_end_playlist( context, name );
855         else if ( strcmp( name, "track" ) == 0 )
856                 on_end_track( context, name );
857         else if ( strcmp( name, "entry" ) == 0 )
858                 on_end_entry( context, name );
859         else if ( strcmp( name, "tractor" ) == 0 )
860                 on_end_tractor( context, name );
861         else if ( strcmp( name, "property" ) == 0 )
862                 on_end_property( context, name );
863         else if ( strcmp( name, "producer" ) == 0 || strcmp( name, "video" ) == 0 )
864                 on_end_producer( context, name );
865         else if ( strcmp( name, "filter" ) == 0 )
866                 on_end_filter( context, name );
867         else if ( strcmp( name, "transition" ) == 0 )
868                 on_end_transition( context, name );
869 }
870
871 static void on_characters( void *ctx, const xmlChar *ch, int len )
872 {
873         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
874         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
875         char *value = calloc( len + 1, 1 );
876
877         value[ len ] = 0;
878         strncpy( value, (const char*) ch, len );
879         
880         if ( context->stack_node_size > 0 )
881                 xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
882
883         else if ( context->property != NULL && context->producer_properties != NULL )
884                 mlt_properties_set( context->producer_properties, context->property, value );
885                 
886         free( value);
887 }
888
889 // The following 3 facilitate entity substitution in the SAX parser
890 static void on_internal_subset( void *ctx, const xmlChar* name,
891         const xmlChar* publicId, const xmlChar* systemId )
892 {
893         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
894         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
895         
896         xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
897 }
898
899 static void on_entity_declaration( void *ctx, const xmlChar* name, int type, 
900         const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
901 {
902         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
903         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
904         
905         xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
906 }
907
908 xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
909 {
910         struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
911         deserialise_context context = ( deserialise_context )( xmlcontext->_private );
912
913         return xmlGetDocEntity( context->entity_doc, name );
914 }
915
916
917 mlt_producer producer_westley_init( char *filename )
918 {
919         xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
920         struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
921         mlt_properties properties = NULL;
922         int i = 0;
923         struct _xmlParserCtxt *xmlcontext;
924         int well_formed = 0;
925         
926         context->producer_map = mlt_properties_new();
927         context->destructors = mlt_properties_new();
928
929         // We need to track the number of registered filters
930         mlt_properties_set_int( context->destructors, "registered", 0 );
931
932         // We need the directory prefix which was used for the westley
933         mlt_properties_set( context->producer_map, "_root", "" );
934         if ( strchr( filename, '/' ) )
935         {
936                 char *root = NULL;
937                 mlt_properties_set( context->producer_map, "_root", filename );
938                 root = mlt_properties_get( context->producer_map, "_root" );
939                 *( strrchr( root, '/' ) + 1 ) = '\0';
940         }
941
942         // Setup SAX callbacks
943         sax->startElement = on_start_element;
944         sax->endElement = on_end_element;
945         sax->characters = on_characters;
946         sax->cdataBlock = on_characters;
947         sax->internalSubset = on_internal_subset;
948         sax->entityDecl = on_entity_declaration;
949         sax->getEntity = on_get_entity;
950
951         // Setup libxml2 SAX parsing
952         xmlInitParser(); 
953         xmlSubstituteEntitiesDefault( 1 );
954         // This is used to facilitate entity substitution in the SAX parser
955         context->entity_doc = xmlNewDoc( "1.0" );
956         xmlcontext = xmlCreateFileParserCtxt( filename );
957         xmlcontext->sax = sax;
958         xmlcontext->_private = ( void* )context;
959         
960         // Parse
961         xmlParseDocument( xmlcontext );
962         well_formed = xmlcontext->wellFormed;
963         
964         // Cleanup after parsing
965         xmlFreeDoc( context->entity_doc );
966         free( sax );
967         xmlcontext->sax = NULL;
968         xmlcontext->_private = NULL;
969         xmlFreeParserCtxt( xmlcontext );
970         xmlMemoryDump( ); // for debugging
971
972         // Get the last producer on the stack
973         mlt_service service = context_pop_service( context );
974         if ( well_formed && service != NULL )
975         {
976                 // Verify it is a producer service (mlt_type="mlt_producer")
977                 // (producer, playlist, multitrack)
978                 char *type = mlt_properties_get( mlt_service_properties( service ), "mlt_type" );
979                 if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 ) )
980                         service = NULL;
981         }
982
983         if ( well_formed && service != NULL )
984         {
985                 // Need the complete producer list for various reasons
986                 properties = context->destructors;
987
988                 // Now make sure we don't have a reference to the service in the properties
989                 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
990                 {
991                         char *name = mlt_properties_get_name( properties, i );
992                         if ( mlt_properties_get_data( properties, name, NULL ) == service )
993                         {
994                                 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
995                                 break;
996                         }
997                 }
998
999                 // We are done referencing destructor property list
1000                 // Set this var to service properties for convenience
1001                 properties = mlt_service_properties( service );
1002         
1003                 // make the returned service destroy the connected services
1004                 mlt_properties_set_data( properties, "__destructors__", context->destructors, 0, (mlt_destructor) mlt_properties_close, NULL );
1005
1006                 // Now assign additional properties
1007                 mlt_properties_set( properties, "resource", filename );
1008
1009                 // This tells consumer_westley not to deep copy
1010                 mlt_properties_set( properties, "westley", "was here" );
1011         }
1012         else
1013         {
1014                 // Return null if not well formed
1015                 service = NULL;
1016                 
1017                 // Clean up
1018                 mlt_properties_close( context->destructors );
1019         }
1020
1021         free( context->stack_service );
1022         mlt_properties_close( context->producer_map );
1023         //free( context );
1024
1025         return MLT_PRODUCER( service );
1026 }