]> git.sesse.net Git - mlt/blob - src/modules/westley/consumer_westley.c
Producer filter attach/detach methods; major rework on westley consumer, minor on...
[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 <libxml/tree.h>
28
29 #define ID_SIZE 128
30
31 // This maintains counters for adding ids to elements
32 struct serialise_context_s
33 {
34         mlt_properties id_map;
35         int producer_count;
36         int multitrack_count;
37         int playlist_count;
38         int tractor_count;
39         int filter_count;
40         int transition_count;
41         int pass;
42         mlt_properties hide_map;
43 };
44 typedef struct serialise_context_s* serialise_context;
45
46 /** Forward references to static functions.
47 */
48
49 static int consumer_start( mlt_consumer parent );
50 static int consumer_is_stopped( mlt_consumer this );
51 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node );
52
53 typedef enum 
54 {
55         westley_existing,
56         westley_producer,
57         westley_multitrack,
58         westley_playlist,
59         westley_tractor,
60         westley_filter,
61         westley_transition
62 }
63 westley_type;
64
65 /** Create or retrieve an id associated to this service.
66 */
67
68 static char *westley_get_id( serialise_context context, mlt_service service, westley_type type )
69 {
70         char *id = NULL;
71         int i = 0;
72         mlt_properties map = context->id_map;
73
74         // Search the map for the service
75         for ( i = 0; i < mlt_properties_count( map ); i ++ )
76                 if ( mlt_properties_get_data_at( map, i, NULL ) == service )
77                         break;
78
79         // If the service is not in the map, and the type indicates a new id is needed...
80         if ( i >= mlt_properties_count( map ) && type != westley_existing )
81         {
82                 // Attempt to reuse existing id
83                 id = mlt_properties_get( mlt_service_properties( service ), "id" );
84
85                 // If no id, or the id is used in the map (for another service), then 
86                 // create a new one.
87                 if ( id == NULL || mlt_properties_get_data( map, id, NULL ) != NULL )
88                 {
89                         char temp[ ID_SIZE ];
90                         do
91                         {
92                                 switch( type )
93                                 {
94                                         case westley_producer:
95                                                 sprintf( temp, "producer%d", context->producer_count ++ );
96                                                 break;
97                                         case westley_multitrack:
98                                                 sprintf( temp, "multitrack%d", context->multitrack_count ++ );
99                                                 break;
100                                         case westley_playlist:
101                                                 sprintf( temp, "playlist%d", context->playlist_count ++ );
102                                                 break;
103                                         case westley_tractor:
104                                                 sprintf( temp, "tractor%d", context->tractor_count ++ );
105                                                 break;
106                                         case westley_filter:
107                                                 sprintf( temp, "filter%d", context->filter_count ++ );
108                                                 break;
109                                         case westley_transition:
110                                                 sprintf( temp, "transition%d", context->transition_count ++ );
111                                                 break;
112                                         case westley_existing:
113                                                 // Never gets here
114                                                 break;
115                                 }
116                         }
117                         while( mlt_properties_get_data( map, temp, NULL ) != NULL );
118
119                         // Set the data at the generated name
120                         mlt_properties_set_data( map, temp, service, 0, NULL, NULL );
121
122                         // Get the pointer to the name (i is the end of the list)
123                         id = mlt_properties_get_name( map, i );
124                 }
125                 else
126                 {
127                         // Store the existing id in the map
128                         mlt_properties_set_data( map, id, service, 0, NULL, NULL );
129                 }
130         }
131         else if ( type == westley_existing )
132         {
133                 id = mlt_properties_get_name( map, i );
134         }
135
136         return id;
137 }
138
139 /** This is what will be called by the factory - anything can be passed in
140         via the argument, but keep it simple.
141 */
142
143 mlt_consumer consumer_westley_init( char *arg )
144 {
145         // Create the consumer object
146         mlt_consumer this = calloc( sizeof( struct mlt_consumer_s ), 1 );
147
148         // If no malloc'd and consumer init ok
149         if ( this != NULL && mlt_consumer_init( this, NULL ) == 0 )
150         {
151                 // Allow thread to be started/stopped
152                 this->start = consumer_start;
153                 this->is_stopped = consumer_is_stopped;
154
155                 mlt_properties_set( mlt_consumer_properties( this ), "resource", arg );
156
157                 // Return the consumer produced
158                 return this;
159         }
160
161         // malloc or consumer init failed
162         free( this );
163
164         // Indicate failure
165         return NULL;
166 }
167
168 static inline void serialise_properties( mlt_properties properties, xmlNode *node )
169 {
170         int i;
171         xmlNode *p;
172         
173         // Enumerate the properties
174         for ( i = 0; i < mlt_properties_count( properties ); i++ )
175         {
176                 char *name = mlt_properties_get_name( properties, i );
177                 if ( name != NULL &&
178                          name[ 0 ] != '_' &&
179                          mlt_properties_get_value( properties, i ) != NULL &&
180                          strcmp( name, "westley" ) != 0 &&
181                          strcmp( name, "in" ) != 0 &&
182                          strcmp( name, "out" ) != 0 )
183                 {
184                         p = xmlNewChild( node, NULL, "property", NULL );
185                         xmlNewProp( p, "name", mlt_properties_get_name( properties, i ) );
186                         xmlNodeSetContent( p, mlt_properties_get_value( properties, i ) );
187                 }
188         }
189 }
190
191 static inline void serialise_producer_filters( serialise_context context, mlt_service service, xmlNode *node )
192 {
193         int i;
194         xmlNode *p;
195         mlt_filter filter = NULL;
196         
197         // Enumerate the filters
198         for ( i = 0; ( filter = mlt_producer_filter( MLT_PRODUCER( service ), i ) ) != NULL; i ++ )
199         {
200                 mlt_properties properties = mlt_filter_properties( filter );
201                 if ( mlt_properties_get_int( properties, "_fezzik" ) == 0 )
202                 {
203                         // Get a new id - if already allocated, do nothing
204                         char *id = westley_get_id( context, mlt_filter_service( filter ), westley_filter );
205                         if ( id == NULL )
206                                 continue;
207
208                         p = xmlNewChild( node, NULL, "filter", NULL );
209                         xmlNewProp( p, "id", id );
210                         serialise_properties( properties, p );
211                 }
212         }
213 }
214
215 static void serialise_producer( serialise_context context, mlt_service service, xmlNode *node )
216 {
217         xmlNode *child = node;
218         mlt_properties properties = mlt_service_properties( service );
219         
220         if ( context->pass == 0 )
221         {
222                 // Get a new id - if already allocated, do nothing
223                 char *id = westley_get_id( context, service, westley_producer );
224                 if ( id == NULL )
225                         return;
226
227                 child = xmlNewChild( node, NULL, "producer", NULL );
228
229                 // Set the id
230                 xmlNewProp( child, "id", id );
231                 serialise_properties( properties, child );
232                 serialise_producer_filters( context, service, child );
233
234                 // Add producer to the map
235                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
236         }
237         else
238         {
239                 // Get a new id - if already allocated, do nothing
240                 char *id = westley_get_id( context, service, westley_existing );
241                 xmlNewProp( node, "producer", id );
242         }
243 }
244
245 static void serialise_multitrack( serialise_context context, mlt_service service, xmlNode *node )
246 {
247         int i;
248         xmlNode *child = node;
249         
250         if ( context->pass == 0 )
251         {
252                 // Iterate over the tracks to collect the producers
253                 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
254                         serialise_service( context, MLT_SERVICE( mlt_multitrack_track( MLT_MULTITRACK( service ), i ) ), node );
255         }
256         else
257         {
258                 // Get a new id - if already allocated, do nothing
259                 char *id = westley_get_id( context, service, westley_multitrack );
260                 if ( id == NULL )
261                         return;
262
263                 // Create the multitrack node
264                 child = xmlNewChild( node, NULL, "multitrack", NULL );
265                 
266                 // Set the id
267                 xmlNewProp( child, "id", id );
268
269                 // Serialise the tracks
270                 for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
271                 {
272                         xmlNode *track = xmlNewChild( child, NULL, "track", NULL );
273                         int hide = 0;
274                         mlt_producer producer = mlt_multitrack_track( MLT_MULTITRACK( service ), i );
275
276                         char *id = westley_get_id( context, MLT_SERVICE( producer ), westley_existing );
277                         xmlNewProp( track, "producer", id );
278                         
279                         hide = mlt_properties_get_int( context->hide_map, id );
280                         if ( hide )
281                                 xmlNewProp( track, "hide", hide == 1 ? "video" : ( hide == 2 ? "audio" : "both" ) );
282                 }
283         }
284 }
285
286 static void serialise_playlist( serialise_context context, mlt_service service, xmlNode *node )
287 {
288         int i;
289         xmlNode *child = node;
290         mlt_playlist_clip_info info;
291         mlt_properties properties = mlt_service_properties( service );
292         
293         if ( context->pass == 0 )
294         {
295                 // Get a new id - if already allocated, do nothing
296                 char *id = westley_get_id( context, service, westley_playlist );
297                 if ( id == NULL )
298                         return;
299
300                 // Iterate over the playlist entries to collect the producers
301                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
302                 {
303                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
304                         {
305                                 if ( info.producer != NULL )
306                                 {
307                                         char *service_s = mlt_properties_get( mlt_producer_properties( info.producer ), "mlt_service" );
308                                         char *resource_s = mlt_properties_get( mlt_producer_properties( info.producer ), "resource" );
309                                         if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
310                                                 serialise_service( context, MLT_SERVICE( info.producer ), node );
311                                         else if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
312                                                 serialise_playlist( context, MLT_SERVICE( info.producer ), node );
313                                 }
314                         }
315                 }
316                 
317                 child = xmlNewChild( node, NULL, "playlist", NULL );
318
319                 // Set the id
320                 xmlNewProp( child, "id", id );
321
322                 // Add producer to the map
323                 mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
324         
325                 // Iterate over the playlist entries
326                 for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
327                 {
328                         if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
329                         {
330                                 char *service_s = mlt_properties_get( mlt_producer_properties( info.producer ), "mlt_service" );
331                                 if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
332                                 {
333                                         char length[ 20 ];
334                                         length[ 19 ] = '\0';
335                                         xmlNode *entry = xmlNewChild( child, NULL, "blank", NULL );
336                                         snprintf( length, 19, "%d", info.frame_count );
337                                         xmlNewProp( entry, "length", length );
338                                 }
339                                 else
340                                 {
341                                         char temp[ 20 ];
342                                         xmlNode *entry = xmlNewChild( child, NULL, "entry", NULL );
343                                         id = westley_get_id( context, MLT_SERVICE( info.producer ), westley_existing );
344                                         xmlNewProp( entry, "producer", id );
345                                         sprintf( temp, "%d", info.frame_in );
346                                         xmlNewProp( entry, "in", temp );
347                                         sprintf( temp, "%d", info.frame_out );
348                                         xmlNewProp( entry, "out", temp );
349                                 }
350                         }
351                 }
352         }
353         else if ( strcmp( (const char*) node->name, "tractor" ) != 0 )
354         {
355                 char *id = westley_get_id( context, service, westley_existing );
356                 xmlNewProp( node, "producer", id );
357         }
358 }
359
360 static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node )
361 {
362         xmlNode *child = node;
363         mlt_properties properties = mlt_service_properties( service );
364         
365         if ( context->pass == 0 )
366         {
367                 // Recurse on connected producer
368                 serialise_service( context, mlt_service_producer( service ), node );
369         }
370         else
371         {
372                 // Get a new id - if already allocated, do nothing
373                 char *id = westley_get_id( context, service, westley_tractor );
374                 if ( id == NULL )
375                         return;
376
377                 child = xmlNewChild( node, NULL, "tractor", NULL );
378
379                 // Set the id
380                 xmlNewProp( child, "id", id );
381                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
382                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
383
384                 // Recurse on connected producer
385                 serialise_service( context, mlt_service_producer( service ), child );
386         }
387 }
388
389 static void serialise_filter( serialise_context context, mlt_service service, xmlNode *node )
390 {
391         xmlNode *child = node;
392         mlt_properties properties = mlt_service_properties( service );
393         
394         // Recurse on connected producer
395         serialise_service( context, mlt_service_producer( service ), node );
396
397         if ( context->pass == 1 )
398         {
399                 // Get a new id - if already allocated, do nothing
400                 char *id = westley_get_id( context, service, westley_filter );
401                 if ( id == NULL )
402                         return;
403
404                 child = xmlNewChild( node, NULL, "filter", NULL );
405
406                 // Set the id
407                 xmlNewProp( child, "id", id );
408                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
409                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
410
411                 serialise_properties( properties, child );
412         }
413 }
414
415 static void serialise_transition( serialise_context context, mlt_service service, xmlNode *node )
416 {
417         xmlNode *child = node;
418         mlt_properties properties = mlt_service_properties( service );
419         
420         // Recurse on connected producer
421         serialise_service( context, MLT_SERVICE( MLT_TRANSITION( service )->producer ), node );
422
423         if ( context->pass == 1 )
424         {
425                 // Get a new id - if already allocated, do nothing
426                 char *id = westley_get_id( context, service, westley_transition );
427                 if ( id == NULL )
428                         return;
429
430                 child = xmlNewChild( node, NULL, "transition", NULL );
431         
432                 // Set the id
433                 xmlNewProp( child, "id", id );
434                 xmlNewProp( child, "in", mlt_properties_get( properties, "in" ) );
435                 xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
436
437                 serialise_properties( properties, child );
438         }
439 }
440
441 static void serialise_service( serialise_context context, mlt_service service, xmlNode *node )
442 {
443         // Iterate over consumer/producer connections
444         while ( service != NULL )
445         {
446                 mlt_properties properties = mlt_service_properties( service );
447                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
448                 
449                 // Tell about the producer
450                 if ( strcmp( mlt_type, "producer" ) == 0 )
451                 {
452                         serialise_producer( context, service, node );
453                         if ( mlt_properties_get( properties, "westley" ) != NULL )
454                                 break;
455                 }
456
457                 // Tell about the framework container producers
458                 else if ( strcmp( mlt_type, "mlt_producer" ) == 0 )
459                 {
460                         char *resource = mlt_properties_get( properties, "resource" );
461                         
462                         // Recurse on multitrack's tracks
463                         if ( strcmp( resource, "<multitrack>" ) == 0 )
464                         {
465                                 serialise_multitrack( context, service, node );
466                                 break;
467                         }
468                         
469                         // Recurse on playlist's clips
470                         else if ( strcmp( resource, "<playlist>" ) == 0 )
471                         {
472                                 serialise_playlist( context, service, node );
473                         }
474                         
475                         // Recurse on tractor's producer
476                         else if ( strcmp( resource, "<tractor>" ) == 0 )
477                         {
478                                 serialise_tractor( context, service, node );
479                                 break;
480                         }
481
482                         // Treat it as a normal producer
483                         else
484                         {
485                                 serialise_producer( context, service, node );
486                         }
487                 }
488                 
489                 // Tell about a filter
490                 else if ( strcmp( mlt_type, "filter" ) == 0 )
491                 {
492                         serialise_filter( context, service, node );
493                         break;
494                 }
495                 
496                 // Tell about a transition
497                 else if ( strcmp( mlt_type, "transition" ) == 0 )
498                 {
499                         serialise_transition( context, service, node );
500                         break;
501                 }
502                 
503                 // Get the next connected service
504                 service = mlt_service_producer( service );
505         }
506 }
507
508 xmlDocPtr westley_make_doc( mlt_service service )
509 {
510         xmlDocPtr doc = xmlNewDoc( "1.0" );
511         xmlNodePtr root = xmlNewNode( NULL, "westley" );
512         struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) );
513         
514         xmlDocSetRootElement( doc, root );
515                 
516         // Construct the context maps
517         context->id_map = mlt_properties_new();
518         context->hide_map = mlt_properties_new();
519         
520         // Ensure producer is a framework producer
521         mlt_properties_set( mlt_service_properties( service ), "mlt_type", "mlt_producer" );
522
523         // In pass one, we serialise the end producers and playlists,
524         // adding them to a map keyed by address.
525         serialise_service( context, service, root );
526
527         // In pass two, we serialise the tractor and reference the
528         // producers and playlists
529         context->pass++;
530         serialise_service( context, service, root );
531
532         // Cleanup resource
533         mlt_properties_close( context->id_map );
534         mlt_properties_close( context->hide_map );
535         free( context );
536         
537         return doc;
538 }
539
540
541 static int consumer_start( mlt_consumer this )
542 {
543         xmlDocPtr doc = NULL;
544         
545         // Get the producer service
546         mlt_service service = mlt_service_producer( mlt_consumer_service( this ) );
547         if ( service != NULL )
548         {
549                 char *resource =  mlt_properties_get( mlt_consumer_properties( this ), "resource" );
550
551                 doc = westley_make_doc( service );
552                 
553                 if ( resource == NULL || !strcmp( resource, "" ) )
554                         xmlDocFormatDump( stdout, doc, 1 );
555                 else
556                         xmlSaveFormatFile( resource, doc, 1 );
557                 
558                 xmlFreeDoc( doc );
559         }
560         
561         mlt_consumer_stop( this );
562
563         return 0;
564 }
565
566 static int consumer_is_stopped( mlt_consumer this )
567 {
568         return 1;
569 }