]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
mlt_filter.c
[mlt] / src / framework / mlt_service.c
1 /*
2  * mlt_service.c -- interface for all service classes
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
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 "config.h"
22 #include "mlt_service.h"
23 #include "mlt_filter.h"
24 #include "mlt_frame.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <pthread.h>
29
30 /** IMPORTANT NOTES
31
32         The base service implements a null frame producing service - as such,
33         it is functional without extension and will produce test cards frames 
34         and PAL sized audio frames.
35
36         PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT 
37         CONTROL THIS IN EXTENDING CLASSES.
38 */
39
40 /** Private service definition.
41 */
42
43 typedef struct
44 {
45         int size;
46         int count;
47         mlt_service *in;
48         mlt_service out;
49         int filter_count;
50         int filter_size;
51         mlt_filter *filters;
52         pthread_mutex_t mutex;
53 }
54 mlt_service_base;
55
56 /** Private methods
57 */
58
59 static void mlt_service_disconnect( mlt_service this );
60 static void mlt_service_connect( mlt_service this, mlt_service that );
61 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
62 static void mlt_service_property_changed( mlt_listener, mlt_properties owner, mlt_service this, void **args );
63
64 /** Constructor
65 */
66
67 int mlt_service_init( mlt_service this, void *child )
68 {
69         int error = 0;
70
71         // Initialise everything to NULL
72         memset( this, 0, sizeof( struct mlt_service_s ) );
73
74         // Assign the child
75         this->child = child;
76
77         // Generate local space
78         this->local = calloc( sizeof( mlt_service_base ), 1 );
79
80         // Associate the methods
81         this->get_frame = service_get_frame;
82         
83         // Initialise the properties
84         error = mlt_properties_init( &this->parent, this );
85         if ( error == 0 )
86         {
87                 this->parent.close = ( mlt_destructor )mlt_service_close;
88                 this->parent.close_object = this;
89
90                 mlt_events_init( &this->parent );
91                 mlt_events_register( &this->parent, "service-changed", NULL );
92                 mlt_events_register( &this->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed );
93                 pthread_mutex_init( &( ( mlt_service_base * )this->local )->mutex, NULL );
94         }
95
96         return error;
97 }
98
99 static void mlt_service_property_changed( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
100 {
101         if ( listener != NULL )
102                 listener( owner, this, ( char * )args[ 0 ] );
103 }
104
105 void mlt_service_lock( mlt_service this )
106 {
107         if ( this != NULL )
108                 pthread_mutex_lock( &( ( mlt_service_base * )this->local )->mutex );
109 }
110
111 void mlt_service_unlock( mlt_service this )
112 {
113         if ( this != NULL )
114                 pthread_mutex_unlock( &( ( mlt_service_base * )this->local )->mutex );
115 }
116
117 mlt_service_type mlt_service_identify( mlt_service this )
118 {
119         mlt_service_type type = invalid_type;
120         if ( this != NULL )
121         {
122                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
123                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
124                 char *resource = mlt_properties_get( properties, "resource" );
125                 if ( mlt_type == NULL )
126                         type = unknown_type;
127                 else if ( resource == NULL || !strcmp( resource, "<producer>" ) )
128                         type = producer_type;
129                 else if ( !strcmp( resource, "<playlist>" ) )
130                         type = playlist_type;
131                 else if ( !strcmp( resource, "<tractor>" ) )
132                         type = tractor_type;
133                 else if ( !strcmp( resource, "<multitrack>" ) )
134                         type = multitrack_type;
135                 else if ( !strcmp( mlt_type, "producer" ) )
136                         type = producer_type;
137                 else if ( !strcmp( mlt_type, "filter" ) )
138                         type = filter_type;
139                 else if ( !strcmp( mlt_type, "transition" ) )
140                         type = transition_type;
141                 else if ( !strcmp( mlt_type, "consumer" ) )
142                         type = consumer_type;
143                 else
144                         type = unknown_type;
145         }
146         return type;
147 }
148
149 /** Connect a producer service.
150         Returns: > 0 warning, == 0 success, < 0 serious error
151                          1 = this service does not accept input
152                          2 = the producer is invalid
153                          3 = the producer is already registered with this consumer
154 */
155
156 int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
157 {
158         int i = 0;
159
160         // Get the service base
161         mlt_service_base *base = this->local;
162
163         // Special case 'track' index - only works for last filter(s) in a particular chain
164         // but allows a filter to apply to the output frame regardless of which track it comes from
165         if ( index == -1 )
166                 index = 0;
167
168         // Check if the producer is already registered with this service
169         for ( i = 0; i < base->count; i ++ )
170                 if ( base->in[ i ] == producer )
171                         return 3;
172
173         // Allocate space
174         if ( index >= base->size )
175         {
176                 int new_size = base->size + index + 10;
177                 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
178                 if ( base->in != NULL )
179                 {
180                         for ( i = base->size; i < new_size; i ++ )
181                                 base->in[ i ] = NULL;
182                         base->size = new_size;
183                 }
184         }
185
186         // If we have space, assign the input
187         if ( base->in != NULL && index >= 0 && index < base->size )
188         {
189                 // Get the current service
190                 mlt_service current = base->in[ index ];
191
192                 // Increment the reference count on this producer
193                 if ( producer != NULL )
194                 {
195                         mlt_service_lock( producer );
196                         mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
197                         mlt_service_unlock( producer );
198                 }
199
200                 // Now we disconnect the producer service from its consumer
201                 mlt_service_disconnect( producer );
202                 
203                 // Add the service to index specified
204                 base->in[ index ] = producer;
205                 
206                 // Determine the number of active tracks
207                 if ( index >= base->count )
208                         base->count = index + 1;
209
210                 // Now we connect the producer to its connected consumer
211                 mlt_service_connect( producer, this );
212
213                 // Close the current service
214                 mlt_service_close( current );
215
216                 // Inform caller that all went well
217                 return 0;
218         }
219         else
220         {
221                 return -1;
222         }
223 }
224
225 /** Disconnect this service from its consumer.
226 */
227
228 static void mlt_service_disconnect( mlt_service this )
229 {
230         if ( this != NULL )
231         {
232                 // Get the service base
233                 mlt_service_base *base = this->local;
234
235                 // Disconnect
236                 base->out = NULL;
237         }
238 }
239
240 /** Obtain the consumer this service is connected to.
241 */
242
243 mlt_service mlt_service_consumer( mlt_service this )
244 {
245         // Get the service base
246         mlt_service_base *base = this->local;
247
248         // Return the connected consumer
249         return base->out;
250 }
251
252 /** Obtain the producer this service is connected to.
253 */
254
255 mlt_service mlt_service_producer( mlt_service this )
256 {
257         // Get the service base
258         mlt_service_base *base = this->local;
259
260         // Return the connected producer
261         return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
262 }
263
264 /** Associate this service to the consumer.
265 */
266
267 static void mlt_service_connect( mlt_service this, mlt_service that )
268 {
269         if ( this != NULL )
270         {
271                 // Get the service base
272                 mlt_service_base *base = this->local;
273
274                 // There's a bit more required here...
275                 base->out = that;
276         }
277 }
278
279 /** Get the first connected producer service.
280 */
281
282 mlt_service mlt_service_get_producer( mlt_service this )
283 {
284         mlt_service producer = NULL;
285
286         // Get the service base
287         mlt_service_base *base = this->local;
288
289         if ( base->in != NULL )
290                 producer = base->in[ 0 ];
291         
292         return producer;
293 }
294
295 /** Default implementation of get_frame.
296 */
297
298 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
299 {
300         mlt_service_base *base = this->local;
301         if ( index < base->count )
302         {
303                 mlt_service producer = base->in[ index ];
304                 if ( producer != NULL )
305                         return mlt_service_get_frame( producer, frame, index );
306         }
307         *frame = mlt_frame_init( );
308         return 0;
309 }
310
311 /** Return the properties object.
312 */
313
314 mlt_properties mlt_service_properties( mlt_service self )
315 {
316         return self != NULL ? &self->parent : NULL;
317 }
318
319 /** Recursively apply attached filters
320 */
321
322 void mlt_service_apply_filters( mlt_service this, mlt_frame frame, int index )
323 {
324         int i;
325         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
326         mlt_properties service_properties = MLT_SERVICE_PROPERTIES( this );
327         mlt_service_base *base = this->local;
328         mlt_position position = mlt_frame_get_position( frame );
329         mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
330         mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
331
332         if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
333         {
334                 // Process the frame with the attached filters
335                 for ( i = 0; i < base->filter_count; i ++ )
336                 {
337                         if ( base->filters[ i ] != NULL )
338                         {
339                                 mlt_position in = mlt_filter_get_in( base->filters[ i ] );
340                                 mlt_position out = mlt_filter_get_out( base->filters[ i ] );
341                                 int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
342                                 if ( !disable && ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) )
343                                 {
344                                         mlt_properties_set_position( frame_properties, "in", in == 0 ? this_in : in );
345                                         mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out : out );
346                                         mlt_filter_process( base->filters[ i ], frame );
347                                         mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
348                                 }
349                         }
350                 }
351         }
352 }
353
354 /** Obtain a frame.
355 */
356
357 int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
358 {
359         int result = 0;
360
361         // Lock the service
362         mlt_service_lock( this );
363
364         // Ensure that the frame is NULL
365         *frame = NULL;
366
367         // Only process if we have a valid service
368         if ( this != NULL && this->get_frame != NULL )
369         {
370                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
371                 mlt_position in = mlt_properties_get_position( properties, "in" );
372                 mlt_position out = mlt_properties_get_position( properties, "out" );
373
374                 result = this->get_frame( this, frame, index );
375
376                 if ( result == 0 )
377                 {
378                         mlt_properties_inc_ref( properties );
379                         properties = MLT_FRAME_PROPERTIES( *frame );
380                         if ( in >=0 && out > 0 )
381                         {
382                                 mlt_properties_set_position( properties, "in", in );
383                                 mlt_properties_set_position( properties, "out", out );
384                         }
385                         mlt_service_apply_filters( this, *frame, 1 );
386                         mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
387                 }
388         }
389
390         // Make sure we return a frame
391         if ( *frame == NULL )
392                 *frame = mlt_frame_init( );
393
394         // Unlock the service
395         mlt_service_unlock( this );
396
397         return result;
398 }
399
400 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
401 {
402         mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
403 }
404
405 /** Attach a filter.
406 */
407
408 int mlt_service_attach( mlt_service this, mlt_filter filter )
409 {
410         int error = this == NULL || filter == NULL;
411         if ( error == 0 )
412         {
413                 int i = 0;
414                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
415                 mlt_service_base *base = this->local;
416
417                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
418                         if ( base->filters[ i ] == filter )
419                                 error = 1;
420
421                 if ( error == 0 )
422                 {
423                         if ( base->filter_count == base->filter_size )
424                         {
425                                 base->filter_size += 10;
426                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
427                         }
428
429                         if ( base->filters != NULL )
430                         {
431                                 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
432                                 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
433                                 base->filters[ base->filter_count ++ ] = filter;
434                                 mlt_events_fire( properties, "service-changed", NULL );
435                                 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
436                                 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
437                         }
438                         else
439                         {
440                                 error = 2;
441                         }
442                 }
443         }
444         return error;
445 }
446
447 /** Detach a filter.
448 */
449
450 int mlt_service_detach( mlt_service this, mlt_filter filter )
451 {
452         int error = this == NULL || filter == NULL;
453         if ( error == 0 )
454         {
455                 int i = 0;
456                 mlt_service_base *base = this->local;
457                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
458
459                 for ( i = 0; i < base->filter_count; i ++ )
460                         if ( base->filters[ i ] == filter )
461                                 break;
462
463                 if ( i < base->filter_count )
464                 {
465                         base->filters[ i ] = NULL;
466                         for ( i ++ ; i < base->filter_count; i ++ )
467                                 base->filters[ i - 1 ] = base->filters[ i ];
468                         base->filter_count --;
469                         mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
470                         mlt_filter_close( filter );
471                         mlt_events_fire( properties, "service-changed", NULL );
472                 }
473         }
474         return error;
475 }
476
477 /** Retrieve a filter.
478 */
479
480 mlt_filter mlt_service_filter( mlt_service this, int index )
481 {
482         mlt_filter filter = NULL;
483         if ( this != NULL )
484         {
485                 mlt_service_base *base = this->local;
486                 if ( index >= 0 && index < base->filter_count )
487                         filter = base->filters[ index ];
488         }
489         return filter;
490 }
491
492 /** Close the service.
493 */
494
495 void mlt_service_close( mlt_service this )
496 {
497         mlt_service_lock( this );
498         if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
499         {
500                 mlt_service_unlock( this );
501                 if ( this->close != NULL )
502                 {
503                         this->close( this->close_object );
504                 }
505                 else
506                 {
507                         mlt_service_base *base = this->local;
508                         int i = 0;
509                         int count = base->filter_count;
510                         mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
511                         while( count -- )
512                                 mlt_service_detach( this, base->filters[ 0 ] );
513                         free( base->filters );
514                         for ( i = 0; i < base->count; i ++ )
515                                 if ( base->in[ i ] != NULL )
516                                         mlt_service_close( base->in[ i ] );
517                         this->parent.close = NULL;
518                         free( base->in );
519                         pthread_mutex_destroy( &base->mutex );
520                         free( base );
521                         mlt_properties_close( &this->parent );
522                 }
523         }
524         else
525         {
526                 mlt_service_unlock( this );
527         }
528 }
529