]> git.sesse.net Git - mlt/blob - src/modules/westley/consumer_westley.c
a5aaaf86b848f131ab4abe120246af6100d6398b
[mlt] / src / modules / westley / consumer_westley.c
1 /*
2  * consumer_westley.c -- a libxml2 serialiser of mlt service networks
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "consumer_westley.h"
22 #include <framework/mlt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <pthread.h>
27 #include <unistd.h>
28 #include <libxml/tree.h>
29
30 #define ID_SIZE 128
31
32 // This maintains counters for adding ids to elements
33 struct serialise_context_s
34 {
35         mlt_properties id_map;
36         int producer_count;
37         int multitrack_count;
38         int playlist_count;
39         int tractor_count;
40         int filter_count;
41         int transition_count;
42         int pass;
43         mlt_properties hide_map;
44         char *root;
45         char *store;
46 };
47 typedef struct serialise_context_s* serialise_context;
48
49 /** Forward references to static functions.
50 */
51
52 static int consumer_start( mlt_consumer parent );
53 static int consumer_is_stopped( mlt_consumer this );
54 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node );
55
56 typedef enum 
57 {
58         westley_existing,
59         westley_producer,
60         westley_multitrack,
61         westley_playlist,
62         westley_tractor,
63         westley_filter,
64         westley_transition
65 }
66 westley_type;
67
68 /** Create or retrieve an id associated to this service.
69 */
70
71 static char *westley_get_id( serialise_context context, mlt_service service, westley_type type )
72 {
73         char *id = NULL;
74         int i = 0;
75         mlt_properties map = context->id_map;
76
77         // Search the map for the service
78         for ( i = 0; i < mlt_properties_count( map ); i ++ )
79                 if ( mlt_properties_get_data_at( map, i, NULL ) == service )
80                         break;
81
82         // If the service is not in the map, and the type indicates a new id is needed...
83         if ( i >= mlt_properties_count( map ) && type != westley_existing )
84         {
85                 // Attempt to reuse existing id
86                 id = mlt_properties_get( mlt_service_properties( service ), "id" );
87
88                 // If no id, or the id is used in the map (for another service), then 
89                 // create a new one.
90                 if ( id == NULL || mlt_properties_get_data( map, id, NULL ) != NULL )
91                 {
92                         char temp[ ID_SIZE ];
93                         do
94                         {
95                                 switch( type )
96                                 {
97                                         case westley_producer:
98                                                 sprintf( temp, "producer%d", context->producer_count ++ );
99                                                 break;
100                                         case westley_multitrack:
101                                                 sprintf( temp, "multitrack%d", context->multitrack_count ++ );
102                                                 break;
103                                         case westley_playlist:
104                                                 sprintf( temp, "playlist%d", context->playlist_count ++ );
105                                                 break;
106                                         case westley_tractor:
107                                                 sprintf( temp, "tractor%d", context->tractor_count ++ );
108                                                 break;
109                                         case westley_filter:
110                                                 sprintf( temp, "filter%d", context->filter_count ++ );
111                                                 break;
112                                         case westley_transition:
113                                                 sprintf( temp, "transition%d", context->transition_count ++ );
114                                                 break;
115                                         case westley_existing:
116                                                 // Never gets here
117                                                 break;
118                                 }
119                         }
120                         while( mlt_properties_get_data( map, temp, NULL ) != NULL );
121
122                         // Set the data at the generated name
123                         mlt_properties_set_data( map, temp, service, 0, NULL, NULL );
124
125                         // Get the pointer to the name (i is the end of the list)
126                         id = mlt_properties_get_name( map, i );
127                 }
128                 else
129                 {
130                         // Store the existing id in the map
131                         mlt_properties_set_data( map, id, service, 0, NULL, NULL );
132                 }
133         }
134         else if ( type == westley_existing )
135         {
136                 id = mlt_properties_get_name( map, i );
137         }
138
139         return id;
140 }
141
142 /** This is what will be called by the factory - anything can be passed in
143         via the argument, but keep it simple.
144 */
145
146 mlt_consumer consumer_westley_init( char *arg )
147 {
148         // Create the consumer object
149         mlt_consumer this = calloc( sizeof( struct mlt_consumer_s ), 1 );
150
151         // If no malloc'd and consumer init ok
152         if ( this != NULL && mlt_consumer_init( this, NULL ) == 0 )
153         {
154                 // Allow thread to be started/stopped
155                 this->start = consumer_start;
156                 this->is_stopped = consumer_is_stopped;
157
158                 mlt_properties_set( mlt_consumer_properties( this ), "resource", arg );
159
160                 // Return the consumer produced
161                 return this;
162         }
163
164         // malloc or consumer init failed
165         free( this );
166
167         // Indicate failure
168         return NULL;
169 }
170
171 static inline void serialise_properties( serialise_context context, mlt_properties properties, xmlNode *node )
172 {
173         int i;
174         xmlNode *p;
175         
176         // Enumerate the properties
177         for ( i = 0; i < mlt_properties_count( properties ); i++ )
178         {
179                 char *name = mlt_properties_get_name( properties, i );
180                 if ( name != NULL &&
181                          name[ 0 ] != '_' &&
182                          mlt_properties_get_value( properties, i ) != NULL &&
183                          strcmp( name, "westley" ) != 0 &&
184                          strcmp( name, "in" ) != 0 &&
185                          strcmp( name, "out" ) != 0 && 
186                          strcmp( name, "id" ) != 0 && 
187                          strcmp( name, "root" ) != 0 && 
188                          strcmp( name, "width" ) != 0 &&
189                          strcmp( name, "height" ) != 0 )
190                 {
191                         char *value = mlt_properties_get_value( properties, i );
192                         p = xmlNewChild( node, NULL, "property", NULL );
193                         xmlNewProp( p, "name", mlt_properties_get_name( properties, i ) );
194                         if ( strcmp( context->root, "" ) && !strncmp( value, context->root, strlen( context->root ) ) )
195                                 xmlNodeSetContent( p, value + strlen( context->root ) + 1 );
196                         else
197                                 xmlNodeSetContent( p, value );
198                 }
199         }
200 }
201
202 static inline void serialise_store_properties( serialise_context context, mlt_properties properties, xmlNode *node )
203 {
204         int i;
205         xmlNode *p;
206         
207         // Enumerate the properties
208         for ( i = 0; context->store != NULL && i < mlt_properties_count( properties ); i++ )
209         {
210                 char *name = mlt_properties_get_name( properties, i );
211                 if ( !strncmp( name, context->store, strlen( context->store ) ) )
212                 {
213                         char *value = mlt_properties_get_value( properties, i );
214                         if ( value != NULL )
215                         {
216                                 p = xmlNewChild( node, NULL, "property", NULL );
217                                 xmlNewProp( p, "name", mlt_properties_get_name( properties, i ) );
218                                 if ( context->root != NULL && strcmp( context->root, "" ) && !strncmp( value, context->root, strlen( context->root ) ) )
219                                         xmlNodeSetContent( p, value + strlen( context->root ) + 1 );
220                                 else
221                                         xmlNodeSetContent( p, value );
222                         }
223                 }
224         }
225 }
226
227 static inline void serialise_service_filters( serialise_context context, mlt_service service, xmlNode *node )
228 {
229         int i;
230         xmlNode *p;
231         mlt_filter filter = NULL;
232         
233         // Enumerate the filters
234         for ( i = 0; ( filter = mlt_producer_filter( MLT_PRODUCER( service ), i ) ) != NULL; i ++ )
235         {
236                 mlt_properties properties = mlt_filter_properties( filter );
237                 if ( mlt_properties_get_int( properties, "_fezzik" ) == 0 )
238                 {
239                         // Get a new id - if already allocated, do nothing
240                         char *id = westley_get_id( context, mlt_filter_service( filter ), westley_filter );
241                         if ( id != NULL )
242                         {
243                                 int in = mlt_properties_get_position( properties, "in" );
244                                 int out = mlt_properties_get_position( properties, "out" );
245                                 p = xmlNewChild( node, NULL, "filter", NULL );
246                                 xmlNewProp( p, "id", id );
247                                 if ( in != 0 || out != 0 )
248                                 {
249                                         char temp[ 20 ];
250                                         sprintf( temp, "%d", in );
251                                         xmlNewProp( p, "in", temp );
252                                         sprintf( temp, "%d", out );
253                                         xmlNewProp( p, "out", temp );
254                                 }
255                                 serialise_properties( context, properties, p );
256                                 serialise_service_filters( context, mlt_filter_service( filter ), p );
257                         }
258                 }
259         }
260 }
261
262 static void serialise_producer( serialise_context context, mlt_service service, xmlNode *node )
263 {
264         xmlNode *child = node;
265         mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( MLT_PRODUCER( service ) ) );
266         
267         if ( context->pass == 0 )
268         {
269                 mlt_properties properties = mlt_service_properties( parent );
270                 // Get a new id - if already allocated, do nothing
271                 char *id = westley_get_id( context, parent, westley_producer );
272                 if ( id == NULL )
273                         return;
274
275                 child = xmlNewChild( node, NULL, "producer", NULL );
276
277                 // Set the id
278                 xmlNewProp( child, "id", id );
279                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
280                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
281                 serialise_properties( context, properties, child );
282                 serialise_service_filters( context, service, child );
283
284                 // Add producer to the map
285                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
286         }
287         else
288         {
289                 char *id = westley_get_id( context, parent, westley_existing );
290                 mlt_properties properties = mlt_service_properties( service );
291                 xmlNewProp( node, "parent", id );
292                 xmlNewProp( node, "in", mlt_properties_get( properties, "in" ) );
293                 xmlNewProp( node, "out", mlt_properties_get( properties, "out" ) );
294         }
295 }
296
297 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node );
298
299 static void serialise_multitrack( serialise_context context, mlt_service service, xmlNode *node )
300 {
301         int i;
302         
303         if ( context->pass == 0 )
304         {
305                 // Iterate over the tracks to collect the producers
306                 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
307                 {
308                         mlt_producer producer = mlt_producer_cut_parent( mlt_multitrack_track( MLT_MULTITRACK( service ), i ) );
309                         serialise_service( context, MLT_SERVICE( producer ), node );
310                 }
311         }
312         else
313         {
314                 // Get a new id - if already allocated, do nothing
315                 char *id = westley_get_id( context, service, westley_multitrack );
316                 if ( id == NULL )
317                         return;
318
319                 // Serialise the tracks
320                 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
321                 {
322                         xmlNode *track = xmlNewChild( node, NULL, "track", NULL );
323                         int hide = 0;
324                         mlt_producer producer = mlt_multitrack_track( MLT_MULTITRACK( service ), i );
325                         mlt_properties properties = mlt_producer_properties( producer );
326
327                         mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( producer ) );
328
329                         char *id = westley_get_id( context, MLT_SERVICE( parent ), westley_existing );
330                         xmlNewProp( track, "producer", id );
331                         if ( mlt_properties_get_int( properties, "_cut" ) == 1 )
332                         {
333                                 xmlNewProp( track, "in", mlt_properties_get( properties, "in" ) );
334                                 xmlNewProp( track, "out", mlt_properties_get( properties, "out" ) );
335                         }
336                         
337                         hide = mlt_properties_get_int( context->hide_map, id );
338                         if ( hide )
339                                 xmlNewProp( track, "hide", hide == 1 ? "video" : ( hide == 2 ? "audio" : "both" ) );
340                 }
341                 serialise_service_filters( context, service, node );
342         }
343 }
344
345 static void serialise_playlist( serialise_context context, mlt_service service, xmlNode *node )
346 {
347         int i;
348         xmlNode *child = node;
349         mlt_playlist_clip_info info;
350         mlt_properties properties = mlt_service_properties( service );
351         
352         if ( context->pass == 0 )
353         {
354                 // Get a new id - if already allocated, do nothing
355                 char *id = westley_get_id( context, service, westley_playlist );
356                 if ( id == NULL )
357                         return;
358
359                 // Iterate over the playlist entries to collect the producers
360                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
361                 {
362                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
363                         {
364                                 if ( info.producer != NULL )
365                                 {
366                                         mlt_producer producer = mlt_producer_cut_parent( info.producer );
367                                         char *service_s = mlt_properties_get( mlt_producer_properties( producer ), "mlt_service" );
368                                         char *resource_s = mlt_properties_get( mlt_producer_properties( producer ), "resource" );
369                                         if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
370                                                 serialise_playlist( context, MLT_SERVICE( producer ), node );
371                                         else if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
372                                                 serialise_service( context, MLT_SERVICE( producer ), node );
373                                 }
374                         }
375                 }
376                 
377                 child = xmlNewChild( node, NULL, "playlist", NULL );
378
379                 // Set the id
380                 xmlNewProp( child, "id", id );
381
382                 // Store application specific properties
383                 serialise_store_properties( context, properties, child );
384
385                 // Add producer to the map
386                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
387         
388                 // Iterate over the playlist entries
389                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
390                 {
391                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
392                         {
393                                 mlt_producer producer = mlt_producer_cut_parent( info.producer );
394                                 char *service_s = mlt_properties_get( mlt_producer_properties( producer ), "mlt_service" );
395                                 if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
396                                 {
397                                         char length[ 20 ];
398                                         length[ 19 ] = '\0';
399                                         xmlNode *entry = xmlNewChild( child, NULL, "blank", NULL );
400                                         snprintf( length, 19, "%d", info.frame_count );
401                                         xmlNewProp( entry, "length", length );
402                                 }
403                                 else
404                                 {
405                                         char temp[ 20 ];
406                                         xmlNode *entry = xmlNewChild( child, NULL, "entry", NULL );
407                                         id = westley_get_id( context, MLT_SERVICE( producer ), westley_existing );
408                                         xmlNewProp( entry, "producer", id );
409                                         sprintf( temp, "%d", info.frame_in );
410                                         xmlNewProp( entry, "in", temp );
411                                         sprintf( temp, "%d", info.frame_out );
412                                         xmlNewProp( entry, "out", temp );
413                                         if ( info.repeat > 1 )
414                                         {
415                                                 sprintf( temp, "%d", info.repeat );
416                                                 xmlNewProp( entry, "repeat", temp );
417                                         }
418                                         if ( mlt_producer_is_cut( info.cut ) )
419                                                 serialise_service_filters( context, mlt_producer_service( info.cut ), entry );
420                                 }
421                         }
422                 }
423
424                 serialise_service_filters( context, service, child );
425         }
426         else if ( strcmp( (const char*) node->name, "tractor" ) != 0 )
427         {
428                 char *id = westley_get_id( context, service, westley_existing );
429                 xmlNewProp( node, "producer", id );
430         }
431 }
432
433 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node )
434 {
435         xmlNode *child = node;
436         mlt_properties properties = mlt_service_properties( service );
437         
438         if ( context->pass == 0 )
439         {
440                 // Recurse on connected producer
441                 serialise_service( context, mlt_service_producer( service ), node );
442         }
443         else
444         {
445                 // Get a new id - if already allocated, do nothing
446                 char *id = westley_get_id( context, service, westley_tractor );
447                 if ( id == NULL )
448                         return;
449
450                 child = xmlNewChild( node, NULL, "tractor", NULL );
451
452                 // Set the id
453                 xmlNewProp( child, "id", id );
454                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
455                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
456
457                 // Store application specific properties
458                 serialise_store_properties( context, mlt_service_properties( service ), child );
459
460                 // Recurse on connected producer
461                 serialise_service( context, mlt_service_producer( service ), child );
462                 serialise_service_filters( context, service, child );
463         }
464 }
465
466 static void serialise_filter( serialise_context context, mlt_service service, xmlNode *node )
467 {
468         xmlNode *child = node;
469         mlt_properties properties = mlt_service_properties( service );
470         
471         // Recurse on connected producer
472         serialise_service( context, mlt_service_producer( service ), node );
473
474         if ( context->pass == 1 )
475         {
476                 // Get a new id - if already allocated, do nothing
477                 char *id = westley_get_id( context, service, westley_filter );
478                 if ( id == NULL )
479                         return;
480
481                 child = xmlNewChild( node, NULL, "filter", NULL );
482
483                 // Set the id
484                 xmlNewProp( child, "id", id );
485                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
486                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
487
488                 serialise_properties( context, properties, child );
489                 serialise_service_filters( context, service, child );
490         }
491 }
492
493 static void serialise_transition( serialise_context context, mlt_service service, xmlNode *node )
494 {
495         xmlNode *child = node;
496         mlt_properties properties = mlt_service_properties( service );
497         
498         // Recurse on connected producer
499         serialise_service( context, MLT_SERVICE( MLT_TRANSITION( service )->producer ), node );
500
501         if ( context->pass == 1 )
502         {
503                 // Get a new id - if already allocated, do nothing
504                 char *id = westley_get_id( context, service, westley_transition );
505                 if ( id == NULL )
506                         return;
507
508                 child = xmlNewChild( node, NULL, "transition", NULL );
509         
510                 // Set the id
511                 xmlNewProp( child, "id", id );
512                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
513                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
514
515                 serialise_properties( context, properties, child );
516                 serialise_service_filters( context, service, child );
517         }
518 }
519
520 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node )
521 {
522         // Iterate over consumer/producer connections
523         while ( service != NULL )
524         {
525                 mlt_properties properties = mlt_service_properties( service );
526                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
527                 
528                 // Tell about the producer
529                 if ( strcmp( mlt_type, "producer" ) == 0 )
530                 {
531                         char *mlt_service = mlt_properties_get( properties, "mlt_service" );
532                         if ( mlt_properties_get( properties, "westley" ) == NULL && !strcmp( mlt_service, "tractor" ) )
533                         {
534                                 context->pass = 0;
535                                 serialise_tractor( context, service, node );
536                                 context->pass = 1;
537                                 serialise_tractor( context, service, node );
538                                 context->pass = 0;
539                                 break;
540                         }
541                         else
542                         {
543                                 serialise_producer( context, service, node );
544                         }
545                         if ( mlt_properties_get( properties, "westley" ) != NULL )
546                                 break;
547                 }
548
549                 // Tell about the framework container producers
550                 else if ( strcmp( mlt_type, "mlt_producer" ) == 0 )
551                 {
552                         char *resource = mlt_properties_get( properties, "resource" );
553                         
554                         // Recurse on multitrack's tracks
555                         if ( strcmp( resource, "<multitrack>" ) == 0 )
556                         {
557                                 serialise_multitrack( context, service, node );
558                                 break;
559                         }
560                         
561                         // Recurse on playlist's clips
562                         else if ( strcmp( resource, "<playlist>" ) == 0 )
563                         {
564                                 serialise_playlist( context, service, node );
565                         }
566                         
567                         // Recurse on tractor's producer
568                         else if ( strcmp( resource, "<tractor>" ) == 0 )
569                         {
570                                 context->pass = 0;
571                                 serialise_tractor( context, service, node );
572                                 context->pass = 1;
573                                 serialise_tractor( context, service, node );
574                                 context->pass = 0;
575                                 break;
576                         }
577
578                         // Treat it as a normal producer
579                         else
580                         {
581                                 serialise_producer( context, service, node );
582                         }
583                 }
584                 
585                 // Tell about a filter
586                 else if ( strcmp( mlt_type, "filter" ) == 0 )
587                 {
588                         serialise_filter( context, service, node );
589                         break;
590                 }
591                 
592                 // Tell about a transition
593                 else if ( strcmp( mlt_type, "transition" ) == 0 )
594                 {
595                         serialise_transition( context, service, node );
596                         break;
597                 }
598                 
599                 // Get the next connected service
600                 service = mlt_service_producer( service );
601         }
602 }
603
604 xmlDocPtr westley_make_doc( mlt_consumer consumer, mlt_service service )
605 {
606         mlt_properties properties = mlt_service_properties( service );
607         xmlDocPtr doc = xmlNewDoc( "1.0" );
608         xmlNodePtr root = xmlNewNode( NULL, "westley" );
609         struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) );
610         
611         xmlDocSetRootElement( doc, root );
612
613         // If we have root, then deal with it now
614         if ( mlt_properties_get( properties, "root" ) != NULL )
615         {
616                 xmlNewProp( root, "root", mlt_properties_get( properties, "root" ) );
617                 context->root = strdup( mlt_properties_get( properties, "root" ) );
618         }
619         else
620         {
621                 context->root = strdup( "" );
622         }
623
624         // Assign the additional 'storage' pattern for properties
625         context->store = mlt_properties_get( mlt_consumer_properties( consumer ), "store" );
626
627         // Assign a title property
628         if ( mlt_properties_get( properties, "title" ) != NULL )
629                 xmlNewProp( root, "title", mlt_properties_get( properties, "title" ) );
630
631         // Construct the context maps
632         context->id_map = mlt_properties_new();
633         context->hide_map = mlt_properties_new();
634         
635         // Ensure producer is a framework producer
636         mlt_properties_set( mlt_service_properties( service ), "mlt_type", "mlt_producer" );
637
638         // In pass one, we serialise the end producers and playlists,
639         // adding them to a map keyed by address.
640         serialise_service( context, service, root );
641
642         // In pass two, we serialise the tractor and reference the
643         // producers and playlists
644         context->pass++;
645         serialise_service( context, service, root );
646
647         // Cleanup resource
648         mlt_properties_close( context->id_map );
649         mlt_properties_close( context->hide_map );
650         free( context->root );
651         free( context );
652         
653         return doc;
654 }
655
656 static int consumer_start( mlt_consumer this )
657 {
658         xmlDocPtr doc = NULL;
659         
660         // Get the producer service
661         mlt_service service = mlt_service_producer( mlt_consumer_service( this ) );
662         if ( service != NULL )
663         {
664                 mlt_properties properties = mlt_consumer_properties( this );
665                 char *resource =  mlt_properties_get( properties, "resource" );
666
667                 // Set the title if provided
668                 if ( mlt_properties_get( properties, "title" ) )
669                         mlt_properties_set( mlt_service_properties( service ), "title", mlt_properties_get( properties, "title" ) );
670                 else if ( mlt_properties_get( mlt_service_properties( service ), "title" ) == NULL )
671                         mlt_properties_set( mlt_service_properties( service ), "title", "Anonymous Submission" );
672
673                 // Check for a root on the consumer properties and pass to service
674                 if ( mlt_properties_get( properties, "root" ) )
675                         mlt_properties_set( mlt_service_properties( service ), "root", mlt_properties_get( properties, "root" ) );
676
677                 // Specify roots in other cases...
678                 if ( resource != NULL && mlt_properties_get( properties, "root" ) == NULL )
679                 {
680                         // Get the current working directory
681                         char *cwd = getcwd( NULL, 0 );
682                         mlt_properties_set( mlt_service_properties( service ), "root", cwd );
683                         free( cwd );
684                 }
685
686                 // Make the document
687                 doc = westley_make_doc( this, service );
688
689                 // Handle the output
690                 if ( resource == NULL || !strcmp( resource, "" ) )
691                 {
692                         xmlDocFormatDump( stdout, doc, 1 );
693                 }
694                 else if ( strchr( resource, '.' ) == NULL )
695                 {
696                         xmlChar *buffer = NULL;
697                         int length = 0;
698                         xmlDocDumpMemory( doc, &buffer, &length );
699                         mlt_properties_set( properties, resource, buffer );
700                         xmlFree( buffer );
701                 }
702                 else
703                 {
704                         xmlSaveFormatFile( resource, doc, 1 );
705                 }
706                 
707                 // Close the document
708                 xmlFreeDoc( doc );
709         }
710         
711         mlt_consumer_stop( this );
712
713         mlt_consumer_stopped( this );
714
715         return 0;
716 }
717
718 static int consumer_is_stopped( mlt_consumer this )
719 {
720         return 1;
721 }