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