]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
0a620c4772b31ad098b66b38a5327348f9943d22
[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 void mlt_service_lock( mlt_service self )
135 {
136         if ( self != NULL )
137                 pthread_mutex_lock( &( ( mlt_service_base * )self->local )->mutex );
138 }
139
140 /** Release a mutual exclusion lock on this service.
141  *
142  * \public \memberof mlt_service_s
143  * \param self the service to unlock
144  */
145
146 void mlt_service_unlock( mlt_service self )
147 {
148         if ( self != NULL )
149                 pthread_mutex_unlock( &( ( mlt_service_base * )self->local )->mutex );
150 }
151
152 /** Identify the subclass of the service.
153  *
154  * \public \memberof mlt_service_s
155  * \param self a service
156  * \return the subclass
157  */
158
159 mlt_service_type mlt_service_identify( mlt_service self )
160 {
161         mlt_service_type type = invalid_type;
162         if ( self != NULL )
163         {
164                 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
165                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
166                 char *resource = mlt_properties_get( properties, "resource" );
167                 if ( mlt_type == NULL )
168                         type = unknown_type;
169                 else if (resource != NULL && !strcmp( resource, "<playlist>" ) )
170                         type = playlist_type;
171                 else if (resource != NULL && !strcmp( resource, "<tractor>" ) )
172                         type = tractor_type;
173                 else if (resource != NULL && !strcmp( resource, "<multitrack>" ) )
174                         type = multitrack_type;
175                 else if ( !strcmp( mlt_type, "producer" ) )
176                         type = producer_type;
177                 else if ( !strcmp( mlt_type, "filter" ) )
178                         type = filter_type;
179                 else if ( !strcmp( mlt_type, "transition" ) )
180                         type = transition_type;
181                 else if ( !strcmp( mlt_type, "consumer" ) )
182                         type = consumer_type;
183                 else
184                         type = unknown_type;
185         }
186         return type;
187 }
188
189 /** Connect a producer to the service.
190  *
191  * \public \memberof mlt_service_s
192  * \param self a service
193  * \param producer a producer
194  * \param index which of potentially multiple producers to this service (0 based)
195  * \return > 0 warning, == 0 success, < 0 serious error,
196  *         1 = this service does not accept input,
197  *         2 = the producer is invalid,
198  *         3 = the producer is already registered with self consumer
199  */
200
201 int mlt_service_connect_producer( mlt_service self, mlt_service producer, int index )
202 {
203         int i = 0;
204
205         // Get the service base
206         mlt_service_base *base = self->local;
207
208         // Special case 'track' index - only works for last filter(s) in a particular chain
209         // but allows a filter to apply to the output frame regardless of which track it comes from
210         if ( index == -1 )
211                 index = 0;
212
213         // Check if the producer is already registered with this service
214         for ( i = 0; i < base->count; i ++ )
215                 if ( base->in[ i ] == producer )
216                         return 3;
217
218         // Allocate space
219         if ( index >= base->size )
220         {
221                 int new_size = base->size + index + 10;
222                 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
223                 if ( base->in != NULL )
224                 {
225                         for ( i = base->size; i < new_size; i ++ )
226                                 base->in[ i ] = NULL;
227                         base->size = new_size;
228                 }
229         }
230
231         // If we have space, assign the input
232         if ( base->in != NULL && index >= 0 && index < base->size )
233         {
234                 // Get the current service
235                 mlt_service current = base->in[ index ];
236
237                 // Increment the reference count on this producer
238                 if ( producer != NULL )
239                         mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
240
241                 // Now we disconnect the producer service from its consumer
242                 mlt_service_disconnect( producer );
243
244                 // Add the service to index specified
245                 base->in[ index ] = producer;
246
247                 // Determine the number of active tracks
248                 if ( index >= base->count )
249                         base->count = index + 1;
250
251                 // Now we connect the producer to its connected consumer
252                 mlt_service_connect( producer, self );
253
254                 // Close the current service
255                 mlt_service_close( current );
256
257                 // Inform caller that all went well
258                 return 0;
259         }
260         else
261         {
262                 return -1;
263         }
264 }
265
266 /** Disconnect a service from its consumer.
267  *
268  * \private \memberof mlt_service_s
269  * \param self a service
270  */
271
272 static void mlt_service_disconnect( mlt_service self )
273 {
274         if ( self != NULL )
275         {
276                 // Get the service base
277                 mlt_service_base *base = self->local;
278
279                 // Disconnect
280                 base->out = NULL;
281         }
282 }
283
284 /** Obtain the consumer a service is connected to.
285  *
286  * \public \memberof mlt_service_s
287  * \param self a service
288  * \return the consumer
289  */
290
291 mlt_service mlt_service_consumer( mlt_service self )
292 {
293         // Get the service base
294         mlt_service_base *base = self->local;
295
296         // Return the connected consumer
297         return base->out;
298 }
299
300 /** Obtain the producer a service is connected to.
301  *
302  * \public \memberof mlt_service_s
303  * \param self a service
304  * \return the last-most producer
305  */
306
307 mlt_service mlt_service_producer( mlt_service self )
308 {
309         // Get the service base
310         mlt_service_base *base = self->local;
311
312         // Return the connected producer
313         return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
314 }
315
316 /** Associate a service to a consumer.
317  *
318  * Overwrites connection to any existing consumer.
319  * \private \memberof mlt_service_s
320  * \param self a service
321  * \param that a consumer
322  */
323
324 static void mlt_service_connect( mlt_service self, mlt_service that )
325 {
326         if ( self != NULL )
327         {
328                 // Get the service base
329                 mlt_service_base *base = self->local;
330
331                 // There's a bit more required here...
332                 base->out = that;
333         }
334 }
335
336 /** Get the first connected producer.
337  *
338  * \public \memberof mlt_service_s
339  * \param self a service
340  * \return the first producer
341  */
342
343 mlt_service mlt_service_get_producer( mlt_service self )
344 {
345         mlt_service producer = NULL;
346
347         // Get the service base
348         mlt_service_base *base = self->local;
349
350         if ( base->in != NULL )
351                 producer = base->in[ 0 ];
352
353         return producer;
354 }
355
356 /** Default implementation of the get_frame virtual function.
357  *
358  * \private \memberof mlt_service_s
359  * \param self a service
360  * \param[out] frame a frame by reference
361  * \param index as determined by the producer
362  * \return false
363  */
364
365 static int service_get_frame( mlt_service self, mlt_frame_ptr frame, int index )
366 {
367         mlt_service_base *base = self->local;
368         if ( index < base->count )
369         {
370                 mlt_service producer = base->in[ index ];
371                 if ( producer != NULL )
372                         return mlt_service_get_frame( producer, frame, index );
373         }
374         *frame = mlt_frame_init( self );
375         return 0;
376 }
377
378 /** Return the properties object.
379  *
380  * \public \memberof mlt_service_s
381  * \param self a service
382  * \return the properties
383  */
384
385 mlt_properties mlt_service_properties( mlt_service self )
386 {
387         return self != NULL ? &self->parent : NULL;
388 }
389
390 /** Recursively apply attached filters.
391  *
392  * \public \memberof mlt_service_s
393  * \param self a service
394  * \param frame a frame
395  * \param index used to track depth of recursion, top caller should supply 0
396  */
397
398 void mlt_service_apply_filters( mlt_service self, mlt_frame frame, int index )
399 {
400         int i;
401         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
402         mlt_properties service_properties = MLT_SERVICE_PROPERTIES( self );
403         mlt_service_base *base = self->local;
404         mlt_position position = mlt_frame_get_position( frame );
405         mlt_position self_in = mlt_properties_get_position( service_properties, "in" );
406         mlt_position self_out = mlt_properties_get_position( service_properties, "out" );
407
408         if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
409         {
410                 // Process the frame with the attached filters
411                 for ( i = 0; i < base->filter_count; i ++ )
412                 {
413                         if ( base->filters[ i ] != NULL )
414                         {
415                                 mlt_position in = mlt_filter_get_in( base->filters[ i ] );
416                                 mlt_position out = mlt_filter_get_out( base->filters[ i ] );
417                                 int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
418                                 if ( !disable && ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) ) )
419                                 {
420                                         mlt_properties_set_position( frame_properties, "in", in == 0 ? self_in : in );
421                                         mlt_properties_set_position( frame_properties, "out", out == 0 ? self_out : out );
422                                         mlt_filter_process( base->filters[ i ], frame );
423                                         mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
424                                 }
425                         }
426                 }
427         }
428 }
429
430 /** Obtain a frame.
431  *
432  * \public \memberof mlt_service_s
433  * \param self a service
434  * \param[out] frame a frame by reference
435  * \param index as determined by the producer
436  * \return true if there was an error
437  */
438
439 int mlt_service_get_frame( mlt_service self, mlt_frame_ptr frame, int index )
440 {
441         int result = 0;
442
443         // Lock the service
444         mlt_service_lock( self );
445
446         // Ensure that the frame is NULL
447         *frame = NULL;
448
449         // Only process if we have a valid service
450         if ( self != NULL && self->get_frame != NULL )
451         {
452                 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
453                 mlt_position in = mlt_properties_get_position( properties, "in" );
454                 mlt_position out = mlt_properties_get_position( properties, "out" );
455                 mlt_position position = mlt_service_identify( self ) == producer_type ? mlt_producer_position( MLT_PRODUCER( self ) ) : -1;
456
457                 result = self->get_frame( self, frame, index );
458
459                 if ( result == 0 )
460                 {
461                         mlt_properties_inc_ref( properties );
462                         properties = MLT_FRAME_PROPERTIES( *frame );
463                         
464                         if ( in >=0 && out > 0 )
465                         {
466                                 mlt_properties_set_position( properties, "in", in );
467                                 mlt_properties_set_position( properties, "out", out );
468                         }
469                         mlt_service_apply_filters( self, *frame, 1 );
470                         mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), self );
471                         
472                         if ( mlt_service_identify( self ) == producer_type &&
473                              mlt_properties_get_int( MLT_SERVICE_PROPERTIES( self ), "_need_previous_next" ) )
474                         {
475                                 // Save the new position from self->get_frame
476                                 mlt_position new_position = mlt_producer_position( MLT_PRODUCER( self ) );
477                                 
478                                 // Get the preceding frame, unfiltered
479                                 mlt_frame previous_frame;
480                                 mlt_producer_seek( MLT_PRODUCER(self), position - 1 );
481                                 result = self->get_frame( self, &previous_frame, index );
482                                 if ( !result )
483                                         mlt_properties_set_data( properties, "previous frame",
484                                                 previous_frame, 0, ( mlt_destructor ) mlt_frame_close, NULL );
485
486                                 // Get the following frame, unfiltered
487                                 mlt_frame next_frame;
488                                 mlt_producer_seek( MLT_PRODUCER(self), position + 1 );
489                                 result = self->get_frame( self, &next_frame, index );
490                                 if ( !result )
491                                 {
492                                         mlt_properties_set_data( properties, "next frame",
493                                                 next_frame, 0, ( mlt_destructor ) mlt_frame_close, NULL );
494                                 }
495                                 
496                                 // Restore the new position
497                                 mlt_producer_seek( MLT_PRODUCER(self), new_position );
498                         }
499                 }
500         }
501
502         // Make sure we return a frame
503         if ( *frame == NULL )
504                 *frame = mlt_frame_init( self );
505
506         // Unlock the service
507         mlt_service_unlock( self );
508
509         return result;
510 }
511
512 /** The service-changed event handler.
513  *
514  * \private \memberof mlt_service_s
515  * \param owner ignored
516  * \param self the service on which the "service-changed" event is fired
517  */
518
519 static void mlt_service_filter_changed( mlt_service owner, mlt_service self )
520 {
521         mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "service-changed", NULL );
522 }
523
524 /** The property-changed event handler.
525  *
526  * \private \memberof mlt_service_s
527  * \param owner ignored
528  * \param self the service on which the "property-changed" event is fired
529  * \param name the name of the property that changed
530  */
531
532 static void mlt_service_filter_property_changed( mlt_service owner, mlt_service self, char *name )
533 {
534     mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "property-changed", name, NULL );
535 }
536
537 /** Attach a filter.
538  *
539  * \public \memberof mlt_service_s
540  * \param self a service
541  * \param filter the filter to attach
542  * \return true if there was an error
543  */
544
545 int mlt_service_attach( mlt_service self, mlt_filter filter )
546 {
547         int error = self == NULL || filter == NULL;
548         if ( error == 0 )
549         {
550                 int i = 0;
551                 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
552                 mlt_service_base *base = self->local;
553
554                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
555                         if ( base->filters[ i ] == filter )
556                                 error = 1;
557
558                 if ( error == 0 )
559                 {
560                         if ( base->filter_count == base->filter_size )
561                         {
562                                 base->filter_size += 10;
563                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
564                         }
565
566                         if ( base->filters != NULL )
567                         {
568                                 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
569                                 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
570                                 base->filters[ base->filter_count ++ ] = filter;
571                                 mlt_properties_set_data( props, "service", self, 0, NULL, NULL );
572                                 mlt_events_fire( properties, "service-changed", NULL );
573                                 mlt_events_fire( props, "service-changed", NULL );
574                                 mlt_service cp = mlt_properties_get_data( properties, "_cut_parent", NULL );
575                                 if ( cp )
576                                         mlt_events_fire( MLT_SERVICE_PROPERTIES(cp), "service-changed", NULL );
577                                 mlt_events_listen( props, self, "service-changed", ( mlt_listener )mlt_service_filter_changed );
578                                 mlt_events_listen( props, self, "property-changed", ( mlt_listener )mlt_service_filter_property_changed );
579                         }
580                         else
581                         {
582                                 error = 2;
583                         }
584                 }
585         }
586         return error;
587 }
588
589 /** Detach a filter.
590  *
591  * \public \memberof mlt_service_s
592  * \param self a service
593  * \param filter the filter to detach
594  * \return true if there was an error
595  */
596
597 int mlt_service_detach( mlt_service self, mlt_filter filter )
598 {
599         int error = self == NULL || filter == NULL;
600         if ( error == 0 )
601         {
602                 int i = 0;
603                 mlt_service_base *base = self->local;
604                 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
605
606                 for ( i = 0; i < base->filter_count; i ++ )
607                         if ( base->filters[ i ] == filter )
608                                 break;
609
610                 if ( i < base->filter_count )
611                 {
612                         base->filters[ i ] = NULL;
613                         for ( i ++ ; i < base->filter_count; i ++ )
614                                 base->filters[ i - 1 ] = base->filters[ i ];
615                         base->filter_count --;
616                         mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), self );
617                         mlt_filter_close( filter );
618                         mlt_events_fire( properties, "service-changed", NULL );
619                 }
620         }
621         return error;
622 }
623
624 /** Get the number of filters attached.
625  *
626  * \public \memberof mlt_service_s
627  * \param self a service
628  * \return the number of attached filters or -1 if there was an error
629  */
630
631 int mlt_service_filter_count( mlt_service self )
632 {
633         int result = -1;
634         if ( self )
635         {
636                 mlt_service_base *base = self->local;
637                 result = base->filter_count;
638         }
639         return result;
640 }
641
642 /** Reorder the attached filters.
643  *
644  * \public \memberof mlt_service_s
645  * \param self a service
646  * \param from the current index value of the filter to move
647  * \param to the new index value for the filter specified in \p from
648  * \return true if there was an error
649  */
650
651 int mlt_service_move_filter( mlt_service self, int from, int to )
652 {
653         int error = -1;
654         if ( self )
655         {
656                 mlt_service_base *base = self->local;
657                 if ( from < 0 ) from = 0;
658                 if ( from >= base->filter_count ) from = base->filter_count - 1;
659                 if ( to < 0 ) to = 0;
660                 if ( to >= base->filter_count ) to = base->filter_count - 1;
661                 if ( from != to && base->filter_count > 1 )
662                 {
663                         mlt_filter filter = base->filters[from];
664                         int i;
665                         if ( from > to )
666                         {
667                                 for ( i = from; i > to; i-- )
668                                         base->filters[i] = base->filters[i - 1];
669                         }
670                         else
671                         {
672                                 for ( i = from; i < to; i++ )
673                                         base->filters[i] = base->filters[i + 1];
674                         }
675                         base->filters[to] = filter;
676                         mlt_events_fire( MLT_SERVICE_PROPERTIES(self), "service-changed", NULL );
677                         error = 0;
678                 }
679         }
680         return error;
681 }
682
683 /** Retrieve an attached filter.
684  *
685  * \public \memberof mlt_service_s
686  * \param self a service
687  * \param index which one of potentially multiple filters
688  * \return the filter or null if there was an error
689  */
690
691 mlt_filter mlt_service_filter( mlt_service self, int index )
692 {
693         mlt_filter filter = NULL;
694         if ( self != NULL )
695         {
696                 mlt_service_base *base = self->local;
697                 if ( index >= 0 && index < base->filter_count )
698                         filter = base->filters[ index ];
699         }
700         return filter;
701 }
702
703 /** Retrieve the profile.
704  *
705  * \public \memberof mlt_service_s
706  * \param self a service
707  * \return the profile
708  */
709
710 mlt_profile mlt_service_profile( mlt_service self )
711 {
712         return self? mlt_properties_get_data( MLT_SERVICE_PROPERTIES( self ), "_profile", NULL ) : NULL;
713 }
714
715 /** Set the profile for a service.
716  *
717  * \public \memberof mlt_service_s
718  * \param self a service
719  * \param profile the profile to set onto the service
720  */
721
722 void mlt_service_set_profile( mlt_service self, mlt_profile profile )
723 {
724     mlt_properties_set_data( MLT_SERVICE_PROPERTIES( self ), "_profile", profile, 0, NULL, NULL );
725 }
726
727 /** Destroy a service.
728  *
729  * \public \memberof mlt_service_s
730  * \param self the service to destroy
731  */
732
733 void mlt_service_close( mlt_service self )
734 {
735         if ( self != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( self ) ) <= 0 )
736         {
737                 if ( self->close != NULL )
738                 {
739                         self->close( self->close_object );
740                 }
741                 else
742                 {
743                         mlt_service_base *base = self->local;
744                         int i = 0;
745                         int count = base->filter_count;
746                         mlt_events_block( MLT_SERVICE_PROPERTIES( self ), self );
747                         while( count -- )
748                                 mlt_service_detach( self, base->filters[ 0 ] );
749                         free( base->filters );
750                         for ( i = 0; i < base->count; i ++ )
751                                 if ( base->in[ i ] != NULL )
752                                         mlt_service_close( base->in[ i ] );
753                         self->parent.close = NULL;
754                         free( base->in );
755                         pthread_mutex_destroy( &base->mutex );
756                         free( base );
757                         mlt_properties_close( &self->parent );
758                 }
759         }
760 }
761
762 /** Release a service's cache items.
763  *
764  * \private \memberof mlt_service_s
765  * \param self a service
766  */
767
768 void mlt_service_cache_purge( mlt_service self )
769 {
770         mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
771
772         if ( caches )
773         {
774                 int i = mlt_properties_count( caches );
775                 while ( i-- )
776                 {
777                         mlt_cache_purge( mlt_properties_get_data_at( caches, i, NULL ), self );
778                         mlt_properties_set_data( mlt_global_properties(), mlt_properties_get_name( caches, i ), NULL, 0, NULL, NULL );
779                 }
780         }
781 }
782
783 /** Lookup the cache object for a service.
784  *
785  * \private \memberof mlt_service_s
786  * \param self a service
787  * \param name a name for the object
788  * \return a cache
789  */
790
791 static mlt_cache get_cache( mlt_service self, const char *name )
792 {
793         mlt_cache result = NULL;
794         mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
795
796         if ( !caches )
797         {
798                 caches = mlt_properties_new();
799                 mlt_properties_set_data( mlt_global_properties(), "caches", caches, 0, ( mlt_destructor )mlt_properties_close, NULL );
800         }
801         if ( caches )
802         {
803                 result = mlt_properties_get_data( caches, name, NULL );
804                 if ( !result )
805                 {
806                         result = mlt_cache_init();
807                         mlt_properties_set_data( caches, name, result, 0, ( mlt_destructor )mlt_cache_close, NULL );
808                 }
809         }
810         
811         return result;
812 }
813
814 /** Put an object into a service's cache.
815  *
816  * \public \memberof mlt_service_s
817  * \param self a service
818  * \param name a name for the object that is unique to the service class, but not to the instance
819  * \param data an opaque pointer to the object to put into the cache
820  * \param size the number of bytes pointed to by data
821  * \param destructor a function that releases the data
822  */
823
824 void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor )
825 {
826         mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p data %p\n", __FUNCTION__, name, self, data );
827         mlt_cache cache = get_cache( self, name );
828
829         if ( cache )
830                 mlt_cache_put( cache, self, data, size, destructor );
831 }
832
833 /** Get an object from a service's cache.
834  *
835  * \public \memberof mlt_service_s
836  * \param self a service
837  * \param name a name for the object that is unique to the service class, but not to the instance
838  * \return a cache item or NULL if an object is not found
839  * \see mlt_cache_item_data
840  */
841
842 mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name )
843 {
844         mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p\n", __FUNCTION__, name, self );
845         mlt_cache_item result = NULL;
846         mlt_cache cache = get_cache( self, name );
847
848         if ( cache )
849                 result = mlt_cache_get( cache, self );
850
851         return result;
852 }
853
854 /** Set the number of items to cache for the named cache.
855  *
856  * \public \memberof mlt_service_s
857  * \param self a service
858  * \param name a name for the object that is unique to the service class, but not to the instance
859  * \param size the number of items to cache
860  */
861
862 void mlt_service_cache_set_size( mlt_service self, const char *name, int size )
863 {
864         mlt_cache cache = get_cache( self, name );
865         if ( cache )
866                 mlt_cache_set_size( cache, size );
867 }
868
869 /** Get the current maximum size of 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  * \return the current maximum number of items to cache or zero if there is an error
875  */
876
877 int mlt_service_cache_get_size( mlt_service self, const char *name )
878 {
879         mlt_cache cache = get_cache( self, name );
880         if ( cache )
881                 return mlt_cache_get_size( cache );
882         else
883                 return 0;
884 }