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>
32 #include <libxml/parser.h>
33 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
34 #include <libxml/tree.h>
36 #define STACK_SIZE 1000
37 #define BRANCH_SIG_LEN 4000
39 #define _x (const xmlChar*)
40 #define _s (const char*)
44 extern xmlDocPtr xml_make_doc( mlt_service service );
61 mlt_dummy_filter_type,
62 mlt_dummy_transition_type,
63 mlt_dummy_producer_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 typedef struct deserialise_context_s *deserialise_context;
90 /** Convert the numerical current branch address to a dot-delimited string.
92 static char *serialise_branch( deserialise_context this, char *s )
97 for ( i = 0; i < this->depth; i++ )
99 int len = strlen( s );
100 snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", this->branch[ i ] );
108 static int context_push_service( deserialise_context this, mlt_service that, enum service_type type )
110 int ret = this->stack_service_size >= STACK_SIZE - 1;
113 this->stack_service[ this->stack_service_size ] = that;
114 this->stack_types[ this->stack_service_size++ ] = type;
116 // Record the tree branch on which this service lives
117 if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_xml_branch" ) == NULL )
119 char s[ BRANCH_SIG_LEN ];
120 mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( this, s ) );
129 static mlt_service context_pop_service( deserialise_context this, enum service_type *type )
131 mlt_service result = NULL;
133 *type = invalid_type;
134 if ( this->stack_service_size > 0 )
136 result = this->stack_service[ -- this->stack_service_size ];
138 *type = this->stack_types[ this->stack_service_size ];
146 static int context_push_node( deserialise_context this, xmlNodePtr node )
148 int ret = this->stack_node_size >= STACK_SIZE - 1;
150 this->stack_node[ this->stack_node_size ++ ] = node;
157 static xmlNodePtr context_pop_node( deserialise_context this )
159 xmlNodePtr result = NULL;
160 if ( this->stack_node_size > 0 )
161 result = this->stack_node[ -- this->stack_node_size ];
166 // Set the destructor on a new service
167 static void track_service( mlt_properties properties, void *service, mlt_destructor destructor )
169 int registered = mlt_properties_get_int( properties, "registered" );
170 char *key = mlt_properties_get( properties, "registered" );
171 mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
172 mlt_properties_set_int( properties, "registered", ++ registered );
176 // Prepend the property value with the document root
177 static inline void qualify_property( deserialise_context context, mlt_properties properties, const char *name )
179 char *resource = mlt_properties_get( properties, name );
180 if ( resource != NULL && resource[0] )
182 // Qualify file name properties
183 char *root = mlt_properties_get( context->producer_map, "root" );
184 if ( root != NULL && strcmp( root, "" ) )
186 char *full_resource = malloc( strlen( root ) + strlen( resource ) + 2 );
187 if ( resource[ 0 ] != '/' && strchr( resource, ':' ) == NULL )
189 strcpy( full_resource, root );
190 strcat( full_resource, "/" );
191 strcat( full_resource, resource );
195 strcpy( full_resource, resource );
197 mlt_properties_set( properties, name, full_resource );
198 free( full_resource );
204 /** This function adds a producer to a playlist or multitrack when
205 there is no entry or track element.
208 static int add_producer( deserialise_context context, mlt_service service, mlt_position in, mlt_position out )
210 // Return value (0 = service remains top of stack, 1 means it can be removed)
213 // Get the parent producer
214 enum service_type type = mlt_invalid_type;
215 mlt_service container = context_pop_service( context, &type );
218 if ( service != NULL && container != NULL )
220 char *container_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( container ), "_xml_branch" );
221 char *service_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_xml_branch" );
222 contained = !strncmp( container_branch, service_branch, strlen( container_branch ) );
227 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
228 char *hide_s = mlt_properties_get( properties, "hide" );
230 // Indicate that this service is no longer top of stack
235 case mlt_tractor_type:
237 mlt_multitrack multitrack = mlt_tractor_multitrack( MLT_TRACTOR( container ) );
238 mlt_multitrack_connect( multitrack, MLT_PRODUCER( service ), mlt_multitrack_count( multitrack ) );
241 case mlt_multitrack_type:
243 mlt_multitrack_connect( MLT_MULTITRACK( container ),
244 MLT_PRODUCER( service ),
245 mlt_multitrack_count( MLT_MULTITRACK( container ) ) );
248 case mlt_playlist_type:
250 mlt_playlist_append_io( MLT_PLAYLIST( container ), MLT_PRODUCER( service ), in, out );
255 fprintf( stderr, "Producer defined inside something that isn't a container\n" );
259 // Set the hide state of the track producer
260 if ( hide_s != NULL )
262 if ( strcmp( hide_s, "video" ) == 0 )
263 mlt_properties_set_int( properties, "hide", 1 );
264 else if ( strcmp( hide_s, "audio" ) == 0 )
265 mlt_properties_set_int( properties, "hide", 2 );
266 else if ( strcmp( hide_s, "both" ) == 0 )
267 mlt_properties_set_int( properties, "hide", 3 );
271 // Put the parent producer back
272 if ( container != NULL )
273 context_push_service( context, container, type );
278 /** Attach filters defined on that to this.
281 static void attach_filters( mlt_service this, mlt_service that )
286 mlt_filter filter = NULL;
287 for ( i = 0; ( filter = mlt_service_filter( that, i ) ) != NULL; i ++ )
289 mlt_service_attach( this, filter );
290 attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) );
295 static void on_start_profile( deserialise_context context, const xmlChar *name, const xmlChar **atts)
297 mlt_profile p = context->profile;
298 for ( ; atts != NULL && *atts != NULL; atts += 2 )
300 if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 || xmlStrcmp( atts[ 0 ], _x("profile") ) == 0 )
302 mlt_profile my_profile = mlt_profile_init( _s(atts[ 1 ]) );
305 p->description = strdup( my_profile->description );
306 p->display_aspect_den = my_profile->display_aspect_den;
307 p->display_aspect_num = my_profile->display_aspect_num;
308 p->frame_rate_den = my_profile->frame_rate_den;
309 p->frame_rate_num = my_profile->frame_rate_num;
310 p->width = my_profile->width;
311 p->height = my_profile->height;
312 p->progressive = my_profile->progressive;
313 p->sample_aspect_den = my_profile->sample_aspect_den;
314 p->sample_aspect_num = my_profile->sample_aspect_num;
315 p->colorspace = my_profile->colorspace;
317 mlt_profile_close( my_profile );
320 else if ( xmlStrcmp( atts[ 0 ], _x("description") ) == 0 )
322 if ( p->description )
323 free( p->description );
324 p->description = strdup( _s(atts[ 1 ]) );
327 else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_den") ) == 0 )
328 p->display_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 );
329 else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_num") ) == 0 )
330 p->display_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 );
331 else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_num") ) == 0 )
332 p->sample_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 );
333 else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_den") ) == 0 )
334 p->sample_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 );
335 else if ( xmlStrcmp( atts[ 0 ], _x("width") ) == 0 )
336 p->width = strtol( _s(atts[ 1 ]), NULL, 0 );
337 else if ( xmlStrcmp( atts[ 0 ], _x("height") ) == 0 )
338 p->height = strtol( _s(atts[ 1 ]), NULL, 0 );
339 else if ( xmlStrcmp( atts[ 0 ], _x("progressive") ) == 0 )
340 p->progressive = strtol( _s(atts[ 1 ]), NULL, 0 );
341 else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_num") ) == 0 )
342 p->frame_rate_num = strtol( _s(atts[ 1 ]), NULL, 0 );
343 else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_den") ) == 0 )
344 p->frame_rate_den = strtol( _s(atts[ 1 ]), NULL, 0 );
345 else if ( xmlStrcmp( atts[ 0 ], _x("colorspace") ) == 0 )
346 p->colorspace = strtol( _s(atts[ 1 ]), NULL, 0 );
350 static void on_start_tractor( deserialise_context context, const xmlChar *name, const xmlChar **atts)
352 mlt_tractor tractor = mlt_tractor_new( );
353 mlt_service service = MLT_TRACTOR_SERVICE( tractor );
354 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
356 track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
358 for ( ; atts != NULL && *atts != NULL; atts += 2 )
359 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
361 mlt_properties_set_int( MLT_TRACTOR_PROPERTIES( tractor ), "global_feed", 1 );
363 if ( mlt_properties_get( properties, "id" ) != NULL )
364 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
366 context_push_service( context, service, mlt_tractor_type );
369 static void on_end_tractor( deserialise_context context, const xmlChar *name )
372 enum service_type type;
373 mlt_service tractor = context_pop_service( context, &type );
375 if ( tractor != NULL && type == mlt_tractor_type )
377 // See if the tractor should be added to a playlist or multitrack
378 if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
379 context_push_service( context, tractor, type );
383 fprintf( stderr, "Invalid state for tractor\n" );
387 static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
389 enum service_type type;
390 mlt_service parent = context_pop_service( context, &type );
392 // If we don't have a parent, then create one now, providing we're in a state where we can
393 if ( parent == NULL || ( type == mlt_playlist_type || type == mlt_multitrack_type ) )
395 mlt_tractor tractor = NULL;
396 // Push the parent back
397 if ( parent != NULL )
398 context_push_service( context, parent, type );
400 // Create a tractor to contain the multitrack
401 tractor = mlt_tractor_new( );
402 parent = MLT_TRACTOR_SERVICE( tractor );
403 track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close );
404 type = mlt_tractor_type;
406 // Flag it as a synthesised tractor for clean up later
407 mlt_properties_set_int( MLT_SERVICE_PROPERTIES( parent ), "loader_synth", 1 );
410 if ( type == mlt_tractor_type )
412 mlt_service service = MLT_SERVICE( mlt_tractor_multitrack( MLT_TRACTOR( parent ) ) );
413 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
414 for ( ; atts != NULL && *atts != NULL; atts += 2 )
415 mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
417 if ( mlt_properties_get( properties, "id" ) != NULL )
418 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties,"id" ), service, 0, NULL, NULL );
420 context_push_service( context, parent, type );
421 context_push_service( context, service, mlt_multitrack_type );
425 fprintf( stderr, "Invalid multitrack position\n" );
429 static void on_end_multitrack( deserialise_context context, const xmlChar *name )
431 // Get the multitrack from the stack
432 enum service_type type;
433 mlt_service service = context_pop_service( context, &type );
435 if ( service == NULL || type != mlt_multitrack_type )
436 fprintf( stderr, "End multitrack in the wrong state...\n" );
439 static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
441 mlt_playlist playlist = mlt_playlist_init( );
442 mlt_service service = MLT_PLAYLIST_SERVICE( playlist );
443 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
445 track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
447 for ( ; atts != NULL && *atts != NULL; atts += 2 )
449 mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
451 // Out will be overwritten later as we append, so we need to save it
452 if ( xmlStrcmp( atts[ 0 ], _x("out") ) == 0 )
453 mlt_properties_set( properties, "_xml.out", ( const char* )atts[ 1 ] );
456 if ( mlt_properties_get( properties, "id" ) != NULL )
457 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
459 context_push_service( context, service, mlt_playlist_type );
462 static void on_end_playlist( deserialise_context context, const xmlChar *name )
464 // Get the playlist from the stack
465 enum service_type type;
466 mlt_service service = context_pop_service( context, &type );
468 if ( service != NULL && type == mlt_playlist_type )
470 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
471 mlt_position in = -1;
472 mlt_position out = -1;
474 if ( mlt_properties_get( properties, "in" ) )
475 in = mlt_properties_get_position( properties, "in" );
476 if ( mlt_properties_get( properties, "out" ) )
477 out = mlt_properties_get_position( properties, "out" );
479 // See if the playlist should be added to a playlist or multitrack
480 if ( add_producer( context, service, in, out ) == 0 )
481 context_push_service( context, service, type );
485 fprintf( stderr, "Invalid state of playlist end %d\n", type );
489 static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
491 // use a dummy service to hold properties to allow arbitrary nesting
492 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
493 mlt_service_init( service, NULL );
495 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
497 context_push_service( context, service, mlt_dummy_producer_type );
499 for ( ; atts != NULL && *atts != NULL; atts += 2 )
500 mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
503 // Parse a SMIL clock value (as produced by Kino 0.9.1) and return position in frames
504 static mlt_position parse_clock_value( char *value, double fps )
506 // This implementation expects a fully specified clock value - no optional
508 char *pos, *copy = strdup( value );
510 mlt_position result = -1;
513 pos = strchr( value, ':' );
520 pos = strchr( value, ':' );
527 pos = strchr( value, '.' );
536 result = ( fps * ( ( (hh * 3600) + (mm * 60) + ss ) * 1000 + ms ) / 1000 + 0.5 );
541 static void on_end_producer( deserialise_context context, const xmlChar *name )
543 enum service_type type;
544 mlt_service service = context_pop_service( context, &type );
545 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
547 if ( service != NULL && type == mlt_dummy_producer_type )
549 mlt_service producer = NULL;
551 qualify_property( context, properties, "resource" );
552 char *resource = mlt_properties_get( properties, "resource" );
554 // Let Kino-SMIL src be a synonym for resource
555 if ( resource == NULL )
557 qualify_property( context, properties, "src" );
558 resource = mlt_properties_get( properties, "src" );
561 // Instantiate the producer
562 if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
564 char *service_name = mlt_properties_get( properties, "mlt_service" );
567 char *temp = calloc( 1, strlen( service_name ) + strlen( resource ) + 2 );
568 strcat( temp, service_name );
570 strcat( temp, resource );
571 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, temp ) );
576 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, service_name ) );
580 // Just in case the plugin requested doesn't exist...
581 if ( producer == NULL && resource != NULL )
582 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) );
584 if ( producer == NULL )
585 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "+INVALID.txt" ) );
587 if ( producer == NULL )
588 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "colour:red" ) );
590 // Track this producer
591 track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
593 // Propogate the properties
594 qualify_property( context, properties, "resource" );
595 qualify_property( context, properties, "luma" );
596 qualify_property( context, properties, "luma.resource" );
597 qualify_property( context, properties, "composite.luma" );
598 qualify_property( context, properties, "producer.resource" );
600 // Handle in/out properties separately
601 mlt_position in = -1;
602 mlt_position out = -1;
605 if ( mlt_properties_get( properties, "in" ) != NULL )
606 in = mlt_properties_get_position( properties, "in" );
607 // Let Kino-SMIL clipBegin be a synonym for in
608 if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
610 if ( strchr( mlt_properties_get( properties, "clipBegin" ), ':' ) )
612 in = parse_clock_value( mlt_properties_get( properties, "clipBegin" ),
613 mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
615 // Parse frames value
616 in = mlt_properties_get_position( properties, "clipBegin" );
619 if ( mlt_properties_get( properties, "out" ) != NULL )
620 out = mlt_properties_get_position( properties, "out" );
621 // Let Kino-SMIL clipEnd be a synonym for out
622 if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
624 if ( strchr( mlt_properties_get( properties, "clipEnd" ), ':' ) )
626 out = parse_clock_value( mlt_properties_get( properties, "clipEnd" ),
627 mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
629 // Parse frames value
630 out = mlt_properties_get_position( properties, "clipEnd" );
633 mlt_properties_set( properties, "in", NULL );
634 mlt_properties_set( properties, "out", NULL );
636 // Inherit the properties
637 mlt_properties_inherit( MLT_SERVICE_PROPERTIES( producer ), properties );
639 // Attach all filters from service onto producer
640 attach_filters( producer, service );
642 // Add the producer to the producer map
643 if ( mlt_properties_get( properties, "id" ) != NULL )
644 mlt_properties_set_data( context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL );
646 // See if the producer should be added to a playlist or multitrack
647 if ( add_producer( context, producer, in, out ) == 0 )
649 // Otherwise, set in and out on...
650 if ( in != -1 || out != -1 )
652 // Get the parent service
653 enum service_type type;
654 mlt_service parent = context_pop_service( context, &type );
655 if ( parent != NULL )
657 // Get the parent properties
658 properties = MLT_SERVICE_PROPERTIES( parent );
660 char *resource = mlt_properties_get( properties, "resource" );
662 // Put the parent producer back
663 context_push_service( context, parent, type );
665 // If the parent is a track or entry
666 if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
668 if ( in > -1 ) mlt_properties_set_position( properties, "in", in );
669 if ( out > -1 ) mlt_properties_set_position( properties, "out", out );
673 // Otherwise, set in and out on producer directly
674 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
679 // Otherwise, set in and out on producer directly
680 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
684 // Push the producer onto the stack
685 context_push_service( context, producer, mlt_producer_type );
688 mlt_service_close( service );
692 static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
694 // Get the playlist from the stack
695 enum service_type type;
696 mlt_service service = context_pop_service( context, &type );
697 mlt_position length = 0;
699 if ( type == mlt_playlist_type && service != NULL )
701 // Look for the length attribute
702 for ( ; atts != NULL && *atts != NULL; atts += 2 )
704 if ( xmlStrcmp( atts[0], _x("length") ) == 0 )
706 length = atoll( _s(atts[1]) );
711 // Append a blank to the playlist
712 mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
714 // Push the playlist back onto the stack
715 context_push_service( context, service, type );
719 fprintf( stderr, "blank without a playlist - a definite no no\n" );
723 static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
725 mlt_producer entry = NULL;
726 mlt_properties temp = mlt_properties_new( );
728 for ( ; atts != NULL && *atts != NULL; atts += 2 )
730 mlt_properties_set( temp, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
732 // Look for the producer attribute
733 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
735 mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
736 if ( producer != NULL )
737 mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
741 // If we have a valid entry
742 if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
744 mlt_playlist_clip_info info;
745 enum service_type parent_type = invalid_type;
746 mlt_service parent = context_pop_service( context, &parent_type );
747 mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
749 if ( parent_type == mlt_playlist_type )
751 // Append the producer to the playlist
752 mlt_position in = -1;
753 mlt_position out = -1;
754 if ( mlt_properties_get( temp, "in" ) )
755 in = mlt_properties_get_position( temp, "in" );
756 if ( mlt_properties_get( temp, "out" ) )
757 out = mlt_properties_get_position( temp, "out" );
758 mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer, in, out );
760 // Handle the repeat property
761 if ( mlt_properties_get_int( temp, "repeat" ) > 0 )
763 mlt_playlist_repeat_clip( MLT_PLAYLIST( parent ),
764 mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1,
765 mlt_properties_get_int( temp, "repeat" ) );
768 mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
773 fprintf( stderr, "Entry not part of a playlist...\n" );
776 context_push_service( context, parent, parent_type );
779 // Push the cut onto the stack
780 context_push_service( context, MLT_PRODUCER_SERVICE( entry ), mlt_entry_type );
782 mlt_properties_close( temp );
785 static void on_end_entry( deserialise_context context, const xmlChar *name )
787 // Get the entry from the stack
788 enum service_type entry_type = invalid_type;
789 mlt_service entry = context_pop_service( context, &entry_type );
791 if ( entry == NULL && entry_type != mlt_entry_type )
793 fprintf( stderr, "Invalid state at end of entry\n" );
797 static void on_start_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
799 // use a dummy service to hold properties to allow arbitrary nesting
800 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
801 mlt_service_init( service, NULL );
803 // Push the dummy service onto the stack
804 context_push_service( context, service, mlt_entry_type );
806 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "resource", "<track>" );
808 for ( ; atts != NULL && *atts != NULL; atts += 2 )
810 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
812 // Look for the producer attribute
813 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
815 mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
816 if ( producer != NULL )
817 mlt_properties_set_data( MLT_SERVICE_PROPERTIES( service ), "producer", producer, 0, NULL, NULL );
822 static void on_end_track( deserialise_context context, const xmlChar *name )
824 // Get the track from the stack
825 enum service_type track_type;
826 mlt_service track = context_pop_service( context, &track_type );
828 if ( track != NULL && track_type == mlt_entry_type )
830 mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
831 enum service_type parent_type = invalid_type;
832 mlt_service parent = context_pop_service( context, &parent_type );
833 mlt_multitrack multitrack = NULL;
835 mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
836 mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
838 if ( parent_type == mlt_tractor_type )
839 multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
840 else if ( parent_type == mlt_multitrack_type )
841 multitrack = MLT_MULTITRACK( parent );
843 fprintf( stderr, "track contained in an invalid container\n" );
845 if ( multitrack != NULL )
847 // Set producer i/o if specified
848 if ( mlt_properties_get( track_props, "in" ) != NULL ||
849 mlt_properties_get( track_props, "out" ) != NULL )
851 mlt_position in = -1;
852 mlt_position out = -1;
853 if ( mlt_properties_get( track_props, "in" ) )
854 in = mlt_properties_get_position( track_props, "in" );
855 if ( mlt_properties_get( track_props, "out" ) )
856 out = mlt_properties_get_position( track_props, "out" );
857 mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ), in, out );
858 mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
859 mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( cut ), track_props );
860 track_props = MLT_PRODUCER_PROPERTIES( cut );
861 mlt_producer_close( cut );
865 mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
868 // Set the hide state of the track producer
869 char *hide_s = mlt_properties_get( track_props, "hide" );
870 if ( hide_s != NULL )
872 if ( strcmp( hide_s, "video" ) == 0 )
873 mlt_properties_set_int( producer_props, "hide", 1 );
874 else if ( strcmp( hide_s, "audio" ) == 0 )
875 mlt_properties_set_int( producer_props, "hide", 2 );
876 else if ( strcmp( hide_s, "both" ) == 0 )
877 mlt_properties_set_int( producer_props, "hide", 3 );
881 if ( parent != NULL )
882 context_push_service( context, parent, parent_type );
884 mlt_service_close( track );
888 fprintf( stderr, "Invalid state at end of track\n" );
892 static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
894 // use a dummy service to hold properties to allow arbitrary nesting
895 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
896 mlt_service_init( service, NULL );
898 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
900 context_push_service( context, service, mlt_dummy_filter_type );
902 // Set the properties
903 for ( ; atts != NULL && *atts != NULL; atts += 2 )
904 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
907 static void on_end_filter( deserialise_context context, const xmlChar *name )
909 enum service_type type;
910 mlt_service service = context_pop_service( context, &type );
911 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
913 enum service_type parent_type = invalid_type;
914 mlt_service parent = context_pop_service( context, &parent_type );
916 if ( service != NULL && type == mlt_dummy_filter_type )
918 mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, mlt_properties_get( properties, "mlt_service" ), NULL ) );
919 mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
921 track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
923 // Propogate the properties
924 qualify_property( context, properties, "resource" );
925 qualify_property( context, properties, "luma" );
926 qualify_property( context, properties, "luma.resource" );
927 qualify_property( context, properties, "composite.luma" );
928 qualify_property( context, properties, "producer.resource" );
929 mlt_properties_inherit( filter_props, properties );
931 // Attach all filters from service onto filter
932 attach_filters( filter, service );
934 // Associate the filter with the parent
935 if ( parent != NULL )
937 if ( parent_type == mlt_tractor_type )
939 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
940 mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
941 mlt_filter_set_in_and_out( MLT_FILTER( filter ),
942 mlt_properties_get_int( properties, "in" ),
943 mlt_properties_get_int( properties, "out" ) );
947 mlt_service_attach( parent, MLT_FILTER( filter ) );
950 // Put the parent back on the stack
951 context_push_service( context, parent, parent_type );
955 fprintf( stderr, "filter closed with invalid parent...\n" );
958 // Close the dummy filter service
959 mlt_service_close( service );
963 fprintf( stderr, "Invalid top of stack on filter close\n" );
967 static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
969 // use a dummy service to hold properties to allow arbitrary nesting
970 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
971 mlt_service_init( service, NULL );
973 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
975 context_push_service( context, service, mlt_dummy_transition_type );
977 // Set the properties
978 for ( ; atts != NULL && *atts != NULL; atts += 2 )
979 mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
982 static void on_end_transition( deserialise_context context, const xmlChar *name )
984 enum service_type type;
985 mlt_service service = context_pop_service( context, &type );
986 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
988 enum service_type parent_type = invalid_type;
989 mlt_service parent = context_pop_service( context, &parent_type );
991 if ( service != NULL && type == mlt_dummy_transition_type )
993 char *id = mlt_properties_get( properties, "mlt_service" );
994 mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) );
995 mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
997 track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
999 // Propogate the properties
1000 qualify_property( context, properties, "resource" );
1001 qualify_property( context, properties, "luma" );
1002 qualify_property( context, properties, "luma.resource" );
1003 qualify_property( context, properties, "composite.luma" );
1004 qualify_property( context, properties, "producer.resource" );
1005 mlt_properties_inherit( effect_props, properties );
1007 // Attach all filters from service onto effect
1008 attach_filters( effect, service );
1010 // Associate the filter with the parent
1011 if ( parent != NULL )
1013 if ( parent_type == mlt_tractor_type )
1015 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
1016 if ( mlt_properties_get_int( properties, "a_track" ) == mlt_properties_get_int( properties, "b_track" ) )
1017 mlt_properties_set_int( properties, "b_track", mlt_properties_get_int( properties, "a_track" ) + 1 );
1018 mlt_field_plant_transition( field, MLT_TRANSITION( effect ),
1019 mlt_properties_get_int( properties, "a_track" ),
1020 mlt_properties_get_int( properties, "b_track" ) );
1021 mlt_transition_set_in_and_out( MLT_TRANSITION( effect ),
1022 mlt_properties_get_int( properties, "in" ),
1023 mlt_properties_get_int( properties, "out" ) );
1027 fprintf( stderr, "Misplaced transition - ignoring\n" );
1030 // Put the parent back on the stack
1031 context_push_service( context, parent, parent_type );
1035 fprintf( stderr, "transition closed with invalid parent...\n" );
1038 // Close the dummy filter service
1039 mlt_service_close( service );
1043 fprintf( stderr, "Invalid top of stack on transition close\n" );
1047 static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1049 enum service_type type;
1050 mlt_service service = context_pop_service( context, &type );
1051 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1052 const char *value = NULL;
1054 if ( service != NULL )
1056 // Set the properties
1057 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1059 if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 )
1060 context->property = strdup( _s(atts[ 1 ]) );
1061 else if ( xmlStrcmp( atts[ 0 ], _x("value") ) == 0 )
1062 value = _s(atts[ 1 ]);
1065 if ( context->property != NULL )
1066 mlt_properties_set( properties, context->property, value == NULL ? "" : value );
1068 // Tell parser to collect any further nodes for serialisation
1069 context->is_value = 1;
1071 context_push_service( context, service, type );
1075 fprintf( stderr, "Property without a service '%s'?\n", ( const char * )name );
1079 static void on_end_property( deserialise_context context, const xmlChar *name )
1081 enum service_type type;
1082 mlt_service service = context_pop_service( context, &type );
1083 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1085 if ( service != NULL )
1087 // Tell parser to stop building a tree
1088 context->is_value = 0;
1090 // See if there is a xml tree for the value
1091 if ( context->property != NULL && context->value_doc != NULL )
1096 // Serialise the tree to get value
1097 xmlDocDumpMemory( context->value_doc, &value, &size );
1098 mlt_properties_set( properties, context->property, _s(value) );
1100 xmlFreeFunc xmlFree = NULL;
1101 xmlMemGet( &xmlFree, NULL, NULL, NULL);
1104 xmlFreeDoc( context->value_doc );
1105 context->value_doc = NULL;
1108 // Close this property handling
1109 free( context->property );
1110 context->property = NULL;
1112 context_push_service( context, service, type );
1116 fprintf( stderr, "Property without a service '%s'??\n", (const char *)name );
1120 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
1122 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1123 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1125 //printf("on_start_element: %s\n", name );
1126 if ( context->pass == 0 )
1128 if ( xmlStrcmp( name, _x("mlt") ) == 0 ||
1129 xmlStrcmp( name, _x("profile") ) == 0 ||
1130 xmlStrcmp( name, _x("profileinfo") ) == 0 )
1131 on_start_profile( context, name, atts );
1134 context->branch[ context->depth ] ++;
1137 // Build a tree from nodes within a property value
1138 if ( context->is_value == 1 && context->pass == 1 )
1140 xmlNodePtr node = xmlNewNode( NULL, name );
1142 if ( context->value_doc == NULL )
1145 context->value_doc = xmlNewDoc( _x("1.0") );
1146 xmlDocSetRootElement( context->value_doc, node );
1150 // Append child to tree
1151 xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
1153 context_push_node( context, node );
1155 // Set the attributes
1156 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1157 xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
1159 else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1160 on_start_tractor( context, name, atts );
1161 else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1162 on_start_multitrack( context, name, atts );
1163 else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1164 on_start_playlist( context, name, atts );
1165 else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1166 on_start_producer( context, name, atts );
1167 else if ( xmlStrcmp( name, _x("blank") ) == 0 )
1168 on_start_blank( context, name, atts );
1169 else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1170 on_start_entry( context, name, atts );
1171 else if ( xmlStrcmp( name, _x("track") ) == 0 )
1172 on_start_track( context, name, atts );
1173 else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1174 on_start_filter( context, name, atts );
1175 else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1176 on_start_transition( context, name, atts );
1177 else if ( xmlStrcmp( name, _x("property") ) == 0 )
1178 on_start_property( context, name, atts );
1179 else if ( xmlStrcmp( name, _x("westley") ) == 0 || xmlStrcmp( name, _x("mlt") ) == 0 )
1181 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1183 if ( xmlStrcmp( atts[0], _x("LC_NUMERIC") ) )
1184 mlt_properties_set( context->producer_map, _s( atts[0] ), _s(atts[1] ) );
1186 setlocale( LC_NUMERIC, _s( atts[1] ) );
1191 static void on_end_element( void *ctx, const xmlChar *name )
1193 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1194 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1196 //printf("on_end_element: %s\n", name );
1197 if ( context->is_value == 1 && context->pass == 1 && xmlStrcmp( name, _x("property") ) != 0 )
1198 context_pop_node( context );
1199 else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1200 on_end_multitrack( context, name );
1201 else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1202 on_end_playlist( context, name );
1203 else if ( xmlStrcmp( name, _x("track") ) == 0 )
1204 on_end_track( context, name );
1205 else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1206 on_end_entry( context, name );
1207 else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1208 on_end_tractor( context, name );
1209 else if ( xmlStrcmp( name, _x("property") ) == 0 )
1210 on_end_property( context, name );
1211 else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1212 on_end_producer( context, name );
1213 else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1214 on_end_filter( context, name );
1215 else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1216 on_end_transition( context, name );
1218 context->branch[ context->depth ] = 0;
1222 static void on_characters( void *ctx, const xmlChar *ch, int len )
1224 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1225 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1226 char *value = calloc( len + 1, 1 );
1227 enum service_type type;
1228 mlt_service service = context_pop_service( context, &type );
1229 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1231 if ( service != NULL )
1232 context_push_service( context, service, type );
1235 strncpy( value, (const char*) ch, len );
1237 if ( context->stack_node_size > 0 )
1238 xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
1240 // libxml2 generates an on_characters immediately after a get_entity within
1241 // an element value, and we ignore it because it is called again during
1242 // actual substitution.
1243 else if ( context->property != NULL && context->entity_is_replace == 0 )
1245 char *s = mlt_properties_get( properties, context->property );
1248 // Append new text to existing content
1249 char *new = calloc( strlen( s ) + len + 1, 1 );
1251 strcat( new, value );
1252 mlt_properties_set( properties, context->property, new );
1256 mlt_properties_set( properties, context->property, value );
1258 context->entity_is_replace = 0;
1263 /** Convert parameters parsed from resource into entity declarations.
1265 static void params_to_entities( deserialise_context context )
1267 if ( context->params != NULL )
1271 // Add our params as entitiy declarations
1272 for ( i = 0; i < mlt_properties_count( context->params ); i++ )
1274 xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
1275 xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
1276 context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, _s(name) ) );
1280 mlt_properties_close( context->params );
1281 context->params = NULL;
1285 // The following 3 facilitate entity substitution in the SAX parser
1286 static void on_internal_subset( void *ctx, const xmlChar* name,
1287 const xmlChar* publicId, const xmlChar* systemId )
1289 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1290 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1292 context->publicId = publicId;
1293 context->systemId = systemId;
1294 xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
1296 // Override default entities with our parameters
1297 params_to_entities( context );
1300 // TODO: Check this with Dan... I think this is for parameterisation
1301 // but it's breaking standard escaped entities (like < etc).
1302 static void on_entity_declaration( void *ctx, const xmlChar* name, int type,
1303 const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
1305 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1306 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1308 xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
1311 // TODO: Check this functionality (see on_entity_declaration)
1312 static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
1314 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1315 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1316 xmlEntityPtr e = NULL;
1318 // Setup for entity declarations if not ready
1319 if ( xmlGetIntSubset( context->entity_doc ) == NULL )
1321 xmlCreateIntSubset( context->entity_doc, _x("mlt"), _x(""), _x("") );
1322 context->publicId = _x("");
1323 context->systemId = _x("");
1326 // Add our parameters if not already
1327 params_to_entities( context );
1329 e = xmlGetPredefinedEntity( name );
1331 // Send signal to on_characters that an entity substitutin is pending
1334 e = xmlGetDocEntity( context->entity_doc, name );
1336 context->entity_is_replace = 1;
1342 /** Convert a hexadecimal character to its value.
1344 static int tohex( char p )
1346 return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
1349 /** Decode a url-encoded string containing hexadecimal character sequences.
1351 static char *url_decode( char *dest, char *src )
1359 *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
1372 /** Extract the filename from a URL attaching parameters to a properties list.
1374 static void parse_url( mlt_properties properties, char *url )
1377 int n = strlen( url );
1381 for ( i = 0; i < n; i++ )
1398 if ( name != NULL && value != NULL )
1399 mlt_properties_set( properties, name, value );
1405 if ( name != NULL && value != NULL )
1406 mlt_properties_set( properties, name, value );
1409 // Quick workaround to avoid unecessary libxml2 warnings
1410 static int file_exists( char *file )
1412 char *name = strdup( file );
1414 if ( name != NULL && strchr( name, '?' ) )
1415 *( strchr( name, '?' ) ) = '\0';
1418 FILE *f = fopen( name, "r" );
1420 if ( exists ) fclose( f );
1426 mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data )
1428 xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
1429 struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
1430 mlt_properties properties = NULL;
1432 struct _xmlParserCtxt *xmlcontext;
1433 int well_formed = 0;
1434 char *filename = NULL;
1435 int info = strcmp( id, "xml-string" ) ? 0 : 1;
1437 if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) )
1440 context = calloc( 1, sizeof( struct deserialise_context_s ) );
1441 if ( context == NULL )
1444 context->producer_map = mlt_properties_new();
1445 context->destructors = mlt_properties_new();
1446 context->params = mlt_properties_new();
1447 context->profile = profile;
1449 // Decode URL and parse parameters
1450 mlt_properties_set( context->producer_map, "root", "" );
1453 filename = strdup( data );
1454 parse_url( context->params, url_decode( filename, data ) );
1456 // We need the directory prefix which was used for the xml
1457 if ( strchr( filename, '/' ) )
1460 mlt_properties_set( context->producer_map, "root", filename );
1461 root = mlt_properties_get( context->producer_map, "root" );
1462 *( strrchr( root, '/' ) ) = '\0';
1464 // If we don't have an absolute path here, we're heading for disaster...
1465 if ( root[ 0 ] != '/' )
1467 char *cwd = getcwd( NULL, 0 );
1468 char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
1469 sprintf( real, "%s/%s", cwd, root );
1470 mlt_properties_set( context->producer_map, "root", real );
1477 // We need to track the number of registered filters
1478 mlt_properties_set_int( context->destructors, "registered", 0 );
1480 // Setup SAX callbacks
1481 sax->startElement = on_start_element;
1483 // Setup libxml2 SAX parsing
1485 xmlSubstituteEntitiesDefault( 1 );
1486 // This is used to facilitate entity substitution in the SAX parser
1487 context->entity_doc = xmlNewDoc( _x("1.0") );
1489 xmlcontext = xmlCreateFileParserCtxt( filename );
1491 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1493 // Invalid context - clean up and return NULL
1494 if ( xmlcontext == NULL )
1496 mlt_properties_close( context->producer_map );
1497 mlt_properties_close( context->destructors );
1498 mlt_properties_close( context->params );
1506 xmlcontext->sax = sax;
1507 xmlcontext->_private = ( void* )context;
1508 xmlParseDocument( xmlcontext );
1510 // Cleanup after parsing
1511 xmlcontext->sax = NULL;
1512 xmlcontext->_private = NULL;
1513 xmlFreeParserCtxt( xmlcontext );
1514 context->stack_node_size = 0;
1515 context->stack_service_size = 0;
1517 // Setup the second pass
1520 xmlcontext = xmlCreateFileParserCtxt( filename );
1522 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1524 // Invalid context - clean up and return NULL
1525 if ( xmlcontext == NULL )
1527 mlt_properties_close( context->producer_map );
1528 mlt_properties_close( context->destructors );
1529 mlt_properties_close( context->params );
1530 xmlFreeDoc( context->entity_doc );
1537 // Setup SAX callbacks
1538 sax->endElement = on_end_element;
1539 sax->characters = on_characters;
1540 sax->cdataBlock = on_characters;
1541 sax->internalSubset = on_internal_subset;
1542 sax->entityDecl = on_entity_declaration;
1543 sax->getEntity = on_get_entity;
1546 xmlcontext->sax = sax;
1547 xmlcontext->_private = ( void* )context;
1548 xmlParseDocument( xmlcontext );
1549 well_formed = xmlcontext->wellFormed;
1551 // Cleanup after parsing
1552 xmlFreeDoc( context->entity_doc );
1554 xmlMemoryDump( ); // for debugging
1556 // Get the last producer on the stack
1557 enum service_type type;
1558 mlt_service service = context_pop_service( context, &type );
1559 if ( well_formed && service != NULL )
1561 // Verify it is a producer service (mlt_type="mlt_producer")
1562 // (producer, playlist, multitrack)
1563 char *type = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "mlt_type" );
1564 if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
1569 xmlDocPtr doc = xml_make_doc( service );
1570 xmlDocFormatDump( stdout, doc, 1 );
1575 if ( well_formed && service != NULL )
1577 char *title = mlt_properties_get( context->producer_map, "title" );
1579 // Need the complete producer list for various reasons
1580 properties = context->destructors;
1582 // Now make sure we don't have a reference to the service in the properties
1583 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
1585 char *name = mlt_properties_get_name( properties, i );
1586 if ( mlt_properties_get_data_at( properties, i, NULL ) == service )
1588 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
1593 // We are done referencing destructor property list
1594 // Set this var to service properties for convenience
1595 properties = MLT_SERVICE_PROPERTIES( service );
1598 mlt_properties_set( properties, "title", title );
1600 // Optimise for overlapping producers
1601 mlt_producer_optimise( MLT_PRODUCER( service ) );
1603 // Handle deep copies
1604 if ( getenv( "MLT_XML_DEEP" ) == NULL )
1606 // Now assign additional properties
1608 mlt_properties_set( properties, "resource", data );
1610 // This tells consumer_xml not to deep copy
1611 mlt_properties_set( properties, "xml", "was here" );
1615 // Allow the project to be edited
1616 mlt_properties_set( properties, "_xml", "was here" );
1617 mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
1622 // Return null if not well formed
1627 mlt_properties_close( context->producer_map );
1628 if ( context->params != NULL )
1629 mlt_properties_close( context->params );
1630 mlt_properties_close( context->destructors );
1634 return MLT_PRODUCER( service );