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