]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
93ee56b8167ea4278a619a4445e9e5b1cf9edde3
[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 ( !strcmp( mlt_type, "producer" ) )
128                         type = producer_type;
129                 else if ( !strcmp( mlt_type, "mlt_producer" ) )
130                 {
131                         if ( resource == NULL || !strcmp( resource, "<producer>" ) )
132                                 type = producer_type;
133                         else if ( !strcmp( resource, "<playlist>" ) )
134                                 type = playlist_type;
135                         else if ( !strcmp( resource, "<tractor>" ) )
136                                 type = tractor_type;
137                         else if ( !strcmp( resource, "<multitrack>" ) )
138                                 type = multitrack_type;
139                 }
140                 else if ( !strcmp( mlt_type, "filter" ) )
141                         type = filter_type;
142                 else if ( !strcmp( mlt_type, "transition" ) )
143                         type = transition_type;
144                 else if ( !strcmp( mlt_type, "consumer" ) )
145                         type = consumer_type;
146                 else
147                         type = unknown_type;
148         }
149         return type;
150 }
151
152 /** Connect a producer service.
153         Returns: > 0 warning, == 0 success, < 0 serious error
154                          1 = this service does not accept input
155                          2 = the producer is invalid
156                          3 = the producer is already registered with this consumer
157 */
158
159 int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
160 {
161         int i = 0;
162
163         // Get the service base
164         mlt_service_base *base = this->local;
165
166         // Check if the producer is already registered with this service
167         for ( i = 0; i < base->count; i ++ )
168                 if ( base->in[ i ] == producer )
169                         return 3;
170
171         // Allocate space
172         if ( index >= base->size )
173         {
174                 int new_size = base->size + index + 10;
175                 base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
176                 if ( base->in != NULL )
177                 {
178                         for ( i = base->size; i < new_size; i ++ )
179                                 base->in[ i ] = NULL;
180                         base->size = new_size;
181                 }
182         }
183
184         // If we have space, assign the input
185         if ( base->in != NULL && index >= 0 && index < base->size )
186         {
187                 // Get the current service
188                 mlt_service current = base->in[ index ];
189
190                 // Increment the reference count on this producer
191                 if ( producer != NULL )
192                 {
193                         mlt_service_lock( producer );
194                         mlt_properties_inc_ref( mlt_service_properties( producer ) );
195                         mlt_service_unlock( producer );
196                 }
197
198                 // Now we disconnect the producer service from its consumer
199                 mlt_service_disconnect( producer );
200                 
201                 // Add the service to index specified
202                 base->in[ index ] = producer;
203                 
204                 // Determine the number of active tracks
205                 if ( index >= base->count )
206                         base->count = index + 1;
207
208                 // Now we connect the producer to its connected consumer
209                 mlt_service_connect( producer, this );
210
211                 // Close the current service
212                 mlt_service_close( current );
213
214                 // Inform caller that all went well
215                 return 0;
216         }
217         else
218         {
219                 return -1;
220         }
221 }
222
223 /** Disconnect this service from its consumer.
224 */
225
226 static void mlt_service_disconnect( mlt_service this )
227 {
228         if ( this != NULL )
229         {
230                 // Get the service base
231                 mlt_service_base *base = this->local;
232
233                 // Disconnect
234                 base->out = NULL;
235         }
236 }
237
238 /** Obtain the consumer this service is connected to.
239 */
240
241 mlt_service mlt_service_consumer( mlt_service this )
242 {
243         // Get the service base
244         mlt_service_base *base = this->local;
245
246         // Return the connected consumer
247         return base->out;
248 }
249
250 /** Obtain the producer this service is connected to.
251 */
252
253 mlt_service mlt_service_producer( mlt_service this )
254 {
255         // Get the service base
256         mlt_service_base *base = this->local;
257
258         // Return the connected producer
259         return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
260 }
261
262 /** Associate this service to the consumer.
263 */
264
265 static void mlt_service_connect( mlt_service this, mlt_service that )
266 {
267         if ( this != NULL )
268         {
269                 // Get the service base
270                 mlt_service_base *base = this->local;
271
272                 // There's a bit more required here...
273                 base->out = that;
274         }
275 }
276
277 /** Get the first connected producer service.
278 */
279
280 mlt_service mlt_service_get_producer( mlt_service this )
281 {
282         mlt_service producer = NULL;
283
284         // Get the service base
285         mlt_service_base *base = this->local;
286
287         if ( base->in != NULL )
288                 producer = base->in[ 0 ];
289         
290         return producer;
291 }
292
293 /** Default implementation of get_frame.
294 */
295
296 static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
297 {
298         mlt_service_base *base = this->local;
299         if ( index < base->count )
300         {
301                 mlt_service producer = base->in[ index ];
302                 if ( producer != NULL )
303                         return mlt_service_get_frame( producer, frame, index );
304         }
305         *frame = mlt_frame_init( );
306         return 0;
307 }
308
309 /** Return the properties object.
310 */
311
312 mlt_properties mlt_service_properties( mlt_service self )
313 {
314         return self != NULL ? &self->parent : NULL;
315 }
316
317 /** Recursively apply attached filters
318 */
319
320 void mlt_service_apply_filters( mlt_service this, mlt_frame frame, int index )
321 {
322         int i;
323         mlt_properties frame_properties = mlt_frame_properties( frame );
324         mlt_properties service_properties = mlt_service_properties( this );
325         mlt_service_base *base = this->local;
326         mlt_position position = mlt_frame_get_position( frame );
327         mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
328         mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
329
330         if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
331         {
332                 // Process the frame with the attached filters
333                 for ( i = 0; i < base->filter_count; i ++ )
334                 {
335                         if ( base->filters[ i ] != NULL )
336                         {
337                                 mlt_position in = mlt_filter_get_in( base->filters[ i ] );
338                                 mlt_position out = mlt_filter_get_out( base->filters[ i ] );
339                                 if ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) )
340                                 {
341                                         mlt_properties_set_position( frame_properties, "in", in == 0 ? this_in : in );
342                                         mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out : out );
343                                         mlt_filter_process( base->filters[ i ], frame );
344                                         mlt_service_apply_filters( mlt_filter_service( base->filters[ i ] ), frame, index + 1 );
345                                 }
346                         }
347                 }
348         }
349 }
350
351 /** Obtain a frame.
352 */
353
354 int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
355 {
356         mlt_service_lock( this );
357         if ( this != NULL && this->get_frame != NULL )
358         {
359                 char uniq[ 20 ];
360                 int result = 0;
361                 mlt_properties properties = mlt_service_properties( this );
362                 mlt_position in = mlt_properties_get_position( properties, "in" );
363                 mlt_position out = mlt_properties_get_position( properties, "out" );
364                 mlt_properties_inc_ref( properties );
365                 mlt_service_unlock( this );
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                         sprintf( uniq, "gf_%p", this );
377                         if ( mlt_properties_get_data( properties, uniq, NULL ) != NULL )
378                                 fprintf( stderr, "%s is not unique in this frame\n", uniq );
379                         mlt_properties_set_data( properties, uniq, this, 0, ( mlt_destructor )mlt_service_close, NULL );
380                 }
381                 else
382                 {
383                         mlt_service_close( this );
384                 }
385                 return result;
386         }
387         mlt_service_unlock( this );
388         *frame = mlt_frame_init( );
389         return 0;
390 }
391
392 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
393 {
394         mlt_events_fire( mlt_service_properties( this ), "service-changed", NULL );
395 }
396
397 /** Attach a filter.
398 */
399
400 int mlt_service_attach( mlt_service this, mlt_filter filter )
401 {
402         int error = this == NULL || filter == NULL;
403         if ( error == 0 )
404         {
405                 int i = 0;
406                 mlt_properties properties = mlt_service_properties( this );
407                 mlt_service_base *base = this->local;
408
409                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
410                         if ( base->filters[ i ] == filter )
411                                 error = 1;
412
413                 if ( error == 0 )
414                 {
415                         if ( base->filter_count == base->filter_size )
416                         {
417                                 base->filter_size += 10;
418                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
419                         }
420
421                         if ( base->filters != NULL )
422                         {
423                                 mlt_properties props = mlt_filter_properties( filter );
424                                 mlt_properties_inc_ref( mlt_filter_properties( filter ) );
425                                 base->filters[ base->filter_count ++ ] = filter;
426                                 mlt_events_fire( properties, "service-changed", NULL );
427                                 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
428                                 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
429                         }
430                         else
431                         {
432                                 error = 2;
433                         }
434                 }
435         }
436         return error;
437 }
438
439 /** Detach a filter.
440 */
441
442 int mlt_service_detach( mlt_service this, mlt_filter filter )
443 {
444         int error = this == NULL || filter == NULL;
445         if ( error == 0 )
446         {
447                 int i = 0;
448                 mlt_service_base *base = this->local;
449                 mlt_properties properties = mlt_service_properties( this );
450
451                 for ( i = 0; i < base->filter_count; i ++ )
452                         if ( base->filters[ i ] == filter )
453                                 break;
454
455                 if ( i < base->filter_count )
456                 {
457                         base->filters[ i ] = NULL;
458                         for ( i ++ ; i < base->filter_count; i ++ )
459                                 base->filters[ i - 1 ] = base->filters[ i ];
460                         base->filter_count --;
461                         mlt_events_disconnect( mlt_filter_properties( filter ), this );
462                         mlt_filter_close( filter );
463                         mlt_events_fire( properties, "service-changed", NULL );
464                 }
465         }
466         return error;
467 }
468
469 /** Retrieve a filter.
470 */
471
472 mlt_filter mlt_service_filter( mlt_service this, int index )
473 {
474         mlt_filter filter = NULL;
475         if ( this != NULL )
476         {
477                 mlt_service_base *base = this->local;
478                 if ( index >= 0 && index < base->filter_count )
479                         filter = base->filters[ index ];
480         }
481         return filter;
482 }
483
484 /** Close the service.
485 */
486
487 void mlt_service_close( mlt_service this )
488 {
489         mlt_service_lock( this );
490         if ( this != NULL && mlt_properties_dec_ref( mlt_service_properties( this ) ) <= 0 )
491         {
492                 mlt_service_unlock( this );
493                 if ( this->close != NULL )
494                 {
495                         this->close( this->close_object );
496                 }
497                 else
498                 {
499                         mlt_service_base *base = this->local;
500                         int i = 0;
501                         int count = base->filter_count;
502                         mlt_events_block( mlt_service_properties( this ), this );
503                         while( count -- )
504                                 mlt_service_detach( this, base->filters[ 0 ] );
505                         free( base->filters );
506                         for ( i = 0; i < base->count; i ++ )
507                                 if ( base->in[ i ] != NULL )
508                                         mlt_service_close( base->in[ i ] );
509                         this->parent.close = NULL;
510                         free( base->in );
511                         pthread_mutex_destroy( &base->mutex );
512                         free( base );
513                         mlt_properties_close( &this->parent );
514                 }
515         }
516         else
517         {
518                 mlt_service_unlock( this );
519         }
520 }
521