]> git.sesse.net Git - mlt/blob - src/modules/westley/consumer_westley.c
Big modification - switch to macros for parent class access
[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 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 void serialise_store_properties( serialise_context context, mlt_properties properties, xmlNode *node, char *store )
203 {
204         int i;
205         xmlNode *p;
206         
207         // Enumerate the properties
208         for ( i = 0; store != NULL && i < mlt_properties_count( properties ); i++ )
209         {
210                 char *name = mlt_properties_get_name( properties, i );
211                 if ( !strncmp( name, store, strlen( 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_producer_is_cut( producer ) )
332                         {
333                                 xmlNewProp( track, "in", mlt_properties_get( properties, "in" ) );
334                                 xmlNewProp( track, "out", mlt_properties_get( properties, "out" ) );
335                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store );
336                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "meta." );
337                                 serialise_service_filters( context, MLT_PRODUCER_SERVICE( producer ), track );
338                         }
339                         
340                         hide = mlt_properties_get_int( context->hide_map, id );
341                         if ( hide )
342                                 xmlNewProp( track, "hide", hide == 1 ? "video" : ( hide == 2 ? "audio" : "both" ) );
343                 }
344                 serialise_service_filters( context, service, node );
345         }
346 }
347
348 static void serialise_playlist( serialise_context context, mlt_service service, xmlNode *node )
349 {
350         int i;
351         xmlNode *child = node;
352         mlt_playlist_clip_info info;
353         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
354         
355         if ( context->pass == 0 )
356         {
357                 // Get a new id - if already allocated, do nothing
358                 char *id = westley_get_id( context, service, westley_playlist );
359                 if ( id == NULL )
360                         return;
361
362                 // Iterate over the playlist entries to collect the producers
363                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
364                 {
365                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
366                         {
367                                 if ( info.producer != NULL )
368                                 {
369                                         mlt_producer producer = mlt_producer_cut_parent( info.producer );
370                                         char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
371                                         char *resource_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "resource" );
372                                         if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
373                                                 serialise_playlist( context, MLT_SERVICE( producer ), node );
374                                         else if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
375                                                 serialise_service( context, MLT_SERVICE( producer ), node );
376                                 }
377                         }
378                 }
379                 
380                 child = xmlNewChild( node, NULL, "playlist", NULL );
381
382                 // Set the id
383                 xmlNewProp( child, "id", id );
384
385                 // Store application specific properties
386                 serialise_store_properties( context, properties, child, context->store );
387                 serialise_store_properties( context, properties, child, "meta." );
388
389                 // Add producer to the map
390                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
391         
392                 // Iterate over the playlist entries
393                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
394                 {
395                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
396                         {
397                                 mlt_producer producer = mlt_producer_cut_parent( info.producer );
398                                 char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
399                                 if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
400                                 {
401                                         char length[ 20 ];
402                                         length[ 19 ] = '\0';
403                                         xmlNode *entry = xmlNewChild( child, NULL, "blank", NULL );
404                                         snprintf( length, 19, "%d", info.frame_count );
405                                         xmlNewProp( entry, "length", length );
406                                 }
407                                 else
408                                 {
409                                         char temp[ 20 ];
410                                         xmlNode *entry = xmlNewChild( child, NULL, "entry", NULL );
411                                         id = westley_get_id( context, MLT_SERVICE( producer ), westley_existing );
412                                         xmlNewProp( entry, "producer", id );
413                                         sprintf( temp, "%d", info.frame_in );
414                                         xmlNewProp( entry, "in", temp );
415                                         sprintf( temp, "%d", info.frame_out );
416                                         xmlNewProp( entry, "out", temp );
417                                         if ( info.repeat > 1 )
418                                         {
419                                                 sprintf( temp, "%d", info.repeat );
420                                                 xmlNewProp( entry, "repeat", temp );
421                                         }
422                                         if ( mlt_producer_is_cut( info.cut ) )
423                                         {
424                                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, context->store );
425                                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "meta." );
426                                                 serialise_service_filters( context, MLT_PRODUCER_SERVICE( info.cut ), entry );
427                                         }
428                                 }
429                         }
430                 }
431
432                 serialise_service_filters( context, service, child );
433         }
434         else if ( strcmp( (const char*) node->name, "tractor" ) != 0 )
435         {
436                 char *id = westley_get_id( context, service, westley_existing );
437                 xmlNewProp( node, "producer", id );
438         }
439 }
440
441 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node )
442 {
443         xmlNode *child = node;
444         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
445         
446         if ( context->pass == 0 )
447         {
448                 // Recurse on connected producer
449                 serialise_service( context, mlt_service_producer( service ), node );
450         }
451         else
452         {
453                 // Get a new id - if already allocated, do nothing
454                 char *id = westley_get_id( context, service, westley_tractor );
455                 if ( id == NULL )
456                         return;
457
458                 child = xmlNewChild( node, NULL, "tractor", NULL );
459
460                 // Set the id
461                 xmlNewProp( child, "id", id );
462                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
463                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
464
465                 // Store application specific properties
466                 serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store );
467                 serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "meta." );
468
469                 // Recurse on connected producer
470                 serialise_service( context, mlt_service_producer( service ), child );
471                 serialise_service_filters( context, service, child );
472         }
473 }
474
475 static void serialise_filter( serialise_context context, mlt_service service, xmlNode *node )
476 {
477         xmlNode *child = node;
478         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
479         
480         // Recurse on connected producer
481         serialise_service( context, mlt_service_producer( service ), node );
482
483         if ( context->pass == 1 )
484         {
485                 // Get a new id - if already allocated, do nothing
486                 char *id = westley_get_id( context, service, westley_filter );
487                 if ( id == NULL )
488                         return;
489
490                 child = xmlNewChild( node, NULL, "filter", NULL );
491
492                 // Set the id
493                 xmlNewProp( child, "id", id );
494                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
495                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
496
497                 serialise_properties( context, properties, child );
498                 serialise_service_filters( context, service, child );
499         }
500 }
501
502 static void serialise_transition( serialise_context context, mlt_service service, xmlNode *node )
503 {
504         xmlNode *child = node;
505         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
506         
507         // Recurse on connected producer
508         serialise_service( context, MLT_SERVICE( MLT_TRANSITION( service )->producer ), node );
509
510         if ( context->pass == 1 )
511         {
512                 // Get a new id - if already allocated, do nothing
513                 char *id = westley_get_id( context, service, westley_transition );
514                 if ( id == NULL )
515                         return;
516
517                 child = xmlNewChild( node, NULL, "transition", NULL );
518         
519                 // Set the id
520                 xmlNewProp( child, "id", id );
521                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
522                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
523
524                 serialise_properties( context, properties, child );
525                 serialise_service_filters( context, service, child );
526         }
527 }
528
529 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node )
530 {
531         // Iterate over consumer/producer connections
532         while ( service != NULL )
533         {
534                 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
535                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
536                 
537                 // Tell about the producer
538                 if ( strcmp( mlt_type, "producer" ) == 0 )
539                 {
540                         char *mlt_service = mlt_properties_get( properties, "mlt_service" );
541                         if ( mlt_properties_get( properties, "westley" ) == NULL && !strcmp( mlt_service, "tractor" ) )
542                         {
543                                 context->pass = 0;
544                                 serialise_tractor( context, service, node );
545                                 context->pass = 1;
546                                 serialise_tractor( context, service, node );
547                                 context->pass = 0;
548                                 break;
549                         }
550                         else
551                         {
552                                 serialise_producer( context, service, node );
553                         }
554                         if ( mlt_properties_get( properties, "westley" ) != NULL )
555                                 break;
556                 }
557
558                 // Tell about the framework container producers
559                 else if ( strcmp( mlt_type, "mlt_producer" ) == 0 )
560                 {
561                         char *resource = mlt_properties_get( properties, "resource" );
562                         
563                         // Recurse on multitrack's tracks
564                         if ( strcmp( resource, "<multitrack>" ) == 0 )
565                         {
566                                 serialise_multitrack( context, service, node );
567                                 break;
568                         }
569                         
570                         // Recurse on playlist's clips
571                         else if ( strcmp( resource, "<playlist>" ) == 0 )
572                         {
573                                 serialise_playlist( context, service, node );
574                         }
575                         
576                         // Recurse on tractor's producer
577                         else if ( strcmp( resource, "<tractor>" ) == 0 )
578                         {
579                                 context->pass = 0;
580                                 serialise_tractor( context, service, node );
581                                 context->pass = 1;
582                                 serialise_tractor( context, service, node );
583                                 context->pass = 0;
584                                 break;
585                         }
586
587                         // Treat it as a normal producer
588                         else
589                         {
590                                 serialise_producer( context, service, node );
591                         }
592                 }
593                 
594                 // Tell about a filter
595                 else if ( strcmp( mlt_type, "filter" ) == 0 )
596                 {
597                         serialise_filter( context, service, node );
598                         break;
599                 }
600                 
601                 // Tell about a transition
602                 else if ( strcmp( mlt_type, "transition" ) == 0 )
603                 {
604                         serialise_transition( context, service, node );
605                         break;
606                 }
607                 
608                 // Get the next connected service
609                 service = mlt_service_producer( service );
610         }
611 }
612
613 xmlDocPtr westley_make_doc( mlt_consumer consumer, mlt_service service )
614 {
615         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
616         xmlDocPtr doc = xmlNewDoc( "1.0" );
617         xmlNodePtr root = xmlNewNode( NULL, "westley" );
618         struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) );
619         
620         xmlDocSetRootElement( doc, root );
621
622         // If we have root, then deal with it now
623         if ( mlt_properties_get( properties, "root" ) != NULL )
624         {
625                 xmlNewProp( root, "root", mlt_properties_get( properties, "root" ) );
626                 context->root = strdup( mlt_properties_get( properties, "root" ) );
627         }
628         else
629         {
630                 context->root = strdup( "" );
631         }
632
633         // Assign the additional 'storage' pattern for properties
634         context->store = mlt_properties_get( MLT_CONSUMER_PROPERTIES( consumer ), "store" );
635
636         // Assign a title property
637         if ( mlt_properties_get( properties, "title" ) != NULL )
638                 xmlNewProp( root, "title", mlt_properties_get( properties, "title" ) );
639
640         // Construct the context maps
641         context->id_map = mlt_properties_new();
642         context->hide_map = mlt_properties_new();
643         
644         // Ensure producer is a framework producer
645         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "mlt_type", "mlt_producer" );
646
647         // In pass one, we serialise the end producers and playlists,
648         // adding them to a map keyed by address.
649         serialise_service( context, service, root );
650
651         // In pass two, we serialise the tractor and reference the
652         // producers and playlists
653         context->pass++;
654         serialise_service( context, service, root );
655
656         // Cleanup resource
657         mlt_properties_close( context->id_map );
658         mlt_properties_close( context->hide_map );
659         free( context->root );
660         free( context );
661         
662         return doc;
663 }
664
665 static int consumer_start( mlt_consumer this )
666 {
667         xmlDocPtr doc = NULL;
668         
669         // Get the producer service
670         mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( this ) );
671         if ( service != NULL )
672         {
673                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
674                 char *resource =  mlt_properties_get( properties, "resource" );
675
676                 // Set the title if provided
677                 if ( mlt_properties_get( properties, "title" ) )
678                         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", mlt_properties_get( properties, "title" ) );
679                 else if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" ) == NULL )
680                         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", "Anonymous Submission" );
681
682                 // Check for a root on the consumer properties and pass to service
683                 if ( mlt_properties_get( properties, "root" ) )
684                         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", mlt_properties_get( properties, "root" ) );
685
686                 // Specify roots in other cases...
687                 if ( resource != NULL && mlt_properties_get( properties, "root" ) == NULL )
688                 {
689                         // Get the current working directory
690                         char *cwd = getcwd( NULL, 0 );
691                         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", cwd );
692                         free( cwd );
693                 }
694
695                 // Make the document
696                 doc = westley_make_doc( this, service );
697
698                 // Handle the output
699                 if ( resource == NULL || !strcmp( resource, "" ) )
700                 {
701                         xmlDocFormatDump( stdout, doc, 1 );
702                 }
703                 else if ( strchr( resource, '.' ) == NULL )
704                 {
705                         xmlChar *buffer = NULL;
706                         int length = 0;
707                         xmlDocDumpMemory( doc, &buffer, &length );
708                         mlt_properties_set( properties, resource, buffer );
709                         xmlFree( buffer );
710                 }
711                 else
712                 {
713                         xmlSaveFormatFile( resource, doc, 1 );
714                 }
715                 
716                 // Close the document
717                 xmlFreeDoc( doc );
718         }
719         
720         mlt_consumer_stop( this );
721
722         mlt_consumer_stopped( this );
723
724         return 0;
725 }
726
727 static int consumer_is_stopped( mlt_consumer this )
728 {
729         return 1;
730 }