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