]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
Add doxygen documentation for mlt_profile, mlt_pool, mlt_repository, and mlt_factory.
[mlt] / 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 }