Merge ../mlt
[melted] / 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
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <pthread.h>
35
36
37 /* IMPORTANT NOTES
38
39 The base service implements a null frame producing service - as such,
40 it is functional without extension and will produce test cards frames
41 and PAL sized audio frames.
42
43 PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
44 CONTROL THIS IN EXTENDING CLASSES.
45 */
46
47 /** \brief private service definition */
48
49 typedef struct
50 {
51 int size;
52 int count;
53 mlt_service *in;
54 mlt_service out;
55 int filter_count;
56 int filter_size;
57 mlt_filter *filters;
58 pthread_mutex_t mutex;
59 }
60 mlt_service_base;
61
62 /* Private methods
63 */
64
65 static void mlt_service_disconnect( mlt_service this );
66 static void mlt_service_connect( mlt_service this, mlt_service that );
67 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
68 static void mlt_service_property_changed( mlt_listener, mlt_properties owner, mlt_service this, void **args );
69 static void purge_cache( mlt_service self );
70
71 /** Initialize a service.
72 *
73 * \public \memberof mlt_service_s
74 * \param this 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 this, void *child )
80 {
81 int error = 0;
82
83 // Initialise everything to NULL
84 memset( this, 0, sizeof( struct mlt_service_s ) );
85
86 // Assign the child
87 this->child = child;
88
89 // Generate local space
90 this->local = calloc( sizeof( mlt_service_base ), 1 );
91
92 // Associate the methods
93 this->get_frame = service_get_frame;
94
95 // Initialise the properties
96 error = mlt_properties_init( &this->parent, this );
97 if ( error == 0 )
98 {
99 this->parent.close = ( mlt_destructor )mlt_service_close;
100 this->parent.close_object = this;
101
102 mlt_events_init( &this->parent );
103 mlt_events_register( &this->parent, "service-changed", NULL );
104 mlt_events_register( &this->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed );
105 pthread_mutex_init( &( ( mlt_service_base * )this->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 this 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 this, void **args )
123 {
124 if ( listener != NULL )
125 listener( owner, this, ( char * )args[ 0 ] );
126 }
127
128 /** Acquire a mutual exclusion lock on this service.
129 *
130 * \public \memberof mlt_service_s
131 * \param this the service to lock
132 */
133
134 void mlt_service_lock( mlt_service this )
135 {
136 if ( this != NULL )
137 pthread_mutex_lock( &( ( mlt_service_base * )this->local )->mutex );
138 }
139
140 /** Release a mutual exclusion lock on this service.
141 *
142 * \public \memberof mlt_service_s
143 * \param this the service to unlock
144 */
145
146 void mlt_service_unlock( mlt_service this )
147 {
148 if ( this != NULL )
149 pthread_mutex_unlock( &( ( mlt_service_base * )this->local )->mutex );
150 }
151
152 /** Identify the subclass of the service.
153 *
154 * \public \memberof mlt_service_s
155 * \param this a service
156 * \return the subclass
157 */
158
159 mlt_service_type mlt_service_identify( mlt_service this )
160 {
161 mlt_service_type type = invalid_type;
162 if ( this != NULL )
163 {
164 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
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 this 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 this consumer
199 */
200
201 int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
202 {
203 int i = 0;
204
205 // Get the service base
206 mlt_service_base *base = this->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, this );
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 this service from its consumer.
267 *
268 * \public \memberof mlt_service_s
269 * \param this a service
270 */
271
272 static void mlt_service_disconnect( mlt_service this )
273 {
274 if ( this != NULL )
275 {
276 // Get the service base
277 mlt_service_base *base = this->local;
278
279 // Disconnect
280 base->out = NULL;
281 }
282 }
283
284 /** Obtain the consumer this service is connected to.
285 *
286 * \public \memberof mlt_service_s
287 * \param this a service
288 * \return the consumer
289 */
290
291 mlt_service mlt_service_consumer( mlt_service this )
292 {
293 // Get the service base
294 mlt_service_base *base = this->local;
295
296 // Return the connected consumer
297 return base->out;
298 }
299
300 /** Obtain the producer this service is connected to.
301 *
302 * \public \memberof mlt_service_s
303 * \param this a service
304 * \return the last-most producer
305 */
306
307 mlt_service mlt_service_producer( mlt_service this )
308 {
309 // Get the service base
310 mlt_service_base *base = this->local;
311
312 // Return the connected producer
313 return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
314 }
315
316 /** Associate this service to a consumer.
317 *
318 * Overwrites connection to any existing consumer.
319 * \private \memberof mlt_service_s
320 * \param this a service
321 * \param that a consumer
322 */
323
324 static void mlt_service_connect( mlt_service this, mlt_service that )
325 {
326 if ( this != NULL )
327 {
328 // Get the service base
329 mlt_service_base *base = this->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 this a service
340 * \return the first producer
341 */
342
343 mlt_service mlt_service_get_producer( mlt_service this )
344 {
345 mlt_service producer = NULL;
346
347 // Get the service base
348 mlt_service_base *base = this->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 this 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 this, mlt_frame_ptr frame, int index )
366 {
367 mlt_service_base *base = this->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( this );
375 return 0;
376 }
377
378 /** Return the properties object.
379 *
380 * \public \memberof mlt_service_s
381 * \param this a service
382 * \return the properties
383 */
384
385 mlt_properties mlt_service_properties( mlt_service this )
386 {
387 return this != NULL ? &this->parent : NULL;
388 }
389
390 /** Recursively apply attached filters.
391 *
392 * \public \memberof mlt_service_s
393 * \param this 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 this, 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( this );
403 mlt_service_base *base = this->local;
404 mlt_position position = mlt_frame_get_position( frame );
405 mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
406 mlt_position this_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 ? this_in : in );
421 mlt_properties_set_position( frame_properties, "out", out == 0 ? this_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 this 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 this, mlt_frame_ptr frame, int index )
440 {
441 int result = 0;
442
443 // Lock the service
444 mlt_service_lock( this );
445
446 // Ensure that the frame is NULL
447 *frame = NULL;
448
449 // Only process if we have a valid service
450 if ( this != NULL && this->get_frame != NULL )
451 {
452 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
453 mlt_position in = mlt_properties_get_position( properties, "in" );
454 mlt_position out = mlt_properties_get_position( properties, "out" );
455
456 result = this->get_frame( this, frame, index );
457
458 if ( result == 0 )
459 {
460 mlt_properties_inc_ref( properties );
461 properties = MLT_FRAME_PROPERTIES( *frame );
462 if ( in >=0 && out > 0 )
463 {
464 mlt_properties_set_position( properties, "in", in );
465 mlt_properties_set_position( properties, "out", out );
466 }
467 mlt_service_apply_filters( this, *frame, 1 );
468 mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
469 }
470 }
471
472 // Make sure we return a frame
473 if ( *frame == NULL )
474 *frame = mlt_frame_init( this );
475
476 // Unlock the service
477 mlt_service_unlock( this );
478
479 return result;
480 }
481
482 /** The service-changed event handler.
483 *
484 * \private \memberof mlt_service_s
485 * \param owner ignored
486 * \param this the service on which the "service-changed" event is fired
487 */
488
489 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
490 {
491 mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
492 }
493
494 /** Attach a filter.
495 *
496 * \public \memberof mlt_service_s
497 * \param this a service
498 * \param filter the filter to attach
499 * \return true if there was an error
500 */
501
502 int mlt_service_attach( mlt_service this, mlt_filter filter )
503 {
504 int error = this == NULL || filter == NULL;
505 if ( error == 0 )
506 {
507 int i = 0;
508 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
509 mlt_service_base *base = this->local;
510
511 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
512 if ( base->filters[ i ] == filter )
513 error = 1;
514
515 if ( error == 0 )
516 {
517 if ( base->filter_count == base->filter_size )
518 {
519 base->filter_size += 10;
520 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
521 }
522
523 if ( base->filters != NULL )
524 {
525 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
526 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
527 base->filters[ base->filter_count ++ ] = filter;
528 mlt_events_fire( properties, "service-changed", NULL );
529 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
530 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
531 }
532 else
533 {
534 error = 2;
535 }
536 }
537 }
538 return error;
539 }
540
541 /** Detach a filter.
542 *
543 * \public \memberof mlt_service_s
544 * \param this a service
545 * \param filter the filter to detach
546 * \return true if there was an error
547 */
548
549 int mlt_service_detach( mlt_service this, mlt_filter filter )
550 {
551 int error = this == NULL || filter == NULL;
552 if ( error == 0 )
553 {
554 int i = 0;
555 mlt_service_base *base = this->local;
556 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
557
558 for ( i = 0; i < base->filter_count; i ++ )
559 if ( base->filters[ i ] == filter )
560 break;
561
562 if ( i < base->filter_count )
563 {
564 base->filters[ i ] = NULL;
565 for ( i ++ ; i < base->filter_count; i ++ )
566 base->filters[ i - 1 ] = base->filters[ i ];
567 base->filter_count --;
568 mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
569 mlt_filter_close( filter );
570 mlt_events_fire( properties, "service-changed", NULL );
571 }
572 }
573 return error;
574 }
575
576 /** Retrieve a filter.
577 *
578 * \public \memberof mlt_service_s
579 * \param this a service
580 * \param index which one of potentially multiple filters
581 * \return the filter or null if there was an error
582 */
583
584 mlt_filter mlt_service_filter( mlt_service this, int index )
585 {
586 mlt_filter filter = NULL;
587 if ( this != NULL )
588 {
589 mlt_service_base *base = this->local;
590 if ( index >= 0 && index < base->filter_count )
591 filter = base->filters[ index ];
592 }
593 return filter;
594 }
595
596 /** Retrieve the profile.
597 *
598 * \public \memberof mlt_service_s
599 * \param this a service
600 * \return the profile
601 */
602
603 mlt_profile mlt_service_profile( mlt_service this )
604 {
605 return mlt_properties_get_data( MLT_SERVICE_PROPERTIES( this ), "_profile", NULL );
606 }
607
608 /** Destroy a service.
609 *
610 * \public \memberof mlt_service_s
611 * \param this the service to destroy
612 */
613
614 void mlt_service_close( mlt_service this )
615 {
616 if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
617 {
618 if ( this->close != NULL )
619 {
620 this->close( this->close_object );
621 }
622 else
623 {
624 mlt_service_base *base = this->local;
625 int i = 0;
626 int count = base->filter_count;
627 mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
628 while( count -- )
629 mlt_service_detach( this, base->filters[ 0 ] );
630 free( base->filters );
631 for ( i = 0; i < base->count; i ++ )
632 if ( base->in[ i ] != NULL )
633 mlt_service_close( base->in[ i ] );
634 this->parent.close = NULL;
635 free( base->in );
636 pthread_mutex_destroy( &base->mutex );
637 free( base );
638 purge_cache( this );
639 mlt_properties_close( &this->parent );
640 }
641 }
642 else
643 {
644 mlt_service_unlock( this );
645 }
646 }
647
648 /** Release a service's cache items.
649 *
650 * \private \memberof mlt_service_s
651 * \param self a service
652 */
653
654 static void purge_cache( mlt_service self )
655 {
656 mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
657
658 if ( caches )
659 {
660 int i = mlt_properties_count( caches );
661 while ( i-- )
662 {
663 mlt_cache_purge( mlt_properties_get_data_at( caches, i, NULL ), self );
664 mlt_properties_set_data( mlt_global_properties(), mlt_properties_get_name( caches, i ), NULL, 0, NULL, NULL );
665 }
666 }
667 }
668
669 /** Lookup the cache object for a service.
670 *
671 * \private \memberof mlt_service_s
672 * \param self a service
673 * \param name a name for the object
674 * \return a cache
675 */
676
677 static mlt_cache get_cache( mlt_service self, const char *name )
678 {
679 mlt_cache result = NULL;
680 mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
681
682 if ( !caches )
683 {
684 caches = mlt_properties_new();
685 mlt_properties_set_data( mlt_global_properties(), "caches", caches, 0, ( mlt_destructor )mlt_properties_close, NULL );
686 }
687 if ( caches )
688 {
689 result = mlt_properties_get_data( caches, name, NULL );
690 if ( !result )
691 {
692 result = mlt_cache_init();
693 mlt_properties_set_data( caches, name, result, 0, ( mlt_destructor )mlt_cache_close, NULL );
694 }
695 }
696
697 return result;
698 }
699
700 /** Put an object into a service's cache.
701 *
702 * \public \memberof mlt_service_s
703 * \param self a service
704 * \param name a name for the object that is unique to the service class, but not to the instance
705 * \param data an opaque pointer to the object to put into the cache
706 * \param size the number of bytes pointed to by data
707 * \param destructor a function that releases the data
708 */
709
710 void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor )
711 {
712 mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p data %p\n", __FUNCTION__, name, self, data );
713 mlt_cache cache = get_cache( self, name );
714
715 if ( cache )
716 mlt_cache_put( cache, self, data, size, destructor );
717 }
718
719 /** Get an object from a service's cache.
720 *
721 * \public \memberof mlt_service_s
722 * \param self a service
723 * \param name a name for the object that is unique to the service class, but not to the instance
724 * \return a cache item or NULL if an object is not found
725 * \see mlt_cache_item_data
726 */
727
728 mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name )
729 {
730 mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p\n", __FUNCTION__, name, self );
731 mlt_cache_item result = NULL;
732 mlt_cache cache = get_cache( self, name );
733
734 if ( cache )
735 result = mlt_cache_get( cache, self );
736
737 return result;
738 }