3 * \brief interface definition for all service classes
6 * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7 * \author Charles Yates <charles.yates@pandora.be>
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.
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.
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
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"
30 #include "mlt_producer.h"
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.
44 PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
45 CONTROL THIS IN EXTENDING CLASSES.
48 /** \brief private service definition */
59 pthread_mutex_t mutex;
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 );
71 /** Initialize a service.
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
79 int mlt_service_init( mlt_service self, void *child )
83 // Initialise everything to NULL
84 memset( self, 0, sizeof( struct mlt_service_s ) );
89 // Generate local space
90 self->local = calloc( 1, sizeof( mlt_service_base ) );
92 // Associate the methods
93 self->get_frame = service_get_frame;
95 // Initialise the properties
96 error = mlt_properties_init( &self->parent, self );
99 self->parent.close = ( mlt_destructor )mlt_service_close;
100 self->parent.close_object = self;
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 );
111 /** The transmitter for property changes.
113 * Invokes the listener.
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
122 static void mlt_service_property_changed( mlt_listener listener, mlt_properties owner, mlt_service self, void **args )
124 if ( listener != NULL )
125 listener( owner, self, ( char * )args[ 0 ] );
128 /** Acquire a mutual exclusion lock on this service.
130 * \public \memberof mlt_service_s
131 * \param self the service to lock
134 void mlt_service_lock( mlt_service self )
137 pthread_mutex_lock( &( ( mlt_service_base * )self->local )->mutex );
140 /** Release a mutual exclusion lock on this service.
142 * \public \memberof mlt_service_s
143 * \param self the service to unlock
146 void mlt_service_unlock( mlt_service self )
149 pthread_mutex_unlock( &( ( mlt_service_base * )self->local )->mutex );
152 /** Identify the subclass of the service.
154 * \public \memberof mlt_service_s
155 * \param self a service
156 * \return the subclass
159 mlt_service_type mlt_service_identify( mlt_service self )
161 mlt_service_type type = invalid_type;
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 )
169 else if (resource != NULL && !strcmp( resource, "<playlist>" ) )
170 type = playlist_type;
171 else if (resource != NULL && !strcmp( resource, "<tractor>" ) )
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" ) )
179 else if ( !strcmp( mlt_type, "transition" ) )
180 type = transition_type;
181 else if ( !strcmp( mlt_type, "consumer" ) )
182 type = consumer_type;
189 /** Connect a producer to the service.
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
201 int mlt_service_connect_producer( mlt_service self, mlt_service producer, int index )
205 // Get the service base
206 mlt_service_base *base = self->local;
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
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 )
219 if ( index >= base->size )
221 int new_size = base->size + index + 10;
222 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
223 if ( base->in != NULL )
225 for ( i = base->size; i < new_size; i ++ )
226 base->in[ i ] = NULL;
227 base->size = new_size;
231 // If we have space, assign the input
232 if ( base->in != NULL && index >= 0 && index < base->size )
234 // Get the current service
235 mlt_service current = base->in[ index ];
237 // Increment the reference count on this producer
238 if ( producer != NULL )
239 mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
241 // Now we disconnect the producer service from its consumer
242 mlt_service_disconnect( producer );
244 // Add the service to index specified
245 base->in[ index ] = producer;
247 // Determine the number of active tracks
248 if ( index >= base->count )
249 base->count = index + 1;
251 // Now we connect the producer to its connected consumer
252 mlt_service_connect( producer, self );
254 // Close the current service
255 mlt_service_close( current );
257 // Inform caller that all went well
266 /** Disconnect a service from its consumer.
268 * \private \memberof mlt_service_s
269 * \param self a service
272 static void mlt_service_disconnect( mlt_service self )
276 // Get the service base
277 mlt_service_base *base = self->local;
284 /** Obtain the consumer a service is connected to.
286 * \public \memberof mlt_service_s
287 * \param self a service
288 * \return the consumer
291 mlt_service mlt_service_consumer( mlt_service self )
293 // Get the service base
294 mlt_service_base *base = self->local;
296 // Return the connected consumer
300 /** Obtain the producer a service is connected to.
302 * \public \memberof mlt_service_s
303 * \param self a service
304 * \return the last-most producer
307 mlt_service mlt_service_producer( mlt_service self )
309 // Get the service base
310 mlt_service_base *base = self->local;
312 // Return the connected producer
313 return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
316 /** Associate a service to a consumer.
318 * Overwrites connection to any existing consumer.
319 * \private \memberof mlt_service_s
320 * \param self a service
321 * \param that a consumer
324 static void mlt_service_connect( mlt_service self, mlt_service that )
328 // Get the service base
329 mlt_service_base *base = self->local;
331 // There's a bit more required here...
336 /** Get the first connected producer.
338 * \public \memberof mlt_service_s
339 * \param self a service
340 * \return the first producer
343 mlt_service mlt_service_get_producer( mlt_service self )
345 mlt_service producer = NULL;
347 // Get the service base
348 mlt_service_base *base = self->local;
350 if ( base->in != NULL )
351 producer = base->in[ 0 ];
356 /** Default implementation of the get_frame virtual function.
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
365 static int service_get_frame( mlt_service self, mlt_frame_ptr frame, int index )
367 mlt_service_base *base = self->local;
368 if ( index < base->count )
370 mlt_service producer = base->in[ index ];
371 if ( producer != NULL )
372 return mlt_service_get_frame( producer, frame, index );
374 *frame = mlt_frame_init( self );
378 /** Return the properties object.
380 * \public \memberof mlt_service_s
381 * \param self a service
382 * \return the properties
385 mlt_properties mlt_service_properties( mlt_service self )
387 return self != NULL ? &self->parent : NULL;
390 /** Recursively apply attached filters.
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
398 void mlt_service_apply_filters( mlt_service self, mlt_frame frame, int index )
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" );
408 if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
410 // Process the frame with the attached filters
411 for ( i = 0; i < base->filter_count; i ++ )
413 if ( base->filters[ i ] != NULL )
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 ) ) ) )
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 );
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
439 int mlt_service_get_frame( mlt_service self, mlt_frame_ptr frame, int index )
444 mlt_service_lock( self );
446 // Ensure that the frame is NULL
449 // Only process if we have a valid service
450 if ( self != NULL && self->get_frame != NULL )
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;
457 result = self->get_frame( self, frame, index );
461 mlt_properties_inc_ref( properties );
462 properties = MLT_FRAME_PROPERTIES( *frame );
464 if ( in >=0 && out > 0 )
466 mlt_properties_set_position( properties, "in", in );
467 mlt_properties_set_position( properties, "out", out );
469 mlt_service_apply_filters( self, *frame, 1 );
470 mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), self );
472 if ( mlt_service_identify( self ) == producer_type &&
473 mlt_properties_get_int( MLT_SERVICE_PROPERTIES( self ), "_need_previous_next" ) )
475 // Save the new position from self->get_frame
476 mlt_position new_position = mlt_producer_position( MLT_PRODUCER( self ) );
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 );
483 mlt_properties_set_data( properties, "previous frame",
484 previous_frame, 0, ( mlt_destructor ) mlt_frame_close, NULL );
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 );
492 mlt_properties_set_data( properties, "next frame",
493 next_frame, 0, ( mlt_destructor ) mlt_frame_close, NULL );
496 // Restore the new position
497 mlt_producer_seek( MLT_PRODUCER(self), new_position );
502 // Make sure we return a frame
503 if ( *frame == NULL )
504 *frame = mlt_frame_init( self );
506 // Unlock the service
507 mlt_service_unlock( self );
512 /** The service-changed event handler.
514 * \private \memberof mlt_service_s
515 * \param owner ignored
516 * \param self the service on which the "service-changed" event is fired
519 static void mlt_service_filter_changed( mlt_service owner, mlt_service self )
521 mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "service-changed", NULL );
524 /** The property-changed event handler.
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
532 static void mlt_service_filter_property_changed( mlt_service owner, mlt_service self, char *name )
534 mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "property-changed", name, NULL );
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
545 int mlt_service_attach( mlt_service self, mlt_filter filter )
547 int error = self == NULL || filter == NULL;
551 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
552 mlt_service_base *base = self->local;
554 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
555 if ( base->filters[ i ] == filter )
560 if ( base->filter_count == base->filter_size )
562 base->filter_size += 10;
563 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
566 if ( base->filters != NULL )
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 );
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 );
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
597 int mlt_service_detach( mlt_service self, mlt_filter filter )
599 int error = self == NULL || filter == NULL;
603 mlt_service_base *base = self->local;
604 mlt_properties properties = MLT_SERVICE_PROPERTIES( self );
606 for ( i = 0; i < base->filter_count; i ++ )
607 if ( base->filters[ i ] == filter )
610 if ( i < base->filter_count )
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 );
624 /** Get the number of filters attached.
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
631 int mlt_service_filter_count( mlt_service self )
636 mlt_service_base *base = self->local;
637 result = base->filter_count;
642 /** Reorder the attached filters.
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
651 int mlt_service_move_filter( mlt_service self, int from, int to )
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 )
663 mlt_filter filter = base->filters[from];
667 for ( i = from; i > to; i-- )
668 base->filters[i] = base->filters[i - 1];
672 for ( i = from; i < to; i++ )
673 base->filters[i] = base->filters[i + 1];
675 base->filters[to] = filter;
676 mlt_events_fire( MLT_SERVICE_PROPERTIES(self), "service-changed", NULL );
683 /** Retrieve an attached filter.
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
691 mlt_filter mlt_service_filter( mlt_service self, int index )
693 mlt_filter filter = NULL;
696 mlt_service_base *base = self->local;
697 if ( index >= 0 && index < base->filter_count )
698 filter = base->filters[ index ];
703 /** Retrieve the profile.
705 * \public \memberof mlt_service_s
706 * \param self a service
707 * \return the profile
710 mlt_profile mlt_service_profile( mlt_service self )
712 return self? mlt_properties_get_data( MLT_SERVICE_PROPERTIES( self ), "_profile", NULL ) : NULL;
715 /** Set the profile for a service.
717 * \public \memberof mlt_service_s
718 * \param self a service
719 * \param profile the profile to set onto the service
722 void mlt_service_set_profile( mlt_service self, mlt_profile profile )
724 mlt_properties_set_data( MLT_SERVICE_PROPERTIES( self ), "_profile", profile, 0, NULL, NULL );
727 /** Destroy a service.
729 * \public \memberof mlt_service_s
730 * \param self the service to destroy
733 void mlt_service_close( mlt_service self )
735 if ( self != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( self ) ) <= 0 )
737 if ( self->close != NULL )
739 self->close( self->close_object );
743 mlt_service_base *base = self->local;
745 int count = base->filter_count;
746 mlt_events_block( MLT_SERVICE_PROPERTIES( self ), self );
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;
755 pthread_mutex_destroy( &base->mutex );
757 mlt_properties_close( &self->parent );
762 /** Release a service's cache items.
764 * \private \memberof mlt_service_s
765 * \param self a service
768 void mlt_service_cache_purge( mlt_service self )
770 mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
774 int i = mlt_properties_count( caches );
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 );
783 /** Lookup the cache object for a service.
785 * \private \memberof mlt_service_s
786 * \param self a service
787 * \param name a name for the object
791 static mlt_cache get_cache( mlt_service self, const char *name )
793 mlt_cache result = NULL;
794 mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
798 caches = mlt_properties_new();
799 mlt_properties_set_data( mlt_global_properties(), "caches", caches, 0, ( mlt_destructor )mlt_properties_close, NULL );
803 result = mlt_properties_get_data( caches, name, NULL );
806 result = mlt_cache_init();
807 mlt_properties_set_data( caches, name, result, 0, ( mlt_destructor )mlt_cache_close, NULL );
814 /** Put an object into a service's cache.
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
824 void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor )
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 );
830 mlt_cache_put( cache, self, data, size, destructor );
833 /** Get an object from a service's cache.
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
842 mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name )
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 );
849 result = mlt_cache_get( cache, self );
854 /** Set the number of items to cache for the named cache.
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
862 void mlt_service_cache_set_size( mlt_service self, const char *name, int size )
864 mlt_cache cache = get_cache( self, name );
866 mlt_cache_set_size( cache, size );
869 /** Get the current maximum size of the named cache.
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
877 int mlt_service_cache_get_size( mlt_service self, const char *name )
879 mlt_cache cache = get_cache( self, name );
881 return mlt_cache_get_size( cache );