]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
More playlist modifications; service locking; sticky services on frame
[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                         fprintf( stderr, "Ugh case\n" );
384                         mlt_service_close( this );
385                 }
386                 return result;
387         }
388         mlt_service_unlock( this );
389         *frame = mlt_frame_init( );
390         return 0;
391 }
392
393 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
394 {
395         mlt_events_fire( mlt_service_properties( this ), "service-changed", NULL );
396 }
397
398 /** Attach a filter.
399 */
400
401 int mlt_service_attach( mlt_service this, mlt_filter filter )
402 {
403         int error = this == NULL || filter == NULL;
404         if ( error == 0 )
405         {
406                 int i = 0;
407                 mlt_properties properties = mlt_service_properties( this );
408                 mlt_service_base *base = this->local;
409
410                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
411                         if ( base->filters[ i ] == filter )
412                                 error = 1;
413
414                 if ( error == 0 )
415                 {
416                         if ( base->filter_count == base->filter_size )
417                         {
418                                 base->filter_size += 10;
419                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
420                         }
421
422                         if ( base->filters != NULL )
423                         {
424                                 mlt_properties props = mlt_filter_properties( filter );
425                                 mlt_properties_inc_ref( mlt_filter_properties( filter ) );
426                                 base->filters[ base->filter_count ++ ] = filter;
427                                 mlt_events_fire( properties, "service-changed", NULL );
428                                 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
429                                 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
430                         }
431                         else
432                         {
433                                 error = 2;
434                         }
435                 }
436         }
437         return error;
438 }
439
440 /** Detach a filter.
441 */
442
443 int mlt_service_detach( mlt_service this, mlt_filter filter )
444 {
445         int error = this == NULL || filter == NULL;
446         if ( error == 0 )
447         {
448                 int i = 0;
449                 mlt_service_base *base = this->local;
450                 mlt_properties properties = mlt_service_properties( this );
451
452                 for ( i = 0; i < base->filter_count; i ++ )
453                         if ( base->filters[ i ] == filter )
454                                 break;
455
456                 if ( i < base->filter_count )
457                 {
458                         base->filters[ i ] = NULL;
459                         for ( i ++ ; i < base->filter_count; i ++ )
460                                 base->filters[ i - 1 ] = base->filters[ i ];
461                         base->filter_count --;
462                         mlt_events_disconnect( mlt_filter_properties( filter ), this );
463                         mlt_filter_close( filter );
464                         mlt_events_fire( properties, "service-changed", NULL );
465                 }
466         }
467         return error;
468 }
469
470 /** Retrieve a filter.
471 */
472
473 mlt_filter mlt_service_filter( mlt_service this, int index )
474 {
475         mlt_filter filter = NULL;
476         if ( this != NULL )
477         {
478                 mlt_service_base *base = this->local;
479                 if ( index >= 0 && index < base->filter_count )
480                         filter = base->filters[ index ];
481         }
482         return filter;
483 }
484
485 /** Close the service.
486 */
487
488 void mlt_service_close( mlt_service this )
489 {
490         mlt_service_lock( this );
491         if ( this != NULL && mlt_properties_dec_ref( mlt_service_properties( this ) ) <= 0 )
492         {
493                 mlt_service_unlock( this );
494                 if ( this->close != NULL )
495                 {
496                         this->close( this->close_object );
497                 }
498                 else
499                 {
500                         mlt_service_base *base = this->local;
501                         int i = 0;
502                         int count = base->filter_count;
503                         mlt_events_block( mlt_service_properties( this ), this );
504                         while( count -- )
505                                 mlt_service_detach( this, base->filters[ 0 ] );
506                         free( base->filters );
507                         for ( i = 0; i < base->count; i ++ )
508                                 if ( base->in[ i ] != NULL )
509                                         mlt_service_close( base->in[ i ] );
510                         this->parent.close = NULL;
511                         free( base->in );
512                         pthread_mutex_destroy( &base->mutex );
513                         free( base );
514                         mlt_properties_close( &this->parent );
515                 }
516         }
517         else
518         {
519                 mlt_service_unlock( this );
520         }
521 }
522