]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
mlt_properties.c, mlt_service.c: bugfix to make reference counting and service closur...
[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, "<playlist>" ) )
127                         type = playlist_type;
128                 else if (resource != NULL && !strcmp( resource, "<tractor>" ) )
129                         type = tractor_type;
130                 else if (resource != NULL && !strcmp( resource, "<multitrack>" ) )
131                         type = multitrack_type;
132                 else if ( !strcmp( mlt_type, "producer" ) )
133                         type = producer_type;
134                 else if ( !strcmp( mlt_type, "filter" ) )
135                         type = filter_type;
136                 else if ( !strcmp( mlt_type, "transition" ) )
137                         type = transition_type;
138                 else if ( !strcmp( mlt_type, "consumer" ) )
139                         type = consumer_type;
140                 else
141                         type = unknown_type;
142         }
143         return type;
144 }
145
146 /** Connect a producer service.
147         Returns: > 0 warning, == 0 success, < 0 serious error
148                          1 = this service does not accept input
149                          2 = the producer is invalid
150                          3 = the producer is already registered with this consumer
151 */
152
153 int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
154 {
155         int i = 0;
156
157         // Get the service base
158         mlt_service_base *base = this->local;
159
160         // Special case 'track' index - only works for last filter(s) in a particular chain
161         // but allows a filter to apply to the output frame regardless of which track it comes from
162         if ( index == -1 )
163                 index = 0;
164
165         // Check if the producer is already registered with this service
166         for ( i = 0; i < base->count; i ++ )
167                 if ( base->in[ i ] == producer )
168                         return 3;
169
170         // Allocate space
171         if ( index >= base->size )
172         {
173                 int new_size = base->size + index + 10;
174                 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
175                 if ( base->in != NULL )
176                 {
177                         for ( i = base->size; i < new_size; i ++ )
178                                 base->in[ i ] = NULL;
179                         base->size = new_size;
180                 }
181         }
182
183         // If we have space, assign the input
184         if ( base->in != NULL && index >= 0 && index < base->size )
185         {
186                 // Get the current service
187                 mlt_service current = base->in[ index ];
188
189                 // Increment the reference count on this producer
190                 if ( producer != NULL )
191                         mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
192
193                 // Now we disconnect the producer service from its consumer
194                 mlt_service_disconnect( producer );
195                 
196                 // Add the service to index specified
197                 base->in[ index ] = producer;
198                 
199                 // Determine the number of active tracks
200                 if ( index >= base->count )
201                         base->count = index + 1;
202
203                 // Now we connect the producer to its connected consumer
204                 mlt_service_connect( producer, this );
205
206                 // Close the current service
207                 mlt_service_close( current );
208
209                 // Inform caller that all went well
210                 return 0;
211         }
212         else
213         {
214                 return -1;
215         }
216 }
217
218 /** Disconnect this service from its consumer.
219 */
220
221 static void mlt_service_disconnect( mlt_service this )
222 {
223         if ( this != NULL )
224         {
225                 // Get the service base
226                 mlt_service_base *base = this->local;
227
228                 // Disconnect
229                 base->out = NULL;
230         }
231 }
232
233 /** Obtain the consumer this service is connected to.
234 */
235
236 mlt_service mlt_service_consumer( mlt_service this )
237 {
238         // Get the service base
239         mlt_service_base *base = this->local;
240
241         // Return the connected consumer
242         return base->out;
243 }
244
245 /** Obtain the producer this service is connected to.
246 */
247
248 mlt_service mlt_service_producer( mlt_service this )
249 {
250         // Get the service base
251         mlt_service_base *base = this->local;
252
253         // Return the connected producer
254         return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
255 }
256
257 /** Associate this service to the consumer.
258 */
259
260 static void mlt_service_connect( mlt_service this, mlt_service that )
261 {
262         if ( this != NULL )
263         {
264                 // Get the service base
265                 mlt_service_base *base = this->local;
266
267                 // There's a bit more required here...
268                 base->out = that;
269         }
270 }
271
272 /** Get the first connected producer service.
273 */
274
275 mlt_service mlt_service_get_producer( mlt_service this )
276 {
277         mlt_service producer = NULL;
278
279         // Get the service base
280         mlt_service_base *base = this->local;
281
282         if ( base->in != NULL )
283                 producer = base->in[ 0 ];
284         
285         return producer;
286 }
287
288 /** Default implementation of get_frame.
289 */
290
291 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
292 {
293         mlt_service_base *base = this->local;
294         if ( index < base->count )
295         {
296                 mlt_service producer = base->in[ index ];
297                 if ( producer != NULL )
298                         return mlt_service_get_frame( producer, frame, index );
299         }
300         *frame = mlt_frame_init( this );
301         return 0;
302 }
303
304 /** Return the properties object.
305 */
306
307 mlt_properties mlt_service_properties( mlt_service self )
308 {
309         return self != NULL ? &self->parent : NULL;
310 }
311
312 /** Recursively apply attached filters
313 */
314
315 void mlt_service_apply_filters( mlt_service this, mlt_frame frame, int index )
316 {
317         int i;
318         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
319         mlt_properties service_properties = MLT_SERVICE_PROPERTIES( this );
320         mlt_service_base *base = this->local;
321         mlt_position position = mlt_frame_get_position( frame );
322         mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
323         mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
324
325         if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
326         {
327                 // Process the frame with the attached filters
328                 for ( i = 0; i < base->filter_count; i ++ )
329                 {
330                         if ( base->filters[ i ] != NULL )
331                         {
332                                 mlt_position in = mlt_filter_get_in( base->filters[ i ] );
333                                 mlt_position out = mlt_filter_get_out( base->filters[ i ] );
334                                 int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
335                                 if ( !disable && ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) ) )
336                                 {
337                                         mlt_properties_set_position( frame_properties, "in", in == 0 ? this_in : in );
338                                         mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out : out );
339                                         mlt_filter_process( base->filters[ i ], frame );
340                                         mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
341                                 }
342                         }
343                 }
344         }
345 }
346
347 /** Obtain a frame.
348 */
349
350 int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
351 {
352         int result = 0;
353
354         // Lock the service
355         mlt_service_lock( this );
356
357         // Ensure that the frame is NULL
358         *frame = NULL;
359
360         // Only process if we have a valid service
361         if ( this != NULL && this->get_frame != NULL )
362         {
363                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
364                 mlt_position in = mlt_properties_get_position( properties, "in" );
365                 mlt_position out = mlt_properties_get_position( properties, "out" );
366
367                 result = this->get_frame( this, frame, index );
368
369                 if ( result == 0 )
370                 {
371                         mlt_properties_inc_ref( properties );
372                         properties = MLT_FRAME_PROPERTIES( *frame );
373                         if ( in >=0 && out > 0 )
374                         {
375                                 mlt_properties_set_position( properties, "in", in );
376                                 mlt_properties_set_position( properties, "out", out );
377                         }
378                         mlt_service_apply_filters( this, *frame, 1 );
379                         mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
380                 }
381         }
382
383         // Make sure we return a frame
384         if ( *frame == NULL )
385                 *frame = mlt_frame_init( this );
386
387         // Unlock the service
388         mlt_service_unlock( this );
389
390         return result;
391 }
392
393 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
394 {
395         mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
396 }
397
398 /** Attach a filter.
399 */
400
401 int mlt_service_attach( mlt_service this, mlt_filter filter )
402 {
403         int error = this == NULL || filter == NULL;
404         if ( error == 0 )
405         {
406                 int i = 0;
407                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
408                 mlt_service_base *base = this->local;
409
410                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
411                         if ( base->filters[ i ] == filter )
412                                 error = 1;
413
414                 if ( error == 0 )
415                 {
416                         if ( base->filter_count == base->filter_size )
417                         {
418                                 base->filter_size += 10;
419                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
420                         }
421
422                         if ( base->filters != NULL )
423                         {
424                                 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
425                                 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
426                                 base->filters[ base->filter_count ++ ] = filter;
427                                 mlt_events_fire( properties, "service-changed", NULL );
428                                 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
429                                 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
430                         }
431                         else
432                         {
433                                 error = 2;
434                         }
435                 }
436         }
437         return error;
438 }
439
440 /** Detach a filter.
441 */
442
443 int mlt_service_detach( mlt_service this, mlt_filter filter )
444 {
445         int error = this == NULL || filter == NULL;
446         if ( error == 0 )
447         {
448                 int i = 0;
449                 mlt_service_base *base = this->local;
450                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
451
452                 for ( i = 0; i < base->filter_count; i ++ )
453                         if ( base->filters[ i ] == filter )
454                                 break;
455
456                 if ( i < base->filter_count )
457                 {
458                         base->filters[ i ] = NULL;
459                         for ( i ++ ; i < base->filter_count; i ++ )
460                                 base->filters[ i - 1 ] = base->filters[ i ];
461                         base->filter_count --;
462                         mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
463                         mlt_filter_close( filter );
464                         mlt_events_fire( properties, "service-changed", NULL );
465                 }
466         }
467         return error;
468 }
469
470 /** Retrieve a filter.
471 */
472
473 mlt_filter mlt_service_filter( mlt_service this, int index )
474 {
475         mlt_filter filter = NULL;
476         if ( this != NULL )
477         {
478                 mlt_service_base *base = this->local;
479                 if ( index >= 0 && index < base->filter_count )
480                         filter = base->filters[ index ];
481         }
482         return filter;
483 }
484
485 /** Retrieve the profile.
486 */
487
488 mlt_profile mlt_service_profile( mlt_service this )
489 {
490         return mlt_properties_get_data( MLT_SERVICE_PROPERTIES( this ), "_profile", NULL );
491 }
492
493 /** Close the service.
494 */
495
496 void mlt_service_close( mlt_service this )
497 {
498         if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
499         {
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