2 * consumer_xml.c -- a libxml2 serialiser 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 #include <framework/mlt.h>
27 #include <libxml/tree.h>
31 #define _x (const xmlChar*)
32 #define _s (const char*)
34 // This maintains counters for adding ids to elements
35 struct serialise_context_s
37 mlt_properties id_map;
45 mlt_properties hide_map;
49 typedef struct serialise_context_s* serialise_context;
51 /** Forward references to static functions.
54 static int consumer_start( mlt_consumer parent );
55 static int consumer_is_stopped( mlt_consumer this );
56 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node );
70 /** Create or retrieve an id associated to this service.
73 static char *xml_get_id( serialise_context context, mlt_service service, xml_type type )
77 mlt_properties map = context->id_map;
79 // Search the map for the service
80 for ( i = 0; i < mlt_properties_count( map ); i ++ )
81 if ( mlt_properties_get_data_at( map, i, NULL ) == service )
84 // If the service is not in the map, and the type indicates a new id is needed...
85 if ( i >= mlt_properties_count( map ) && type != xml_existing )
87 // Attempt to reuse existing id
88 id = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "id" );
90 // If no id, or the id is used in the map (for another service), then
92 if ( id == NULL || mlt_properties_get_data( map, id, NULL ) != NULL )
100 sprintf( temp, "producer%d", context->producer_count ++ );
103 sprintf( temp, "multitrack%d", context->multitrack_count ++ );
106 sprintf( temp, "playlist%d", context->playlist_count ++ );
109 sprintf( temp, "tractor%d", context->tractor_count ++ );
112 sprintf( temp, "filter%d", context->filter_count ++ );
115 sprintf( temp, "transition%d", context->transition_count ++ );
122 while( mlt_properties_get_data( map, temp, NULL ) != NULL );
124 // Set the data at the generated name
125 mlt_properties_set_data( map, temp, service, 0, NULL, NULL );
127 // Get the pointer to the name (i is the end of the list)
128 id = mlt_properties_get_name( map, i );
132 // Store the existing id in the map
133 mlt_properties_set_data( map, id, service, 0, NULL, NULL );
136 else if ( type == xml_existing )
138 id = mlt_properties_get_name( map, i );
144 /** This is what will be called by the factory - anything can be passed in
145 via the argument, but keep it simple.
148 mlt_consumer consumer_xml_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
150 // Create the consumer object
151 mlt_consumer this = calloc( sizeof( struct mlt_consumer_s ), 1 );
153 // If no malloc'd and consumer init ok
154 if ( this != NULL && mlt_consumer_init( this, NULL, profile ) == 0 )
156 // Allow thread to be started/stopped
157 this->start = consumer_start;
158 this->is_stopped = consumer_is_stopped;
160 mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "resource", arg );
162 // Return the consumer produced
166 // malloc or consumer init failed
173 static void serialise_properties( serialise_context context, mlt_properties properties, xmlNode *node )
178 // Enumerate the properties
179 for ( i = 0; i < mlt_properties_count( properties ); i++ )
181 char *name = mlt_properties_get_name( properties, i );
184 mlt_properties_get_value( properties, i ) != NULL &&
185 strcmp( name, "mlt" ) != 0 &&
186 strcmp( name, "in" ) != 0 &&
187 strcmp( name, "out" ) != 0 &&
188 strcmp( name, "id" ) != 0 &&
189 strcmp( name, "title" ) != 0 &&
190 strcmp( name, "root" ) != 0 &&
191 strcmp( name, "width" ) != 0 &&
192 strcmp( name, "height" ) != 0 )
194 char *value = mlt_properties_get_value( properties, i );
195 int rootlen = strlen( context->root );
196 if ( rootlen && !strncmp( value, context->root, rootlen ) && value[ rootlen ] == '/' )
197 value += rootlen + 1;
198 p = xmlNewTextChild( node, NULL, _x("property"), _x(value) );
199 xmlNewProp( p, _x("name"), _x(name) );
204 static void serialise_store_properties( serialise_context context, mlt_properties properties, xmlNode *node, const char *store )
209 // Enumerate the properties
210 for ( i = 0; store != NULL && i < mlt_properties_count( properties ); i++ )
212 char *name = mlt_properties_get_name( properties, i );
213 if ( !strncmp( name, store, strlen( store ) ) )
215 char *value = mlt_properties_get_value( properties, i );
218 int rootlen = strlen( context->root );
219 if ( rootlen && !strncmp( value, context->root, rootlen ) && value[ rootlen ] == '/' )
220 value += rootlen + 1;
221 p = xmlNewTextChild( node, NULL, _x("property"), _x(value) );
222 xmlNewProp( p, _x("name"), _x(name) );
228 static inline void serialise_service_filters( serialise_context context, mlt_service service, xmlNode *node )
232 mlt_filter filter = NULL;
234 // Enumerate the filters
235 for ( i = 0; ( filter = mlt_producer_filter( MLT_PRODUCER( service ), i ) ) != NULL; i ++ )
237 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
238 if ( mlt_properties_get_int( properties, "_loader" ) == 0 )
240 // Get a new id - if already allocated, do nothing
241 char *id = xml_get_id( context, MLT_FILTER_SERVICE( filter ), xml_filter );
244 int in = mlt_properties_get_position( properties, "in" );
245 int out = mlt_properties_get_position( properties, "out" );
246 p = xmlNewChild( node, NULL, _x("filter"), NULL );
247 xmlNewProp( p, _x("id"), _x(id) );
248 if ( mlt_properties_get( properties, "title" ) )
249 xmlNewProp( p, _x("title"), _x(mlt_properties_get( properties, "title" )) );
250 if ( in != 0 || out != 0 )
253 sprintf( temp, "%d", in );
254 xmlNewProp( p, _x("in"), _x(temp) );
255 sprintf( temp, "%d", out );
256 xmlNewProp( p, _x("out"), _x(temp) );
258 serialise_properties( context, properties, p );
259 serialise_service_filters( context, MLT_FILTER_SERVICE( filter ), p );
265 static void serialise_producer( serialise_context context, mlt_service service, xmlNode *node )
267 xmlNode *child = node;
268 mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( MLT_PRODUCER( service ) ) );
270 if ( context->pass == 0 )
272 mlt_properties properties = MLT_SERVICE_PROPERTIES( parent );
273 // Get a new id - if already allocated, do nothing
274 char *id = xml_get_id( context, parent, xml_producer );
278 child = xmlNewChild( node, NULL, _x("producer"), NULL );
281 xmlNewProp( child, _x("id"), _x(id) );
282 if ( mlt_properties_get( properties, "title" ) )
283 xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
284 xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
285 xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
286 serialise_properties( context, properties, child );
287 serialise_service_filters( context, service, child );
289 // Add producer to the map
290 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
294 char *id = xml_get_id( context, parent, xml_existing );
295 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
296 xmlNewProp( node, _x("parent"), _x(id) );
297 xmlNewProp( node, _x("in"), _x(mlt_properties_get( properties, "in" )) );
298 xmlNewProp( node, _x("out"), _x(mlt_properties_get( properties, "out" )) );
302 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node );
304 static void serialise_multitrack( serialise_context context, mlt_service service, xmlNode *node )
308 if ( context->pass == 0 )
310 // Iterate over the tracks to collect the producers
311 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
313 mlt_producer producer = mlt_producer_cut_parent( mlt_multitrack_track( MLT_MULTITRACK( service ), i ) );
314 serialise_service( context, MLT_SERVICE( producer ), node );
319 // Get a new id - if already allocated, do nothing
320 char *id = xml_get_id( context, service, xml_multitrack );
324 // Serialise the tracks
325 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
327 xmlNode *track = xmlNewChild( node, NULL, _x("track"), NULL );
329 mlt_producer producer = mlt_multitrack_track( MLT_MULTITRACK( service ), i );
330 mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
332 mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( producer ) );
334 char *id = xml_get_id( context, MLT_SERVICE( parent ), xml_existing );
335 xmlNewProp( track, _x("producer"), _x(id) );
336 if ( mlt_producer_is_cut( producer ) )
338 xmlNewProp( track, _x("in"), _x(mlt_properties_get( properties, "in" )) );
339 xmlNewProp( track, _x("out"), _x(mlt_properties_get( properties, "out" )) );
340 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store );
341 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "meta." );
342 serialise_service_filters( context, MLT_PRODUCER_SERVICE( producer ), track );
345 hide = mlt_properties_get_int( context->hide_map, id );
347 xmlNewProp( track, _x("hide"), _x( hide == 1 ? "video" : ( hide == 2 ? "audio" : "both" ) ) );
349 serialise_service_filters( context, service, node );
353 static void serialise_playlist( serialise_context context, mlt_service service, xmlNode *node )
356 xmlNode *child = node;
357 mlt_playlist_clip_info info;
358 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
360 if ( context->pass == 0 )
362 // Get a new id - if already allocated, do nothing
363 char *id = xml_get_id( context, service, xml_playlist );
367 // Iterate over the playlist entries to collect the producers
368 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
370 if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
372 if ( info.producer != NULL )
374 mlt_producer producer = mlt_producer_cut_parent( info.producer );
375 char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
376 char *resource_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "resource" );
377 if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
378 serialise_playlist( context, MLT_SERVICE( producer ), node );
379 else if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
380 serialise_service( context, MLT_SERVICE( producer ), node );
385 child = xmlNewChild( node, NULL, _x("playlist"), NULL );
388 xmlNewProp( child, _x("id"), _x(id) );
389 if ( mlt_properties_get( properties, "title" ) )
390 xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
392 // Store application specific properties
393 serialise_store_properties( context, properties, child, context->store );
394 serialise_store_properties( context, properties, child, "meta." );
396 // Add producer to the map
397 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
399 // Iterate over the playlist entries
400 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
402 if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
404 mlt_producer producer = mlt_producer_cut_parent( info.producer );
405 char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
406 if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
410 xmlNode *entry = xmlNewChild( child, NULL, _x("blank"), NULL );
411 snprintf( length, 19, "%d", (int)info.frame_count );
412 xmlNewProp( entry, _x("length"), _x(length) );
417 xmlNode *entry = xmlNewChild( child, NULL, _x("entry"), NULL );
418 id = xml_get_id( context, MLT_SERVICE( producer ), xml_existing );
419 xmlNewProp( entry, _x("producer"), _x(id) );
420 sprintf( temp, "%d", (int)info.frame_in );
421 xmlNewProp( entry, _x("in"), _x(temp) );
422 sprintf( temp, "%d", (int)info.frame_out );
423 xmlNewProp( entry, _x("out"), _x(temp) );
424 if ( info.repeat > 1 )
426 sprintf( temp, "%d", info.repeat );
427 xmlNewProp( entry, _x("repeat"), _x(temp) );
429 if ( mlt_producer_is_cut( info.cut ) )
431 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, context->store );
432 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "meta." );
433 serialise_service_filters( context, MLT_PRODUCER_SERVICE( info.cut ), entry );
439 serialise_service_filters( context, service, child );
441 else if ( xmlStrcmp( node->name, _x("tractor") ) != 0 )
443 char *id = xml_get_id( context, service, xml_existing );
444 xmlNewProp( node, _x("producer"), _x(id) );
448 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node )
450 xmlNode *child = node;
451 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
453 if ( context->pass == 0 )
455 // Recurse on connected producer
456 serialise_service( context, mlt_service_producer( service ), node );
460 // Get a new id - if already allocated, do nothing
461 char *id = xml_get_id( context, service, xml_tractor );
465 child = xmlNewChild( node, NULL, _x("tractor"), NULL );
468 xmlNewProp( child, _x("id"), _x(id) );
469 if ( mlt_properties_get( properties, "title" ) )
470 xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
471 if ( mlt_properties_get( properties, "global_feed" ) )
472 xmlNewProp( child, _x("global_feed"), _x(mlt_properties_get( properties, "global_feed" )) );
473 xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
474 xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
476 // Store application specific properties
477 serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store );
478 serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "meta." );
480 // Recurse on connected producer
481 serialise_service( context, mlt_service_producer( service ), child );
482 serialise_service_filters( context, service, child );
486 static void serialise_filter( serialise_context context, mlt_service service, xmlNode *node )
488 xmlNode *child = node;
489 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
491 // Recurse on connected producer
492 serialise_service( context, mlt_service_producer( service ), node );
494 if ( context->pass == 1 )
496 // Get a new id - if already allocated, do nothing
497 char *id = xml_get_id( context, service, xml_filter );
501 child = xmlNewChild( node, NULL, _x("filter"), NULL );
504 xmlNewProp( child, _x("id"), _x(id) );
505 if ( mlt_properties_get( properties, "title" ) )
506 xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
507 xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
508 xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
510 serialise_properties( context, properties, child );
511 serialise_service_filters( context, service, child );
515 static void serialise_transition( serialise_context context, mlt_service service, xmlNode *node )
517 xmlNode *child = node;
518 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
520 // Recurse on connected producer
521 serialise_service( context, MLT_SERVICE( MLT_TRANSITION( service )->producer ), node );
523 if ( context->pass == 1 )
525 // Get a new id - if already allocated, do nothing
526 char *id = xml_get_id( context, service, xml_transition );
530 child = xmlNewChild( node, NULL, _x("transition"), NULL );
533 xmlNewProp( child, _x("id"), _x(id) );
534 if ( mlt_properties_get( properties, "title" ) )
535 xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
536 xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
537 xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
539 serialise_properties( context, properties, child );
540 serialise_service_filters( context, service, child );
544 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node )
546 // Iterate over consumer/producer connections
547 while ( service != NULL )
549 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
550 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
552 // Tell about the producer
553 if ( strcmp( mlt_type, "producer" ) == 0 )
555 char *mlt_service = mlt_properties_get( properties, "mlt_service" );
556 if ( mlt_properties_get( properties, "xml" ) == NULL && ( mlt_service != NULL && !strcmp( mlt_service, "tractor" ) ) )
559 serialise_tractor( context, service, node );
561 serialise_tractor( context, service, node );
567 serialise_producer( context, service, node );
569 if ( mlt_properties_get( properties, "xml" ) != NULL )
573 // Tell about the framework container producers
574 else if ( strcmp( mlt_type, "mlt_producer" ) == 0 )
576 char *resource = mlt_properties_get( properties, "resource" );
578 // Recurse on multitrack's tracks
579 if ( strcmp( resource, "<multitrack>" ) == 0 )
581 serialise_multitrack( context, service, node );
585 // Recurse on playlist's clips
586 else if ( strcmp( resource, "<playlist>" ) == 0 )
588 serialise_playlist( context, service, node );
591 // Recurse on tractor's producer
592 else if ( strcmp( resource, "<tractor>" ) == 0 )
595 serialise_tractor( context, service, node );
597 serialise_tractor( context, service, node );
602 // Treat it as a normal producer
605 serialise_producer( context, service, node );
609 // Tell about a filter
610 else if ( strcmp( mlt_type, "filter" ) == 0 )
612 serialise_filter( context, service, node );
616 // Tell about a transition
617 else if ( strcmp( mlt_type, "transition" ) == 0 )
619 serialise_transition( context, service, node );
623 // Get the next connected service
624 service = mlt_service_producer( service );
628 xmlDocPtr xml_make_doc( mlt_consumer consumer, mlt_service service )
630 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
631 xmlDocPtr doc = xmlNewDoc( _x("1.0") );
632 xmlNodePtr root = xmlNewNode( NULL, _x("mlt") );
633 struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) );
634 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) );
637 xmlDocSetRootElement( doc, root );
639 // Indicate the numeric locale
640 xmlNewProp( root, _x("LC_NUMERIC"), _x( setlocale( LC_NUMERIC, NULL ) ) );
642 // If we have root, then deal with it now
643 if ( mlt_properties_get( properties, "root" ) != NULL )
645 xmlNewProp( root, _x("root"), _x(mlt_properties_get( properties, "root" )) );
646 context->root = strdup( mlt_properties_get( properties, "root" ) );
650 context->root = strdup( "" );
653 // Assign the additional 'storage' pattern for properties
654 context->store = mlt_properties_get( MLT_CONSUMER_PROPERTIES( consumer ), "store" );
656 // Assign a title property
657 if ( mlt_properties_get( properties, "title" ) != NULL )
658 xmlNewProp( root, _x("title"), _x(mlt_properties_get( properties, "title" )) );
659 mlt_properties_set_int( properties, "global_feed", 1 );
661 // Add a profile child element
664 xmlNodePtr profile_node = xmlNewChild( root, NULL, _x("profile"), NULL );
665 if ( profile->description )
666 xmlNewProp( profile_node, _x("description"), _x(profile->description) );
667 sprintf( tmpstr, "%d", profile->width );
668 xmlNewProp( profile_node, _x("width"), _x(tmpstr) );
669 sprintf( tmpstr, "%d", profile->height );
670 xmlNewProp( profile_node, _x("height"), _x(tmpstr) );
671 sprintf( tmpstr, "%d", profile->progressive );
672 xmlNewProp( profile_node, _x("progressive"), _x(tmpstr) );
673 sprintf( tmpstr, "%d", profile->sample_aspect_num );
674 xmlNewProp( profile_node, _x("sample_aspect_num"), _x(tmpstr) );
675 sprintf( tmpstr, "%d", profile->sample_aspect_den );
676 xmlNewProp( profile_node, _x("sample_aspect_den"), _x(tmpstr) );
677 sprintf( tmpstr, "%d", profile->display_aspect_num );
678 xmlNewProp( profile_node, _x("display_aspect_num"), _x(tmpstr) );
679 sprintf( tmpstr, "%d", profile->display_aspect_den );
680 xmlNewProp( profile_node, _x("display_aspect_den"), _x(tmpstr) );
681 sprintf( tmpstr, "%d", profile->frame_rate_num );
682 xmlNewProp( profile_node, _x("frame_rate_num"), _x(tmpstr) );
683 sprintf( tmpstr, "%d", profile->frame_rate_den );
684 xmlNewProp( profile_node, _x("frame_rate_den"), _x(tmpstr) );
685 sprintf( tmpstr, "%d", profile->colorspace );
686 xmlNewProp( profile_node, _x("colorspace"), _x(tmpstr) );
689 // Construct the context maps
690 context->id_map = mlt_properties_new();
691 context->hide_map = mlt_properties_new();
693 // Ensure producer is a framework producer
694 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "mlt_type", "mlt_producer" );
696 // In pass one, we serialise the end producers and playlists,
697 // adding them to a map keyed by address.
698 serialise_service( context, service, root );
700 // In pass two, we serialise the tractor and reference the
701 // producers and playlists
703 serialise_service( context, service, root );
706 mlt_properties_close( context->id_map );
707 mlt_properties_close( context->hide_map );
708 free( context->root );
714 static int consumer_start( mlt_consumer this )
716 xmlDocPtr doc = NULL;
718 // Get the producer service
719 mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( this ) );
720 if ( service != NULL )
722 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
723 char *resource = mlt_properties_get( properties, "resource" );
725 // Set the title if provided
726 if ( mlt_properties_get( properties, "title" ) )
727 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", mlt_properties_get( properties, "title" ) );
728 else if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" ) == NULL )
729 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", "Anonymous Submission" );
731 // Check for a root on the consumer properties and pass to service
732 if ( mlt_properties_get( properties, "root" ) )
733 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", mlt_properties_get( properties, "root" ) );
735 // Specify roots in other cases...
736 if ( resource != NULL && mlt_properties_get( properties, "root" ) == NULL )
738 // Get the current working directory
739 char *cwd = getcwd( NULL, 0 );
740 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", cwd );
745 doc = xml_make_doc( this, service );
748 if ( resource == NULL || !strcmp( resource, "" ) )
750 xmlDocFormatDump( stdout, doc, 1 );
752 else if ( strchr( resource, '.' ) == NULL )
754 xmlChar *buffer = NULL;
756 xmlDocDumpMemoryEnc( doc, &buffer, &length, "utf-8" );
757 mlt_properties_set( properties, resource, _s(buffer) );
759 xmlFreeFunc xmlFree = NULL;
760 xmlMemGet( &xmlFree, NULL, NULL, NULL);
766 xmlSaveFormatFileEnc( resource, doc, "utf-8", 1 );
769 // Close the document
773 mlt_consumer_stop( this );
775 mlt_consumer_stopped( this );
780 static int consumer_is_stopped( mlt_consumer this )