]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
46197804ae0bc4f355740ff2d5f99d3f5f1409e4
[mlt] / src / framework / mlt_service.c
1 /*
2  * mlt_service.c -- interface for all service classes
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22 #include "mlt_service.h"
23 #include "mlt_filter.h"
24 #include "mlt_frame.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <pthread.h>
29
30 /** IMPORTANT NOTES
31
32         The base service implements a null frame producing service - as such,
33         it is functional without extension and will produce test cards frames 
34         and PAL sized audio frames.
35
36         PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT 
37         CONTROL THIS IN EXTENDING CLASSES.
38 */
39
40 /** Private service definition.
41 */
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 /** Constructor
65 */
66
67 int mlt_service_init( mlt_service this, void *child )
68 {
69         int error = 0;
70
71         // Initialise everything to NULL
72         memset( this, 0, sizeof( struct mlt_service_s ) );
73
74         // Assign the child
75         this->child = child;
76
77         // Generate local space
78         this->local = calloc( sizeof( mlt_service_base ), 1 );
79
80         // Associate the methods
81         this->get_frame = service_get_frame;
82         
83         // Initialise the properties
84         error = mlt_properties_init( &this->parent, this );
85         if ( error == 0 )
86         {
87                 this->parent.close = ( mlt_destructor )mlt_service_close;
88                 this->parent.close_object = this;
89
90                 mlt_events_init( &this->parent );
91                 mlt_events_register( &this->parent, "service-changed", NULL );
92                 mlt_events_register( &this->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed );
93                 pthread_mutex_init( &( ( mlt_service_base * )this->local )->mutex, NULL );
94         }
95
96         return error;
97 }
98
99 static void mlt_service_property_changed( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
100 {
101         if ( listener != NULL )
102                 listener( owner, this, ( char * )args[ 0 ] );
103 }
104
105 void mlt_service_lock( mlt_service this )
106 {
107         if ( this != NULL )
108                 pthread_mutex_lock( &( ( mlt_service_base * )this->local )->mutex );
109 }
110
111 void mlt_service_unlock( mlt_service this )
112 {
113         if ( this != NULL )
114                 pthread_mutex_unlock( &( ( mlt_service_base * )this->local )->mutex );
115 }
116
117 mlt_service_type mlt_service_identify( mlt_service this )
118 {
119         mlt_service_type type = invalid_type;
120         if ( this != NULL )
121         {
122                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
123                 char *mlt_type = mlt_properties_get( properties, "mlt_type" );
124                 char *resource = mlt_properties_get( properties, "resource" );
125                 if ( mlt_type == NULL )
126                         type = unknown_type;
127                 else if ( resource == NULL || !strcmp( resource, "<producer>" ) )
128                         type = producer_type;
129                 else if ( !strcmp( resource, "<playlist>" ) )
130                         type = playlist_type;
131                 else if ( !strcmp( resource, "<tractor>" ) )
132                         type = tractor_type;
133                 else if ( !strcmp( resource, "<multitrack>" ) )
134                         type = multitrack_type;
135                 else if ( !strcmp( mlt_type, "producer" ) )
136                         type = producer_type;
137                 else if ( !strcmp( mlt_type, "filter" ) )
138                         type = filter_type;
139                 else if ( !strcmp( mlt_type, "transition" ) )
140                         type = transition_type;
141                 else if ( !strcmp( mlt_type, "consumer" ) )
142                         type = consumer_type;
143                 else
144                         type = unknown_type;
145         }
146         return type;
147 }
148
149 /** Connect a producer service.
150         Returns: > 0 warning, == 0 success, < 0 serious error
151                          1 = this service does not accept input
152                          2 = the producer is invalid
153                          3 = the producer is already registered with this consumer
154 */
155
156 int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
157 {
158         int i = 0;
159
160         // Get the service base
161         mlt_service_base *base = this->local;
162
163         // Special case 'track' index - only works for last filter(s) in a particular chain
164         // but allows a filter to apply to the output frame regardless of which track it comes from
165         if ( index == -1 )
166                 index = 0;
167
168         // Check if the producer is already registered with this service
169         for ( i = 0; i < base->count; i ++ )
170                 if ( base->in[ i ] == producer )
171                         return 3;
172
173         // Allocate space
174         if ( index >= base->size )
175         {
176                 int new_size = base->size + index + 10;
177                 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
178                 if ( base->in != NULL )
179                 {
180                         for ( i = base->size; i < new_size; i ++ )
181                                 base->in[ i ] = NULL;
182                         base->size = new_size;
183                 }
184         }
185
186         // If we have space, assign the input
187         if ( base->in != NULL && index >= 0 && index < base->size )
188         {
189                 // Get the current service
190                 mlt_service current = base->in[ index ];
191
192                 // Increment the reference count on this producer
193                 if ( producer != NULL )
194                 {
195                         mlt_service_lock( producer );
196                         mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
197                         mlt_service_unlock( producer );
198                 }
199
200                 // Now we disconnect the producer service from its consumer
201                 mlt_service_disconnect( producer );
202                 
203                 // Add the service to index specified
204                 base->in[ index ] = producer;
205                 
206                 // Determine the number of active tracks
207                 if ( index >= base->count )
208                         base->count = index + 1;
209
210                 // Now we connect the producer to its connected consumer
211                 mlt_service_connect( producer, this );
212
213                 // Close the current service
214                 mlt_service_close( current );
215
216                 // Inform caller that all went well
217                 return 0;
218         }
219         else
220         {
221                 return -1;
222         }
223 }
224
225 /** Disconnect this service from its consumer.
226 */
227
228 static void mlt_service_disconnect( mlt_service this )
229 {
230         if ( this != NULL )
231         {
232                 // Get the service base
233                 mlt_service_base *base = this->local;
234
235                 // Disconnect
236                 base->out = NULL;
237         }
238 }
239
240 /** Obtain the consumer this service is connected to.
241 */
242
243 mlt_service mlt_service_consumer( mlt_service this )
244 {
245         // Get the service base
246         mlt_service_base *base = this->local;
247
248         // Return the connected consumer
249         return base->out;
250 }
251
252 /** Obtain the producer this service is connected to.
253 */
254
255 mlt_service mlt_service_producer( mlt_service this )
256 {
257         // Get the service base
258         mlt_service_base *base = this->local;
259
260         // Return the connected producer
261         return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
262 }
263
264 /** Associate this service to the consumer.
265 */
266
267 static void mlt_service_connect( mlt_service this, mlt_service that )
268 {
269         if ( this != NULL )
270         {
271                 // Get the service base
272                 mlt_service_base *base = this->local;
273
274                 // There's a bit more required here...
275                 base->out = that;
276         }
277 }
278
279 /** Get the first connected producer service.
280 */
281
282 mlt_service mlt_service_get_producer( mlt_service this )
283 {
284         mlt_service producer = NULL;
285
286         // Get the service base
287         mlt_service_base *base = this->local;
288
289         if ( base->in != NULL )
290                 producer = base->in[ 0 ];
291         
292         return producer;
293 }
294
295 /** Default implementation of get_frame.
296 */
297
298 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
299 {
300         mlt_service_base *base = this->local;
301         if ( index < base->count )
302         {
303                 mlt_service producer = base->in[ index ];
304                 if ( producer != NULL )
305                         return mlt_service_get_frame( producer, frame, index );
306         }
307         *frame = mlt_frame_init( );
308         return 0;
309 }
310
311 /** Return the properties object.
312 */
313
314 mlt_properties mlt_service_properties( mlt_service self )
315 {
316         return self != NULL ? &self->parent : NULL;
317 }
318
319 /** Recursively apply attached filters
320 */
321
322 void mlt_service_apply_filters( mlt_service this, mlt_frame frame, int index )
323 {
324         int i;
325         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
326         mlt_properties service_properties = MLT_SERVICE_PROPERTIES( this );
327         mlt_service_base *base = this->local;
328         mlt_position position = mlt_frame_get_position( frame );
329         mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
330         mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
331
332         if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
333         {
334                 // Process the frame with the attached filters
335                 for ( i = 0; i < base->filter_count; i ++ )
336                 {
337                         if ( base->filters[ i ] != NULL )
338                         {
339                                 mlt_position in = mlt_filter_get_in( base->filters[ i ] );
340                                 mlt_position out = mlt_filter_get_out( base->filters[ i ] );
341                                 if ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) )
342                                 {
343                                         mlt_properties_set_position( frame_properties, "in", in == 0 ? this_in : in );
344                                         mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out : out );
345                                         mlt_filter_process( base->filters[ i ], frame );
346                                         mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
347                                 }
348                         }
349                 }
350         }
351 }
352
353 /** Obtain a frame.
354 */
355
356 int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
357 {
358         mlt_service_lock( this );
359         if ( this != NULL && this->get_frame != NULL )
360         {
361                 int result = 0;
362                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
363                 mlt_position in = mlt_properties_get_position( properties, "in" );
364                 mlt_position out = mlt_properties_get_position( properties, "out" );
365                 mlt_properties_inc_ref( properties );
366                 result = this->get_frame( this, frame, index );
367                 if ( result == 0 )
368                 {
369                         properties = MLT_FRAME_PROPERTIES( *frame );
370                         if ( in >=0 && out > 0 )
371                         {
372                                 mlt_properties_set_position( properties, "in", in );
373                                 mlt_properties_set_position( properties, "out", out );
374                         }
375                         mlt_service_apply_filters( this, *frame, 1 );
376                         mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
377                         mlt_service_unlock( this );
378                 }
379                 else
380                 {
381                         mlt_service_unlock( this );
382                         mlt_service_close( this );
383                 }
384                 return result;
385         }
386         mlt_service_unlock( this );
387         *frame = mlt_frame_init( );
388         return 0;
389 }
390
391 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
392 {
393         mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
394 }
395
396 /** Attach a filter.
397 */
398
399 int mlt_service_attach( mlt_service this, mlt_filter filter )
400 {
401         int error = this == NULL || filter == NULL;
402         if ( error == 0 )
403         {
404                 int i = 0;
405                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
406                 mlt_service_base *base = this->local;
407
408                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
409                         if ( base->filters[ i ] == filter )
410                                 error = 1;
411
412                 if ( error == 0 )
413                 {
414                         if ( base->filter_count == base->filter_size )
415                         {
416                                 base->filter_size += 10;
417                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
418                         }
419
420                         if ( base->filters != NULL )
421                         {
422                                 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
423                                 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
424                                 base->filters[ base->filter_count ++ ] = filter;
425                                 mlt_events_fire( properties, "service-changed", NULL );
426                                 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
427                                 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
428                         }
429                         else
430                         {
431                                 error = 2;
432                         }
433                 }
434         }
435         return error;
436 }
437
438 /** Detach a filter.
439 */
440
441 int mlt_service_detach( mlt_service this, mlt_filter filter )
442 {
443         int error = this == NULL || filter == NULL;
444         if ( error == 0 )
445         {
446                 int i = 0;
447                 mlt_service_base *base = this->local;
448                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
449
450                 for ( i = 0; i < base->filter_count; i ++ )
451                         if ( base->filters[ i ] == filter )
452                                 break;
453
454                 if ( i < base->filter_count )
455                 {
456                         base->filters[ i ] = NULL;
457                         for ( i ++ ; i < base->filter_count; i ++ )
458                                 base->filters[ i - 1 ] = base->filters[ i ];
459                         base->filter_count --;
460                         mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
461                         mlt_filter_close( filter );
462                         mlt_events_fire( properties, "service-changed", NULL );
463                 }
464         }
465         return error;
466 }
467
468 /** Retrieve a filter.
469 */
470
471 mlt_filter mlt_service_filter( mlt_service this, int index )
472 {
473         mlt_filter filter = NULL;
474         if ( this != NULL )
475         {
476                 mlt_service_base *base = this->local;
477                 if ( index >= 0 && index < base->filter_count )
478                         filter = base->filters[ index ];
479         }
480         return filter;
481 }
482
483 /** Close the service.
484 */
485
486 void mlt_service_close( mlt_service this )
487 {
488         mlt_service_lock( this );
489         if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
490         {
491                 mlt_service_unlock( this );
492                 if ( this->close != NULL )
493                 {
494                         this->close( this->close_object );
495                 }
496                 else
497                 {
498                         mlt_service_base *base = this->local;
499                         int i = 0;
500                         int count = base->filter_count;
501                         mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
502                         while( count -- )
503                                 mlt_service_detach( this, base->filters[ 0 ] );
504                         free( base->filters );
505                         for ( i = 0; i < base->count; i ++ )
506                                 if ( base->in[ i ] != NULL )
507                                         mlt_service_close( base->in[ i ] );
508                         this->parent.close = NULL;
509                         free( base->in );
510                         pthread_mutex_destroy( &base->mutex );
511                         free( base );
512                         mlt_properties_close( &this->parent );
513                 }
514         }
515         else
516         {
517                 mlt_service_unlock( this );
518         }
519 }
520