]> git.sesse.net Git - mlt/blob - src/modules/xml/consumer_xml.c
Add 'all' property to xml consumer.
[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 <locale.h>
27 #include <libxml/tree.h>
28 #include <pthread.h>
29
30 #define ID_SIZE 128
31
32 #define _x (const xmlChar*)
33 #define _s (const char*)
34
35 // This maintains counters for adding ids to elements
36 struct serialise_context_s
37 {
38         mlt_properties id_map;
39         int producer_count;
40         int multitrack_count;
41         int playlist_count;
42         int tractor_count;
43         int filter_count;
44         int transition_count;
45         int pass;
46         mlt_properties hide_map;
47         char *root;
48         char *store;
49 };
50 typedef struct serialise_context_s* serialise_context;
51
52 /** Forward references to static functions.
53 */
54
55 static int consumer_start( mlt_consumer parent );
56 static int consumer_stop( mlt_consumer parent );
57 static int consumer_is_stopped( mlt_consumer this );
58 static void *consumer_thread( void *arg );
59 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node );
60
61 typedef enum 
62 {
63         xml_existing,
64         xml_producer,
65         xml_multitrack,
66         xml_playlist,
67         xml_tractor,
68         xml_filter,
69         xml_transition
70 }
71 xml_type;
72
73 /** Create or retrieve an id associated to this service.
74 */
75
76 static char *xml_get_id( serialise_context context, mlt_service service, xml_type type )
77 {
78         char *id = NULL;
79         int i = 0;
80         mlt_properties map = context->id_map;
81
82         // Search the map for the service
83         for ( i = 0; i < mlt_properties_count( map ); i ++ )
84                 if ( mlt_properties_get_data_at( map, i, NULL ) == service )
85                         break;
86
87         // If the service is not in the map, and the type indicates a new id is needed...
88         if ( i >= mlt_properties_count( map ) && type != xml_existing )
89         {
90                 // Attempt to reuse existing id
91                 id = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "id" );
92
93                 // If no id, or the id is used in the map (for another service), then 
94                 // create a new one.
95                 if ( id == NULL || mlt_properties_get_data( map, id, NULL ) != NULL )
96                 {
97                         char temp[ ID_SIZE ];
98                         do
99                         {
100                                 switch( type )
101                                 {
102                                         case xml_producer:
103                                                 sprintf( temp, "producer%d", context->producer_count ++ );
104                                                 break;
105                                         case xml_multitrack:
106                                                 sprintf( temp, "multitrack%d", context->multitrack_count ++ );
107                                                 break;
108                                         case xml_playlist:
109                                                 sprintf( temp, "playlist%d", context->playlist_count ++ );
110                                                 break;
111                                         case xml_tractor:
112                                                 sprintf( temp, "tractor%d", context->tractor_count ++ );
113                                                 break;
114                                         case xml_filter:
115                                                 sprintf( temp, "filter%d", context->filter_count ++ );
116                                                 break;
117                                         case xml_transition:
118                                                 sprintf( temp, "transition%d", context->transition_count ++ );
119                                                 break;
120                                         case xml_existing:
121                                                 // Never gets here
122                                                 break;
123                                 }
124                         }
125                         while( mlt_properties_get_data( map, temp, NULL ) != NULL );
126
127                         // Set the data at the generated name
128                         mlt_properties_set_data( map, temp, service, 0, NULL, NULL );
129
130                         // Get the pointer to the name (i is the end of the list)
131                         id = mlt_properties_get_name( map, i );
132                 }
133                 else
134                 {
135                         // Store the existing id in the map
136                         mlt_properties_set_data( map, id, service, 0, NULL, NULL );
137                 }
138         }
139         else if ( type == xml_existing )
140         {
141                 id = mlt_properties_get_name( map, i );
142         }
143
144         return id;
145 }
146
147 /** This is what will be called by the factory - anything can be passed in
148         via the argument, but keep it simple.
149 */
150
151 mlt_consumer consumer_xml_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
152 {
153         // Create the consumer object
154         mlt_consumer this = calloc( sizeof( struct mlt_consumer_s ), 1 );
155
156         // If no malloc'd and consumer init ok
157         if ( this != NULL && mlt_consumer_init( this, NULL, profile ) == 0 )
158         {
159                 // Allow thread to be started/stopped
160                 this->start = consumer_start;
161                 this->stop = consumer_stop;
162                 this->is_stopped = consumer_is_stopped;
163
164                 mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "resource", arg );
165                 mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "real_time", -1 );
166                 mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "prefill", 1 );
167                 mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "terminate_on_pause", 1 );
168
169                 // Return the consumer produced
170                 return this;
171         }
172
173         // malloc or consumer init failed
174         free( this );
175
176         // Indicate failure
177         return NULL;
178 }
179
180 static void serialise_properties( serialise_context context, mlt_properties properties, xmlNode *node )
181 {
182         int i;
183         xmlNode *p;
184         
185         // Enumerate the properties
186         for ( i = 0; i < mlt_properties_count( properties ); i++ )
187         {
188                 char *name = mlt_properties_get_name( properties, i );
189                 if ( name != NULL &&
190                          name[ 0 ] != '_' &&
191                          mlt_properties_get_value( properties, i ) != NULL &&
192                          strcmp( name, "mlt" ) != 0 &&
193                          strcmp( name, "in" ) != 0 &&
194                          strcmp( name, "out" ) != 0 && 
195                          strcmp( name, "id" ) != 0 && 
196                          strcmp( name, "title" ) != 0 && 
197                          strcmp( name, "root" ) != 0 && 
198                          strcmp( name, "width" ) != 0 &&
199                          strcmp( name, "height" ) != 0 )
200                 {
201                         char *value = mlt_properties_get_value( properties, i );
202                         int rootlen = strlen( context->root );
203                         if ( rootlen && !strncmp( value, context->root, rootlen ) && value[ rootlen ] == '/' )
204                                 value += rootlen + 1;
205                         p = xmlNewTextChild( node, NULL, _x("property"), _x(value) );
206                         xmlNewProp( p, _x("name"), _x(name) );
207                 }
208         }
209 }
210
211 static void serialise_store_properties( serialise_context context, mlt_properties properties, xmlNode *node, const char *store )
212 {
213         int i;
214         xmlNode *p;
215         
216         // Enumerate the properties
217         for ( i = 0; store != NULL && i < mlt_properties_count( properties ); i++ )
218         {
219                 char *name = mlt_properties_get_name( properties, i );
220                 if ( !strncmp( name, store, strlen( store ) ) )
221                 {
222                         char *value = mlt_properties_get_value( properties, i );
223                         if ( value != NULL )
224                         {
225                                 int rootlen = strlen( context->root );
226                                 if ( rootlen && !strncmp( value, context->root, rootlen ) && value[ rootlen ] == '/' )
227                                         value += rootlen + 1;
228                                 p = xmlNewTextChild( node, NULL, _x("property"), _x(value) );
229                                 xmlNewProp( p, _x("name"), _x(name) );
230                         }
231                 }
232         }
233 }
234
235 static inline void serialise_service_filters( serialise_context context, mlt_service service, xmlNode *node )
236 {
237         int i;
238         xmlNode *p;
239         mlt_filter filter = NULL;
240         
241         // Enumerate the filters
242         for ( i = 0; ( filter = mlt_producer_filter( MLT_PRODUCER( service ), i ) ) != NULL; i ++ )
243         {
244                 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
245                 if ( mlt_properties_get_int( properties, "_loader" ) == 0 )
246                 {
247                         // Get a new id - if already allocated, do nothing
248                         char *id = xml_get_id( context, MLT_FILTER_SERVICE( filter ), xml_filter );
249                         if ( id != NULL )
250                         {
251                                 int in = mlt_properties_get_position( properties, "in" );
252                                 int out = mlt_properties_get_position( properties, "out" );
253                                 p = xmlNewChild( node, NULL, _x("filter"), NULL );
254                                 xmlNewProp( p, _x("id"), _x(id) );
255                                 if ( mlt_properties_get( properties, "title" ) )
256                                         xmlNewProp( p, _x("title"), _x(mlt_properties_get( properties, "title" )) );
257                                 if ( in != 0 || out != 0 )
258                                 {
259                                         char temp[ 20 ];
260                                         sprintf( temp, "%d", in );
261                                         xmlNewProp( p, _x("in"), _x(temp) );
262                                         sprintf( temp, "%d", out );
263                                         xmlNewProp( p, _x("out"), _x(temp) );
264                                 }
265                                 serialise_properties( context, properties, p );
266                                 serialise_service_filters( context, MLT_FILTER_SERVICE( filter ), p );
267                         }
268                 }
269         }
270 }
271
272 static void serialise_producer( serialise_context context, mlt_service service, xmlNode *node )
273 {
274         xmlNode *child = node;
275         mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( MLT_PRODUCER( service ) ) );
276         
277         if ( context->pass == 0 )
278         {
279                 mlt_properties properties = MLT_SERVICE_PROPERTIES( parent );
280                 // Get a new id - if already allocated, do nothing
281                 char *id = xml_get_id( context, parent, xml_producer );
282                 if ( id == NULL )
283                         return;
284
285                 child = xmlNewChild( node, NULL, _x("producer"), NULL );
286
287                 // Set the id
288                 xmlNewProp( child, _x("id"), _x(id) );
289                 if ( mlt_properties_get( properties, "title" ) )
290                         xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
291                 xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
292                 xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
293                 serialise_properties( context, properties, child );
294                 serialise_service_filters( context, service, child );
295
296                 // Add producer to the map
297                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
298         }
299         else
300         {
301                 char *id = xml_get_id( context, parent, xml_existing );
302                 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
303                 xmlNewProp( node, _x("parent"), _x(id) );
304                 xmlNewProp( node, _x("in"), _x(mlt_properties_get( properties, "in" )) );
305                 xmlNewProp( node, _x("out"), _x(mlt_properties_get( properties, "out" )) );
306         }
307 }
308
309 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node );
310
311 static void serialise_multitrack( serialise_context context, mlt_service service, xmlNode *node )
312 {
313         int i;
314         
315         if ( context->pass == 0 )
316         {
317                 // Iterate over the tracks to collect the producers
318                 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
319                 {
320                         mlt_producer producer = mlt_producer_cut_parent( mlt_multitrack_track( MLT_MULTITRACK( service ), i ) );
321                         serialise_service( context, MLT_SERVICE( producer ), node );
322                 }
323         }
324         else
325         {
326                 // Get a new id - if already allocated, do nothing
327                 char *id = xml_get_id( context, service, xml_multitrack );
328                 if ( id == NULL )
329                         return;
330
331                 // Serialise the tracks
332                 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
333                 {
334                         xmlNode *track = xmlNewChild( node, NULL, _x("track"), NULL );
335                         int hide = 0;
336                         mlt_producer producer = mlt_multitrack_track( MLT_MULTITRACK( service ), i );
337                         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
338
339                         mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( producer ) );
340
341                         char *id = xml_get_id( context, MLT_SERVICE( parent ), xml_existing );
342                         xmlNewProp( track, _x("producer"), _x(id) );
343                         if ( mlt_producer_is_cut( producer ) )
344                         {
345                                 xmlNewProp( track, _x("in"), _x(mlt_properties_get( properties, "in" )) );
346                                 xmlNewProp( track, _x("out"), _x(mlt_properties_get( properties, "out" )) );
347                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store );
348                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "meta." );
349                                 serialise_service_filters( context, MLT_PRODUCER_SERVICE( producer ), track );
350                         }
351                         
352                         hide = mlt_properties_get_int( context->hide_map, id );
353                         if ( hide )
354                                 xmlNewProp( track, _x("hide"), _x( hide == 1 ? "video" : ( hide == 2 ? "audio" : "both" ) ) );
355                 }
356                 serialise_service_filters( context, service, node );
357         }
358 }
359
360 static void serialise_playlist( serialise_context context, mlt_service service, xmlNode *node )
361 {
362         int i;
363         xmlNode *child = node;
364         mlt_playlist_clip_info info;
365         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
366         
367         if ( context->pass == 0 )
368         {
369                 // Get a new id - if already allocated, do nothing
370                 char *id = xml_get_id( context, service, xml_playlist );
371                 if ( id == NULL )
372                         return;
373
374                 // Iterate over the playlist entries to collect the producers
375                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
376                 {
377                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
378                         {
379                                 if ( info.producer != NULL )
380                                 {
381                                         mlt_producer producer = mlt_producer_cut_parent( info.producer );
382                                         char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
383                                         char *resource_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "resource" );
384                                         if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
385                                                 serialise_playlist( context, MLT_SERVICE( producer ), node );
386                                         else if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
387                                                 serialise_service( context, MLT_SERVICE( producer ), node );
388                                 }
389                         }
390                 }
391                 
392                 child = xmlNewChild( node, NULL, _x("playlist"), NULL );
393
394                 // Set the id
395                 xmlNewProp( child, _x("id"), _x(id) );
396                 if ( mlt_properties_get( properties, "title" ) )
397                         xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
398
399                 // Store application specific properties
400                 serialise_store_properties( context, properties, child, context->store );
401                 serialise_store_properties( context, properties, child, "meta." );
402
403                 // Add producer to the map
404                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
405         
406                 // Iterate over the playlist entries
407                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
408                 {
409                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
410                         {
411                                 mlt_producer producer = mlt_producer_cut_parent( info.producer );
412                                 char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
413                                 if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
414                                 {
415                                         char length[ 20 ];
416                                         length[ 19 ] = '\0';
417                                         xmlNode *entry = xmlNewChild( child, NULL, _x("blank"), NULL );
418                                         snprintf( length, 19, "%d", (int)info.frame_count );
419                                         xmlNewProp( entry, _x("length"), _x(length) );
420                                 }
421                                 else
422                                 {
423                                         char temp[ 20 ];
424                                         xmlNode *entry = xmlNewChild( child, NULL, _x("entry"), NULL );
425                                         id = xml_get_id( context, MLT_SERVICE( producer ), xml_existing );
426                                         xmlNewProp( entry, _x("producer"), _x(id) );
427                                         sprintf( temp, "%d", (int)info.frame_in );
428                                         xmlNewProp( entry, _x("in"), _x(temp) );
429                                         sprintf( temp, "%d", (int)info.frame_out );
430                                         xmlNewProp( entry, _x("out"), _x(temp) );
431                                         if ( info.repeat > 1 )
432                                         {
433                                                 sprintf( temp, "%d", info.repeat );
434                                                 xmlNewProp( entry, _x("repeat"), _x(temp) );
435                                         }
436                                         if ( mlt_producer_is_cut( info.cut ) )
437                                         {
438                                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, context->store );
439                                                 serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "meta." );
440                                                 serialise_service_filters( context, MLT_PRODUCER_SERVICE( info.cut ), entry );
441                                         }
442                                 }
443                         }
444                 }
445
446                 serialise_service_filters( context, service, child );
447         }
448         else if ( xmlStrcmp( node->name, _x("tractor") ) != 0 )
449         {
450                 char *id = xml_get_id( context, service, xml_existing );
451                 xmlNewProp( node, _x("producer"), _x(id) );
452         }
453 }
454
455 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node )
456 {
457         xmlNode *child = node;
458         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
459         
460         if ( context->pass == 0 )
461         {
462                 // Recurse on connected producer
463                 serialise_service( context, mlt_service_producer( service ), node );
464         }
465         else
466         {
467                 // Get a new id - if already allocated, do nothing
468                 char *id = xml_get_id( context, service, xml_tractor );
469                 if ( id == NULL )
470                         return;
471
472                 child = xmlNewChild( node, NULL, _x("tractor"), NULL );
473
474                 // Set the id
475                 xmlNewProp( child, _x("id"), _x(id) );
476                 if ( mlt_properties_get( properties, "title" ) )
477                         xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
478                 if ( mlt_properties_get( properties, "global_feed" ) )
479                         xmlNewProp( child, _x("global_feed"), _x(mlt_properties_get( properties, "global_feed" )) );
480                 xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
481                 xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
482
483                 // Store application specific properties
484                 serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store );
485                 serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "meta." );
486
487                 // Recurse on connected producer
488                 serialise_service( context, mlt_service_producer( service ), child );
489                 serialise_service_filters( context, service, child );
490         }
491 }
492
493 static void serialise_filter( serialise_context context, mlt_service service, xmlNode *node )
494 {
495         xmlNode *child = node;
496         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
497         
498         // Recurse on connected producer
499         serialise_service( context, mlt_service_producer( service ), node );
500
501         if ( context->pass == 1 )
502         {
503                 // Get a new id - if already allocated, do nothing
504                 char *id = xml_get_id( context, service, xml_filter );
505                 if ( id == NULL )
506                         return;
507
508                 child = xmlNewChild( node, NULL, _x("filter"), NULL );
509
510                 // Set the id
511                 xmlNewProp( child, _x("id"), _x(id) );
512                 if ( mlt_properties_get( properties, "title" ) )
513                         xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
514                 xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
515                 xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
516
517                 serialise_properties( context, properties, child );
518                 serialise_service_filters( context, service, child );
519         }
520 }
521
522 static void serialise_transition( serialise_context context, mlt_service service, xmlNode *node )
523 {
524         xmlNode *child = node;
525         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
526         
527         // Recurse on connected producer
528         serialise_service( context, MLT_SERVICE( MLT_TRANSITION( service )->producer ), node );
529
530         if ( context->pass == 1 )
531         {
532                 // Get a new id - if already allocated, do nothing
533                 char *id = xml_get_id( context, service, xml_transition );
534                 if ( id == NULL )
535                         return;
536
537                 child = xmlNewChild( node, NULL, _x("transition"), NULL );
538         
539                 // Set the id
540                 xmlNewProp( child, _x("id"), _x(id) );
541                 if ( mlt_properties_get( properties, "title" ) )
542                         xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
543                 xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
544                 xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
545
546                 serialise_properties( context, properties, child );
547                 serialise_service_filters( context, service, child );
548         }
549 }
550
551 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node )
552 {
553         // Iterate over consumer/producer connections
554         while ( service != NULL )
555         {
556                 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
557                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
558                 
559                 // Tell about the producer
560                 if ( strcmp( mlt_type, "producer" ) == 0 )
561                 {
562                         char *mlt_service = mlt_properties_get( properties, "mlt_service" );
563                         if ( mlt_properties_get( properties, "xml" ) == NULL && ( mlt_service != NULL && !strcmp( mlt_service, "tractor" ) ) )
564                         {
565                                 context->pass = 0;
566                                 serialise_tractor( context, service, node );
567                                 context->pass = 1;
568                                 serialise_tractor( context, service, node );
569                                 context->pass = 0;
570                                 break;
571                         }
572                         else
573                         {
574                                 serialise_producer( context, service, node );
575                         }
576                         if ( mlt_properties_get( properties, "xml" ) != NULL )
577                                 break;
578                 }
579
580                 // Tell about the framework container producers
581                 else if ( strcmp( mlt_type, "mlt_producer" ) == 0 )
582                 {
583                         char *resource = mlt_properties_get( properties, "resource" );
584                         
585                         // Recurse on multitrack's tracks
586                         if ( resource && strcmp( resource, "<multitrack>" ) == 0 )
587                         {
588                                 serialise_multitrack( context, service, node );
589                                 break;
590                         }
591                         
592                         // Recurse on playlist's clips
593                         else if ( resource && strcmp( resource, "<playlist>" ) == 0 )
594                         {
595                                 serialise_playlist( context, service, node );
596                         }
597                         
598                         // Recurse on tractor's producer
599                         else if ( resource && strcmp( resource, "<tractor>" ) == 0 )
600                         {
601                                 context->pass = 0;
602                                 serialise_tractor( context, service, node );
603                                 context->pass = 1;
604                                 serialise_tractor( context, service, node );
605                                 context->pass = 0;
606                                 break;
607                         }
608
609                         // Treat it as a normal producer
610                         else
611                         {
612                                 serialise_producer( context, service, node );
613                         }
614                 }
615                 
616                 // Tell about a filter
617                 else if ( strcmp( mlt_type, "filter" ) == 0 )
618                 {
619                         serialise_filter( context, service, node );
620                         break;
621                 }
622                 
623                 // Tell about a transition
624                 else if ( strcmp( mlt_type, "transition" ) == 0 )
625                 {
626                         serialise_transition( context, service, node );
627                         break;
628                 }
629                 
630                 // Get the next connected service
631                 service = mlt_service_producer( service );
632         }
633 }
634
635 xmlDocPtr xml_make_doc( mlt_consumer consumer, mlt_service service )
636 {
637         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
638         xmlDocPtr doc = xmlNewDoc( _x("1.0") );
639         xmlNodePtr root = xmlNewNode( NULL, _x("mlt") );
640         struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) );
641         mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) );
642         char tmpstr[ 32 ];
643
644         xmlDocSetRootElement( doc, root );
645
646         // Indicate the numeric locale
647         xmlNewProp( root, _x("LC_NUMERIC"), _x( setlocale( LC_NUMERIC, NULL ) ) );
648
649         // If we have root, then deal with it now
650         if ( mlt_properties_get( properties, "root" ) != NULL )
651         {
652                 xmlNewProp( root, _x("root"), _x(mlt_properties_get( properties, "root" )) );
653                 context->root = strdup( mlt_properties_get( properties, "root" ) );
654         }
655         else
656         {
657                 context->root = strdup( "" );
658         }
659
660         // Assign the additional 'storage' pattern for properties
661         context->store = mlt_properties_get( MLT_CONSUMER_PROPERTIES( consumer ), "store" );
662
663         // Assign a title property
664         if ( mlt_properties_get( properties, "title" ) != NULL )
665                 xmlNewProp( root, _x("title"), _x(mlt_properties_get( properties, "title" )) );
666         mlt_properties_set_int( properties, "global_feed", 1 );
667
668         // Add a profile child element
669         if ( profile )
670         {
671                 xmlNodePtr profile_node = xmlNewChild( root, NULL, _x("profile"), NULL );
672                 if ( profile->description )
673                         xmlNewProp( profile_node, _x("description"), _x(profile->description) );
674                 sprintf( tmpstr, "%d", profile->width );
675                 xmlNewProp( profile_node, _x("width"), _x(tmpstr) );
676                 sprintf( tmpstr, "%d", profile->height );
677                 xmlNewProp( profile_node, _x("height"), _x(tmpstr) );
678                 sprintf( tmpstr, "%d", profile->progressive );
679                 xmlNewProp( profile_node, _x("progressive"), _x(tmpstr) );
680                 sprintf( tmpstr, "%d", profile->sample_aspect_num );
681                 xmlNewProp( profile_node, _x("sample_aspect_num"), _x(tmpstr) );
682                 sprintf( tmpstr, "%d", profile->sample_aspect_den );
683                 xmlNewProp( profile_node, _x("sample_aspect_den"), _x(tmpstr) );
684                 sprintf( tmpstr, "%d", profile->display_aspect_num );
685                 xmlNewProp( profile_node, _x("display_aspect_num"), _x(tmpstr) );
686                 sprintf( tmpstr, "%d", profile->display_aspect_den );
687                 xmlNewProp( profile_node, _x("display_aspect_den"), _x(tmpstr) );
688                 sprintf( tmpstr, "%d", profile->frame_rate_num );
689                 xmlNewProp( profile_node, _x("frame_rate_num"), _x(tmpstr) );
690                 sprintf( tmpstr, "%d", profile->frame_rate_den );
691                 xmlNewProp( profile_node, _x("frame_rate_den"), _x(tmpstr) );
692                 sprintf( tmpstr, "%d", profile->colorspace );
693                 xmlNewProp( profile_node, _x("colorspace"), _x(tmpstr) );
694         }
695
696         // Construct the context maps
697         context->id_map = mlt_properties_new();
698         context->hide_map = mlt_properties_new();
699         
700         // Ensure producer is a framework producer
701         mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "mlt_type", "mlt_producer" );
702
703         // In pass one, we serialise the end producers and playlists,
704         // adding them to a map keyed by address.
705         serialise_service( context, service, root );
706
707         // In pass two, we serialise the tractor and reference the
708         // producers and playlists
709         context->pass++;
710         serialise_service( context, service, root );
711
712         // Cleanup resource
713         mlt_properties_close( context->id_map );
714         mlt_properties_close( context->hide_map );
715         free( context->root );
716         free( context );
717         
718         return doc;
719 }
720
721
722 static void output_xml( mlt_consumer this )
723 {
724         // Get the producer service
725         mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( this ) );
726         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
727         char *resource =  mlt_properties_get( properties, "resource" );
728         xmlDocPtr doc = NULL;
729
730         if ( !service ) return;
731
732         // Set the title if provided
733         if ( mlt_properties_get( properties, "title" ) )
734                 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", mlt_properties_get( properties, "title" ) );
735         else if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" ) == NULL )
736                 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", "Anonymous Submission" );
737
738         // Check for a root on the consumer properties and pass to service
739         if ( mlt_properties_get( properties, "root" ) )
740                 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", mlt_properties_get( properties, "root" ) );
741
742         // Specify roots in other cases...
743         if ( resource != NULL && mlt_properties_get( properties, "root" ) == NULL )
744         {
745                 // Get the current working directory
746                 char *cwd = getcwd( NULL, 0 );
747                 mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", cwd );
748                 free( cwd );
749         }
750
751         // Make the document
752         doc = xml_make_doc( this, service );
753
754         // Handle the output
755         if ( resource == NULL || !strcmp( resource, "" ) )
756         {
757                 xmlDocFormatDump( stdout, doc, 1 );
758         }
759         else if ( strchr( resource, '.' ) == NULL )
760         {
761                 xmlChar *buffer = NULL;
762                 int length = 0;
763                 xmlDocDumpMemoryEnc( doc, &buffer, &length, "utf-8" );
764                 mlt_properties_set( properties, resource, _s(buffer) );
765 #ifdef WIN32
766                 xmlFreeFunc xmlFree = NULL;
767                 xmlMemGet( &xmlFree, NULL, NULL, NULL);
768 #endif
769                 xmlFree( buffer );
770         }
771         else
772         {
773                 xmlSaveFormatFileEnc( resource, doc, "utf-8", 1 );
774         }
775
776         // Close the document
777         xmlFreeDoc( doc );
778 }
779 static int consumer_start( mlt_consumer this )
780 {
781         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
782
783         if ( mlt_properties_get_int( properties, "all" ) )
784         {
785                 // Check that we're not already running
786                 if ( !mlt_properties_get_int( properties, "running" ) )
787                 {
788                         // Allocate a thread
789                         pthread_t *thread = calloc( 1, sizeof( pthread_t ) );
790
791                         // Assign the thread to properties
792                         mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL );
793
794                         // Set the running state
795                         mlt_properties_set_int( properties, "running", 1 );
796                         mlt_properties_set_int( properties, "joined", 0 );
797
798                         // Create the thread
799                         pthread_create( thread, NULL, consumer_thread, this );
800                 }
801         }
802         else
803         {
804                 output_xml( this );
805                 mlt_consumer_stop( this );
806                 mlt_consumer_stopped( this );
807         }
808         return 0;
809 }
810
811 static int consumer_is_stopped( mlt_consumer this )
812 {
813         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
814         return !mlt_properties_get_int( properties, "running" );
815 }
816
817 static int consumer_stop( mlt_consumer this )
818 {
819         // Get the properties
820         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
821
822         // Check that we're running
823         if ( !mlt_properties_get_int( properties, "joined" ) )
824         {
825                 // Get the thread
826                 pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL );
827
828                 // Stop the thread
829                 mlt_properties_set_int( properties, "running", 0 );
830                 mlt_properties_set_int( properties, "joined", 1 );
831
832                 // Wait for termination
833                 if ( thread )
834                         pthread_join( *thread, NULL );
835         }
836
837         return 0;
838 }
839
840 static void *consumer_thread( void *arg )
841 {
842         // Map the argument to the object
843         mlt_consumer this = arg;
844
845         // Get the properties
846         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
847
848         // Convenience functionality
849         int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
850         int terminated = 0;
851
852         // Frame and size
853         mlt_frame frame = NULL;
854
855         // Loop while running
856         while( !terminated && mlt_properties_get_int( properties, "running" ) )
857         {
858                 // Get the frame
859                 frame = mlt_consumer_rt_frame( this );
860
861                 // Check for termination
862                 if ( terminate_on_pause && frame != NULL )
863                         terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0;
864
865                 // Check that we have a frame to work with
866                 if ( frame )
867                 {
868                         int width = 0, height = 0;
869                         int frequency = mlt_properties_get_int( properties, "frequency" );
870                         int channels = mlt_properties_get_int( properties, "channels" );
871                         int samples = 0;
872                         mlt_image_format iformat = mlt_image_yuv422;
873                         mlt_audio_format aformat = mlt_audio_s16;
874                         uint8_t *buffer;
875
876                         mlt_frame_get_image( frame, &buffer, &iformat, &width, &height, 0 );
877                         mlt_frame_get_audio( frame, (void**) &buffer, &aformat, &frequency, &channels, &samples );
878
879                         // Close the frame
880                         mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
881                         mlt_frame_close( frame );
882                 }
883         }
884         output_xml( this );
885
886         // Indicate that the consumer is stopped
887         mlt_properties_set_int( properties, "running", 0 );
888         mlt_consumer_stopped( this );
889
890         return NULL;
891 }