mlt_filter.[ch], mlt_transition.[ch], mlt_consumer.[ch]: improve doxygen for filter...
[melted] / src / framework / mlt_service.c
1 /**
2 * \file mlt_service.c
3 * \brief interface definition for all service classes
4 *
5 * Copyright (C) 2003-2008 Ushodaya Enterprises Limited
6 * \author Charles Yates <charles.yates@pandora.be>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include "mlt_service.h"
24 #include "mlt_filter.h"
25 #include "mlt_frame.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <pthread.h>
30
31 /* IMPORTANT NOTES
32
33 The base service implements a null frame producing service - as such,
34 it is functional without extension and will produce test cards frames
35 and PAL sized audio frames.
36
37 PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
38 CONTROL THIS IN EXTENDING CLASSES.
39 */
40
41 /** \brief private service definition */
42
43 typedef struct
44 {
45 int size;
46 int count;
47 mlt_service *in;
48 mlt_service out;
49 int filter_count;
50 int filter_size;
51 mlt_filter *filters;
52 pthread_mutex_t mutex;
53 }
54 mlt_service_base;
55
56 /* Private methods
57 */
58
59 static void mlt_service_disconnect( mlt_service this );
60 static void mlt_service_connect( mlt_service this, mlt_service that );
61 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
62 static void mlt_service_property_changed( mlt_listener, mlt_properties owner, mlt_service this, void **args );
63
64 /** Initialize a service.
65 *
66 * \public \memberof mlt_service_s
67 * \param this the service structure to initialize
68 * \param child pointer to the child object for the subclass
69 * \return true if there was an error
70 */
71
72 int mlt_service_init( mlt_service this, void *child )
73 {
74 int error = 0;
75
76 // Initialise everything to NULL
77 memset( this, 0, sizeof( struct mlt_service_s ) );
78
79 // Assign the child
80 this->child = child;
81
82 // Generate local space
83 this->local = calloc( sizeof( mlt_service_base ), 1 );
84
85 // Associate the methods
86 this->get_frame = service_get_frame;
87
88 // Initialise the properties
89 error = mlt_properties_init( &this->parent, this );
90 if ( error == 0 )
91 {
92 this->parent.close = ( mlt_destructor )mlt_service_close;
93 this->parent.close_object = this;
94
95 mlt_events_init( &this->parent );
96 mlt_events_register( &this->parent, "service-changed", NULL );
97 mlt_events_register( &this->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed );
98 pthread_mutex_init( &( ( mlt_service_base * )this->local )->mutex, NULL );
99 }
100
101 return error;
102 }
103
104 /** The transmitter for property changes.
105 *
106 * Invokes the listener.
107 *
108 * \private \memberof mlt_service_s
109 * \param listener a function pointer that will be invoked
110 * \param owner a properties list that will be passed to \p listener
111 * \param this a service that will be passed to \p listener
112 * \param args an array of pointers - the first entry is passed as a string to \p listener
113 */
114
115 static void mlt_service_property_changed( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
116 {
117 if ( listener != NULL )
118 listener( owner, this, ( char * )args[ 0 ] );
119 }
120
121 /** Acquire a mutual exclusion lock on this service.
122 *
123 * \public \memberof mlt_service_s
124 * \param this the service to lock
125 */
126
127 void mlt_service_lock( mlt_service this )
128 {
129 if ( this != NULL )
130 pthread_mutex_lock( &( ( mlt_service_base * )this->local )->mutex );
131 }
132
133 /** Release a mutual exclusion lock on this service.
134 *
135 * \public \memberof mlt_service_s
136 * \param this the service to unlock
137 */
138
139 void mlt_service_unlock( mlt_service this )
140 {
141 if ( this != NULL )
142 pthread_mutex_unlock( &( ( mlt_service_base * )this->local )->mutex );
143 }
144
145 /** Identify the subclass of the service.
146 *
147 * \public \memberof mlt_service_s
148 * \param this a service
149 * \return the subclass
150 */
151
152 mlt_service_type mlt_service_identify( mlt_service this )
153 {
154 mlt_service_type type = invalid_type;
155 if ( this != NULL )
156 {
157 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
158 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
159 char *resource = mlt_properties_get( properties, "resource" );
160 if ( mlt_type == NULL )
161 type = unknown_type;
162 else if (resource != NULL && !strcmp( resource, "<playlist>" ) )
163 type = playlist_type;
164 else if (resource != NULL && !strcmp( resource, "<tractor>" ) )
165 type = tractor_type;
166 else if (resource != NULL && !strcmp( resource, "<multitrack>" ) )
167 type = multitrack_type;
168 else if ( !strcmp( mlt_type, "producer" ) )
169 type = producer_type;
170 else if ( !strcmp( mlt_type, "filter" ) )
171 type = filter_type;
172 else if ( !strcmp( mlt_type, "transition" ) )
173 type = transition_type;
174 else if ( !strcmp( mlt_type, "consumer" ) )
175 type = consumer_type;
176 else
177 type = unknown_type;
178 }
179 return type;
180 }
181
182 /** Connect a producer to the service.
183 *
184 * \public \memberof mlt_service_s
185 * \param this a service
186 * \param producer a producer
187 * \param index which of potentially multiple producers to this service (0 based)
188 * \return > 0 warning, == 0 success, < 0 serious error,
189 * 1 = this service does not accept input,
190 * 2 = the producer is invalid,
191 * 3 = the producer is already registered with this consumer
192 */
193
194 int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
195 {
196 int i = 0;
197
198 // Get the service base
199 mlt_service_base *base = this->local;
200
201 // Special case 'track' index - only works for last filter(s) in a particular chain
202 // but allows a filter to apply to the output frame regardless of which track it comes from
203 if ( index == -1 )
204 index = 0;
205
206 // Check if the producer is already registered with this service
207 for ( i = 0; i < base->count; i ++ )
208 if ( base->in[ i ] == producer )
209 return 3;
210
211 // Allocate space
212 if ( index >= base->size )
213 {
214 int new_size = base->size + index + 10;
215 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
216 if ( base->in != NULL )
217 {
218 for ( i = base->size; i < new_size; i ++ )
219 base->in[ i ] = NULL;
220 base->size = new_size;
221 }
222 }
223
224 // If we have space, assign the input
225 if ( base->in != NULL && index >= 0 && index < base->size )
226 {
227 // Get the current service
228 mlt_service current = base->in[ index ];
229
230 // Increment the reference count on this producer
231 if ( producer != NULL )
232 mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
233
234 // Now we disconnect the producer service from its consumer
235 mlt_service_disconnect( producer );
236
237 // Add the service to index specified
238 base->in[ index ] = producer;
239
240 // Determine the number of active tracks
241 if ( index >= base->count )
242 base->count = index + 1;
243
244 // Now we connect the producer to its connected consumer
245 mlt_service_connect( producer, this );
246
247 // Close the current service
248 mlt_service_close( current );
249
250 // Inform caller that all went well
251 return 0;
252 }
253 else
254 {
255 return -1;
256 }
257 }
258
259 /** Disconnect this service from its consumer.
260 *
261 * \public \memberof mlt_service_s
262 * \param this a service
263 */
264
265 static void mlt_service_disconnect( mlt_service this )
266 {
267 if ( this != NULL )
268 {
269 // Get the service base
270 mlt_service_base *base = this->local;
271
272 // Disconnect
273 base->out = NULL;
274 }
275 }
276
277 /** Obtain the consumer this service is connected to.
278 *
279 * \public \memberof mlt_service_s
280 * \param this a service
281 * \return the consumer
282 */
283
284 mlt_service mlt_service_consumer( mlt_service this )
285 {
286 // Get the service base
287 mlt_service_base *base = this->local;
288
289 // Return the connected consumer
290 return base->out;
291 }
292
293 /** Obtain the producer this service is connected to.
294 *
295 * \public \memberof mlt_service_s
296 * \param this a service
297 * \return the last-most producer
298 */
299
300 mlt_service mlt_service_producer( mlt_service this )
301 {
302 // Get the service base
303 mlt_service_base *base = this->local;
304
305 // Return the connected producer
306 return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
307 }
308
309 /** Associate this service to a consumer.
310 *
311 * Overwrites connection to any existing consumer.
312 * \private \memberof mlt_service_s
313 * \param this a service
314 * \param that a consumer
315 */
316
317 static void mlt_service_connect( mlt_service this, mlt_service that )
318 {
319 if ( this != NULL )
320 {
321 // Get the service base
322 mlt_service_base *base = this->local;
323
324 // There's a bit more required here...
325 base->out = that;
326 }
327 }
328
329 /** Get the first connected producer.
330 *
331 * \public \memberof mlt_service_s
332 * \param this a service
333 * \return the first producer
334 */
335
336 mlt_service mlt_service_get_producer( mlt_service this )
337 {
338 mlt_service producer = NULL;
339
340 // Get the service base
341 mlt_service_base *base = this->local;
342
343 if ( base->in != NULL )
344 producer = base->in[ 0 ];
345
346 return producer;
347 }
348
349 /** Default implementation of the get_frame virtual function.
350 *
351 * \private \memberof mlt_service_s
352 * \param this a service
353 * \param[out] frame a frame by reference
354 * \param index as determined by the producer
355 * \return false
356 */
357
358 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
359 {
360 mlt_service_base *base = this->local;
361 if ( index < base->count )
362 {
363 mlt_service producer = base->in[ index ];
364 if ( producer != NULL )
365 return mlt_service_get_frame( producer, frame, index );
366 }
367 *frame = mlt_frame_init( this );
368 return 0;
369 }
370
371 /** Return the properties object.
372 *
373 * \public \memberof mlt_service_s
374 * \param this a service
375 * \return the properties
376 */
377
378 mlt_properties mlt_service_properties( mlt_service this )
379 {
380 return this != NULL ? &this->parent : NULL;
381 }
382
383 /** Recursively apply attached filters.
384 *
385 * \public \memberof mlt_service_s
386 * \param this a service
387 * \param frame a frame
388 * \param index used to track depth of recursion, top caller should supply 0
389 */
390
391 void mlt_service_apply_filters( mlt_service this, mlt_frame frame, int index )
392 {
393 int i;
394 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
395 mlt_properties service_properties = MLT_SERVICE_PROPERTIES( this );
396 mlt_service_base *base = this->local;
397 mlt_position position = mlt_frame_get_position( frame );
398 mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
399 mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
400
401 if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
402 {
403 // Process the frame with the attached filters
404 for ( i = 0; i < base->filter_count; i ++ )
405 {
406 if ( base->filters[ i ] != NULL )
407 {
408 mlt_position in = mlt_filter_get_in( base->filters[ i ] );
409 mlt_position out = mlt_filter_get_out( base->filters[ i ] );
410 int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
411 if ( !disable && ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) ) )
412 {
413 mlt_properties_set_position( frame_properties, "in", in == 0 ? this_in : in );
414 mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out : out );
415 mlt_filter_process( base->filters[ i ], frame );
416 mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
417 }
418 }
419 }
420 }
421 }
422
423 /** Obtain a frame.
424 *
425 * \public \memberof mlt_service_s
426 * \param this a service
427 * \param[out] frame a frame by reference
428 * \param index as determined by the producer
429 * \return true if there was an error
430 */
431
432 int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
433 {
434 int result = 0;
435
436 // Lock the service
437 mlt_service_lock( this );
438
439 // Ensure that the frame is NULL
440 *frame = NULL;
441
442 // Only process if we have a valid service
443 if ( this != NULL && this->get_frame != NULL )
444 {
445 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
446 mlt_position in = mlt_properties_get_position( properties, "in" );
447 mlt_position out = mlt_properties_get_position( properties, "out" );
448
449 result = this->get_frame( this, frame, index );
450
451 if ( result == 0 )
452 {
453 mlt_properties_inc_ref( properties );
454 properties = MLT_FRAME_PROPERTIES( *frame );
455 if ( in >=0 && out > 0 )
456 {
457 mlt_properties_set_position( properties, "in", in );
458 mlt_properties_set_position( properties, "out", out );
459 }
460 mlt_service_apply_filters( this, *frame, 1 );
461 mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
462 }
463 }
464
465 // Make sure we return a frame
466 if ( *frame == NULL )
467 *frame = mlt_frame_init( this );
468
469 // Unlock the service
470 mlt_service_unlock( this );
471
472 return result;
473 }
474
475 /** The service-changed event handler.
476 *
477 * \private \memberof mlt_service_s
478 * \param owner ignored
479 * \param this the service on which the "service-changed" event is fired
480 */
481
482 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
483 {
484 mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
485 }
486
487 /** Attach a filter.
488 *
489 * \public \memberof mlt_service_s
490 * \param this a service
491 * \param filter the filter to attach
492 * \return true if there was an error
493 */
494
495 int mlt_service_attach( mlt_service this, mlt_filter filter )
496 {
497 int error = this == NULL || filter == NULL;
498 if ( error == 0 )
499 {
500 int i = 0;
501 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
502 mlt_service_base *base = this->local;
503
504 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
505 if ( base->filters[ i ] == filter )
506 error = 1;
507
508 if ( error == 0 )
509 {
510 if ( base->filter_count == base->filter_size )
511 {
512 base->filter_size += 10;
513 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
514 }
515
516 if ( base->filters != NULL )
517 {
518 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
519 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
520 base->filters[ base->filter_count ++ ] = filter;
521 mlt_events_fire( properties, "service-changed", NULL );
522 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
523 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
524 }
525 else
526 {
527 error = 2;
528 }
529 }
530 }
531 return error;
532 }
533
534 /** Detach a filter.
535 *
536 * \public \memberof mlt_service_s
537 * \param this a service
538 * \param filter the filter to detach
539 * \return true if there was an error
540 */
541
542 int mlt_service_detach( mlt_service this, mlt_filter filter )
543 {
544 int error = this == NULL || filter == NULL;
545 if ( error == 0 )
546 {
547 int i = 0;
548 mlt_service_base *base = this->local;
549 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
550
551 for ( i = 0; i < base->filter_count; i ++ )
552 if ( base->filters[ i ] == filter )
553 break;
554
555 if ( i < base->filter_count )
556 {
557 base->filters[ i ] = NULL;
558 for ( i ++ ; i < base->filter_count; i ++ )
559 base->filters[ i - 1 ] = base->filters[ i ];
560 base->filter_count --;
561 mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
562 mlt_filter_close( filter );
563 mlt_events_fire( properties, "service-changed", NULL );
564 }
565 }
566 return error;
567 }
568
569 /** Retrieve a filter.
570 *
571 * \public \memberof mlt_service_s
572 * \param this a service
573 * \param index which one of potentially multiple filters
574 * \return the filter or null if there was an error
575 */
576
577 mlt_filter mlt_service_filter( mlt_service this, int index )
578 {
579 mlt_filter filter = NULL;
580 if ( this != NULL )
581 {
582 mlt_service_base *base = this->local;
583 if ( index >= 0 && index < base->filter_count )
584 filter = base->filters[ index ];
585 }
586 return filter;
587 }
588
589 /** Retrieve the profile.
590 *
591 * \public \memberof mlt_service_s
592 * \param this a service
593 * \return the profile
594 */
595
596 mlt_profile mlt_service_profile( mlt_service this )
597 {
598 return mlt_properties_get_data( MLT_SERVICE_PROPERTIES( this ), "_profile", NULL );
599 }
600
601 /** Destroy a service.
602 *
603 * \public \memberof mlt_service_s
604 * \param this the service to destroy
605 */
606
607 void mlt_service_close( mlt_service this )
608 {
609 if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
610 {
611 if ( this->close != NULL )
612 {
613 this->close( this->close_object );
614 }
615 else
616 {
617 mlt_service_base *base = this->local;
618 int i = 0;
619 int count = base->filter_count;
620 mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
621 while( count -- )
622 mlt_service_detach( this, base->filters[ 0 ] );
623 free( base->filters );
624 for ( i = 0; i < base->count; i ++ )
625 if ( base->in[ i ] != NULL )
626 mlt_service_close( base->in[ i ] );
627 this->parent.close = NULL;
628 free( base->in );
629 pthread_mutex_destroy( &base->mutex );
630 free( base );
631 mlt_properties_close( &this->parent );
632 }
633 }
634 else
635 {
636 mlt_service_unlock( this );
637 }
638 }