2 * producer_xml.c -- a libxml2 parser of mlt service networks
3 * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
4 * Author: Dan Dennedy <dan@dennedy.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 // TODO: destroy unreferenced producers (they are currently destroyed
22 // when the returned producer is closed).
24 #include <framework/mlt.h>
31 #include <libxml/parser.h>
32 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
33 #include <libxml/tree.h>
35 #define STACK_SIZE 1000
36 #define BRANCH_SIG_LEN 4000
38 #define _x (const xmlChar*)
39 #define _s (const char*)
43 extern xmlDocPtr xml_make_doc( mlt_service service );
60 mlt_dummy_filter_type,
61 mlt_dummy_transition_type,
62 mlt_dummy_producer_type,
63 mlt_dummy_consumer_type
66 struct deserialise_context_s
68 enum service_type stack_types[ STACK_SIZE ];
69 mlt_service stack_service[ STACK_SIZE ];
70 int stack_service_size;
71 mlt_properties producer_map;
72 mlt_properties destructors;
76 xmlNodePtr stack_node[ STACK_SIZE ];
79 int entity_is_replace;
81 int branch[ STACK_SIZE ];
82 const xmlChar *publicId;
83 const xmlChar *systemId;
84 mlt_properties params;
88 mlt_consumer consumer;
90 typedef struct deserialise_context_s *deserialise_context;
92 /** Convert the numerical current branch address to a dot-delimited string.
94 static char *serialise_branch( deserialise_context this, char *s )
99 for ( i = 0; i < this->depth; i++ )
101 int len = strlen( s );
102 snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", this->branch[ i ] );
110 static int context_push_service( deserialise_context this, mlt_service that, enum service_type type )
112 int ret = this->stack_service_size >= STACK_SIZE - 1;
115 this->stack_service[ this->stack_service_size ] = that;
116 this->stack_types[ this->stack_service_size++ ] = type;
118 // Record the tree branch on which this service lives
119 if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_xml_branch" ) == NULL )
121 char s[ BRANCH_SIG_LEN ];
122 mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( this, s ) );
131 static mlt_service context_pop_service( deserialise_context this, enum service_type *type )
133 mlt_service result = NULL;
135 *type = invalid_type;
136 if ( this->stack_service_size > 0 )
138 result = this->stack_service[ -- this->stack_service_size ];
140 *type = this->stack_types[ this->stack_service_size ];
148 static int context_push_node( deserialise_context this, xmlNodePtr node )
150 int ret = this->stack_node_size >= STACK_SIZE - 1;
152 this->stack_node[ this->stack_node_size ++ ] = node;
159 static xmlNodePtr context_pop_node( deserialise_context this )
161 xmlNodePtr result = NULL;
162 if ( this->stack_node_size > 0 )
163 result = this->stack_node[ -- this->stack_node_size ];
168 // Set the destructor on a new service
169 static void track_service( mlt_properties properties, void *service, mlt_destructor destructor )
171 int registered = mlt_properties_get_int( properties, "registered" );
172 char *key = mlt_properties_get( properties, "registered" );
173 mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
174 mlt_properties_set_int( properties, "registered", ++ registered );
178 // Prepend the property value with the document root
179 static inline void qualify_property( deserialise_context context, mlt_properties properties, const char *name )
181 char *resource = mlt_properties_get( properties, name );
182 if ( resource != NULL && resource[0] )
184 // Qualify file name properties
185 char *root = mlt_properties_get( context->producer_map, "root" );
186 if ( root != NULL && strcmp( root, "" ) )
188 char *full_resource = malloc( strlen( root ) + strlen( resource ) + 2 );
189 if ( resource[ 0 ] != '/' && strchr( resource, ':' ) == NULL )
191 strcpy( full_resource, root );
192 strcat( full_resource, "/" );
193 strcat( full_resource, resource );
197 strcpy( full_resource, resource );
199 mlt_properties_set( properties, name, full_resource );
200 free( full_resource );
206 /** This function adds a producer to a playlist or multitrack when
207 there is no entry or track element.
210 static int add_producer( deserialise_context context, mlt_service service, mlt_position in, mlt_position out )
212 // Return value (0 = service remains top of stack, 1 means it can be removed)
215 // Get the parent producer
216 enum service_type type = mlt_invalid_type;
217 mlt_service container = context_pop_service( context, &type );
220 if ( service != NULL && container != NULL )
222 char *container_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( container ), "_xml_branch" );
223 char *service_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_xml_branch" );
224 contained = !strncmp( container_branch, service_branch, strlen( container_branch ) );
229 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
230 char *hide_s = mlt_properties_get( properties, "hide" );
232 // Indicate that this service is no longer top of stack
237 case mlt_tractor_type:
239 mlt_multitrack multitrack = mlt_tractor_multitrack( MLT_TRACTOR( container ) );
240 mlt_multitrack_connect( multitrack, MLT_PRODUCER( service ), mlt_multitrack_count( multitrack ) );
243 case mlt_multitrack_type:
245 mlt_multitrack_connect( MLT_MULTITRACK( container ),
246 MLT_PRODUCER( service ),
247 mlt_multitrack_count( MLT_MULTITRACK( container ) ) );
250 case mlt_playlist_type:
252 mlt_playlist_append_io( MLT_PLAYLIST( container ), MLT_PRODUCER( service ), in, out );
257 fprintf( stderr, "Producer defined inside something that isn't a container\n" );
261 // Set the hide state of the track producer
262 if ( hide_s != NULL )
264 if ( strcmp( hide_s, "video" ) == 0 )
265 mlt_properties_set_int( properties, "hide", 1 );
266 else if ( strcmp( hide_s, "audio" ) == 0 )
267 mlt_properties_set_int( properties, "hide", 2 );
268 else if ( strcmp( hide_s, "both" ) == 0 )
269 mlt_properties_set_int( properties, "hide", 3 );
273 // Put the parent producer back
274 if ( container != NULL )
275 context_push_service( context, container, type );
280 /** Attach filters defined on that to this.
283 static void attach_filters( mlt_service this, mlt_service that )
288 mlt_filter filter = NULL;
289 for ( i = 0; ( filter = mlt_service_filter( that, i ) ) != NULL; i ++ )
291 mlt_service_attach( this, filter );
292 attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) );
297 static void on_start_profile( deserialise_context context, const xmlChar *name, const xmlChar **atts)
299 mlt_profile p = context->profile;
300 for ( ; atts != NULL && *atts != NULL; atts += 2 )
302 if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 || xmlStrcmp( atts[ 0 ], _x("profile") ) == 0 )
304 mlt_profile my_profile = mlt_profile_init( _s(atts[ 1 ]) );
307 p->description = strdup( my_profile->description );
308 p->display_aspect_den = my_profile->display_aspect_den;
309 p->display_aspect_num = my_profile->display_aspect_num;
310 p->frame_rate_den = my_profile->frame_rate_den;
311 p->frame_rate_num = my_profile->frame_rate_num;
312 p->width = my_profile->width;
313 p->height = my_profile->height;
314 p->progressive = my_profile->progressive;
315 p->sample_aspect_den = my_profile->sample_aspect_den;
316 p->sample_aspect_num = my_profile->sample_aspect_num;
317 p->colorspace = my_profile->colorspace;
319 mlt_profile_close( my_profile );
322 else if ( xmlStrcmp( atts[ 0 ], _x("description") ) == 0 )
324 if ( p->description )
325 free( p->description );
326 p->description = strdup( _s(atts[ 1 ]) );
329 else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_den") ) == 0 )
330 p->display_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 );
331 else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_num") ) == 0 )
332 p->display_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 );
333 else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_num") ) == 0 )
334 p->sample_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 );
335 else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_den") ) == 0 )
336 p->sample_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 );
337 else if ( xmlStrcmp( atts[ 0 ], _x("width") ) == 0 )
338 p->width = strtol( _s(atts[ 1 ]), NULL, 0 );
339 else if ( xmlStrcmp( atts[ 0 ], _x("height") ) == 0 )
340 p->height = strtol( _s(atts[ 1 ]), NULL, 0 );
341 else if ( xmlStrcmp( atts[ 0 ], _x("progressive") ) == 0 )
342 p->progressive = strtol( _s(atts[ 1 ]), NULL, 0 );
343 else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_num") ) == 0 )
344 p->frame_rate_num = strtol( _s(atts[ 1 ]), NULL, 0 );
345 else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_den") ) == 0 )
346 p->frame_rate_den = strtol( _s(atts[ 1 ]), NULL, 0 );
347 else if ( xmlStrcmp( atts[ 0 ], _x("colorspace") ) == 0 )
348 p->colorspace = strtol( _s(atts[ 1 ]), NULL, 0 );
352 static void on_start_tractor( deserialise_context context, const xmlChar *name, const xmlChar **atts)
354 mlt_tractor tractor = mlt_tractor_new( );
355 mlt_service service = MLT_TRACTOR_SERVICE( tractor );
356 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
358 track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
359 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( service ), context->lc_numeric );
361 for ( ; atts != NULL && *atts != NULL; atts += 2 )
362 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
364 mlt_properties_set_int( MLT_TRACTOR_PROPERTIES( tractor ), "global_feed", 1 );
366 if ( mlt_properties_get( properties, "id" ) != NULL )
367 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
369 context_push_service( context, service, mlt_tractor_type );
372 static void on_end_tractor( deserialise_context context, const xmlChar *name )
375 enum service_type type;
376 mlt_service tractor = context_pop_service( context, &type );
378 if ( tractor != NULL && type == mlt_tractor_type )
380 // See if the tractor should be added to a playlist or multitrack
381 if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
382 context_push_service( context, tractor, type );
386 fprintf( stderr, "Invalid state for tractor\n" );
390 static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
392 enum service_type type;
393 mlt_service parent = context_pop_service( context, &type );
395 // If we don't have a parent, then create one now, providing we're in a state where we can
396 if ( parent == NULL || ( type == mlt_playlist_type || type == mlt_multitrack_type ) )
398 mlt_tractor tractor = NULL;
399 // Push the parent back
400 if ( parent != NULL )
401 context_push_service( context, parent, type );
403 // Create a tractor to contain the multitrack
404 tractor = mlt_tractor_new( );
405 parent = MLT_TRACTOR_SERVICE( tractor );
406 track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close );
407 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( parent ), context->lc_numeric );
408 type = mlt_tractor_type;
410 // Flag it as a synthesised tractor for clean up later
411 mlt_properties_set_int( MLT_SERVICE_PROPERTIES( parent ), "loader_synth", 1 );
414 if ( type == mlt_tractor_type )
416 mlt_service service = MLT_SERVICE( mlt_tractor_multitrack( MLT_TRACTOR( parent ) ) );
417 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
418 for ( ; atts != NULL && *atts != NULL; atts += 2 )
419 mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
421 if ( mlt_properties_get( properties, "id" ) != NULL )
422 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties,"id" ), service, 0, NULL, NULL );
424 context_push_service( context, parent, type );
425 context_push_service( context, service, mlt_multitrack_type );
429 fprintf( stderr, "Invalid multitrack position\n" );
433 static void on_end_multitrack( deserialise_context context, const xmlChar *name )
435 // Get the multitrack from the stack
436 enum service_type type;
437 mlt_service service = context_pop_service( context, &type );
439 if ( service == NULL || type != mlt_multitrack_type )
440 fprintf( stderr, "End multitrack in the wrong state...\n" );
443 static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
445 mlt_playlist playlist = mlt_playlist_init( );
446 mlt_service service = MLT_PLAYLIST_SERVICE( playlist );
447 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
449 track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
451 for ( ; atts != NULL && *atts != NULL; atts += 2 )
453 mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
455 // Out will be overwritten later as we append, so we need to save it
456 if ( xmlStrcmp( atts[ 0 ], _x("out") ) == 0 )
457 mlt_properties_set( properties, "_xml.out", ( const char* )atts[ 1 ] );
460 if ( mlt_properties_get( properties, "id" ) != NULL )
461 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
463 context_push_service( context, service, mlt_playlist_type );
466 static void on_end_playlist( deserialise_context context, const xmlChar *name )
468 // Get the playlist from the stack
469 enum service_type type;
470 mlt_service service = context_pop_service( context, &type );
472 if ( service != NULL && type == mlt_playlist_type )
474 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
475 mlt_position in = -1;
476 mlt_position out = -1;
478 if ( mlt_properties_get( properties, "in" ) )
479 in = mlt_properties_get_position( properties, "in" );
480 if ( mlt_properties_get( properties, "out" ) )
481 out = mlt_properties_get_position( properties, "out" );
483 // See if the playlist should be added to a playlist or multitrack
484 if ( add_producer( context, service, in, out ) == 0 )
485 context_push_service( context, service, type );
489 fprintf( stderr, "Invalid state of playlist end %d\n", type );
493 static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
495 // use a dummy service to hold properties to allow arbitrary nesting
496 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
497 mlt_service_init( service, NULL );
499 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
501 context_push_service( context, service, mlt_dummy_producer_type );
503 for ( ; atts != NULL && *atts != NULL; atts += 2 )
504 mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
507 // Parse a SMIL clock value (as produced by Kino 0.9.1) and return position in frames
508 static mlt_position parse_clock_value( char *value, double fps )
510 // This implementation expects a fully specified clock value - no optional
512 char *pos, *copy = strdup( value );
514 mlt_position result = -1;
517 pos = strchr( value, ':' );
524 pos = strchr( value, ':' );
531 pos = strchr( value, '.' );
540 result = ( fps * ( ( (hh * 3600) + (mm * 60) + ss ) * 1000 + ms ) / 1000 + 0.5 );
545 static void on_end_producer( deserialise_context context, const xmlChar *name )
547 enum service_type type;
548 mlt_service service = context_pop_service( context, &type );
549 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
551 if ( service != NULL && type == mlt_dummy_producer_type )
553 mlt_service producer = NULL;
555 qualify_property( context, properties, "resource" );
556 char *resource = mlt_properties_get( properties, "resource" );
558 // Let Kino-SMIL src be a synonym for resource
559 if ( resource == NULL )
561 qualify_property( context, properties, "src" );
562 resource = mlt_properties_get( properties, "src" );
565 // Instantiate the producer
566 if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
568 char *service_name = mlt_properties_get( properties, "mlt_service" );
571 char *temp = calloc( 1, strlen( service_name ) + strlen( resource ) + 2 );
572 strcat( temp, service_name );
574 strcat( temp, resource );
575 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, temp ) );
580 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, service_name ) );
584 // Just in case the plugin requested doesn't exist...
585 if ( producer == NULL && resource != NULL )
586 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) );
588 if ( producer == NULL )
589 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "+INVALID.txt" ) );
591 if ( producer == NULL )
592 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "colour:red" ) );
594 // Track this producer
595 track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
596 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( producer ), context->lc_numeric );
598 // Propogate the properties
599 qualify_property( context, properties, "resource" );
600 qualify_property( context, properties, "luma" );
601 qualify_property( context, properties, "luma.resource" );
602 qualify_property( context, properties, "composite.luma" );
603 qualify_property( context, properties, "producer.resource" );
605 // Handle in/out properties separately
606 mlt_position in = -1;
607 mlt_position out = -1;
610 if ( mlt_properties_get( properties, "in" ) != NULL )
611 in = mlt_properties_get_position( properties, "in" );
612 // Let Kino-SMIL clipBegin be a synonym for in
613 if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
615 if ( strchr( mlt_properties_get( properties, "clipBegin" ), ':' ) )
617 in = parse_clock_value( mlt_properties_get( properties, "clipBegin" ),
618 mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
620 // Parse frames value
621 in = mlt_properties_get_position( properties, "clipBegin" );
624 if ( mlt_properties_get( properties, "out" ) != NULL )
625 out = mlt_properties_get_position( properties, "out" );
626 // Let Kino-SMIL clipEnd be a synonym for out
627 if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
629 if ( strchr( mlt_properties_get( properties, "clipEnd" ), ':' ) )
631 out = parse_clock_value( mlt_properties_get( properties, "clipEnd" ),
632 mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
634 // Parse frames value
635 out = mlt_properties_get_position( properties, "clipEnd" );
638 mlt_properties_set( properties, "in", NULL );
639 mlt_properties_set( properties, "out", NULL );
641 // Inherit the properties
642 mlt_properties_inherit( MLT_SERVICE_PROPERTIES( producer ), properties );
644 // Attach all filters from service onto producer
645 attach_filters( producer, service );
647 // Add the producer to the producer map
648 if ( mlt_properties_get( properties, "id" ) != NULL )
649 mlt_properties_set_data( context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL );
651 // See if the producer should be added to a playlist or multitrack
652 if ( add_producer( context, producer, in, out ) == 0 )
654 // Otherwise, set in and out on...
655 if ( in != -1 || out != -1 )
657 // Get the parent service
658 enum service_type type;
659 mlt_service parent = context_pop_service( context, &type );
660 if ( parent != NULL )
662 // Get the parent properties
663 properties = MLT_SERVICE_PROPERTIES( parent );
665 char *resource = mlt_properties_get( properties, "resource" );
667 // Put the parent producer back
668 context_push_service( context, parent, type );
670 // If the parent is a track or entry
671 if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
673 if ( in > -1 ) mlt_properties_set_position( properties, "in", in );
674 if ( out > -1 ) mlt_properties_set_position( properties, "out", out );
678 // Otherwise, set in and out on producer directly
679 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
684 // Otherwise, set in and out on producer directly
685 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
689 // Push the producer onto the stack
690 context_push_service( context, producer, mlt_producer_type );
693 mlt_service_close( service );
697 static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
699 // Get the playlist from the stack
700 enum service_type type;
701 mlt_service service = context_pop_service( context, &type );
702 mlt_position length = 0;
704 if ( type == mlt_playlist_type && service != NULL )
706 // Look for the length attribute
707 for ( ; atts != NULL && *atts != NULL; atts += 2 )
709 if ( xmlStrcmp( atts[0], _x("length") ) == 0 )
711 length = atoll( _s(atts[1]) );
716 // Append a blank to the playlist
717 mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
719 // Push the playlist back onto the stack
720 context_push_service( context, service, type );
724 fprintf( stderr, "blank without a playlist - a definite no no\n" );
728 static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
730 mlt_producer entry = NULL;
731 mlt_properties temp = mlt_properties_new( );
733 for ( ; atts != NULL && *atts != NULL; atts += 2 )
735 mlt_properties_set( temp, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
737 // Look for the producer attribute
738 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
740 mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
741 if ( producer != NULL )
742 mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
746 // If we have a valid entry
747 if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
749 mlt_playlist_clip_info info;
750 enum service_type parent_type = invalid_type;
751 mlt_service parent = context_pop_service( context, &parent_type );
752 mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
754 if ( parent_type == mlt_playlist_type )
756 // Append the producer to the playlist
757 mlt_position in = -1;
758 mlt_position out = -1;
759 if ( mlt_properties_get( temp, "in" ) )
760 in = mlt_properties_get_position( temp, "in" );
761 if ( mlt_properties_get( temp, "out" ) )
762 out = mlt_properties_get_position( temp, "out" );
763 mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer, in, out );
765 // Handle the repeat property
766 if ( mlt_properties_get_int( temp, "repeat" ) > 0 )
768 mlt_playlist_repeat_clip( MLT_PLAYLIST( parent ),
769 mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1,
770 mlt_properties_get_int( temp, "repeat" ) );
773 mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
778 fprintf( stderr, "Entry not part of a playlist...\n" );
781 context_push_service( context, parent, parent_type );
784 // Push the cut onto the stack
785 context_push_service( context, MLT_PRODUCER_SERVICE( entry ), mlt_entry_type );
787 mlt_properties_close( temp );
790 static void on_end_entry( deserialise_context context, const xmlChar *name )
792 // Get the entry from the stack
793 enum service_type entry_type = invalid_type;
794 mlt_service entry = context_pop_service( context, &entry_type );
796 if ( entry == NULL && entry_type != mlt_entry_type )
798 fprintf( stderr, "Invalid state at end of entry\n" );
802 static void on_start_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
804 // use a dummy service to hold properties to allow arbitrary nesting
805 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
806 mlt_service_init( service, NULL );
808 // Push the dummy service onto the stack
809 context_push_service( context, service, mlt_entry_type );
811 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "resource", "<track>" );
813 for ( ; atts != NULL && *atts != NULL; atts += 2 )
815 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
817 // Look for the producer attribute
818 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
820 mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
821 if ( producer != NULL )
822 mlt_properties_set_data( MLT_SERVICE_PROPERTIES( service ), "producer", producer, 0, NULL, NULL );
827 static void on_end_track( deserialise_context context, const xmlChar *name )
829 // Get the track from the stack
830 enum service_type track_type;
831 mlt_service track = context_pop_service( context, &track_type );
833 if ( track != NULL && track_type == mlt_entry_type )
835 mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
836 enum service_type parent_type = invalid_type;
837 mlt_service parent = context_pop_service( context, &parent_type );
838 mlt_multitrack multitrack = NULL;
840 mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
841 mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
843 if ( parent_type == mlt_tractor_type )
844 multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
845 else if ( parent_type == mlt_multitrack_type )
846 multitrack = MLT_MULTITRACK( parent );
848 fprintf( stderr, "track contained in an invalid container\n" );
850 if ( multitrack != NULL )
852 // Set producer i/o if specified
853 if ( mlt_properties_get( track_props, "in" ) != NULL ||
854 mlt_properties_get( track_props, "out" ) != NULL )
856 mlt_position in = -1;
857 mlt_position out = -1;
858 if ( mlt_properties_get( track_props, "in" ) )
859 in = mlt_properties_get_position( track_props, "in" );
860 if ( mlt_properties_get( track_props, "out" ) )
861 out = mlt_properties_get_position( track_props, "out" );
862 mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ), in, out );
863 mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
864 mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( cut ), track_props );
865 track_props = MLT_PRODUCER_PROPERTIES( cut );
866 mlt_producer_close( cut );
870 mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
873 // Set the hide state of the track producer
874 char *hide_s = mlt_properties_get( track_props, "hide" );
875 if ( hide_s != NULL )
877 if ( strcmp( hide_s, "video" ) == 0 )
878 mlt_properties_set_int( producer_props, "hide", 1 );
879 else if ( strcmp( hide_s, "audio" ) == 0 )
880 mlt_properties_set_int( producer_props, "hide", 2 );
881 else if ( strcmp( hide_s, "both" ) == 0 )
882 mlt_properties_set_int( producer_props, "hide", 3 );
886 if ( parent != NULL )
887 context_push_service( context, parent, parent_type );
889 mlt_service_close( track );
893 fprintf( stderr, "Invalid state at end of track\n" );
897 static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
899 // use a dummy service to hold properties to allow arbitrary nesting
900 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
901 mlt_service_init( service, NULL );
903 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
905 context_push_service( context, service, mlt_dummy_filter_type );
907 // Set the properties
908 for ( ; atts != NULL && *atts != NULL; atts += 2 )
909 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
912 static void on_end_filter( deserialise_context context, const xmlChar *name )
914 enum service_type type;
915 mlt_service service = context_pop_service( context, &type );
916 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
918 enum service_type parent_type = invalid_type;
919 mlt_service parent = context_pop_service( context, &parent_type );
921 if ( service != NULL && type == mlt_dummy_filter_type )
923 mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, mlt_properties_get( properties, "mlt_service" ), NULL ) );
924 mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
926 track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
927 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( filter ), context->lc_numeric );
929 // Propogate the properties
930 qualify_property( context, properties, "resource" );
931 qualify_property( context, properties, "luma" );
932 qualify_property( context, properties, "luma.resource" );
933 qualify_property( context, properties, "composite.luma" );
934 qualify_property( context, properties, "producer.resource" );
935 mlt_properties_inherit( filter_props, properties );
937 // Attach all filters from service onto filter
938 attach_filters( filter, service );
940 // Associate the filter with the parent
941 if ( parent != NULL )
943 if ( parent_type == mlt_tractor_type )
945 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
946 mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
947 mlt_filter_set_in_and_out( MLT_FILTER( filter ),
948 mlt_properties_get_int( properties, "in" ),
949 mlt_properties_get_int( properties, "out" ) );
953 mlt_service_attach( parent, MLT_FILTER( filter ) );
956 // Put the parent back on the stack
957 context_push_service( context, parent, parent_type );
961 fprintf( stderr, "filter closed with invalid parent...\n" );
964 // Close the dummy filter service
965 mlt_service_close( service );
969 fprintf( stderr, "Invalid top of stack on filter close\n" );
973 static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
975 // use a dummy service to hold properties to allow arbitrary nesting
976 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
977 mlt_service_init( service, NULL );
979 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
981 context_push_service( context, service, mlt_dummy_transition_type );
983 // Set the properties
984 for ( ; atts != NULL && *atts != NULL; atts += 2 )
985 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
988 static void on_end_transition( deserialise_context context, const xmlChar *name )
990 enum service_type type;
991 mlt_service service = context_pop_service( context, &type );
992 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
994 enum service_type parent_type = invalid_type;
995 mlt_service parent = context_pop_service( context, &parent_type );
997 if ( service != NULL && type == mlt_dummy_transition_type )
999 char *id = mlt_properties_get( properties, "mlt_service" );
1000 mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) );
1001 mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
1003 track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
1004 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( effect ), context->lc_numeric );
1006 // Propogate the properties
1007 qualify_property( context, properties, "resource" );
1008 qualify_property( context, properties, "luma" );
1009 qualify_property( context, properties, "luma.resource" );
1010 qualify_property( context, properties, "composite.luma" );
1011 qualify_property( context, properties, "producer.resource" );
1012 mlt_properties_inherit( effect_props, properties );
1014 // Attach all filters from service onto effect
1015 attach_filters( effect, service );
1017 // Associate the filter with the parent
1018 if ( parent != NULL )
1020 if ( parent_type == mlt_tractor_type )
1022 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
1023 if ( mlt_properties_get_int( properties, "a_track" ) == mlt_properties_get_int( properties, "b_track" ) )
1024 mlt_properties_set_int( properties, "b_track", mlt_properties_get_int( properties, "a_track" ) + 1 );
1025 mlt_field_plant_transition( field, MLT_TRANSITION( effect ),
1026 mlt_properties_get_int( properties, "a_track" ),
1027 mlt_properties_get_int( properties, "b_track" ) );
1028 mlt_transition_set_in_and_out( MLT_TRANSITION( effect ),
1029 mlt_properties_get_int( properties, "in" ),
1030 mlt_properties_get_int( properties, "out" ) );
1034 fprintf( stderr, "Misplaced transition - ignoring\n" );
1037 // Put the parent back on the stack
1038 context_push_service( context, parent, parent_type );
1042 fprintf( stderr, "transition closed with invalid parent...\n" );
1045 // Close the dummy filter service
1046 mlt_service_close( service );
1050 fprintf( stderr, "Invalid top of stack on transition close\n" );
1054 static void on_start_consumer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1056 if ( context->pass == 1 )
1058 mlt_consumer consumer = mlt_consumer_new( context->profile );
1059 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
1061 mlt_properties_set_lcnumeric( properties, context->lc_numeric );
1062 context_push_service( context, MLT_CONSUMER_SERVICE(consumer), mlt_dummy_consumer_type );
1064 // Set the properties from attributes
1065 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1066 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
1070 static void on_end_consumer( deserialise_context context, const xmlChar *name )
1072 if ( context->pass == 1 )
1074 // Get the consumer from the stack
1075 enum service_type type;
1076 mlt_service service = context_pop_service( context, &type );
1078 if ( service && type == mlt_dummy_consumer_type )
1080 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1081 qualify_property( context, properties, "resource" );
1082 char *resource = mlt_properties_get( properties, "resource" );
1084 // Instantiate the consumer
1085 context->consumer = mlt_factory_consumer( context->profile, mlt_properties_get( properties, "mlt_service" ), resource );
1086 if ( context->consumer )
1088 // Track this consumer
1089 track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
1090 mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
1092 // Propogate the properties
1093 qualify_property( context, properties, "target" );
1095 // Inherit the properties
1096 mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties );
1099 mlt_service_close( service );
1104 static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1106 enum service_type type;
1107 mlt_service service = context_pop_service( context, &type );
1108 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1109 const char *value = NULL;
1111 if ( service != NULL )
1113 // Set the properties
1114 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1116 if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 )
1117 context->property = strdup( _s(atts[ 1 ]) );
1118 else if ( xmlStrcmp( atts[ 0 ], _x("value") ) == 0 )
1119 value = _s(atts[ 1 ]);
1122 if ( context->property != NULL )
1123 mlt_properties_set( properties, context->property, value == NULL ? "" : value );
1125 // Tell parser to collect any further nodes for serialisation
1126 context->is_value = 1;
1128 context_push_service( context, service, type );
1132 fprintf( stderr, "Property without a service '%s'?\n", ( const char * )name );
1136 static void on_end_property( deserialise_context context, const xmlChar *name )
1138 enum service_type type;
1139 mlt_service service = context_pop_service( context, &type );
1140 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1142 if ( service != NULL )
1144 // Tell parser to stop building a tree
1145 context->is_value = 0;
1147 // See if there is a xml tree for the value
1148 if ( context->property != NULL && context->value_doc != NULL )
1153 // Serialise the tree to get value
1154 xmlDocDumpMemory( context->value_doc, &value, &size );
1155 mlt_properties_set( properties, context->property, _s(value) );
1157 xmlFreeFunc xmlFree = NULL;
1158 xmlMemGet( &xmlFree, NULL, NULL, NULL);
1161 xmlFreeDoc( context->value_doc );
1162 context->value_doc = NULL;
1165 // Close this property handling
1166 free( context->property );
1167 context->property = NULL;
1169 context_push_service( context, service, type );
1173 fprintf( stderr, "Property without a service '%s'??\n", (const char *)name );
1177 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
1179 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1180 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1182 //printf("on_start_element: %s\n", name );
1183 if ( context->pass == 0 )
1185 if ( xmlStrcmp( name, _x("mlt") ) == 0 ||
1186 xmlStrcmp( name, _x("profile") ) == 0 ||
1187 xmlStrcmp( name, _x("profileinfo") ) == 0 )
1188 on_start_profile( context, name, atts );
1191 context->branch[ context->depth ] ++;
1194 // Build a tree from nodes within a property value
1195 if ( context->is_value == 1 && context->pass == 1 )
1197 xmlNodePtr node = xmlNewNode( NULL, name );
1199 if ( context->value_doc == NULL )
1202 context->value_doc = xmlNewDoc( _x("1.0") );
1203 xmlDocSetRootElement( context->value_doc, node );
1207 // Append child to tree
1208 xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
1210 context_push_node( context, node );
1212 // Set the attributes
1213 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1214 xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
1216 else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1217 on_start_tractor( context, name, atts );
1218 else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1219 on_start_multitrack( context, name, atts );
1220 else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1221 on_start_playlist( context, name, atts );
1222 else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1223 on_start_producer( context, name, atts );
1224 else if ( xmlStrcmp( name, _x("blank") ) == 0 )
1225 on_start_blank( context, name, atts );
1226 else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1227 on_start_entry( context, name, atts );
1228 else if ( xmlStrcmp( name, _x("track") ) == 0 )
1229 on_start_track( context, name, atts );
1230 else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1231 on_start_filter( context, name, atts );
1232 else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1233 on_start_transition( context, name, atts );
1234 else if ( xmlStrcmp( name, _x("property") ) == 0 )
1235 on_start_property( context, name, atts );
1236 else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1237 on_start_consumer( context, name, atts );
1238 else if ( xmlStrcmp( name, _x("westley") ) == 0 || xmlStrcmp( name, _x("mlt") ) == 0 )
1240 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1242 if ( xmlStrcmp( atts[0], _x("LC_NUMERIC") ) )
1243 mlt_properties_set( context->producer_map, _s( atts[0] ), _s(atts[1] ) );
1244 else if ( !context->lc_numeric )
1245 context->lc_numeric = strdup( _s( atts[1] ) );
1250 static void on_end_element( void *ctx, const xmlChar *name )
1252 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1253 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1255 //printf("on_end_element: %s\n", name );
1256 if ( context->is_value == 1 && context->pass == 1 && xmlStrcmp( name, _x("property") ) != 0 )
1257 context_pop_node( context );
1258 else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1259 on_end_multitrack( context, name );
1260 else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1261 on_end_playlist( context, name );
1262 else if ( xmlStrcmp( name, _x("track") ) == 0 )
1263 on_end_track( context, name );
1264 else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1265 on_end_entry( context, name );
1266 else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1267 on_end_tractor( context, name );
1268 else if ( xmlStrcmp( name, _x("property") ) == 0 )
1269 on_end_property( context, name );
1270 else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1271 on_end_producer( context, name );
1272 else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1273 on_end_filter( context, name );
1274 else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1275 on_end_transition( context, name );
1276 else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1277 on_end_consumer( context, name );
1279 context->branch[ context->depth ] = 0;
1283 static void on_characters( void *ctx, const xmlChar *ch, int len )
1285 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1286 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1287 char *value = calloc( len + 1, 1 );
1288 enum service_type type;
1289 mlt_service service = context_pop_service( context, &type );
1290 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1292 if ( service != NULL )
1293 context_push_service( context, service, type );
1296 strncpy( value, (const char*) ch, len );
1298 if ( context->stack_node_size > 0 )
1299 xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
1301 // libxml2 generates an on_characters immediately after a get_entity within
1302 // an element value, and we ignore it because it is called again during
1303 // actual substitution.
1304 else if ( context->property != NULL && context->entity_is_replace == 0 )
1306 char *s = mlt_properties_get( properties, context->property );
1309 // Append new text to existing content
1310 char *new = calloc( strlen( s ) + len + 1, 1 );
1312 strcat( new, value );
1313 mlt_properties_set( properties, context->property, new );
1317 mlt_properties_set( properties, context->property, value );
1319 context->entity_is_replace = 0;
1324 /** Convert parameters parsed from resource into entity declarations.
1326 static void params_to_entities( deserialise_context context )
1328 if ( context->params != NULL )
1332 // Add our params as entitiy declarations
1333 for ( i = 0; i < mlt_properties_count( context->params ); i++ )
1335 xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
1336 xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
1337 context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, _s(name) ) );
1341 mlt_properties_close( context->params );
1342 context->params = NULL;
1346 // The following 3 facilitate entity substitution in the SAX parser
1347 static void on_internal_subset( void *ctx, const xmlChar* name,
1348 const xmlChar* publicId, const xmlChar* systemId )
1350 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1351 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1353 context->publicId = publicId;
1354 context->systemId = systemId;
1355 xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
1357 // Override default entities with our parameters
1358 params_to_entities( context );
1361 // TODO: Check this with Dan... I think this is for parameterisation
1362 // but it's breaking standard escaped entities (like < etc).
1363 static void on_entity_declaration( void *ctx, const xmlChar* name, int type,
1364 const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
1366 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1367 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1369 xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
1372 // TODO: Check this functionality (see on_entity_declaration)
1373 static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
1375 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1376 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1377 xmlEntityPtr e = NULL;
1379 // Setup for entity declarations if not ready
1380 if ( xmlGetIntSubset( context->entity_doc ) == NULL )
1382 xmlCreateIntSubset( context->entity_doc, _x("mlt"), _x(""), _x("") );
1383 context->publicId = _x("");
1384 context->systemId = _x("");
1387 // Add our parameters if not already
1388 params_to_entities( context );
1390 e = xmlGetPredefinedEntity( name );
1392 // Send signal to on_characters that an entity substitutin is pending
1395 e = xmlGetDocEntity( context->entity_doc, name );
1397 context->entity_is_replace = 1;
1403 /** Convert a hexadecimal character to its value.
1405 static int tohex( char p )
1407 return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
1410 /** Decode a url-encoded string containing hexadecimal character sequences.
1412 static char *url_decode( char *dest, char *src )
1420 *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
1433 /** Extract the filename from a URL attaching parameters to a properties list.
1435 static void parse_url( mlt_properties properties, char *url )
1438 int n = strlen( url );
1442 for ( i = 0; i < n; i++ )
1459 if ( name != NULL && value != NULL )
1460 mlt_properties_set( properties, name, value );
1466 if ( name != NULL && value != NULL )
1467 mlt_properties_set( properties, name, value );
1470 // Quick workaround to avoid unecessary libxml2 warnings
1471 static int file_exists( char *file )
1473 char *name = strdup( file );
1475 if ( name != NULL && strchr( name, '?' ) )
1476 *( strchr( name, '?' ) ) = '\0';
1479 FILE *f = fopen( name, "r" );
1481 if ( exists ) fclose( f );
1487 mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data )
1489 xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
1490 struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
1491 mlt_properties properties = NULL;
1493 struct _xmlParserCtxt *xmlcontext;
1494 int well_formed = 0;
1495 char *filename = NULL;
1496 int info = strcmp( id, "xml-string" ) ? 0 : 1;
1498 if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) )
1501 context = calloc( 1, sizeof( struct deserialise_context_s ) );
1502 if ( context == NULL )
1505 context->producer_map = mlt_properties_new();
1506 context->destructors = mlt_properties_new();
1507 context->params = mlt_properties_new();
1508 context->profile = profile;
1510 // Decode URL and parse parameters
1511 mlt_properties_set( context->producer_map, "root", "" );
1514 filename = strdup( data );
1515 parse_url( context->params, url_decode( filename, data ) );
1517 // We need the directory prefix which was used for the xml
1518 if ( strchr( filename, '/' ) )
1521 mlt_properties_set( context->producer_map, "root", filename );
1522 root = mlt_properties_get( context->producer_map, "root" );
1523 *( strrchr( root, '/' ) ) = '\0';
1525 // If we don't have an absolute path here, we're heading for disaster...
1526 if ( root[ 0 ] != '/' )
1528 char *cwd = getcwd( NULL, 0 );
1529 char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
1530 sprintf( real, "%s/%s", cwd, root );
1531 mlt_properties_set( context->producer_map, "root", real );
1538 // We need to track the number of registered filters
1539 mlt_properties_set_int( context->destructors, "registered", 0 );
1541 // Setup SAX callbacks
1542 sax->startElement = on_start_element;
1544 // Setup libxml2 SAX parsing
1546 xmlSubstituteEntitiesDefault( 1 );
1547 // This is used to facilitate entity substitution in the SAX parser
1548 context->entity_doc = xmlNewDoc( _x("1.0") );
1550 xmlcontext = xmlCreateFileParserCtxt( filename );
1552 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1554 // Invalid context - clean up and return NULL
1555 if ( xmlcontext == NULL )
1557 mlt_properties_close( context->producer_map );
1558 mlt_properties_close( context->destructors );
1559 mlt_properties_close( context->params );
1567 xmlcontext->sax = sax;
1568 xmlcontext->_private = ( void* )context;
1569 xmlParseDocument( xmlcontext );
1571 // Cleanup after parsing
1572 xmlcontext->sax = NULL;
1573 xmlcontext->_private = NULL;
1574 xmlFreeParserCtxt( xmlcontext );
1575 context->stack_node_size = 0;
1576 context->stack_service_size = 0;
1578 // Setup the second pass
1581 xmlcontext = xmlCreateFileParserCtxt( filename );
1583 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1585 // Invalid context - clean up and return NULL
1586 if ( xmlcontext == NULL )
1588 mlt_properties_close( context->producer_map );
1589 mlt_properties_close( context->destructors );
1590 mlt_properties_close( context->params );
1591 xmlFreeDoc( context->entity_doc );
1598 // Setup SAX callbacks
1599 sax->endElement = on_end_element;
1600 sax->characters = on_characters;
1601 sax->cdataBlock = on_characters;
1602 sax->internalSubset = on_internal_subset;
1603 sax->entityDecl = on_entity_declaration;
1604 sax->getEntity = on_get_entity;
1607 xmlcontext->sax = sax;
1608 xmlcontext->_private = ( void* )context;
1609 xmlParseDocument( xmlcontext );
1610 well_formed = xmlcontext->wellFormed;
1612 // Cleanup after parsing
1613 xmlFreeDoc( context->entity_doc );
1615 xmlMemoryDump( ); // for debugging
1617 // Get the last producer on the stack
1618 enum service_type type;
1619 mlt_service service = context_pop_service( context, &type );
1620 if ( well_formed && service != NULL )
1622 // Verify it is a producer service (mlt_type="mlt_producer")
1623 // (producer, playlist, multitrack)
1624 char *type = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "mlt_type" );
1625 if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
1630 xmlDocPtr doc = xml_make_doc( service );
1631 xmlDocFormatDump( stdout, doc, 1 );
1636 if ( well_formed && service != NULL )
1638 char *title = mlt_properties_get( context->producer_map, "title" );
1640 // Need the complete producer list for various reasons
1641 properties = context->destructors;
1643 // Now make sure we don't have a reference to the service in the properties
1644 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
1646 char *name = mlt_properties_get_name( properties, i );
1647 if ( mlt_properties_get_data_at( properties, i, NULL ) == service )
1649 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
1654 // We are done referencing destructor property list
1655 // Set this var to service properties for convenience
1656 properties = MLT_SERVICE_PROPERTIES( service );
1659 mlt_properties_set( properties, "title", title );
1661 // Optimise for overlapping producers
1662 mlt_producer_optimise( MLT_PRODUCER( service ) );
1664 // Handle deep copies
1665 if ( getenv( "MLT_XML_DEEP" ) == NULL )
1667 // Now assign additional properties
1669 mlt_properties_set( properties, "resource", data );
1671 // This tells consumer_xml not to deep copy
1672 mlt_properties_set( properties, "xml", "was here" );
1676 // Allow the project to be edited
1677 mlt_properties_set( properties, "_xml", "was here" );
1678 mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
1681 // Make consumer available
1682 mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES( context->consumer ) );
1683 mlt_properties_set_data( properties, "consumer", context->consumer, 0,
1684 (mlt_destructor) mlt_consumer_close, NULL );
1688 // Return null if not well formed
1693 mlt_properties_close( context->producer_map );
1694 if ( context->params != NULL )
1695 mlt_properties_close( context->params );
1696 mlt_properties_close( context->destructors );
1697 if ( context->lc_numeric )
1698 free( context->lc_numeric );
1702 return MLT_PRODUCER( service );