Mlt Ref Counts and Playlist split/join
[melted] / src / framework / mlt_multitrack.c
1 /*
2 * mlt_multitrack.c -- multitrack service class
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
23 #include "mlt_multitrack.h"
24 #include "mlt_playlist.h"
25 #include "mlt_frame.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 /** Private definition.
31 */
32
33 struct mlt_multitrack_s
34 {
35 // We're extending producer here
36 struct mlt_producer_s parent;
37 mlt_producer *list;
38 int size;
39 int count;
40 };
41
42 /** Forward reference.
43 */
44
45 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
46
47 /** Constructor.
48 */
49
50 mlt_multitrack mlt_multitrack_init( )
51 {
52 // Allocate the multitrack object
53 mlt_multitrack this = calloc( sizeof( struct mlt_multitrack_s ), 1 );
54
55 if ( this != NULL )
56 {
57 mlt_producer producer = &this->parent;
58 if ( mlt_producer_init( producer, this ) == 0 )
59 {
60 mlt_properties properties = mlt_multitrack_properties( this );
61 producer->get_frame = producer_get_frame;
62 mlt_properties_set_data( properties, "multitrack", this, 0, NULL, NULL );
63 mlt_properties_set( properties, "log_id", "multitrack" );
64 mlt_properties_set( properties, "resource", "<multitrack>" );
65 }
66 else
67 {
68 free( this );
69 this = NULL;
70 }
71 }
72
73 return this;
74 }
75
76 /** Get the producer associated to this multitrack.
77 */
78
79 mlt_producer mlt_multitrack_producer( mlt_multitrack this )
80 {
81 return this != NULL ? &this->parent : NULL;
82 }
83
84 /** Get the service associated this multitrack.
85 */
86
87 mlt_service mlt_multitrack_service( mlt_multitrack this )
88 {
89 return mlt_producer_service( mlt_multitrack_producer( this ) );
90 }
91
92 /** Get the properties associated this multitrack.
93 */
94
95 mlt_properties mlt_multitrack_properties( mlt_multitrack this )
96 {
97 return mlt_service_properties( mlt_multitrack_service( this ) );
98 }
99
100 /** Initialise position related information.
101 */
102
103 void mlt_multitrack_refresh( mlt_multitrack this )
104 {
105 int i = 0;
106
107 // Obtain the properties of this multitrack
108 mlt_properties properties = mlt_multitrack_properties( this );
109
110 // We need to ensure that the multitrack reports the longest track as its length
111 mlt_position length = 0;
112
113 // We need to ensure that fps are the same on all services
114 double fps = 0;
115
116 // Obtain stats on all connected services
117 for ( i = 0; i < this->count; i ++ )
118 {
119 // Get the producer from this index
120 mlt_producer producer = this->list[ i ];
121
122 // If it's allocated then, update our stats
123 if ( producer != NULL )
124 {
125 // If we have more than 1 track, we must be in continue mode
126 if ( this->count > 1 )
127 mlt_properties_set( mlt_producer_properties( producer ), "eof", "continue" );
128
129 // Determine the longest length
130 if ( !mlt_properties_get_int( mlt_producer_properties( producer ), "hide" ) )
131 length = mlt_producer_get_playtime( producer ) > length ? mlt_producer_get_playtime( producer ) : length;
132
133 // Handle fps
134 if ( fps == 0 )
135 {
136 // This is the first producer, so it controls the fps
137 fps = mlt_producer_get_fps( producer );
138 }
139 else if ( fps != mlt_producer_get_fps( producer ) )
140 {
141 // Generate a warning for now - the following attempt to fix may fail
142 fprintf( stderr, "Warning: fps mismatch on track %d\n", i );
143
144 // It should be safe to impose fps on an image producer, but not necessarily safe for video
145 mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps );
146 }
147 }
148 }
149
150 // Update multitrack properties now - we'll not destroy the in point here
151 mlt_properties_set_position( properties, "length", length );
152 mlt_properties_set_position( properties, "out", length - 1 );
153 mlt_properties_set_double( properties, "fps", fps );
154 }
155
156 /** Connect a producer to a given track.
157
158 Note that any producer can be connected here, but see special case treatment
159 of playlist in clip point determination below.
160 */
161
162 int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track )
163 {
164 // Connect to the producer to ourselves at the specified track
165 int result = mlt_service_connect_producer( mlt_multitrack_service( this ), mlt_producer_service( producer ), track );
166
167 if ( result == 0 )
168 {
169 // Resize the producer list if need be
170 if ( track >= this->size )
171 {
172 int i;
173 this->list = realloc( this->list, ( track + 10 ) * sizeof( mlt_producer ) );
174 for ( i = this->size; i < track + 10; i ++ )
175 this->list[ i ] = NULL;
176 this->size = track + 10;
177 }
178
179 // Assign the track in our list here
180 this->list[ track ] = producer;
181
182 // Increment the track count if need be
183 if ( track >= this->count )
184 this->count = track + 1;
185
186 // Refresh our stats
187 mlt_multitrack_refresh( this );
188 }
189
190 return result;
191 }
192
193 /** Get the number of tracks.
194 */
195
196 int mlt_multitrack_count( mlt_multitrack this )
197 {
198 return this->count;
199 }
200
201 /** Get an individual track as a producer.
202 */
203
204 mlt_producer mlt_multitrack_track( mlt_multitrack this, int track )
205 {
206 mlt_producer producer = NULL;
207
208 if ( this->list != NULL && track < this->count )
209 producer = this->list[ track ];
210
211 return producer;
212 }
213
214 static int position_compare( const void *p1, const void *p2 )
215 {
216 return *( mlt_position * )p1 - *( mlt_position * )p2;
217 }
218
219 static int add_unique( mlt_position *array, int size, mlt_position position )
220 {
221 int i = 0;
222 for ( i = 0; i < size; i ++ )
223 if ( array[ i ] == position )
224 break;
225 if ( i == size )
226 array[ size ++ ] = position;
227 return size;
228 }
229
230 /** Determine the clip point.
231
232 Special case here: a 'producer' has no concept of multiple clips - only the
233 playlist and multitrack producers have clip functionality. Further to that a
234 multitrack determines clip information from any connected tracks that happen
235 to be playlists.
236
237 Additionally, it must locate clips in the correct order, for example, consider
238 the following track arrangement:
239
240 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
241 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
242
243 Note - b clips represent blanks. They are also reported as clip positions.
244
245 When extracting clip positions from these playlists, we should get a sequence of:
246
247 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
248 */
249
250 mlt_position mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index )
251 {
252 mlt_position position = 0;
253 int i = 0;
254 int j = 0;
255 mlt_position *map = malloc( 1000 * sizeof( mlt_position ) );
256 int count = 0;
257
258 for ( i = 0; i < this->count; i ++ )
259 {
260 // Get the producer for this track
261 mlt_producer producer = this->list[ i ];
262
263 // If it's assigned and not a hidden track
264 if ( producer != NULL )
265 {
266 // Get the properties of this producer
267 mlt_properties properties = mlt_producer_properties( producer );
268
269 // Determine if it's a playlist
270 mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
271
272 // Special case consideration of playlists
273 if ( playlist != NULL )
274 {
275 for ( j = 0; j < mlt_playlist_count( playlist ); j ++ )
276 count = add_unique( map, count, mlt_playlist_clip( playlist, mlt_whence_relative_start, j ) );
277 count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
278 }
279 else
280 {
281 count = add_unique( map, count, 0 );
282 count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
283 }
284 }
285 }
286
287 // Now sort the map
288 qsort( map, count, sizeof( mlt_position ), position_compare );
289
290 // Now locate the requested index
291 switch( whence )
292 {
293 case mlt_whence_relative_start:
294 if ( index < count )
295 position = map[ index ];
296 else
297 position = map[ count - 1 ];
298 break;
299
300 case mlt_whence_relative_current:
301 position = mlt_producer_position( mlt_multitrack_producer( this ) );
302 for ( i = 0; i < count - 2; i ++ )
303 if ( position >= map[ i ] && position < map[ i + 1 ] )
304 break;
305 index += i;
306 if ( index >= 0 && index < count )
307 position = map[ index ];
308 else if ( index < 0 )
309 position = map[ 0 ];
310 else
311 position = map[ count - 1 ];
312 break;
313
314 case mlt_whence_relative_end:
315 if ( index < count )
316 position = map[ count - index - 1 ];
317 else
318 position = map[ 0 ];
319 break;
320 }
321
322 // Free the map
323 free( map );
324
325 return position;
326 }
327
328 /** Get frame method.
329
330 Special case here: The multitrack must be used in a conjunction with a downstream
331 tractor-type service, ie:
332
333 Producer1 \
334 Producer2 - multitrack - { filters/transitions } - tractor - consumer
335 Producer3 /
336
337 The get_frame of a tractor pulls frames from it's connected service on all tracks and
338 will terminate as soon as it receives a test card with a last_track property. The
339 important case here is that the mulitrack does not move to the next frame until all
340 tracks have been pulled.
341
342 Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
343 that all producers are positioned on the same frame. It uses the 'last track' logic
344 to determine when to move to the next frame.
345
346 Flaw: if a transition is configured to read from a b-track which happens to trigger
347 the last frame logic (ie: it's configured incorrectly), then things are going to go
348 out of sync.
349
350 See playlist logic too.
351 */
352
353 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index )
354 {
355 // Get the mutiltrack object
356 mlt_multitrack this = parent->child;
357
358 // Check if we have a track for this index
359 if ( index < this->count && this->list[ index ] != NULL )
360 {
361 // Get the producer for this track
362 mlt_producer producer = this->list[ index ];
363
364 // Obtain the current position
365 mlt_position position = mlt_producer_frame( parent );
366
367 // Make sure we're at the same point
368 mlt_producer_seek( producer, position );
369
370 // Get the frame from the producer
371 mlt_service_get_frame( mlt_producer_service( producer ), frame, 0 );
372
373 // Indicate speed of this producer
374 mlt_properties producer_properties = mlt_producer_properties( parent );
375 double speed = mlt_properties_get_double( producer_properties, "_speed" );
376 mlt_properties properties = mlt_frame_properties( *frame );
377 mlt_properties_set_double( properties, "_speed", speed );
378 mlt_properties_set_int( properties, "hide", mlt_properties_get_int( mlt_producer_properties( producer ), "hide" ) );
379 }
380 else
381 {
382 // Generate a test frame
383 *frame = mlt_frame_init( );
384
385 // Update position on the frame we're creating
386 mlt_frame_set_position( *frame, mlt_producer_position( parent ) );
387
388 // Move on to the next frame
389 if ( index >= this->count )
390 {
391 // Let tractor know if we've reached the end
392 mlt_properties_set_int( mlt_frame_properties( *frame ), "last_track", 1 );
393
394 // Move to the next frame
395 mlt_producer_prepare_next( parent );
396 }
397
398 // Refresh our stats
399 //mlt_multitrack_refresh( this );
400 }
401
402 return 0;
403 }
404
405 /** Close this instance.
406 */
407
408 void mlt_multitrack_close( mlt_multitrack this )
409 {
410 if ( this != NULL && mlt_properties_dec_ref( mlt_multitrack_properties( this ) ) <= 0 )
411 {
412 // Close the producer
413 mlt_producer_close( &this->parent );
414
415 // Free the list
416 free( this->list );
417
418 // Free the object
419 free( this );
420 }
421 }