3 * \brief interface definition for all service classes
5 * Copyright (C) 2003-2008 Ushodaya Enterprises Limited
6 * \author Charles Yates <charles.yates@pandora.be>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "mlt_service.h"
24 #include "mlt_filter.h"
25 #include "mlt_frame.h"
33 The base service implements a null frame producing service - as such,
34 it is functional without extension and will produce test cards frames
35 and PAL sized audio frames.
37 PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
38 CONTROL THIS IN EXTENDING CLASSES.
41 /** \brief private service definition */
52 pthread_mutex_t mutex
;
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
);
64 /** Initialize a service.
66 * \public \memberof mlt_service_s
67 * \param this the service structure to initialize
68 * \param child pointer to the child object for the subclass
69 * \return true if there was an error
72 int mlt_service_init( mlt_service
this, void *child
)
76 // Initialise everything to NULL
77 memset( this, 0, sizeof( struct mlt_service_s
) );
82 // Generate local space
83 this->local
= calloc( sizeof( mlt_service_base
), 1 );
85 // Associate the methods
86 this->get_frame
= service_get_frame
;
88 // Initialise the properties
89 error
= mlt_properties_init( &this->parent
, this );
92 this->parent
.close
= ( mlt_destructor
)mlt_service_close
;
93 this->parent
.close_object
= this;
95 mlt_events_init( &this->parent
);
96 mlt_events_register( &this->parent
, "service-changed", NULL
);
97 mlt_events_register( &this->parent
, "property-changed", ( mlt_transmitter
)mlt_service_property_changed
);
98 pthread_mutex_init( &( ( mlt_service_base
* )this->local
)->mutex
, NULL
);
104 /** The transmitter for property changes.
106 * Invokes the listener.
108 * \private \memberof mlt_service_s
109 * \param listener a function pointer that will be invoked
110 * \param owner a properties list that will be passed to \p listener
111 * \param this a service that will be passed to \p listener
112 * \param args an array of pointers - the first entry is passed as a string to \p listener
115 static void mlt_service_property_changed( mlt_listener listener
, mlt_properties owner
, mlt_service
this, void **args
)
117 if ( listener
!= NULL
)
118 listener( owner
, this, ( char * )args
[ 0 ] );
121 /** Acquire a mutual exclusion lock on this service.
123 * \public \memberof mlt_service_s
124 * \param this the service to lock
127 void mlt_service_lock( mlt_service
this )
130 pthread_mutex_lock( &( ( mlt_service_base
* )this->local
)->mutex
);
133 /** Release a mutual exclusion lock on this service.
135 * \public \memberof mlt_service_s
136 * \param this the service to unlock
139 void mlt_service_unlock( mlt_service
this )
142 pthread_mutex_unlock( &( ( mlt_service_base
* )this->local
)->mutex
);
145 /** Identify the subclass of the service.
147 * \public \memberof mlt_service_s
148 * \param this a service
149 * \return the subclass
152 mlt_service_type
mlt_service_identify( mlt_service
this )
154 mlt_service_type type
= invalid_type
;
157 mlt_properties properties
= MLT_SERVICE_PROPERTIES( this );
158 char *mlt_type
= mlt_properties_get( properties
, "mlt_type" );
159 char *resource
= mlt_properties_get( properties
, "resource" );
160 if ( mlt_type
== NULL
)
162 else if (resource
!= NULL
&& !strcmp( resource
, "<playlist>" ) )
163 type
= playlist_type
;
164 else if (resource
!= NULL
&& !strcmp( resource
, "<tractor>" ) )
166 else if (resource
!= NULL
&& !strcmp( resource
, "<multitrack>" ) )
167 type
= multitrack_type
;
168 else if ( !strcmp( mlt_type
, "producer" ) )
169 type
= producer_type
;
170 else if ( !strcmp( mlt_type
, "filter" ) )
172 else if ( !strcmp( mlt_type
, "transition" ) )
173 type
= transition_type
;
174 else if ( !strcmp( mlt_type
, "consumer" ) )
175 type
= consumer_type
;
182 /** Connect a producer to the service.
184 * \public \memberof mlt_service_s
185 * \param this a service
186 * \param producer a producer
187 * \param index which of potentially multiple producers to this service (0 based)
188 * \return > 0 warning, == 0 success, < 0 serious error,
189 * 1 = this service does not accept input,
190 * 2 = the producer is invalid,
191 * 3 = the producer is already registered with this consumer
194 int mlt_service_connect_producer( mlt_service
this, mlt_service producer
, int index
)
198 // Get the service base
199 mlt_service_base
*base
= this->local
;
201 // Special case 'track' index - only works for last filter(s) in a particular chain
202 // but allows a filter to apply to the output frame regardless of which track it comes from
206 // Check if the producer is already registered with this service
207 for ( i
= 0; i
< base
->count
; i
++ )
208 if ( base
->in
[ i
] == producer
)
212 if ( index
>= base
->size
)
214 int new_size
= base
->size
+ index
+ 10;
215 base
->in
= realloc( base
->in
, new_size
* sizeof( mlt_service
) );
216 if ( base
->in
!= NULL
)
218 for ( i
= base
->size
; i
< new_size
; i
++ )
219 base
->in
[ i
] = NULL
;
220 base
->size
= new_size
;
224 // If we have space, assign the input
225 if ( base
->in
!= NULL
&& index
>= 0 && index
< base
->size
)
227 // Get the current service
228 mlt_service current
= base
->in
[ index
];
230 // Increment the reference count on this producer
231 if ( producer
!= NULL
)
232 mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer
) );
234 // Now we disconnect the producer service from its consumer
235 mlt_service_disconnect( producer
);
237 // Add the service to index specified
238 base
->in
[ index
] = producer
;
240 // Determine the number of active tracks
241 if ( index
>= base
->count
)
242 base
->count
= index
+ 1;
244 // Now we connect the producer to its connected consumer
245 mlt_service_connect( producer
, this );
247 // Close the current service
248 mlt_service_close( current
);
250 // Inform caller that all went well
259 /** Disconnect this service from its consumer.
261 * \public \memberof mlt_service_s
262 * \param this a service
265 static void mlt_service_disconnect( mlt_service
this )
269 // Get the service base
270 mlt_service_base
*base
= this->local
;
277 /** Obtain the consumer this service is connected to.
279 * \public \memberof mlt_service_s
280 * \param this a service
281 * \return the consumer
284 mlt_service
mlt_service_consumer( mlt_service
this )
286 // Get the service base
287 mlt_service_base
*base
= this->local
;
289 // Return the connected consumer
293 /** Obtain the producer this service is connected to.
295 * \public \memberof mlt_service_s
296 * \param this a service
297 * \return the last-most producer
300 mlt_service
mlt_service_producer( mlt_service
this )
302 // Get the service base
303 mlt_service_base
*base
= this->local
;
305 // Return the connected producer
306 return base
->count
> 0 ? base
->in
[ base
->count
- 1 ] : NULL
;
309 /** Associate this service to a consumer.
311 * Overwrites connection to any existing consumer.
312 * \private \memberof mlt_service_s
313 * \param this a service
314 * \param that a consumer
317 static void mlt_service_connect( mlt_service
this, mlt_service that
)
321 // Get the service base
322 mlt_service_base
*base
= this->local
;
324 // There's a bit more required here...
329 /** Get the first connected producer.
331 * \public \memberof mlt_service_s
332 * \param this a service
333 * \return the first producer
336 mlt_service
mlt_service_get_producer( mlt_service
this )
338 mlt_service producer
= NULL
;
340 // Get the service base
341 mlt_service_base
*base
= this->local
;
343 if ( base
->in
!= NULL
)
344 producer
= base
->in
[ 0 ];
349 /** Default implementation of the get_frame virtual function.
351 * \private \memberof mlt_service_s
352 * \param this a service
353 * \param[out] frame a frame by reference
354 * \param index as determined by the producer
358 static int service_get_frame( mlt_service
this, mlt_frame_ptr frame
, int index
)
360 mlt_service_base
*base
= this->local
;
361 if ( index
< base
->count
)
363 mlt_service producer
= base
->in
[ index
];
364 if ( producer
!= NULL
)
365 return mlt_service_get_frame( producer
, frame
, index
);
367 *frame
= mlt_frame_init( this );
371 /** Return the properties object.
373 * \public \memberof mlt_service_s
374 * \param this a service
375 * \return the properties
378 mlt_properties
mlt_service_properties( mlt_service
this )
380 return this != NULL ?
&this->parent
: NULL
;
383 /** Recursively apply attached filters.
385 * \public \memberof mlt_service_s
386 * \param this a service
387 * \param frame a frame
388 * \param index used to track depth of recursion, top caller should supply 0
391 void mlt_service_apply_filters( mlt_service
this, mlt_frame frame
, int index
)
394 mlt_properties frame_properties
= MLT_FRAME_PROPERTIES( frame
);
395 mlt_properties service_properties
= MLT_SERVICE_PROPERTIES( this );
396 mlt_service_base
*base
= this->local
;
397 mlt_position position
= mlt_frame_get_position( frame
);
398 mlt_position this_in
= mlt_properties_get_position( service_properties
, "in" );
399 mlt_position this_out
= mlt_properties_get_position( service_properties
, "out" );
401 if ( index
== 0 || mlt_properties_get_int( service_properties
, "_filter_private" ) == 0 )
403 // Process the frame with the attached filters
404 for ( i
= 0; i
< base
->filter_count
; i
++ )
406 if ( base
->filters
[ i
] != NULL
)
408 mlt_position in
= mlt_filter_get_in( base
->filters
[ i
] );
409 mlt_position out
= mlt_filter_get_out( base
->filters
[ i
] );
410 int disable
= mlt_properties_get_int( MLT_FILTER_PROPERTIES( base
->filters
[ i
] ), "disable" );
411 if ( !disable
&& ( ( in
== 0 && out
== 0 ) || ( position
>= in
&& ( position
<= out
|| out
== 0 ) ) ) )
413 mlt_properties_set_position( frame_properties
, "in", in
== 0 ? this_in
: in
);
414 mlt_properties_set_position( frame_properties
, "out", out
== 0 ? this_out
: out
);
415 mlt_filter_process( base
->filters
[ i
], frame
);
416 mlt_service_apply_filters( MLT_FILTER_SERVICE( base
->filters
[ i
] ), frame
, index
+ 1 );
425 * \public \memberof mlt_service_s
426 * \param this a service
427 * \param[out] frame a frame by reference
428 * \param index as determined by the producer
429 * \return true if there was an error
432 int mlt_service_get_frame( mlt_service
this, mlt_frame_ptr frame
, int index
)
437 mlt_service_lock( this );
439 // Ensure that the frame is NULL
442 // Only process if we have a valid service
443 if ( this != NULL
&& this->get_frame
!= NULL
)
445 mlt_properties properties
= MLT_SERVICE_PROPERTIES( this );
446 mlt_position in
= mlt_properties_get_position( properties
, "in" );
447 mlt_position out
= mlt_properties_get_position( properties
, "out" );
449 result
= this->get_frame( this, frame
, index
);
453 mlt_properties_inc_ref( properties
);
454 properties
= MLT_FRAME_PROPERTIES( *frame
);
455 if ( in
>=0 && out
> 0 )
457 mlt_properties_set_position( properties
, "in", in
);
458 mlt_properties_set_position( properties
, "out", out
);
460 mlt_service_apply_filters( this, *frame
, 1 );
461 mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame
), this );
465 // Make sure we return a frame
466 if ( *frame
== NULL
)
467 *frame
= mlt_frame_init( this );
469 // Unlock the service
470 mlt_service_unlock( this );
475 /** The service-changed event handler.
477 * \private \memberof mlt_service_s
478 * \param owner ignored
479 * \param this the service on which the "service-changed" event is fired
482 static void mlt_service_filter_changed( mlt_service owner
, mlt_service
this )
484 mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL
);
489 * \public \memberof mlt_service_s
490 * \param this a service
491 * \param filter the filter to attach
492 * \return true if there was an error
495 int mlt_service_attach( mlt_service
this, mlt_filter filter
)
497 int error
= this == NULL
|| filter
== NULL
;
501 mlt_properties properties
= MLT_SERVICE_PROPERTIES( this );
502 mlt_service_base
*base
= this->local
;
504 for ( i
= 0; error
== 0 && i
< base
->filter_count
; i
++ )
505 if ( base
->filters
[ i
] == filter
)
510 if ( base
->filter_count
== base
->filter_size
)
512 base
->filter_size
+= 10;
513 base
->filters
= realloc( base
->filters
, base
->filter_size
* sizeof( mlt_filter
) );
516 if ( base
->filters
!= NULL
)
518 mlt_properties props
= MLT_FILTER_PROPERTIES( filter
);
519 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter
) );
520 base
->filters
[ base
->filter_count
++ ] = filter
;
521 mlt_events_fire( properties
, "service-changed", NULL
);
522 mlt_events_listen( props
, this, "service-changed", ( mlt_listener
)mlt_service_filter_changed
);
523 mlt_events_listen( props
, this, "property-changed", ( mlt_listener
)mlt_service_filter_changed
);
536 * \public \memberof mlt_service_s
537 * \param this a service
538 * \param filter the filter to detach
539 * \return true if there was an error
542 int mlt_service_detach( mlt_service
this, mlt_filter filter
)
544 int error
= this == NULL
|| filter
== NULL
;
548 mlt_service_base
*base
= this->local
;
549 mlt_properties properties
= MLT_SERVICE_PROPERTIES( this );
551 for ( i
= 0; i
< base
->filter_count
; i
++ )
552 if ( base
->filters
[ i
] == filter
)
555 if ( i
< base
->filter_count
)
557 base
->filters
[ i
] = NULL
;
558 for ( i
++ ; i
< base
->filter_count
; i
++ )
559 base
->filters
[ i
- 1 ] = base
->filters
[ i
];
560 base
->filter_count
--;
561 mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter
), this );
562 mlt_filter_close( filter
);
563 mlt_events_fire( properties
, "service-changed", NULL
);
569 /** Retrieve a filter.
571 * \public \memberof mlt_service_s
572 * \param this a service
573 * \param index which one of potentially multiple filters
574 * \return the filter or null if there was an error
577 mlt_filter
mlt_service_filter( mlt_service
this, int index
)
579 mlt_filter filter
= NULL
;
582 mlt_service_base
*base
= this->local
;
583 if ( index
>= 0 && index
< base
->filter_count
)
584 filter
= base
->filters
[ index
];
589 /** Retrieve the profile.
591 * \public \memberof mlt_service_s
592 * \param this a service
593 * \return the profile
596 mlt_profile
mlt_service_profile( mlt_service
this )
598 return mlt_properties_get_data( MLT_SERVICE_PROPERTIES( this ), "_profile", NULL
);
601 /** Destroy a service.
603 * \public \memberof mlt_service_s
604 * \param this the service to destroy
607 void mlt_service_close( mlt_service
this )
609 if ( this != NULL
&& mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
611 if ( this->close
!= NULL
)
613 this->close( this->close_object
);
617 mlt_service_base
*base
= this->local
;
619 int count
= base
->filter_count
;
620 mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
622 mlt_service_detach( this, base
->filters
[ 0 ] );
623 free( base
->filters
);
624 for ( i
= 0; i
< base
->count
; i
++ )
625 if ( base
->in
[ i
] != NULL
)
626 mlt_service_close( base
->in
[ i
] );
627 this->parent
.close
= NULL
;
629 pthread_mutex_destroy( &base
->mutex
);
631 mlt_properties_close( &this->parent
);
636 mlt_service_unlock( this );