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 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.
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.
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.
23 #include "mlt_multitrack.h"
24 #include "mlt_playlist.h"
25 #include "mlt_frame.h"
30 /** Private definition.
33 struct mlt_multitrack_s
35 // We're extending producer here
36 struct mlt_producer_s parent
;
42 /** Forward reference.
45 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
);
50 mlt_multitrack
mlt_multitrack_init( )
52 // Allocate the multitrack object
53 mlt_multitrack
this = calloc( sizeof( struct mlt_multitrack_s
), 1 );
57 mlt_producer producer
= &this->parent
;
58 if ( mlt_producer_init( producer
, this ) == 0 )
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>" );
76 /** Get the producer associated to this multitrack.
79 mlt_producer
mlt_multitrack_producer( mlt_multitrack
this )
84 /** Get the service associated this multitrack.
87 mlt_service
mlt_multitrack_service( mlt_multitrack
this )
89 return mlt_producer_service( mlt_multitrack_producer( this ) );
92 /** Get the properties associated this multitrack.
95 mlt_properties
mlt_multitrack_properties( mlt_multitrack
this )
97 return mlt_service_properties( mlt_multitrack_service( this ) );
100 /** Initialise position related information.
103 void mlt_multitrack_refresh( mlt_multitrack
this )
107 // Obtain the properties of this multitrack
108 mlt_properties properties
= mlt_multitrack_properties( this );
110 // We need to ensure that the multitrack reports the longest track as its length
111 mlt_position length
= 0;
113 // We need to ensure that fps are the same on all services
116 // Obtain stats on all connected services
117 for ( i
= 0; i
< this->count
; i
++ )
119 // Get the producer from this index
120 mlt_producer producer
= this->list
[ i
];
122 // If it's allocated then, update our stats
123 if ( producer
!= NULL
)
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" );
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
;
136 // This is the first producer, so it controls the fps
137 fps
= mlt_producer_get_fps( producer
);
139 else if ( fps
!= mlt_producer_get_fps( producer
) )
141 // Generate a warning for now - the following attempt to fix may fail
142 fprintf( stderr
, "Warning: fps mismatch on track %d\n", i
);
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
);
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
);
156 /** Connect a producer to a given track.
158 Note that any producer can be connected here, but see special case treatment
159 of playlist in clip point determination below.
162 int mlt_multitrack_connect( mlt_multitrack
this, mlt_producer producer
, int track
)
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
);
169 // Resize the producer list if need be
170 if ( track
>= this->size
)
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;
179 // Assign the track in our list here
180 this->list
[ track
] = producer
;
182 // Increment the track count if need be
183 if ( track
>= this->count
)
184 this->count
= track
+ 1;
187 mlt_multitrack_refresh( this );
193 /** Get the number of tracks.
196 int mlt_multitrack_count( mlt_multitrack
this )
201 /** Get an individual track as a producer.
204 mlt_producer
mlt_multitrack_track( mlt_multitrack
this, int track
)
206 mlt_producer producer
= NULL
;
208 if ( this->list
!= NULL
&& track
< this->count
)
209 producer
= this->list
[ track
];
214 static int position_compare( const void *p1
, const void *p2
)
216 return *( mlt_position
* )p1
- *( mlt_position
* )p2
;
219 static int add_unique( mlt_position
*array
, int size
, mlt_position position
)
222 for ( i
= 0; i
< size
; i
++ )
223 if ( array
[ i
] == position
)
226 array
[ size
++ ] = position
;
230 /** Determine the clip point.
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
237 Additionally, it must locate clips in the correct order, for example, consider
238 the following track arrangement:
240 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
241 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
243 Note - b clips represent blanks. They are also reported as clip positions.
245 When extracting clip positions from these playlists, we should get a sequence of:
247 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
250 mlt_position
mlt_multitrack_clip( mlt_multitrack
this, mlt_whence whence
, int index
)
252 mlt_position position
= 0;
255 mlt_position
*map
= malloc( 1000 * sizeof( mlt_position
) );
258 for ( i
= 0; i
< this->count
; i
++ )
260 // Get the producer for this track
261 mlt_producer producer
= this->list
[ i
];
263 // If it's assigned and not a hidden track
264 if ( producer
!= NULL
)
266 // Get the properties of this producer
267 mlt_properties properties
= mlt_producer_properties( producer
);
269 // Determine if it's a playlist
270 mlt_playlist playlist
= mlt_properties_get_data( properties
, "playlist", NULL
);
272 // Special case consideration of playlists
273 if ( playlist
!= NULL
)
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 );
281 count
= add_unique( map
, count
, 0 );
282 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
288 qsort( map
, count
, sizeof( mlt_position
), position_compare
);
290 // Now locate the requested index
293 case mlt_whence_relative_start
:
295 position
= map
[ index
];
297 position
= map
[ count
- 1 ];
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 ] )
306 if ( index
>= 0 && index
< count
)
307 position
= map
[ index
];
308 else if ( index
< 0 )
311 position
= map
[ count
- 1 ];
314 case mlt_whence_relative_end
:
316 position
= map
[ count
- index
- 1 ];
328 /** Get frame method.
330 Special case here: The multitrack must be used in a conjunction with a downstream
331 tractor-type service, ie:
334 Producer2 - multitrack - { filters/transitions } - tractor - consumer
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.
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.
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
350 See playlist logic too.
353 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
)
355 // Get the mutiltrack object
356 mlt_multitrack
this = parent
->child
;
358 // Check if we have a track for this index
359 if ( index
< this->count
&& this->list
[ index
] != NULL
)
361 // Get the producer for this track
362 mlt_producer producer
= this->list
[ index
];
364 // Obtain the current position
365 mlt_position position
= mlt_producer_frame( parent
);
367 // Make sure we're at the same point
368 mlt_producer_seek( producer
, position
);
370 // Get the frame from the producer
371 mlt_service_get_frame( mlt_producer_service( producer
), frame
, 0 );
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" ) );
382 // Generate a test frame
383 *frame
= mlt_frame_init( );
385 // Update position on the frame we're creating
386 mlt_frame_set_position( *frame
, mlt_producer_position( parent
) );
388 // Move on to the next frame
389 if ( index
>= this->count
)
391 // Let tractor know if we've reached the end
392 mlt_properties_set_int( mlt_frame_properties( *frame
), "last_track", 1 );
394 // Move to the next frame
395 mlt_producer_prepare_next( parent
);
399 //mlt_multitrack_refresh( this );
405 /** Close this instance.
408 void mlt_multitrack_close( mlt_multitrack
this )
410 // Close the producer
411 mlt_producer_close( &this->parent
);