]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
src/framework/*: improve the doxygen documentation (work in progress). This also...
[mlt] / 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 }