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