]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
rc/framework/mlt_frame.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                                 if ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) )
342                                 {
343                                         mlt_properties_set_position( frame_properties, "in", in == 0 ? this_in : in );
344                                         mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out : out );
345                                         mlt_filter_process( base->filters[ i ], frame );
346                                         mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
347                                 }
348                         }
349                 }
350         }
351 }
352
353 /** Obtain a frame.
354 */
355
356 int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
357 {
358         int result = 0;
359
360         // Lock the service
361         mlt_service_lock( this );
362
363         // Ensure that the frame is NULL
364         *frame = NULL;
365
366         // Only process if we have a valid service
367         if ( this != NULL && this->get_frame != NULL )
368         {
369                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
370                 mlt_position in = mlt_properties_get_position( properties, "in" );
371                 mlt_position out = mlt_properties_get_position( properties, "out" );
372
373                 result = this->get_frame( this, frame, index );
374
375                 if ( result == 0 )
376                 {
377                         mlt_properties_inc_ref( properties );
378                         properties = MLT_FRAME_PROPERTIES( *frame );
379                         if ( in >=0 && out > 0 )
380                         {
381                                 mlt_properties_set_position( properties, "in", in );
382                                 mlt_properties_set_position( properties, "out", out );
383                         }
384                         mlt_service_apply_filters( this, *frame, 1 );
385                         mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
386                 }
387         }
388
389         // Make sure we return a frame
390         if ( *frame == NULL )
391                 *frame = mlt_frame_init( );
392
393         // Unlock the service
394         mlt_service_unlock( this );
395
396         return result;
397 }
398
399 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
400 {
401         mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
402 }
403
404 /** Attach a filter.
405 */
406
407 int mlt_service_attach( mlt_service this, mlt_filter filter )
408 {
409         int error = this == NULL || filter == NULL;
410         if ( error == 0 )
411         {
412                 int i = 0;
413                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
414                 mlt_service_base *base = this->local;
415
416                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
417                         if ( base->filters[ i ] == filter )
418                                 error = 1;
419
420                 if ( error == 0 )
421                 {
422                         if ( base->filter_count == base->filter_size )
423                         {
424                                 base->filter_size += 10;
425                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
426                         }
427
428                         if ( base->filters != NULL )
429                         {
430                                 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
431                                 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
432                                 base->filters[ base->filter_count ++ ] = filter;
433                                 mlt_events_fire( properties, "service-changed", NULL );
434                                 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
435                                 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
436                         }
437                         else
438                         {
439                                 error = 2;
440                         }
441                 }
442         }
443         return error;
444 }
445
446 /** Detach a filter.
447 */
448
449 int mlt_service_detach( mlt_service this, mlt_filter filter )
450 {
451         int error = this == NULL || filter == NULL;
452         if ( error == 0 )
453         {
454                 int i = 0;
455                 mlt_service_base *base = this->local;
456                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
457
458                 for ( i = 0; i < base->filter_count; i ++ )
459                         if ( base->filters[ i ] == filter )
460                                 break;
461
462                 if ( i < base->filter_count )
463                 {
464                         base->filters[ i ] = NULL;
465                         for ( i ++ ; i < base->filter_count; i ++ )
466                                 base->filters[ i - 1 ] = base->filters[ i ];
467                         base->filter_count --;
468                         mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
469                         mlt_filter_close( filter );
470                         mlt_events_fire( properties, "service-changed", NULL );
471                 }
472         }
473         return error;
474 }
475
476 /** Retrieve a filter.
477 */
478
479 mlt_filter mlt_service_filter( mlt_service this, int index )
480 {
481         mlt_filter filter = NULL;
482         if ( this != NULL )
483         {
484                 mlt_service_base *base = this->local;
485                 if ( index >= 0 && index < base->filter_count )
486                         filter = base->filters[ index ];
487         }
488         return filter;
489 }
490
491 /** Close the service.
492 */
493
494 void mlt_service_close( mlt_service this )
495 {
496         mlt_service_lock( this );
497         if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
498         {
499                 mlt_service_unlock( this );
500                 if ( this->close != NULL )
501                 {
502                         this->close( this->close_object );
503                 }
504                 else
505                 {
506                         mlt_service_base *base = this->local;
507                         int i = 0;
508                         int count = base->filter_count;
509                         mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
510                         while( count -- )
511                                 mlt_service_detach( this, base->filters[ 0 ] );
512                         free( base->filters );
513                         for ( i = 0; i < base->count; i ++ )
514                                 if ( base->in[ i ] != NULL )
515                                         mlt_service_close( base->in[ i ] );
516                         this->parent.close = NULL;
517                         free( base->in );
518                         pthread_mutex_destroy( &base->mutex );
519                         free( base );
520                         mlt_properties_close( &this->parent );
521                 }
522         }
523         else
524         {
525                 mlt_service_unlock( this );
526         }
527 }
528