]> git.sesse.net Git - mlt/blob - src/modules/westley/consumer_westley.c
Cleanup license declarations and remove dv1394d references.
[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 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.
10  *
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.
15  *
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
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 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, "title" ) != 0 && 
188                          strcmp( name, "root" ) != 0 && 
189                          strcmp( name, "width" ) != 0 &&
190                          strcmp( name, "height" ) != 0 )
191                 {
192                         char *value = mlt_properties_get_value( properties, i );
193                         if ( strcmp( context->root, "" ) && !strncmp( value, context->root, strlen( context->root ) ) )
194                                 value += strlen( context->root ) + 1;
195                         p = xmlNewTextChild( node, NULL, "property", value );
196                         xmlNewProp( p, "name", name );
197                 }
198         }
199 }
200
201 static void serialise_store_properties( serialise_context context, mlt_properties properties, xmlNode *node, char *store )
202 {
203         int i;
204         xmlNode *p;
205         
206         // Enumerate the properties
207         for ( i = 0; store != NULL && i < mlt_properties_count( properties ); i++ )
208         {
209                 char *name = mlt_properties_get_name( properties, i );
210                 if ( !strncmp( name, store, strlen( store ) ) )
211                 {
212                         char *value = mlt_properties_get_value( properties, i );
213                         if ( value != NULL )
214                         {
215                                 if ( strcmp( context->root, "" ) && !strncmp( value, context->root, strlen( context->root ) ) )
216                                         value += strlen( context->root ) + 1;
217                                 p = xmlNewTextChild( node, NULL, "property", value );
218                                 xmlNewProp( p, "name", name );
219                         }
220                 }
221         }
222 }
223
224 static inline void serialise_service_filters( serialise_context context, mlt_service service, xmlNode *node )
225 {
226         int i;
227         xmlNode *p;
228         mlt_filter filter = NULL;
229         
230         // Enumerate the filters
231         for ( i = 0; ( filter = mlt_producer_filter( MLT_PRODUCER( service ), i ) ) != NULL; i ++ )
232         {
233                 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
234                 if ( mlt_properties_get_int( properties, "_fezzik" ) == 0 )
235                 {
236                         // Get a new id - if already allocated, do nothing
237                         char *id = westley_get_id( context, MLT_FILTER_SERVICE( filter ), westley_filter );
238                         if ( id != NULL )
239                         {
240                                 int in = mlt_properties_get_position( properties, "in" );
241                                 int out = mlt_properties_get_position( properties, "out" );
242                                 p = xmlNewChild( node, NULL, "filter", NULL );
243                                 xmlNewProp( p, "id", id );
244                                 if ( mlt_properties_get( properties, "title" ) )
245                                         xmlNewProp( p, "title", mlt_properties_get( properties, "title" ) );
246                                 if ( in != 0 || out != 0 )
247                                 {
248                                         char temp[ 20 ];
249                                         sprintf( temp, "%d", in );
250                                         xmlNewProp( p, "in", temp );
251                                         sprintf( temp, "%d", out );
252                                         xmlNewProp( p, "out", temp );
253                                 }
254                                 serialise_properties( context, properties, p );
255                                 serialise_service_filters( context, MLT_FILTER_SERVICE( filter ), p );
256                         }
257                 }
258         }
259 }
260
261 static void serialise_producer( serialise_context context, mlt_service service, xmlNode *node )
262 {
263         xmlNode *child = node;
264         mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( MLT_PRODUCER( service ) ) );
265         
266         if ( context->pass == 0 )
267         {
268                 mlt_properties properties = MLT_SERVICE_PROPERTIES( parent );
269                 // Get a new id - if already allocated, do nothing
270                 char *id = westley_get_id( context, parent, westley_producer );
271                 if ( id == NULL )
272                         return;
273
274                 child = xmlNewChild( node, NULL, "producer", NULL );
275
276                 // Set the id
277                 xmlNewProp( child, "id", id );
278                 if ( mlt_properties_get( properties, "title" ) )
279                         xmlNewProp( child, "title", mlt_properties_get( properties, "title" ) );
280                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
281                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
282                 serialise_properties( context, properties, child );
283                 serialise_service_filters( context, service, child );
284
285                 // Add producer to the map
286                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
287         }
288         else
289         {
290                 char *id = westley_get_id( context, parent, westley_existing );
291                 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
292                 xmlNewProp( node, "parent", id );
293                 xmlNewProp( node, "in", mlt_properties_get( properties, "in" ) );
294                 xmlNewProp( node, "out", mlt_properties_get( properties, "out" ) );
295         }
296 }
297
298 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node );
299
300 static void serialise_multitrack( serialise_context context, mlt_service service, xmlNode *node )
301 {
302         int i;
303         
304         if ( context->pass == 0 )
305         {
306                 // Iterate over the tracks to collect the producers
307                 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
308                 {
309                         mlt_producer producer = mlt_producer_cut_parent( mlt_multitrack_track( MLT_MULTITRACK( service ), i ) );
310                         serialise_service( context, MLT_SERVICE( producer ), node );
311                 }
312         }
313         else
314         {
315                 // Get a new id - if already allocated, do nothing
316                 char *id = westley_get_id( context, service, westley_multitrack );
317                 if ( id == NULL )
318                         return;
319
320                 // Serialise the tracks
321                 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
322                 {
323                         xmlNode *track = xmlNewChild( node, NULL, "track", NULL );
324                         int hide = 0;
325                         mlt_producer producer = mlt_multitrack_track( MLT_MULTITRACK( service ), i );
326                         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
327
328                         mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( producer ) );
329
330                         char *id = westley_get_id( context, MLT_SERVICE( parent ), westley_existing );
331                         xmlNewProp( track, "producer", id );
332                         if ( mlt_producer_is_cut( producer ) )
333                         {
334                                 xmlNewProp( track, "in", mlt_properties_get( properties, "in" ) );
335                                 xmlNewProp( track, "out", mlt_properties_get( properties, "out" ) );
336                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store );
337                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "meta." );
338                                 serialise_service_filters( context, MLT_PRODUCER_SERVICE( producer ), track );
339                         }
340                         
341                         hide = mlt_properties_get_int( context->hide_map, id );
342                         if ( hide )
343                                 xmlNewProp( track, "hide", hide == 1 ? "video" : ( hide == 2 ? "audio" : "both" ) );
344                 }
345                 serialise_service_filters( context, service, node );
346         }
347 }
348
349 static void serialise_playlist( serialise_context context, mlt_service service, xmlNode *node )
350 {
351         int i;
352         xmlNode *child = node;
353         mlt_playlist_clip_info info;
354         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
355         
356         if ( context->pass == 0 )
357         {
358                 // Get a new id - if already allocated, do nothing
359                 char *id = westley_get_id( context, service, westley_playlist );
360                 if ( id == NULL )
361                         return;
362
363                 // Iterate over the playlist entries to collect the producers
364                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
365                 {
366                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
367                         {
368                                 if ( info.producer != NULL )
369                                 {
370                                         mlt_producer producer = mlt_producer_cut_parent( info.producer );
371                                         char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
372                                         char *resource_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "resource" );
373                                         if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
374                                                 serialise_playlist( context, MLT_SERVICE( producer ), node );
375                                         else if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
376                                                 serialise_service( context, MLT_SERVICE( producer ), node );
377                                 }
378                         }
379                 }
380                 
381                 child = xmlNewChild( node, NULL, "playlist", NULL );
382
383                 // Set the id
384                 xmlNewProp( child, "id", id );
385                 if ( mlt_properties_get( properties, "title" ) )
386                         xmlNewProp( child, "title", mlt_properties_get( properties, "title" ) );
387
388                 // Store application specific properties
389                 serialise_store_properties( context, properties, child, context->store );
390                 serialise_store_properties( context, properties, child, "meta." );
391
392                 // Add producer to the map
393                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
394         
395                 // Iterate over the playlist entries
396                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
397                 {
398                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
399                         {
400                                 mlt_producer producer = mlt_producer_cut_parent( info.producer );
401                                 char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
402                                 if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
403                                 {
404                                         char length[ 20 ];
405                                         length[ 19 ] = '\0';
406                                         xmlNode *entry = xmlNewChild( child, NULL, "blank", NULL );
407                                         snprintf( length, 19, "%d", (int)info.frame_count );
408                                         xmlNewProp( entry, "length", length );
409                                 }
410                                 else
411                                 {
412                                         char temp[ 20 ];
413                                         xmlNode *entry = xmlNewChild( child, NULL, "entry", NULL );
414                                         id = westley_get_id( context, MLT_SERVICE( producer ), westley_existing );
415                                         xmlNewProp( entry, "producer", id );
416                                         sprintf( temp, "%d", (int)info.frame_in );
417                                         xmlNewProp( entry, "in", temp );
418                                         sprintf( temp, "%d", (int)info.frame_out );
419                                         xmlNewProp( entry, "out", temp );
420                                         if ( info.repeat > 1 )
421                                         {
422                                                 sprintf( temp, "%d", info.repeat );
423                                                 xmlNewProp( entry, "repeat", temp );
424                                         }
425                                         if ( mlt_producer_is_cut( info.cut ) )
426                                         {
427                                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, context->store );
428                                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "meta." );
429                                                 serialise_service_filters( context, MLT_PRODUCER_SERVICE( info.cut ), entry );
430                                         }
431                                 }
432                         }
433                 }
434
435                 serialise_service_filters( context, service, child );
436         }
437         else if ( strcmp( (const char*) node->name, "tractor" ) != 0 )
438         {
439                 char *id = westley_get_id( context, service, westley_existing );
440                 xmlNewProp( node, "producer", id );
441         }
442 }
443
444 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node )
445 {
446         xmlNode *child = node;
447         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
448         
449         if ( context->pass == 0 )
450         {
451                 // Recurse on connected producer
452                 serialise_service( context, mlt_service_producer( service ), node );
453         }
454         else
455         {
456                 // Get a new id - if already allocated, do nothing
457                 char *id = westley_get_id( context, service, westley_tractor );
458                 if ( id == NULL )
459                         return;
460
461                 child = xmlNewChild( node, NULL, "tractor", NULL );
462
463                 // Set the id
464                 xmlNewProp( child, "id", id );
465                 if ( mlt_properties_get( properties, "title" ) )
466                         xmlNewProp( child, "title", mlt_properties_get( properties, "title" ) );
467                 if ( mlt_properties_get( properties, "global_feed" ) )
468                         xmlNewProp( child, "global_feed", mlt_properties_get( properties, "global_feed" ) );
469                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
470                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
471
472                 // Store application specific properties
473                 serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store );
474                 serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "meta." );
475
476                 // Recurse on connected producer
477                 serialise_service( context, mlt_service_producer( service ), child );
478                 serialise_service_filters( context, service, child );
479         }
480 }
481
482 static void serialise_filter( serialise_context context, mlt_service service, xmlNode *node )
483 {
484         xmlNode *child = node;
485         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
486         
487         // Recurse on connected producer
488         serialise_service( context, mlt_service_producer( service ), node );
489
490         if ( context->pass == 1 )
491         {
492                 // Get a new id - if already allocated, do nothing
493                 char *id = westley_get_id( context, service, westley_filter );
494                 if ( id == NULL )
495                         return;
496
497                 child = xmlNewChild( node, NULL, "filter", NULL );
498
499                 // Set the id
500                 xmlNewProp( child, "id", id );
501                 if ( mlt_properties_get( properties, "title" ) )
502                         xmlNewProp( child, "title", mlt_properties_get( properties, "title" ) );
503                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
504                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
505
506                 serialise_properties( context, properties, child );
507                 serialise_service_filters( context, service, child );
508         }
509 }
510
511 static void serialise_transition( serialise_context context, mlt_service service, xmlNode *node )
512 {
513         xmlNode *child = node;
514         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
515         
516         // Recurse on connected producer
517         serialise_service( context, MLT_SERVICE( MLT_TRANSITION( service )->producer ), node );
518
519         if ( context->pass == 1 )
520         {
521                 // Get a new id - if already allocated, do nothing
522                 char *id = westley_get_id( context, service, westley_transition );
523                 if ( id == NULL )
524                         return;
525
526                 child = xmlNewChild( node, NULL, "transition", NULL );
527         
528                 // Set the id
529                 xmlNewProp( child, "id", id );
530                 if ( mlt_properties_get( properties, "title" ) )
531                         xmlNewProp( child, "title", mlt_properties_get( properties, "title" ) );
532                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
533                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
534
535                 serialise_properties( context, properties, child );
536                 serialise_service_filters( context, service, child );
537         }
538 }
539
540 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node )
541 {
542         // Iterate over consumer/producer connections
543         while ( service != NULL )
544         {
545                 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
546                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
547                 
548                 // Tell about the producer
549                 if ( strcmp( mlt_type, "producer" ) == 0 )
550                 {
551                         char *mlt_service = mlt_properties_get( properties, "mlt_service" );
552                         if ( mlt_properties_get( properties, "westley" ) == NULL && ( mlt_service != NULL && !strcmp( mlt_service, "tractor" ) ) )
553                         {
554                                 context->pass = 0;
555                                 serialise_tractor( context, service, node );
556                                 context->pass = 1;
557                                 serialise_tractor( context, service, node );
558                                 context->pass = 0;
559                                 break;
560                         }
561                         else
562                         {
563                                 serialise_producer( context, service, node );
564                         }
565                         if ( mlt_properties_get( properties, "westley" ) != NULL )
566                                 break;
567                 }
568
569                 // Tell about the framework container producers
570                 else if ( strcmp( mlt_type, "mlt_producer" ) == 0 )
571                 {
572                         char *resource = mlt_properties_get( properties, "resource" );
573                         
574                         // Recurse on multitrack's tracks
575                         if ( strcmp( resource, "<multitrack>" ) == 0 )
576                         {
577                                 serialise_multitrack( context, service, node );
578                                 break;
579                         }
580                         
581                         // Recurse on playlist's clips
582                         else if ( strcmp( resource, "<playlist>" ) == 0 )
583                         {
584                                 serialise_playlist( context, service, node );
585                         }
586                         
587                         // Recurse on tractor's producer
588                         else if ( strcmp( resource, "<tractor>" ) == 0 )
589                         {
590                                 context->pass = 0;
591                                 serialise_tractor( context, service, node );
592                                 context->pass = 1;
593                                 serialise_tractor( context, service, node );
594                                 context->pass = 0;
595                                 break;
596                         }
597
598                         // Treat it as a normal producer
599                         else
600                         {
601                                 serialise_producer( context, service, node );
602                         }
603                 }
604                 
605                 // Tell about a filter
606                 else if ( strcmp( mlt_type, "filter" ) == 0 )
607                 {
608                         serialise_filter( context, service, node );
609                         break;
610                 }
611                 
612                 // Tell about a transition
613                 else if ( strcmp( mlt_type, "transition" ) == 0 )
614                 {
615                         serialise_transition( context, service, node );
616                         break;
617                 }
618                 
619                 // Get the next connected service
620                 service = mlt_service_producer( service );
621         }
622 }
623
624 xmlDocPtr westley_make_doc( mlt_consumer consumer, mlt_service service )
625 {
626         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
627         xmlDocPtr doc = xmlNewDoc( "1.0" );
628         xmlNodePtr root = xmlNewNode( NULL, "westley" );
629         struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) );
630         
631         xmlDocSetRootElement( doc, root );
632
633         // If we have root, then deal with it now
634         if ( mlt_properties_get( properties, "root" ) != NULL )
635         {
636                 xmlNewProp( root, "root", mlt_properties_get( properties, "root" ) );
637                 context->root = strdup( mlt_properties_get( properties, "root" ) );
638         }
639         else
640         {
641                 context->root = strdup( "" );
642         }
643
644         // Assign the additional 'storage' pattern for properties
645         context->store = mlt_properties_get( MLT_CONSUMER_PROPERTIES( consumer ), "store" );
646
647         // Assign a title property
648         if ( mlt_properties_get( properties, "title" ) != NULL )
649                 xmlNewProp( root, "title", mlt_properties_get( properties, "title" ) );
650         mlt_properties_set_int( properties, "global_feed", 1 );
651
652         // Construct the context maps
653         context->id_map = mlt_properties_new();
654         context->hide_map = mlt_properties_new();
655         
656         // Ensure producer is a framework producer
657         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "mlt_type", "mlt_producer" );
658
659         // In pass one, we serialise the end producers and playlists,
660         // adding them to a map keyed by address.
661         serialise_service( context, service, root );
662
663         // In pass two, we serialise the tractor and reference the
664         // producers and playlists
665         context->pass++;
666         serialise_service( context, service, root );
667
668         // Cleanup resource
669         mlt_properties_close( context->id_map );
670         mlt_properties_close( context->hide_map );
671         free( context->root );
672         free( context );
673         
674         return doc;
675 }
676
677 static int consumer_start( mlt_consumer this )
678 {
679         xmlDocPtr doc = NULL;
680         
681         // Get the producer service
682         mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( this ) );
683         if ( service != NULL )
684         {
685                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
686                 char *resource =  mlt_properties_get( properties, "resource" );
687
688                 // Set the title if provided
689                 if ( mlt_properties_get( properties, "title" ) )
690                         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", mlt_properties_get( properties, "title" ) );
691                 else if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" ) == NULL )
692                         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", "Anonymous Submission" );
693
694                 // Check for a root on the consumer properties and pass to service
695                 if ( mlt_properties_get( properties, "root" ) )
696                         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", mlt_properties_get( properties, "root" ) );
697
698                 // Specify roots in other cases...
699                 if ( resource != NULL && mlt_properties_get( properties, "root" ) == NULL )
700                 {
701                         // Get the current working directory
702                         char *cwd = getcwd( NULL, 0 );
703                         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", cwd );
704                         free( cwd );
705                 }
706
707                 // Make the document
708                 doc = westley_make_doc( this, service );
709
710                 // Handle the output
711                 if ( resource == NULL || !strcmp( resource, "" ) )
712                 {
713                         xmlDocFormatDump( stdout, doc, 1 );
714                 }
715                 else if ( strchr( resource, '.' ) == NULL )
716                 {
717                         xmlChar *buffer = NULL;
718                         int length = 0;
719                         xmlDocDumpMemoryEnc( doc, &buffer, &length, "utf-8" );
720                         mlt_properties_set( properties, resource, buffer );
721                         xmlFree( buffer );
722                 }
723                 else
724                 {
725                         xmlSaveFormatFileEnc( resource, doc, "utf-8", 1 );
726                 }
727                 
728                 // Close the document
729                 xmlFreeDoc( doc );
730         }
731         
732         mlt_consumer_stop( this );
733
734         mlt_consumer_stopped( this );
735
736         return 0;
737 }
738
739 static int consumer_is_stopped( mlt_consumer this )
740 {
741         return 1;
742 }