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