]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
a9f5eda5f4d937bdd59fca6227a2b91f7a359e37
[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 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 }