2 * \file mlt_multitrack.c
3 * \brief multitrack service class
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_multitrack.h"
24 #include "mlt_playlist.h"
25 #include "mlt_frame.h"
30 /* Forward reference. */
32 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
34 /** Construct and initialize a new multitrack.
37 mlt_multitrack mlt_multitrack_init( )
39 // Allocate the multitrack object
40 mlt_multitrack this = calloc( sizeof( struct mlt_multitrack_s ), 1 );
44 mlt_producer producer = &this->parent;
45 if ( mlt_producer_init( producer, this ) == 0 )
47 mlt_properties properties = MLT_MULTITRACK_PROPERTIES( this );
48 producer->get_frame = producer_get_frame;
49 mlt_properties_set_data( properties, "multitrack", this, 0, NULL, NULL );
50 mlt_properties_set( properties, "log_id", "multitrack" );
51 mlt_properties_set( properties, "resource", "<multitrack>" );
52 mlt_properties_set_int( properties, "in", 0 );
53 mlt_properties_set_int( properties, "out", -1 );
54 mlt_properties_set_int( properties, "length", 0 );
55 producer->close = ( mlt_destructor )mlt_multitrack_close;
67 /** Get the producer associated to this multitrack.
70 mlt_producer mlt_multitrack_producer( mlt_multitrack this )
72 return this != NULL ? &this->parent : NULL;
75 /** Get the service associated this multitrack.
78 mlt_service mlt_multitrack_service( mlt_multitrack this )
80 return MLT_MULTITRACK_SERVICE( this );
83 /** Get the properties associated this multitrack.
86 mlt_properties mlt_multitrack_properties( mlt_multitrack this )
88 return MLT_MULTITRACK_PROPERTIES( this );
91 /** Initialise position related information.
94 void mlt_multitrack_refresh( mlt_multitrack this )
98 // Obtain the properties of this multitrack
99 mlt_properties properties = MLT_MULTITRACK_PROPERTIES( this );
101 // We need to ensure that the multitrack reports the longest track as its length
102 mlt_position length = 0;
104 // Obtain stats on all connected services
105 for ( i = 0; i < this->count; i ++ )
107 // Get the producer from this index
108 mlt_track track = this->list[ i ];
109 mlt_producer producer = track->producer;
111 // If it's allocated then, update our stats
112 if ( producer != NULL )
114 // If we have more than 1 track, we must be in continue mode
115 if ( this->count > 1 )
116 mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "continue" );
118 // Determine the longest length
119 //if ( !mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "hide" ) )
120 length = mlt_producer_get_playtime( producer ) > length ? mlt_producer_get_playtime( producer ) : length;
124 // Update multitrack properties now - we'll not destroy the in point here
125 mlt_events_block( properties, properties );
126 mlt_properties_set_position( properties, "length", length );
127 mlt_events_unblock( properties, properties );
128 mlt_properties_set_position( properties, "out", length - 1 );
131 /** Listener for producers on the playlist.
134 static void mlt_multitrack_listener( mlt_producer producer, mlt_multitrack this )
136 mlt_multitrack_refresh( this );
139 /** Connect a producer to a given track.
141 Note that any producer can be connected here, but see special case treatment
142 of playlist in clip point determination below.
145 int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track )
147 // Connect to the producer to ourselves at the specified track
148 int result = mlt_service_connect_producer( MLT_MULTITRACK_SERVICE( this ), MLT_PRODUCER_SERVICE( producer ), track );
152 // Resize the producer list if need be
153 if ( track >= this->size )
156 this->list = realloc( this->list, ( track + 10 ) * sizeof( mlt_track ) );
157 for ( i = this->size; i < track + 10; i ++ )
158 this->list[ i ] = NULL;
159 this->size = track + 10;
162 if ( this->list[ track ] != NULL )
164 mlt_event_close( this->list[ track ]->event );
165 mlt_producer_close( this->list[ track ]->producer );
169 this->list[ track ] = malloc( sizeof( struct mlt_track_s ) );
172 // Assign the track in our list here
173 this->list[ track ]->producer = producer;
174 this->list[ track ]->event = mlt_events_listen( MLT_PRODUCER_PROPERTIES( producer ), this,
175 "producer-changed", ( mlt_listener )mlt_multitrack_listener );
176 mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
177 mlt_event_inc_ref( this->list[ track ]->event );
179 // Increment the track count if need be
180 if ( track >= this->count )
181 this->count = track + 1;
184 mlt_multitrack_refresh( this );
190 /** Get the number of tracks.
193 int mlt_multitrack_count( mlt_multitrack this )
198 /** Get an individual track as a producer.
201 mlt_producer mlt_multitrack_track( mlt_multitrack this, int track )
203 mlt_producer producer = NULL;
205 if ( this->list != NULL && track < this->count )
206 producer = this->list[ track ]->producer;
211 static int position_compare( const void *p1, const void *p2 )
213 return *( mlt_position * )p1 - *( mlt_position * )p2;
216 static int add_unique( mlt_position *array, int size, mlt_position position )
219 for ( i = 0; i < size; i ++ )
220 if ( array[ i ] == position )
223 array[ size ++ ] = position;
227 /** Determine the clip point.
229 Special case here: a 'producer' has no concept of multiple clips - only the
230 playlist and multitrack producers have clip functionality. Further to that a
231 multitrack determines clip information from any connected tracks that happen
234 Additionally, it must locate clips in the correct order, for example, consider
235 the following track arrangement:
237 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
238 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
240 Note - b clips represent blanks. They are also reported as clip positions.
242 When extracting clip positions from these playlists, we should get a sequence of:
244 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
247 mlt_position mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index )
249 mlt_position position = 0;
252 mlt_position *map = malloc( 1000 * sizeof( mlt_position ) );
255 for ( i = 0; i < this->count; i ++ )
257 // Get the producer for this track
258 mlt_producer producer = this->list[ i ]->producer;
260 // If it's assigned and not a hidden track
261 if ( producer != NULL )
263 // Get the properties of this producer
264 mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
266 // Determine if it's a playlist
267 mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
269 // Special case consideration of playlists
270 if ( playlist != NULL )
272 for ( j = 0; j < mlt_playlist_count( playlist ); j ++ )
273 count = add_unique( map, count, mlt_playlist_clip( playlist, mlt_whence_relative_start, j ) );
274 count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
278 count = add_unique( map, count, 0 );
279 count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
285 qsort( map, count, sizeof( mlt_position ), position_compare );
287 // Now locate the requested index
290 case mlt_whence_relative_start:
292 position = map[ index ];
294 position = map[ count - 1 ];
297 case mlt_whence_relative_current:
298 position = mlt_producer_position( MLT_MULTITRACK_PRODUCER( this ) );
299 for ( i = 0; i < count - 2; i ++ )
300 if ( position >= map[ i ] && position < map[ i + 1 ] )
303 if ( index >= 0 && index < count )
304 position = map[ index ];
305 else if ( index < 0 )
308 position = map[ count - 1 ];
311 case mlt_whence_relative_end:
313 position = map[ count - index - 1 ];
325 /** Get frame method.
327 Special case here: The multitrack must be used in a conjunction with a downstream
328 tractor-type service, ie:
331 Producer2 - multitrack - { filters/transitions } - tractor - consumer
334 The get_frame of a tractor pulls frames from it's connected service on all tracks and
335 will terminate as soon as it receives a test card with a last_track property. The
336 important case here is that the mulitrack does not move to the next frame until all
337 tracks have been pulled.
339 Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
340 that all producers are positioned on the same frame. It uses the 'last track' logic
341 to determine when to move to the next frame.
343 Flaw: if a transition is configured to read from a b-track which happens to trigger
344 the last frame logic (ie: it's configured incorrectly), then things are going to go
347 See playlist logic too.
350 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index )
352 // Get the mutiltrack object
353 mlt_multitrack this = parent->child;
355 // Check if we have a track for this index
356 if ( index < this->count && this->list[ index ] != NULL )
358 // Get the producer for this track
359 mlt_producer producer = this->list[ index ]->producer;
361 // Get the track hide property
362 int hide = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer ) ), "hide" );
364 // Obtain the current position
365 mlt_position position = mlt_producer_frame( parent );
367 // Get the parent properties
368 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( parent );
371 double speed = mlt_properties_get_double( producer_properties, "_speed" );
373 // Make sure we're at the same point
374 mlt_producer_seek( producer, position );
376 // Get the frame from the producer
377 mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), frame, 0 );
379 // Indicate speed of this producer
380 mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
381 mlt_properties_set_double( properties, "_speed", speed );
382 mlt_properties_set_position( properties, "_position", position );
383 mlt_properties_set_int( properties, "hide", hide );
387 // Generate a test frame
388 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
390 // Update position on the frame we're creating
391 mlt_frame_set_position( *frame, mlt_producer_position( parent ) );
393 // Move on to the next frame
394 if ( index >= this->count )
396 // Let tractor know if we've reached the end
397 mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "last_track", 1 );
399 // Move to the next frame
400 mlt_producer_prepare_next( parent );
407 /** Close this instance.
410 void mlt_multitrack_close( mlt_multitrack this )
412 if ( this != NULL && mlt_properties_dec_ref( MLT_MULTITRACK_PROPERTIES( this ) ) <= 0 )
415 for ( i = 0; i < this->count; i ++ )
417 if ( this->list[ i ] != NULL )
419 mlt_event_close( this->list[ i ]->event );
420 mlt_producer_close( this->list[ i ]->producer );
421 free( this->list[ i ] );
425 // Close the producer
426 this->parent.close = NULL;
427 mlt_producer_close( &this->parent );