]> git.sesse.net Git - mlt/blob - src/framework/mlt_service.c
Corrections to playlist manipulations and producer type determination
[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                 mlt_service_unlock( this );
362                 result = this->get_frame( this, frame, index );
363                 if ( result == 0 )
364                 {
365                         properties = MLT_FRAME_PROPERTIES( *frame );
366                         if ( in >=0 && out > 0 )
367                         {
368                                 mlt_properties_set_position( properties, "in", in );
369                                 mlt_properties_set_position( properties, "out", out );
370                         }
371                         mlt_service_apply_filters( this, *frame, 1 );
372                         mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
373                 }
374                 else
375                 {
376                         mlt_service_close( this );
377                 }
378                 return result;
379         }
380         mlt_service_unlock( this );
381         *frame = mlt_frame_init( );
382         return 0;
383 }
384
385 static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
386 {
387         mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
388 }
389
390 /** Attach a filter.
391 */
392
393 int mlt_service_attach( mlt_service this, mlt_filter filter )
394 {
395         int error = this == NULL || filter == NULL;
396         if ( error == 0 )
397         {
398                 int i = 0;
399                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
400                 mlt_service_base *base = this->local;
401
402                 for ( i = 0; error == 0 && i < base->filter_count; i ++ )
403                         if ( base->filters[ i ] == filter )
404                                 error = 1;
405
406                 if ( error == 0 )
407                 {
408                         if ( base->filter_count == base->filter_size )
409                         {
410                                 base->filter_size += 10;
411                                 base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
412                         }
413
414                         if ( base->filters != NULL )
415                         {
416                                 mlt_properties props = MLT_FILTER_PROPERTIES( filter );
417                                 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
418                                 base->filters[ base->filter_count ++ ] = filter;
419                                 mlt_events_fire( properties, "service-changed", NULL );
420                                 mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
421                                 mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
422                         }
423                         else
424                         {
425                                 error = 2;
426                         }
427                 }
428         }
429         return error;
430 }
431
432 /** Detach a filter.
433 */
434
435 int mlt_service_detach( mlt_service this, mlt_filter filter )
436 {
437         int error = this == NULL || filter == NULL;
438         if ( error == 0 )
439         {
440                 int i = 0;
441                 mlt_service_base *base = this->local;
442                 mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
443
444                 for ( i = 0; i < base->filter_count; i ++ )
445                         if ( base->filters[ i ] == filter )
446                                 break;
447
448                 if ( i < base->filter_count )
449                 {
450                         base->filters[ i ] = NULL;
451                         for ( i ++ ; i < base->filter_count; i ++ )
452                                 base->filters[ i - 1 ] = base->filters[ i ];
453                         base->filter_count --;
454                         mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
455                         mlt_filter_close( filter );
456                         mlt_events_fire( properties, "service-changed", NULL );
457                 }
458         }
459         return error;
460 }
461
462 /** Retrieve a filter.
463 */
464
465 mlt_filter mlt_service_filter( mlt_service this, int index )
466 {
467         mlt_filter filter = NULL;
468         if ( this != NULL )
469         {
470                 mlt_service_base *base = this->local;
471                 if ( index >= 0 && index < base->filter_count )
472                         filter = base->filters[ index ];
473         }
474         return filter;
475 }
476
477 /** Close the service.
478 */
479
480 void mlt_service_close( mlt_service this )
481 {
482         mlt_service_lock( this );
483         if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
484         {
485                 mlt_service_unlock( this );
486                 if ( this->close != NULL )
487                 {
488                         this->close( this->close_object );
489                 }
490                 else
491                 {
492                         mlt_service_base *base = this->local;
493                         int i = 0;
494                         int count = base->filter_count;
495                         mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
496                         while( count -- )
497                                 mlt_service_detach( this, base->filters[ 0 ] );
498                         free( base->filters );
499                         for ( i = 0; i < base->count; i ++ )
500                                 if ( base->in[ i ] != NULL )
501                                         mlt_service_close( base->in[ i ] );
502                         this->parent.close = NULL;
503                         free( base->in );
504                         pthread_mutex_destroy( &base->mutex );
505                         free( base );
506                         mlt_properties_close( &this->parent );
507                 }
508         }
509         else
510         {
511                 mlt_service_unlock( this );
512         }
513 }
514