93ee56b8167ea4278a619a4445e9e5b1cf9edde3
[melted] / 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