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 listener for property changes.
106 * \private \memberof mlt_service_s
107 * \param listener a function pointer that will be invoked
108 * \param owner a properties list that will be passed to \p listener
109 * \param this a service that will be passed to \p listener
110 * \param args an array of pointers - the first entry is passed as a string to \p listener
113 static void mlt_service_property_changed( mlt_listener listener
, mlt_properties owner
, mlt_service
this, void **args
)
115 if ( listener
!= NULL
)
116 listener( owner
, this, ( char * )args
[ 0 ] );
119 /** Acquire a mutual exclusion lock on this service.
121 * \public \memberof mlt_service_s
122 * \param this the service to lock
125 void mlt_service_lock( mlt_service
this )
128 pthread_mutex_lock( &( ( mlt_service_base
* )this->local
)->mutex
);
131 /** Release a mutual exclusion lock on this service.
133 * \public \memberof mlt_service_s
134 * \param this the service to unlock
137 void mlt_service_unlock( mlt_service
this )
140 pthread_mutex_unlock( &( ( mlt_service_base
* )this->local
)->mutex
);
143 /** Identify the subclass of the service.
145 * \public \memberof mlt_service_s
146 * \param this a service
147 * \return the subclass
150 mlt_service_type
mlt_service_identify( mlt_service
this )
152 mlt_service_type type
= invalid_type
;
155 mlt_properties properties
= MLT_SERVICE_PROPERTIES( this );
156 char *mlt_type
= mlt_properties_get( properties
, "mlt_type" );
157 char *resource
= mlt_properties_get( properties
, "resource" );
158 if ( mlt_type
== NULL
)
160 else if (resource
!= NULL
&& !strcmp( resource
, "<playlist>" ) )
161 type
= playlist_type
;
162 else if (resource
!= NULL
&& !strcmp( resource
, "<tractor>" ) )
164 else if (resource
!= NULL
&& !strcmp( resource
, "<multitrack>" ) )
165 type
= multitrack_type
;
166 else if ( !strcmp( mlt_type
, "producer" ) )
167 type
= producer_type
;
168 else if ( !strcmp( mlt_type
, "filter" ) )
170 else if ( !strcmp( mlt_type
, "transition" ) )
171 type
= transition_type
;
172 else if ( !strcmp( mlt_type
, "consumer" ) )
173 type
= consumer_type
;
180 /** Connect a producer to the service.
182 * \public \memberof mlt_service_s
183 * \param this a service
184 * \param producer a producer
185 * \param index which of potentially multiple producers to this service (0 based)
186 * \return > 0 warning, == 0 success, < 0 serious error,
187 * 1 = this service does not accept input,
188 * 2 = the producer is invalid,
189 * 3 = the producer is already registered with this consumer
192 int mlt_service_connect_producer( mlt_service
this, mlt_service producer
, int index
)
196 // Get the service base
197 mlt_service_base
*base
= this->local
;
199 // Special case 'track' index - only works for last filter(s) in a particular chain
200 // but allows a filter to apply to the output frame regardless of which track it comes from
204 // Check if the producer is already registered with this service
205 for ( i
= 0; i
< base
->count
; i
++ )
206 if ( base
->in
[ i
] == producer
)
210 if ( index
>= base
->size
)
212 int new_size
= base
->size
+ index
+ 10;
213 base
->in
= realloc( base
->in
, new_size
* sizeof( mlt_service
) );
214 if ( base
->in
!= NULL
)
216 for ( i
= base
->size
; i
< new_size
; i
++ )
217 base
->in
[ i
] = NULL
;
218 base
->size
= new_size
;
222 // If we have space, assign the input
223 if ( base
->in
!= NULL
&& index
>= 0 && index
< base
->size
)
225 // Get the current service
226 mlt_service current
= base
->in
[ index
];
228 // Increment the reference count on this producer
229 if ( producer
!= NULL
)
230 mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer
) );
232 // Now we disconnect the producer service from its consumer
233 mlt_service_disconnect( producer
);
235 // Add the service to index specified
236 base
->in
[ index
] = producer
;
238 // Determine the number of active tracks
239 if ( index
>= base
->count
)
240 base
->count
= index
+ 1;
242 // Now we connect the producer to its connected consumer
243 mlt_service_connect( producer
, this );
245 // Close the current service
246 mlt_service_close( current
);
248 // Inform caller that all went well
257 /** Disconnect this service from its consumer.
259 * \public \memberof mlt_service_s
260 * \param this a service
263 static void mlt_service_disconnect( mlt_service
this )
267 // Get the service base
268 mlt_service_base
*base
= this->local
;
275 /** Obtain the consumer this service is connected to.
277 * \public \memberof mlt_service_s
278 * \param this a service
279 * \return the consumer
282 mlt_service
mlt_service_consumer( mlt_service
this )
284 // Get the service base
285 mlt_service_base
*base
= this->local
;
287 // Return the connected consumer
291 /** Obtain the producer this service is connected to.
293 * \public \memberof mlt_service_s
294 * \param this a service
295 * \return the last-most producer
298 mlt_service
mlt_service_producer( mlt_service
this )
300 // Get the service base
301 mlt_service_base
*base
= this->local
;
303 // Return the connected producer
304 return base
->count
> 0 ? base
->in
[ base
->count
- 1 ] : NULL
;
307 /** Associate this service to a consumer.
309 * Overwrites connection to any existing consumer.
310 * \private \memberof mlt_service_s
311 * \param this a service
312 * \param that a consumer
315 static void mlt_service_connect( mlt_service
this, mlt_service that
)
319 // Get the service base
320 mlt_service_base
*base
= this->local
;
322 // There's a bit more required here...
327 /** Get the first connected producer.
329 * \public \memberof mlt_service_s
330 * \param this a service
331 * \return the first producer
334 mlt_service
mlt_service_get_producer( mlt_service
this )
336 mlt_service producer
= NULL
;
338 // Get the service base
339 mlt_service_base
*base
= this->local
;
341 if ( base
->in
!= NULL
)
342 producer
= base
->in
[ 0 ];
347 /** Default implementation of the get_frame virtual function.
349 * \private \memberof mlt_service_s
350 * \param this a service
351 * \param[out] frame a frame by reference
352 * \param index as determined by the producer
356 static int service_get_frame( mlt_service
this, mlt_frame_ptr frame
, int index
)
358 mlt_service_base
*base
= this->local
;
359 if ( index
< base
->count
)
361 mlt_service producer
= base
->in
[ index
];
362 if ( producer
!= NULL
)
363 return mlt_service_get_frame( producer
, frame
, index
);
365 *frame
= mlt_frame_init( this );
369 /** Return the properties object.
371 * \public \memberof mlt_service_s
372 * \param this a service
373 * \return the properties
376 mlt_properties
mlt_service_properties( mlt_service
this )
378 return this != NULL ?
&this->parent
: NULL
;
381 /** Recursively apply attached filters.
383 * \public \memberof mlt_service_s
384 * \param this a service
385 * \param frame a frame
386 * \param index used to track depth of recursion, top caller should supply 0
389 void mlt_service_apply_filters( mlt_service
this, mlt_frame frame
, int index
)
392 mlt_properties frame_properties
= MLT_FRAME_PROPERTIES( frame
);
393 mlt_properties service_properties
= MLT_SERVICE_PROPERTIES( this );
394 mlt_service_base
*base
= this->local
;
395 mlt_position position
= mlt_frame_get_position( frame
);
396 mlt_position this_in
= mlt_properties_get_position( service_properties
, "in" );
397 /** \properties \em out where to stop playing */
398 mlt_position this_out
= mlt_properties_get_position( service_properties
, "out" );
400 if ( index
== 0 || mlt_properties_get_int( service_properties
, "_filter_private" ) == 0 )
402 // Process the frame with the attached filters
403 for ( i
= 0; i
< base
->filter_count
; i
++ )
405 if ( base
->filters
[ i
] != NULL
)
407 mlt_position in
= mlt_filter_get_in( base
->filters
[ i
] );
408 mlt_position out
= mlt_filter_get_out( base
->filters
[ i
] );
409 int disable
= mlt_properties_get_int( MLT_FILTER_PROPERTIES( base
->filters
[ i
] ), "disable" );
410 if ( !disable
&& ( ( in
== 0 && out
== 0 ) || ( position
>= in
&& ( position
<= out
|| out
== 0 ) ) ) )
412 mlt_properties_set_position( frame_properties
, "in", in
== 0 ? this_in
: in
);
413 mlt_properties_set_position( frame_properties
, "out", out
== 0 ? this_out
: out
);
414 mlt_filter_process( base
->filters
[ i
], frame
);
415 mlt_service_apply_filters( MLT_FILTER_SERVICE( base
->filters
[ i
] ), frame
, index
+ 1 );
424 * \public \memberof mlt_service_s
425 * \param this a service
426 * \param[out] frame a frame by reference
427 * \param index as determined by the producer
428 * \return true if there was an error
431 int mlt_service_get_frame( mlt_service
this, mlt_frame_ptr frame
, int index
)
436 mlt_service_lock( this );
438 // Ensure that the frame is NULL
441 // Only process if we have a valid service
442 if ( this != NULL
&& this->get_frame
!= NULL
)
444 mlt_properties properties
= MLT_SERVICE_PROPERTIES( this );
445 mlt_position in
= mlt_properties_get_position( properties
, "in" );
446 mlt_position out
= mlt_properties_get_position( properties
, "out" );
448 result
= this->get_frame( this, frame
, index
);
452 mlt_properties_inc_ref( properties
);
453 properties
= MLT_FRAME_PROPERTIES( *frame
);
454 if ( in
>=0 && out
> 0 )
456 mlt_properties_set_position( properties
, "in", in
);
457 mlt_properties_set_position( properties
, "out", out
);
459 mlt_service_apply_filters( this, *frame
, 1 );
460 mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame
), this );
464 // Make sure we return a frame
465 if ( *frame
== NULL
)
466 *frame
= mlt_frame_init( this );
468 // Unlock the service
469 mlt_service_unlock( this );
474 /** The service-changed event handler.
476 * \private \memberof mlt_service_s
477 * \param owner ignored
478 * \param this the service on which the "service-changed" event is fired
481 static void mlt_service_filter_changed( mlt_service owner
, mlt_service
this )
483 mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL
);
488 * \public \memberof mlt_service_s
489 * \param this a service
490 * \param filter the filter to attach
491 * \return true if there was an error
494 int mlt_service_attach( mlt_service
this, mlt_filter filter
)
496 int error
= this == NULL
|| filter
== NULL
;
500 mlt_properties properties
= MLT_SERVICE_PROPERTIES( this );
501 mlt_service_base
*base
= this->local
;
503 for ( i
= 0; error
== 0 && i
< base
->filter_count
; i
++ )
504 if ( base
->filters
[ i
] == filter
)
509 if ( base
->filter_count
== base
->filter_size
)
511 base
->filter_size
+= 10;
512 base
->filters
= realloc( base
->filters
, base
->filter_size
* sizeof( mlt_filter
) );
515 if ( base
->filters
!= NULL
)
517 mlt_properties props
= MLT_FILTER_PROPERTIES( filter
);
518 mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter
) );
519 base
->filters
[ base
->filter_count
++ ] = filter
;
520 mlt_events_fire( properties
, "service-changed", NULL
);
521 mlt_events_listen( props
, this, "service-changed", ( mlt_listener
)mlt_service_filter_changed
);
522 mlt_events_listen( props
, this, "property-changed", ( mlt_listener
)mlt_service_filter_changed
);
535 * \public \memberof mlt_service_s
536 * \param this a service
537 * \param filter the filter to detach
538 * \return true if there was an error
541 int mlt_service_detach( mlt_service
this, mlt_filter filter
)
543 int error
= this == NULL
|| filter
== NULL
;
547 mlt_service_base
*base
= this->local
;
548 mlt_properties properties
= MLT_SERVICE_PROPERTIES( this );
550 for ( i
= 0; i
< base
->filter_count
; i
++ )
551 if ( base
->filters
[ i
] == filter
)
554 if ( i
< base
->filter_count
)
556 base
->filters
[ i
] = NULL
;
557 for ( i
++ ; i
< base
->filter_count
; i
++ )
558 base
->filters
[ i
- 1 ] = base
->filters
[ i
];
559 base
->filter_count
--;
560 mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter
), this );
561 mlt_filter_close( filter
);
562 mlt_events_fire( properties
, "service-changed", NULL
);
568 /** Retrieve a filter.
570 * \public \memberof mlt_service_s
571 * \param this a service
572 * \param index which one of potentially multiple filters
573 * \return the filter or null if there was an error
576 mlt_filter
mlt_service_filter( mlt_service
this, int index
)
578 mlt_filter filter
= NULL
;
581 mlt_service_base
*base
= this->local
;
582 if ( index
>= 0 && index
< base
->filter_count
)
583 filter
= base
->filters
[ index
];
588 /** Retrieve the profile.
590 * \public \memberof mlt_service_s
591 * \param this a service
592 * \return the profile
595 mlt_profile
mlt_service_profile( mlt_service
this )
597 return mlt_properties_get_data( MLT_SERVICE_PROPERTIES( this ), "_profile", NULL
);
600 /** Destroy a service.
602 * \public \memberof mlt_service_s
603 * \param this the service to destroy
606 void mlt_service_close( mlt_service
this )
608 if ( this != NULL
&& mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
610 if ( this->close
!= NULL
)
612 this->close( this->close_object
);
616 mlt_service_base
*base
= this->local
;
618 int count
= base
->filter_count
;
619 mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
621 mlt_service_detach( this, base
->filters
[ 0 ] );
622 free( base
->filters
);
623 for ( i
= 0; i
< base
->count
; i
++ )
624 if ( base
->in
[ i
] != NULL
)
625 mlt_service_close( base
->in
[ i
] );
626 this->parent
.close
= NULL
;
628 pthread_mutex_destroy( &base
->mutex
);
630 mlt_properties_close( &this->parent
);
635 mlt_service_unlock( this );