d8173d670d6b902c10372efee05a776c70ee8651
[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 ( 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