]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
Stash the backtrace code somewhere.
[mlt] / src / framework / mlt_service.c
1 /**
2  * \file mlt_service.c
3  * \brief interface definition for all service classes
4  * \see mlt_service_s
5  *
6  * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "mlt_service.h"
25 #include "mlt_filter.h"
26 #include "mlt_frame.h"
27 #include "mlt_cache.h"
28 #include "mlt_factory.h"
29 #include "mlt_log.h"
30 #include "mlt_producer.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <pthread.h>
36
37
38 /*  IMPORTANT NOTES
39
40         The base service implements a null frame producing service - as such,
41         it is functional without extension and will produce test cards frames
42         and PAL sized audio frames.
43
44         PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
45         CONTROL THIS IN EXTENDING CLASSES.
46 */
47
48 /** \brief private service definition */
49
50 typedef struct
51 {
52         int size;
53         int count;
54         mlt_service *in;
55         mlt_service out;
56         int filter_count;
57         int filter_size;
58         mlt_filter *filters;
59         pthread_mutex_t mutex;
60 }
61 mlt_service_base;
62
63 /* Private methods
64  */
65
66 static void mlt_service_disconnect( mlt_service self );
67 static void mlt_service_connect( mlt_service self, mlt_service that );
68 static int service_get_frame( mlt_service self, mlt_frame_ptr frame, int index );
69 static void mlt_service_property_changed( mlt_listener, mlt_properties owner, mlt_service self, void **args );
70
71 /** Initialize a service.
72  *
73  * \public \memberof mlt_service_s
74  * \param self the service structure to initialize
75  * \param child pointer to the child object for the subclass
76  * \return true if there was an error
77  */
78
79 int mlt_service_init( mlt_service self, void *child )
80 {
81         int error = 0;
82
83         // Initialise everything to NULL
84         memset( self, 0, sizeof( struct mlt_service_s ) );
85
86         // Assign the child
87         self->child = child;
88
89         // Generate local space
90         self->local = calloc( 1, sizeof( mlt_service_base ) );
91
92         // Associate the methods
93         self->get_frame = service_get_frame;
94
95         // Initialise the properties
96         error = mlt_properties_init( &self->parent, self );
97         if ( error == 0 )
98         {
99                 self->parent.close = ( mlt_destructor )mlt_service_close;
100                 self->parent.close_object = self;
101
102                 mlt_events_init( &self->parent );
103                 mlt_events_register( &self->parent, "service-changed", NULL );
104                 mlt_events_register( &self->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed );
105                 pthread_mutex_init( &( ( mlt_service_base * )self->local )->mutex, NULL );
106         }
107
108         return error;
109 }
110
111 /** The transmitter for property changes.
112  *
113  * Invokes the listener.
114  *
115  * \private \memberof mlt_service_s
116  * \param listener a function pointer that will be invoked
117  * \param owner a properties list that will be passed to \p listener
118  * \param self a service that will be passed to \p listener
119  * \param args an array of pointers - the first entry is passed as a string to \p listener
120  */
121
122 static void mlt_service_property_changed( mlt_listener listener, mlt_properties owner, mlt_service self, void **args )
123 {
124         if ( listener != NULL )
125                 listener( owner, self, ( char * )args[ 0 ] );
126 }
127
128 /** Acquire a mutual exclusion lock on this service.
129  *
130  * \public \memberof mlt_service_s
131  * \param self the service to lock
132  */
133
134 #include <execinfo.h>
135
136 void mlt_service_lock( mlt_service self )
137 {
138         if ( self != NULL )
139                 pthread_mutex_lock( &( ( mlt_service_base * )self->local )->mutex );
140 }
141
142 /** Release a mutual exclusion lock on this service.
143  *
144  * \public \memberof mlt_service_s
145  * \param self the service to unlock
146  */
147
148 void mlt_service_unlock( mlt_service self )
149 {
150         if ( self != NULL )
151                 pthread_mutex_unlock( &( ( mlt_service_base * )self->local )->mutex );
152 }
153
154 /** Identify the subclass of the service.
155  *
156  * \public \memberof mlt_service_s
157  * \param self a service
158  * \return the subclass
159  */
160
161 mlt_service_type mlt_service_identify( mlt_service self )
162 {
163         mlt_service_type type = invalid_type;
164         if ( self != NULL )
165         {
166                 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
167                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
168                 char *resource = mlt_properties_get( properties, "resource" );
169                 if ( mlt_type == NULL )
170                         type = unknown_type;
171                 else if (resource != NULL && !strcmp( resource, "<playlist>" ) )
172                         type = playlist_type;
173                 else if (resource != NULL && !strcmp( resource, "<tractor>" ) )
174                         type = tractor_type;
175                 else if (resource != NULL && !strcmp( resource, "<multitrack>" ) )
176                         type = multitrack_type;
177                 else if ( !strcmp( mlt_type, "producer" ) )
178                         type = producer_type;
179                 else if ( !strcmp( mlt_type, "filter" ) )
180                         type = filter_type;
181                 else if ( !strcmp( mlt_type, "transition" ) )
182                         type = transition_type;
183                 else if ( !strcmp( mlt_type, "consumer" ) )
184                         type = consumer_type;
185                 else
186                         type = unknown_type;
187         }
188         return type;
189 }
190
191 /** Connect a producer to the service.
192  *
193  * \public \memberof mlt_service_s
194  * \param self a service
195  * \param producer a producer
196  * \param index which of potentially multiple producers to this service (0 based)
197  * \return > 0 warning, == 0 success, < 0 serious error,
198  *         1 = this service does not accept input,
199  *         2 = the producer is invalid,
200  *         3 = the producer is already registered with self consumer
201  */
202
203 int mlt_service_connect_producer( mlt_service self, mlt_service producer, int index )
204 {
205         int i = 0;
206
207         // Get the service base
208         mlt_service_base *base = self->local;
209
210         // Special case 'track' index - only works for last filter(s) in a particular chain
211         // but allows a filter to apply to the output frame regardless of which track it comes from
212         if ( index == -1 )
213                 index = 0;
214
215         // Check if the producer is already registered with this service
216         for ( i = 0; i < base->count; i ++ )
217                 if ( base->in[ i ] == producer )
218                         return 3;
219
220         // Allocate space
221         if ( index >= base->size )
222         {
223                 int new_size = base->size + index + 10;
224                 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
225                 if ( base->in != NULL )
226                 {
227                         for ( i = base->size; i < new_size; i ++ )
228                                 base->in[ i ] = NULL;
229                         base->size = new_size;
230                 }
231         }
232
233         // If we have space, assign the input
234         if ( base->in != NULL && index >= 0 && index < base->size )
235         {
236                 // Get the current service
237                 mlt_service current = base->in[ index ];
238
239                 // Increment the reference count on this producer
240                 if ( producer != NULL )
241                         mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
242
243                 // Now we disconnect the producer service from its consumer
244                 mlt_service_disconnect( producer );
245
246                 // Add the service to index specified
247                 base->in[ index ] = producer;
248
249                 // Determine the number of active tracks
250                 if ( index >= base->count )
251                         base->count = index + 1;
252
253                 // Now we connect the producer to its connected consumer
254                 mlt_service_connect( producer, self );
255
256                 // Close the current service
257                 mlt_service_close( current );
258
259                 // Inform caller that all went well
260                 return 0;
261         }
262         else
263         {
264                 return -1;
265         }
266 }
267
268 /** Disconnect a service from its consumer.
269  *
270  * \private \memberof mlt_service_s
271  * \param self a service
272  */
273
274 static void mlt_service_disconnect( mlt_service self )
275 {
276         if ( self != NULL )
277         {
278                 // Get the service base
279                 mlt_service_base *base = self->local;
280
281                 // Disconnect
282                 base->out = NULL;
283         }
284 }
285
286 /** Obtain the consumer a service is connected to.
287  *
288  * \public \memberof mlt_service_s
289  * \param self a service
290  * \return the consumer
291  */
292
293 mlt_service mlt_service_consumer( mlt_service self )
294 {
295         // Get the service base
296         mlt_service_base *base = self->local;
297
298         // Return the connected consumer
299         return base->out;
300 }
301
302 /** Obtain the producer a service is connected to.
303  *
304  * \public \memberof mlt_service_s
305  * \param self a service
306  * \return the last-most producer
307  */
308
309 mlt_service mlt_service_producer( mlt_service self )
310 {
311         // Get the service base
312         mlt_service_base *base = self->local;
313
314         // Return the connected producer
315         return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
316 }
317
318 /** Associate a service to a consumer.
319  *
320  * Overwrites connection to any existing consumer.
321  * \private \memberof mlt_service_s
322  * \param self a service
323  * \param that a consumer
324  */
325
326 static void mlt_service_connect( mlt_service self, mlt_service that )
327 {
328         if ( self != NULL )
329         {
330                 // Get the service base
331                 mlt_service_base *base = self->local;
332
333                 // There's a bit more required here...
334                 base->out = that;
335         }
336 }
337
338 /** Get the first connected producer.
339  *
340  * \public \memberof mlt_service_s
341  * \param self a service
342  * \return the first producer
343  */
344
345 mlt_service mlt_service_get_producer( mlt_service self )
346 {
347         mlt_service producer = NULL;
348
349         // Get the service base
350         mlt_service_base *base = self->local;
351
352         if ( base->in != NULL )
353                 producer = base->in[ 0 ];
354
355         return producer;
356 }
357
358 /** Default implementation of the get_frame virtual function.
359  *
360  * \private \memberof mlt_service_s
361  * \param self a service
362  * \param[out] frame a frame by reference
363  * \param index as determined by the producer
364  * \return false
365  */
366
367 static int service_get_frame( mlt_service self, mlt_frame_ptr frame, int index )
368 {
369         mlt_service_base *base = self->local;
370         if ( index < base->count )
371         {
372                 mlt_service producer = base->in[ index ];
373                 if ( producer != NULL )
374                         return mlt_service_get_frame( producer, frame, index );
375         }
376         *frame = mlt_frame_init( self );
377         return 0;
378 }
379
380 /** Return the properties object.
381  *
382  * \public \memberof mlt_service_s
383  * \param self a service
384  * \return the properties
385  */
386
387 mlt_properties mlt_service_properties( mlt_service self )
388 {
389         return self != NULL ? &self->parent : NULL;
390 }
391
392 /** Recursively apply attached filters.
393  *
394  * \public \memberof mlt_service_s
395  * \param self a service
396  * \param frame a frame
397  * \param index used to track depth of recursion, top caller should supply 0
398  */
399
400 void mlt_service_apply_filters( mlt_service self, mlt_frame frame, int index )
401 {
402         int i;
403         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
404         mlt_properties service_properties = MLT_SERVICE_PROPERTIES( self );
405         mlt_service_base *base = self->local;
406         mlt_position position = mlt_frame_get_position( frame );
407         mlt_position self_in = mlt_properties_get_position( service_properties, "in" );
408         mlt_position self_out = mlt_properties_get_position( service_properties, "out" );
409
410         if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
411         {
412                 // Process the frame with the attached filters
413                 for ( i = 0; i < base->filter_count; i ++ )
414                 {
415                         if ( base->filters[ i ] != NULL )
416                         {
417                                 mlt_position in = mlt_filter_get_in( base->filters[ i ] );
418                                 mlt_position out = mlt_filter_get_out( base->filters[ i ] );
419                                 int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
420                                 if ( !disable && ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) ) )
421                                 {
422                                         mlt_properties_set_position( frame_properties, "in", in == 0 ? self_in : in );
423                                         mlt_properties_set_position( frame_properties, "out", out == 0 ? self_out : out );
424                                         mlt_filter_process( base->filters[ i ], frame );
425                                         mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
426                                 }
427                         }
428                 }
429         }
430 }
431
432 /** Obtain a frame.
433  *
434  * \public \memberof mlt_service_s
435  * \param self a service
436  * \param[out] frame a frame by reference
437  * \param index as determined by the producer
438  * \return true if there was an error
439  */
440
441 int mlt_service_get_frame( mlt_service self, mlt_frame_ptr frame, int index )
442 {
443         int result = 0;
444
445         // Lock the service
446         mlt_service_lock( self );
447
448         // Ensure that the frame is NULL
449         *frame = NULL;
450
451         // Only process if we have a valid service
452         if ( self != NULL && self->get_frame != NULL )
453         {
454                 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
455                 mlt_position in = mlt_properties_get_position( properties, "in" );
456                 mlt_position out = mlt_properties_get_position( properties, "out" );
457                 mlt_position position = mlt_service_identify( self ) == producer_type ? mlt_producer_position( MLT_PRODUCER( self ) ) : -1;
458
459                 result = self->get_frame( self, frame, index );
460
461                 if ( result == 0 )
462                 {
463                         mlt_properties_inc_ref( properties );
464                         properties = MLT_FRAME_PROPERTIES( *frame );
465                         
466                         if ( in >=0 && out > 0 )
467                         {
468                                 mlt_properties_set_position( properties, "in", in );
469                                 mlt_properties_set_position( properties, "out", out );
470                         }
471                         mlt_service_apply_filters( self, *frame, 1 );
472                         mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), self );
473                         
474                         if ( mlt_service_identify( self ) == producer_type &&
475                              mlt_properties_get_int( MLT_SERVICE_PROPERTIES( self ), "_need_previous_next" ) )
476                         {
477                                 // Save the new position from self->get_frame
478                                 mlt_position new_position = mlt_producer_position( MLT_PRODUCER( self ) );
479                                 
480                                 // Get the preceding frame, unfiltered
481                                 mlt_frame previous_frame;
482                                 mlt_producer_seek( MLT_PRODUCER(self), position - 1 );
483                                 result = self->get_frame( self, &previous_frame, index );
484                                 if ( !result )
485                                         mlt_properties_set_data( properties, "previous frame",
486                                                 previous_frame, 0, ( mlt_destructor ) mlt_frame_close, NULL );
487
488                                 // Get the following frame, unfiltered
489                                 mlt_frame next_frame;
490                                 mlt_producer_seek( MLT_PRODUCER(self), position + 1 );
491                                 result = self->get_frame( self, &next_frame, index );
492                                 if ( !result )
493                                 {
494                                         mlt_properties_set_data( properties, "next frame",
495                                                 next_frame, 0, ( mlt_destructor ) mlt_frame_close, NULL );
496                                 }
497                                 
498                                 // Restore the new position
499                                 mlt_producer_seek( MLT_PRODUCER(self), new_position );
500                         }
501                 }
502         }
503
504         // Make sure we return a frame
505         if ( *frame == NULL )
506                 *frame = mlt_frame_init( self );
507
508         // Unlock the service
509         mlt_service_unlock( self );
510
511         return result;
512 }
513
514 /** The service-changed event handler.
515  *
516  * \private \memberof mlt_service_s
517  * \param owner ignored
518  * \param self the service on which the "service-changed" event is fired
519  */
520
521 static void mlt_service_filter_changed( mlt_service owner, mlt_service self )
522 {
523         mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "service-changed", NULL );
524 }
525
526 /** The property-changed event handler.
527  *
528  * \private \memberof mlt_service_s
529  * \param owner ignored
530  * \param self the service on which the "property-changed" event is fired
531  * \param name the name of the property that changed
532  */
533
534 static void mlt_service_filter_property_changed( mlt_service owner, mlt_service self, char *name )
535 {
536     mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "property-changed", name, NULL );
537 }
538
539 /** Attach a filter.
540  *
541  * \public \memberof mlt_service_s
542  * \param self a service
543  * \param filter the filter to attach
544  * \return true if there was an error
545  */
546
547 int mlt_service_attach( mlt_service self, mlt_filter filter )
548 {
549 #if 0
550         void *buffer[1024];
551         int num, i;
552         char **sym;
553         printf("ATTACHING %p -> %p\n", self, filter);
554         for (i = 0; i < 1024; ++i) buffer[i] = 0;
555         num = backtrace(buffer, 1024);
556         sym = backtrace_symbols(buffer, num);
557         for (i = 0; i < num; ++i) {
558                 //printf("%3d: %s\n", sym[i]);
559         }
560         free(sym);
561 #endif
562         int error = self == NULL || filter == NULL;
563         if ( error == 0 )
564         {
565                 int i = 0;
566                 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
567                 mlt_service_base *base = self->local;
568
569                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
570                         if ( base->filters[ i ] == filter )
571                                 error = 1;
572
573                 if ( error == 0 )
574                 {
575                         if ( base->filter_count == base->filter_size )
576                         {
577                                 base->filter_size += 10;
578                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
579                         }
580
581                         if ( base->filters != NULL )
582                         {
583                                 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
584                                 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
585                                 base->filters[ base->filter_count ++ ] = filter;
586                                 mlt_properties_set_data( props, "service", self, 0, NULL, NULL );
587                                 mlt_events_fire( properties, "service-changed", NULL );
588                                 mlt_events_fire( props, "service-changed", NULL );
589                                 mlt_service cp = mlt_properties_get_data( properties, "_cut_parent", NULL );
590                                 if ( cp )
591                                         mlt_events_fire( MLT_SERVICE_PROPERTIES(cp), "service-changed", NULL );
592                                 mlt_events_listen( props, self, "service-changed", ( mlt_listener )mlt_service_filter_changed );
593                                 mlt_events_listen( props, self, "property-changed", ( mlt_listener )mlt_service_filter_property_changed );
594                         }
595                         else
596                         {
597                                 error = 2;
598                         }
599                 }
600         }
601         return error;
602 }
603
604 /** Detach a filter.
605  *
606  * \public \memberof mlt_service_s
607  * \param self a service
608  * \param filter the filter to detach
609  * \return true if there was an error
610  */
611
612 int mlt_service_detach( mlt_service self, mlt_filter filter )
613 {
614         int error = self == NULL || filter == NULL;
615         if ( error == 0 )
616         {
617                 int i = 0;
618                 mlt_service_base *base = self->local;
619                 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
620
621                 for ( i = 0; i < base->filter_count; i ++ )
622                         if ( base->filters[ i ] == filter )
623                                 break;
624
625                 if ( i < base->filter_count )
626                 {
627                         base->filters[ i ] = NULL;
628                         for ( i ++ ; i < base->filter_count; i ++ )
629                                 base->filters[ i - 1 ] = base->filters[ i ];
630                         base->filter_count --;
631                         mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), self );
632                         mlt_filter_close( filter );
633                         mlt_events_fire( properties, "service-changed", NULL );
634                 }
635         }
636         return error;
637 }
638
639 /** Get the number of filters attached.
640  *
641  * \public \memberof mlt_service_s
642  * \param self a service
643  * \return the number of attached filters or -1 if there was an error
644  */
645
646 int mlt_service_filter_count( mlt_service self )
647 {
648         int result = -1;
649         if ( self )
650         {
651                 mlt_service_base *base = self->local;
652                 result = base->filter_count;
653         }
654         return result;
655 }
656
657 /** Reorder the attached filters.
658  *
659  * \public \memberof mlt_service_s
660  * \param self a service
661  * \param from the current index value of the filter to move
662  * \param to the new index value for the filter specified in \p from
663  * \return true if there was an error
664  */
665
666 int mlt_service_move_filter( mlt_service self, int from, int to )
667 {
668         int error = -1;
669         if ( self )
670         {
671                 mlt_service_base *base = self->local;
672                 if ( from < 0 ) from = 0;
673                 if ( from >= base->filter_count ) from = base->filter_count - 1;
674                 if ( to < 0 ) to = 0;
675                 if ( to >= base->filter_count ) to = base->filter_count - 1;
676                 if ( from != to && base->filter_count > 1 )
677                 {
678                         mlt_filter filter = base->filters[from];
679                         int i;
680                         if ( from > to )
681                         {
682                                 for ( i = from; i > to; i-- )
683                                         base->filters[i] = base->filters[i - 1];
684                         }
685                         else
686                         {
687                                 for ( i = from; i < to; i++ )
688                                         base->filters[i] = base->filters[i + 1];
689                         }
690                         base->filters[to] = filter;
691                         mlt_events_fire( MLT_SERVICE_PROPERTIES(self), "service-changed", NULL );
692                         error = 0;
693                 }
694         }
695         return error;
696 }
697
698 /** Retrieve an attached filter.
699  *
700  * \public \memberof mlt_service_s
701  * \param self a service
702  * \param index which one of potentially multiple filters
703  * \return the filter or null if there was an error
704  */
705
706 mlt_filter mlt_service_filter( mlt_service self, int index )
707 {
708         mlt_filter filter = NULL;
709         if ( self != NULL )
710         {
711                 mlt_service_base *base = self->local;
712                 if ( index >= 0 && index < base->filter_count )
713                         filter = base->filters[ index ];
714         }
715         return filter;
716 }
717
718 /** Retrieve the profile.
719  *
720  * \public \memberof mlt_service_s
721  * \param self a service
722  * \return the profile
723  */
724
725 mlt_profile mlt_service_profile( mlt_service self )
726 {
727         return self? mlt_properties_get_data( MLT_SERVICE_PROPERTIES( self ), "_profile", NULL ) : NULL;
728 }
729
730 /** Set the profile for a service.
731  *
732  * \public \memberof mlt_service_s
733  * \param self a service
734  * \param profile the profile to set onto the service
735  */
736
737 void mlt_service_set_profile( mlt_service self, mlt_profile profile )
738 {
739     mlt_properties_set_data( MLT_SERVICE_PROPERTIES( self ), "_profile", profile, 0, NULL, NULL );
740 }
741
742 /** Destroy a service.
743  *
744  * \public \memberof mlt_service_s
745  * \param self the service to destroy
746  */
747
748 void mlt_service_close( mlt_service self )
749 {
750         if ( self != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( self ) ) <= 0 )
751         {
752                 if ( self->close != NULL )
753                 {
754                         self->close( self->close_object );
755                 }
756                 else
757                 {
758                         mlt_service_base *base = self->local;
759                         int i = 0;
760                         int count = base->filter_count;
761                         mlt_events_block( MLT_SERVICE_PROPERTIES( self ), self );
762                         while( count -- )
763                                 mlt_service_detach( self, base->filters[ 0 ] );
764                         free( base->filters );
765                         for ( i = 0; i < base->count; i ++ )
766                                 if ( base->in[ i ] != NULL )
767                                         mlt_service_close( base->in[ i ] );
768                         self->parent.close = NULL;
769                         free( base->in );
770                         pthread_mutex_destroy( &base->mutex );
771                         free( base );
772                         mlt_properties_close( &self->parent );
773                 }
774         }
775 }
776
777 /** Release a service's cache items.
778  *
779  * \private \memberof mlt_service_s
780  * \param self a service
781  */
782
783 void mlt_service_cache_purge( mlt_service self )
784 {
785         mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
786
787         if ( caches )
788         {
789                 int i = mlt_properties_count( caches );
790                 while ( i-- )
791                 {
792                         mlt_cache_purge( mlt_properties_get_data_at( caches, i, NULL ), self );
793                         mlt_properties_set_data( mlt_global_properties(), mlt_properties_get_name( caches, i ), NULL, 0, NULL, NULL );
794                 }
795         }
796 }
797
798 /** Lookup the cache object for a service.
799  *
800  * \private \memberof mlt_service_s
801  * \param self a service
802  * \param name a name for the object
803  * \return a cache
804  */
805
806 static mlt_cache get_cache( mlt_service self, const char *name )
807 {
808         mlt_cache result = NULL;
809         mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
810
811         if ( !caches )
812         {
813                 caches = mlt_properties_new();
814                 mlt_properties_set_data( mlt_global_properties(), "caches", caches, 0, ( mlt_destructor )mlt_properties_close, NULL );
815         }
816         if ( caches )
817         {
818                 result = mlt_properties_get_data( caches, name, NULL );
819                 if ( !result )
820                 {
821                         result = mlt_cache_init();
822                         mlt_properties_set_data( caches, name, result, 0, ( mlt_destructor )mlt_cache_close, NULL );
823                 }
824         }
825         
826         return result;
827 }
828
829 /** Put an object into a service's cache.
830  *
831  * \public \memberof mlt_service_s
832  * \param self a service
833  * \param name a name for the object that is unique to the service class, but not to the instance
834  * \param data an opaque pointer to the object to put into the cache
835  * \param size the number of bytes pointed to by data
836  * \param destructor a function that releases the data
837  */
838
839 void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor )
840 {
841         mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p data %p\n", __FUNCTION__, name, self, data );
842         mlt_cache cache = get_cache( self, name );
843
844         if ( cache )
845                 mlt_cache_put( cache, self, data, size, destructor );
846 }
847
848 /** Get an object from a service's cache.
849  *
850  * \public \memberof mlt_service_s
851  * \param self a service
852  * \param name a name for the object that is unique to the service class, but not to the instance
853  * \return a cache item or NULL if an object is not found
854  * \see mlt_cache_item_data
855  */
856
857 mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name )
858 {
859         mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p\n", __FUNCTION__, name, self );
860         mlt_cache_item result = NULL;
861         mlt_cache cache = get_cache( self, name );
862
863         if ( cache )
864                 result = mlt_cache_get( cache, self );
865
866         return result;
867 }
868
869 /** Set the number of items to cache for the named cache.
870  *
871  * \public \memberof mlt_service_s
872  * \param self a service
873  * \param name a name for the object that is unique to the service class, but not to the instance
874  * \param size the number of items to cache
875  */
876
877 void mlt_service_cache_set_size( mlt_service self, const char *name, int size )
878 {
879         mlt_cache cache = get_cache( self, name );
880         if ( cache )
881                 mlt_cache_set_size( cache, size );
882 }
883
884 /** Get the current maximum size of the named cache.
885  *
886  * \public \memberof mlt_service_s
887  * \param self a service
888  * \param name a name for the object that is unique to the service class, but not to the instance
889  * \return the current maximum number of items to cache or zero if there is an error
890  */
891
892 int mlt_service_cache_get_size( mlt_service self, const char *name )
893 {
894         mlt_cache cache = get_cache( self, name );
895         if ( cache )
896                 return mlt_cache_get_size( cache );
897         else
898                 return 0;
899 }