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