]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
move binary modules to libdir - affects MLT_REPOSITORY
[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 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 "mlt_service.h"
22 #include "mlt_filter.h"
23 #include "mlt_frame.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <pthread.h>
28
29 /** IMPORTANT NOTES
30
31         The base service implements a null frame producing service - as such,
32         it is functional without extension and will produce test cards frames 
33         and PAL sized audio frames.
34
35         PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT 
36         CONTROL THIS IN EXTENDING CLASSES.
37 */
38
39 /** Private service definition.
40 */
41
42 typedef struct
43 {
44         int size;
45         int count;
46         mlt_service *in;
47         mlt_service out;
48         int filter_count;
49         int filter_size;
50         mlt_filter *filters;
51         pthread_mutex_t mutex;
52 }
53 mlt_service_base;
54
55 /** Private methods
56 */
57
58 static void mlt_service_disconnect( mlt_service this );
59 static void mlt_service_connect( mlt_service this, mlt_service that );
60 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
61 static void mlt_service_property_changed( mlt_listener, mlt_properties owner, mlt_service this, void **args );
62
63 /** Constructor
64 */
65
66 int mlt_service_init( mlt_service this, void *child )
67 {
68         int error = 0;
69
70         // Initialise everything to NULL
71         memset( this, 0, sizeof( struct mlt_service_s ) );
72
73         // Assign the child
74         this->child = child;
75
76         // Generate local space
77         this->local = calloc( sizeof( mlt_service_base ), 1 );
78
79         // Associate the methods
80         this->get_frame = service_get_frame;
81         
82         // Initialise the properties
83         error = mlt_properties_init( &this->parent, this );
84         if ( error == 0 )
85         {
86                 this->parent.close = ( mlt_destructor )mlt_service_close;
87                 this->parent.close_object = this;
88
89                 mlt_events_init( &this->parent );
90                 mlt_events_register( &this->parent, "service-changed", NULL );
91                 mlt_events_register( &this->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed );
92                 pthread_mutex_init( &( ( mlt_service_base * )this->local )->mutex, NULL );
93         }
94
95         return error;
96 }
97
98 static void mlt_service_property_changed( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
99 {
100         if ( listener != NULL )
101                 listener( owner, this, ( char * )args[ 0 ] );
102 }
103
104 void mlt_service_lock( mlt_service this )
105 {
106         if ( this != NULL )
107                 pthread_mutex_lock( &( ( mlt_service_base * )this->local )->mutex );
108 }
109
110 void mlt_service_unlock( mlt_service this )
111 {
112         if ( this != NULL )
113                 pthread_mutex_unlock( &( ( mlt_service_base * )this->local )->mutex );
114 }
115
116 mlt_service_type mlt_service_identify( mlt_service this )
117 {
118         mlt_service_type type = invalid_type;
119         if ( this != NULL )
120         {
121                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
122                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
123                 char *resource = mlt_properties_get( properties, "resource" );
124                 if ( mlt_type == NULL )
125                         type = unknown_type;
126                 else if ( resource == NULL || !strcmp( resource, "<producer>" ) )
127                         type = producer_type;
128                 else if ( !strcmp( resource, "<playlist>" ) )
129                         type = playlist_type;
130                 else if ( !strcmp( resource, "<tractor>" ) )
131                         type = tractor_type;
132                 else if ( !strcmp( resource, "<multitrack>" ) )
133                         type = multitrack_type;
134                 else if ( !strcmp( mlt_type, "producer" ) )
135                         type = producer_type;
136                 else if ( !strcmp( mlt_type, "filter" ) )
137                         type = filter_type;
138                 else if ( !strcmp( mlt_type, "transition" ) )
139                         type = transition_type;
140                 else if ( !strcmp( mlt_type, "consumer" ) )
141                         type = consumer_type;
142                 else
143                         type = unknown_type;
144         }
145         return type;
146 }
147
148 /** Connect a producer service.
149         Returns: > 0 warning, == 0 success, < 0 serious error
150                          1 = this service does not accept input
151                          2 = the producer is invalid
152                          3 = the producer is already registered with this consumer
153 */
154
155 int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
156 {
157         int i = 0;
158
159         // Get the service base
160         mlt_service_base *base = this->local;
161
162         // Special case 'track' index - only works for last filter(s) in a particular chain
163         // but allows a filter to apply to the output frame regardless of which track it comes from
164         if ( index == -1 )
165                 index = 0;
166
167         // Check if the producer is already registered with this service
168         for ( i = 0; i < base->count; i ++ )
169                 if ( base->in[ i ] == producer )
170                         return 3;
171
172         // Allocate space
173         if ( index >= base->size )
174         {
175                 int new_size = base->size + index + 10;
176                 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
177                 if ( base->in != NULL )
178                 {
179                         for ( i = base->size; i < new_size; i ++ )
180                                 base->in[ i ] = NULL;
181                         base->size = new_size;
182                 }
183         }
184
185         // If we have space, assign the input
186         if ( base->in != NULL && index >= 0 && index < base->size )
187         {
188                 // Get the current service
189                 mlt_service current = base->in[ index ];
190
191                 // Increment the reference count on this producer
192                 if ( producer != NULL )
193                 {
194                         mlt_service_lock( producer );
195                         mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
196                         mlt_service_unlock( producer );
197                 }
198
199                 // Now we disconnect the producer service from its consumer
200                 mlt_service_disconnect( producer );
201                 
202                 // Add the service to index specified
203                 base->in[ index ] = producer;
204                 
205                 // Determine the number of active tracks
206                 if ( index >= base->count )
207                         base->count = index + 1;
208
209                 // Now we connect the producer to its connected consumer
210                 mlt_service_connect( producer, this );
211
212                 // Close the current service
213                 mlt_service_close( current );
214
215                 // Inform caller that all went well
216                 return 0;
217         }
218         else
219         {
220                 return -1;
221         }
222 }
223
224 /** Disconnect this service from its consumer.
225 */
226
227 static void mlt_service_disconnect( mlt_service this )
228 {
229         if ( this != NULL )
230         {
231                 // Get the service base
232                 mlt_service_base *base = this->local;
233
234                 // Disconnect
235                 base->out = NULL;
236         }
237 }
238
239 /** Obtain the consumer this service is connected to.
240 */
241
242 mlt_service mlt_service_consumer( mlt_service this )
243 {
244         // Get the service base
245         mlt_service_base *base = this->local;
246
247         // Return the connected consumer
248         return base->out;
249 }
250
251 /** Obtain the producer this service is connected to.
252 */
253
254 mlt_service mlt_service_producer( mlt_service this )
255 {
256         // Get the service base
257         mlt_service_base *base = this->local;
258
259         // Return the connected producer
260         return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
261 }
262
263 /** Associate this service to the consumer.
264 */
265
266 static void mlt_service_connect( mlt_service this, mlt_service that )
267 {
268         if ( this != NULL )
269         {
270                 // Get the service base
271                 mlt_service_base *base = this->local;
272
273                 // There's a bit more required here...
274                 base->out = that;
275         }
276 }
277
278 /** Get the first connected producer service.
279 */
280
281 mlt_service mlt_service_get_producer( mlt_service this )
282 {
283         mlt_service producer = NULL;
284
285         // Get the service base
286         mlt_service_base *base = this->local;
287
288         if ( base->in != NULL )
289                 producer = base->in[ 0 ];
290         
291         return producer;
292 }
293
294 /** Default implementation of get_frame.
295 */
296
297 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
298 {
299         mlt_service_base *base = this->local;
300         if ( index < base->count )
301         {
302                 mlt_service producer = base->in[ index ];
303                 if ( producer != NULL )
304                         return mlt_service_get_frame( producer, frame, index );
305         }
306         *frame = mlt_frame_init( this );
307         return 0;
308 }
309
310 /** Return the properties object.
311 */
312
313 mlt_properties mlt_service_properties( mlt_service self )
314 {
315         return self != NULL ? &self->parent : NULL;
316 }
317
318 /** Recursively apply attached filters
319 */
320
321 void mlt_service_apply_filters( mlt_service this, mlt_frame frame, int index )
322 {
323         int i;
324         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
325         mlt_properties service_properties = MLT_SERVICE_PROPERTIES( this );
326         mlt_service_base *base = this->local;
327         mlt_position position = mlt_frame_get_position( frame );
328         mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
329         mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
330
331         if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
332         {
333                 // Process the frame with the attached filters
334                 for ( i = 0; i < base->filter_count; i ++ )
335                 {
336                         if ( base->filters[ i ] != NULL )
337                         {
338                                 mlt_position in = mlt_filter_get_in( base->filters[ i ] );
339                                 mlt_position out = mlt_filter_get_out( base->filters[ i ] );
340                                 int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
341                                 if ( !disable && ( ( 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( this );
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 /** Retrieve the profile.
492 */
493
494 mlt_profile mlt_service_profile( mlt_service this )
495 {
496         return mlt_properties_get_data( MLT_SERVICE_PROPERTIES( this ), "_profile", NULL );
497 }
498
499 /** Close the service.
500 */
501
502 void mlt_service_close( mlt_service this )
503 {
504         mlt_service_lock( this );
505         if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
506         {
507                 mlt_service_unlock( this );
508                 if ( this->close != NULL )
509                 {
510                         this->close( this->close_object );
511                 }
512                 else
513                 {
514                         mlt_service_base *base = this->local;
515                         int i = 0;
516                         int count = base->filter_count;
517                         mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
518                         while( count -- )
519                                 mlt_service_detach( this, base->filters[ 0 ] );
520                         free( base->filters );
521                         for ( i = 0; i < base->count; i ++ )
522                                 if ( base->in[ i ] != NULL )
523                                         mlt_service_close( base->in[ i ] );
524                         this->parent.close = NULL;
525                         free( base->in );
526                         pthread_mutex_destroy( &base->mutex );
527                         free( base );
528                         mlt_properties_close( &this->parent );
529                 }
530         }
531         else
532         {
533                 mlt_service_unlock( this );
534         }
535 }
536