2 * mlt_multitrack.c -- multitrack service class
3 * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "mlt_multitrack.h"
22 #include "mlt_playlist.h"
23 #include "mlt_frame.h"
28 /** Forward reference.
31 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
);
36 mlt_multitrack
mlt_multitrack_init( )
38 // Allocate the multitrack object
39 mlt_multitrack
this = calloc( sizeof( struct mlt_multitrack_s
), 1 );
43 mlt_producer producer
= &this->parent
;
44 if ( mlt_producer_init( producer
, this ) == 0 )
46 mlt_properties properties
= MLT_MULTITRACK_PROPERTIES( this );
47 producer
->get_frame
= producer_get_frame
;
48 mlt_properties_set_data( properties
, "multitrack", this, 0, NULL
, NULL
);
49 mlt_properties_set( properties
, "log_id", "multitrack" );
50 mlt_properties_set( properties
, "resource", "<multitrack>" );
51 mlt_properties_set_int( properties
, "in", 0 );
52 mlt_properties_set_int( properties
, "out", -1 );
53 mlt_properties_set_int( properties
, "length", 0 );
54 producer
->close
= ( mlt_destructor
)mlt_multitrack_close
;
66 /** Get the producer associated to this multitrack.
69 mlt_producer
mlt_multitrack_producer( mlt_multitrack
this )
71 return this != NULL ?
&this->parent
: NULL
;
74 /** Get the service associated this multitrack.
77 mlt_service
mlt_multitrack_service( mlt_multitrack
this )
79 return MLT_MULTITRACK_SERVICE( this );
82 /** Get the properties associated this multitrack.
85 mlt_properties
mlt_multitrack_properties( mlt_multitrack
this )
87 return MLT_MULTITRACK_PROPERTIES( this );
90 /** Initialise position related information.
93 void mlt_multitrack_refresh( mlt_multitrack
this )
97 // Obtain the properties of this multitrack
98 mlt_properties properties
= MLT_MULTITRACK_PROPERTIES( this );
100 // We need to ensure that the multitrack reports the longest track as its length
101 mlt_position length
= 0;
103 // Obtain stats on all connected services
104 for ( i
= 0; i
< this->count
; i
++ )
106 // Get the producer from this index
107 mlt_track track
= this->list
[ i
];
108 mlt_producer producer
= track
->producer
;
110 // If it's allocated then, update our stats
111 if ( producer
!= NULL
)
113 // If we have more than 1 track, we must be in continue mode
114 if ( this->count
> 1 )
115 mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer
), "eof", "continue" );
117 // Determine the longest length
118 //if ( !mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "hide" ) )
119 length
= mlt_producer_get_playtime( producer
) > length ?
mlt_producer_get_playtime( producer
) : length
;
123 // Update multitrack properties now - we'll not destroy the in point here
124 mlt_events_block( properties
, properties
);
125 mlt_properties_set_position( properties
, "length", length
);
126 mlt_events_unblock( properties
, properties
);
127 mlt_properties_set_position( properties
, "out", length
- 1 );
130 /** Listener for producers on the playlist.
133 static void mlt_multitrack_listener( mlt_producer producer
, mlt_multitrack
this )
135 mlt_multitrack_refresh( this );
138 /** Connect a producer to a given track.
140 Note that any producer can be connected here, but see special case treatment
141 of playlist in clip point determination below.
144 int mlt_multitrack_connect( mlt_multitrack
this, mlt_producer producer
, int track
)
146 // Connect to the producer to ourselves at the specified track
147 int result
= mlt_service_connect_producer( MLT_MULTITRACK_SERVICE( this ), MLT_PRODUCER_SERVICE( producer
), track
);
151 // Resize the producer list if need be
152 if ( track
>= this->size
)
155 this->list
= realloc( this->list
, ( track
+ 10 ) * sizeof( mlt_track
) );
156 for ( i
= this->size
; i
< track
+ 10; i
++ )
157 this->list
[ i
] = NULL
;
158 this->size
= track
+ 10;
161 if ( this->list
[ track
] != NULL
)
163 mlt_event_close( this->list
[ track
]->event
);
164 mlt_producer_close( this->list
[ track
]->producer
);
168 this->list
[ track
] = malloc( sizeof( struct mlt_track_s
) );
171 // Assign the track in our list here
172 this->list
[ track
]->producer
= producer
;
173 this->list
[ track
]->event
= mlt_events_listen( MLT_PRODUCER_PROPERTIES( producer
), this,
174 "producer-changed", ( mlt_listener
)mlt_multitrack_listener
);
175 mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer
) );
176 mlt_event_inc_ref( this->list
[ track
]->event
);
178 // Increment the track count if need be
179 if ( track
>= this->count
)
180 this->count
= track
+ 1;
183 mlt_multitrack_refresh( this );
189 /** Get the number of tracks.
192 int mlt_multitrack_count( mlt_multitrack
this )
197 /** Get an individual track as a producer.
200 mlt_producer
mlt_multitrack_track( mlt_multitrack
this, int track
)
202 mlt_producer producer
= NULL
;
204 if ( this->list
!= NULL
&& track
< this->count
)
205 producer
= this->list
[ track
]->producer
;
210 static int position_compare( const void *p1
, const void *p2
)
212 return *( mlt_position
* )p1
- *( mlt_position
* )p2
;
215 static int add_unique( mlt_position
*array
, int size
, mlt_position position
)
218 for ( i
= 0; i
< size
; i
++ )
219 if ( array
[ i
] == position
)
222 array
[ size
++ ] = position
;
226 /** Determine the clip point.
228 Special case here: a 'producer' has no concept of multiple clips - only the
229 playlist and multitrack producers have clip functionality. Further to that a
230 multitrack determines clip information from any connected tracks that happen
233 Additionally, it must locate clips in the correct order, for example, consider
234 the following track arrangement:
236 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
237 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
239 Note - b clips represent blanks. They are also reported as clip positions.
241 When extracting clip positions from these playlists, we should get a sequence of:
243 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
246 mlt_position
mlt_multitrack_clip( mlt_multitrack
this, mlt_whence whence
, int index
)
248 mlt_position position
= 0;
251 mlt_position
*map
= malloc( 1000 * sizeof( mlt_position
) );
254 for ( i
= 0; i
< this->count
; i
++ )
256 // Get the producer for this track
257 mlt_producer producer
= this->list
[ i
]->producer
;
259 // If it's assigned and not a hidden track
260 if ( producer
!= NULL
)
262 // Get the properties of this producer
263 mlt_properties properties
= MLT_PRODUCER_PROPERTIES( producer
);
265 // Determine if it's a playlist
266 mlt_playlist playlist
= mlt_properties_get_data( properties
, "playlist", NULL
);
268 // Special case consideration of playlists
269 if ( playlist
!= NULL
)
271 for ( j
= 0; j
< mlt_playlist_count( playlist
); j
++ )
272 count
= add_unique( map
, count
, mlt_playlist_clip( playlist
, mlt_whence_relative_start
, j
) );
273 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
277 count
= add_unique( map
, count
, 0 );
278 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
284 qsort( map
, count
, sizeof( mlt_position
), position_compare
);
286 // Now locate the requested index
289 case mlt_whence_relative_start
:
291 position
= map
[ index
];
293 position
= map
[ count
- 1 ];
296 case mlt_whence_relative_current
:
297 position
= mlt_producer_position( MLT_MULTITRACK_PRODUCER( this ) );
298 for ( i
= 0; i
< count
- 2; i
++ )
299 if ( position
>= map
[ i
] && position
< map
[ i
+ 1 ] )
302 if ( index
>= 0 && index
< count
)
303 position
= map
[ index
];
304 else if ( index
< 0 )
307 position
= map
[ count
- 1 ];
310 case mlt_whence_relative_end
:
312 position
= map
[ count
- index
- 1 ];
324 /** Get frame method.
326 Special case here: The multitrack must be used in a conjunction with a downstream
327 tractor-type service, ie:
330 Producer2 - multitrack - { filters/transitions } - tractor - consumer
333 The get_frame of a tractor pulls frames from it's connected service on all tracks and
334 will terminate as soon as it receives a test card with a last_track property. The
335 important case here is that the mulitrack does not move to the next frame until all
336 tracks have been pulled.
338 Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
339 that all producers are positioned on the same frame. It uses the 'last track' logic
340 to determine when to move to the next frame.
342 Flaw: if a transition is configured to read from a b-track which happens to trigger
343 the last frame logic (ie: it's configured incorrectly), then things are going to go
346 See playlist logic too.
349 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
)
351 // Get the mutiltrack object
352 mlt_multitrack
this = parent
->child
;
354 // Check if we have a track for this index
355 if ( index
< this->count
&& this->list
[ index
] != NULL
)
357 // Get the producer for this track
358 mlt_producer producer
= this->list
[ index
]->producer
;
360 // Get the track hide property
361 int hide
= mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer
) ), "hide" );
363 // Obtain the current position
364 mlt_position position
= mlt_producer_frame( parent
);
366 // Get the parent properties
367 mlt_properties producer_properties
= MLT_PRODUCER_PROPERTIES( parent
);
370 double speed
= mlt_properties_get_double( producer_properties
, "_speed" );
372 // Make sure we're at the same point
373 mlt_producer_seek( producer
, position
);
375 // Get the frame from the producer
376 mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer
), frame
, 0 );
378 // Indicate speed of this producer
379 mlt_properties properties
= MLT_FRAME_PROPERTIES( *frame
);
380 mlt_properties_set_double( properties
, "_speed", speed
);
381 mlt_properties_set_position( properties
, "_position", position
);
382 mlt_properties_set_int( properties
, "hide", hide
);
386 // Generate a test frame
387 *frame
= mlt_frame_init( MLT_PRODUCER_SERVICE( parent
) );
389 // Update position on the frame we're creating
390 mlt_frame_set_position( *frame
, mlt_producer_position( parent
) );
392 // Move on to the next frame
393 if ( index
>= this->count
)
395 // Let tractor know if we've reached the end
396 mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame
), "last_track", 1 );
398 // Move to the next frame
399 mlt_producer_prepare_next( parent
);
406 /** Close this instance.
409 void mlt_multitrack_close( mlt_multitrack
this )
411 if ( this != NULL
&& mlt_properties_dec_ref( MLT_MULTITRACK_PROPERTIES( this ) ) <= 0 )
414 for ( i
= 0; i
< this->count
; i
++ )
416 if ( this->list
[ i
] != NULL
)
418 mlt_event_close( this->list
[ i
]->event
);
419 mlt_producer_close( this->list
[ i
]->producer
);
420 free( this->list
[ i
] );
424 // Close the producer
425 this->parent
.close
= NULL
;
426 mlt_producer_close( &this->parent
);