]> git.sesse.net Git - mlt/blob - src/framework/mlt_multitrack.c
vorbis producer added, clean up on clip handling in multitrack
[mlt] / 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->parent;
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                         length = mlt_producer_get_playtime( producer ) > length ? mlt_producer_get_playtime( producer ) : length;
131                         
132                         // Handle fps
133                         if ( fps == 0 )
134                         {
135                                 // This is the first producer, so it controls the fps
136                                 fps = mlt_producer_get_fps( producer );
137                         }
138                         else if ( fps != mlt_producer_get_fps( producer ) )
139                         {
140                                 // Generate a warning for now - the following attempt to fix may fail
141                                 fprintf( stderr, "Warning: fps mismatch on track %d\n", i );
142
143                                 // It should be safe to impose fps on an image producer, but not necessarily safe for video
144                                 mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps );
145                         }
146                 }
147         }
148
149         // Update multitrack properties now - we'll not destroy the in point here
150         mlt_properties_set_position( properties, "length", length );
151         mlt_properties_set_position( properties, "out", length - 1 );
152         mlt_properties_set_double( properties, "fps", fps );
153 }
154
155 /** Connect a producer to a given track.
156
157         Note that any producer can be connected here, but see special case treatment
158         of playlist in clip point determination below.
159 */
160
161 int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track )
162 {
163         // Connect to the producer to ourselves at the specified track
164         int result = mlt_service_connect_producer( mlt_multitrack_service( this ), mlt_producer_service( producer ), track );
165
166         if ( result == 0 )
167         {
168                 // Resize the producer list if need be
169                 if ( track >= this->size )
170                 {
171                         int i;
172                         this->list = realloc( this->list, ( track + 10 ) * sizeof( mlt_producer ) );
173                         for ( i = this->size; i < track + 10; i ++ )
174                                 this->list[ i ] = NULL;
175                         this->size = track + 10;
176                 }
177                 
178                 // Assign the track in our list here
179                 this->list[ track ] = producer;
180                 
181                 // Increment the track count if need be
182                 if ( track >= this->count )
183                         this->count = track + 1;
184                         
185                 // Refresh our stats
186                 mlt_multitrack_refresh( this );
187         }
188
189         return result;
190 }
191
192 /** Get the number of tracks.
193 */
194
195 int mlt_multitrack_count( mlt_multitrack this )
196 {
197         return this->count;     
198 }
199
200 /** Get an individual track as a producer.
201 */
202
203 mlt_producer mlt_multitrack_track( mlt_multitrack this, int track )
204 {
205         mlt_producer producer = NULL;
206         
207         if ( this->list != NULL && track < this->count )
208                 producer = this->list[ track ];
209
210         return producer;
211 }
212
213 static int position_compare( const void *p1, const void *p2 )
214 {
215         return *( int64_t * )p1 - *( int64_t * )p2;
216 }
217
218 static int add_unique( mlt_position *array, int size, mlt_position position )
219 {
220         int i = 0;
221         for ( i = 0; i < size; i ++ )
222                 if ( array[ i ] == position )
223                         break;
224         if ( i == size )
225                 array[ size ++ ] = position;
226         return size;
227 }
228
229 /** Determine the clip point.
230
231         Special case here: a 'producer' has no concept of multiple clips - only the 
232         playlist and multitrack producers have clip functionality. Further to that a 
233         multitrack determines clip information from any connected tracks that happen 
234         to be playlists.
235
236         Additionally, it must locate clips in the correct order, for example, consider
237         the following track arrangement:
238
239         playlist1 |0.0     |b0.0      |0.1          |0.1         |0.2           |
240         playlist2 |b1.0  |1.0           |b1.1     |1.1             |
241
242         Note - b clips represent blanks. They are also reported as clip positions.
243
244         When extracting clip positions from these playlists, we should get a sequence of:
245
246         0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
247 */
248
249 mlt_position mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index )
250 {
251         mlt_position position = 0;
252         int i = 0;
253         int j = 0;
254         int64_t *map = malloc( 1000 * sizeof( mlt_position ) );
255         int count = 0;
256
257         for ( i = 0; i < this->count; i ++ )
258         {
259                 // Get the producer for this track
260                 mlt_producer producer = this->list[ i ];
261
262                 // If it's assigned...
263                 if ( producer != NULL )
264                 {
265                         // Get the properties of this producer
266                         mlt_properties properties = mlt_producer_properties( producer );
267
268                         // Determine if it's a playlist
269                         mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
270
271                         // Special case consideration of playlists
272                         if ( playlist != NULL )
273                         {
274                                 for ( j = 0; j < mlt_playlist_count( playlist ); j ++ )
275                                         count = add_unique( map, count, mlt_playlist_clip( playlist, mlt_whence_relative_start, j ) );
276                                 count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
277                         }
278                         else
279                         {
280                                 count = add_unique( map, count, 0 );
281                                 count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
282                         }
283                 }
284         }
285
286         // Now sort the map
287         qsort( map, count, sizeof( int64_t ), position_compare );
288
289         // Now locate the requested index
290         switch( whence )
291         {
292                 case mlt_whence_relative_start:
293                         if ( index < count )
294                                 position = map[ index ];
295                         else
296                                 position = map[ count - 1 ];
297                         break;
298
299                 case mlt_whence_relative_current:
300                         position = mlt_producer_position( mlt_multitrack_producer( this ) );
301                         for ( i = 0; i < count - 2; i ++ ) 
302                                 if ( position >= map[ i ] && position < map[ i + 1 ] )
303                                         break;
304                         index += i;
305                         if ( index >= 0 && index < count )
306                                 position = map[ index ];
307                         else if ( index < 0 )
308                                 position = map[ 0 ];
309                         else
310                                 position = map[ count - 1 ];
311                         break;
312
313                 case mlt_whence_relative_end:
314                         if ( index < count )
315                                 position = map[ count - index - 1 ];
316                         else
317                                 position = map[ 0 ];
318                         break;
319         }
320
321         // Free the map
322         free( map );
323
324         return position;
325 }
326
327 /** Get frame method.
328
329         Special case here: The multitrack must be used in a conjunction with a downstream
330         tractor-type service, ie:
331
332         Producer1 \
333         Producer2 - multitrack - { filters/transitions } - tractor - consumer
334         Producer3 /
335
336         The get_frame of a tractor pulls frames from it's connected service on all tracks and 
337         will terminate as soon as it receives a test card with a last_track property. The 
338         important case here is that the mulitrack does not move to the next frame until all
339         tracks have been pulled. 
340
341         Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
342         that all producers are positioned on the same frame. It uses the 'last track' logic
343         to determine when to move to the next frame.
344
345         Flaw: if a transition is configured to read from a b-track which happens to trigger
346         the last frame logic (ie: it's configured incorrectly), then things are going to go
347         out of sync.
348
349         See playlist logic too.
350 */
351
352 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index )
353 {
354         // Get the mutiltrack object
355         mlt_multitrack this = parent->child;
356
357         // Check if we have a track for this index
358         if ( index < this->count && this->list[ index ] != NULL )
359         {
360                 // Get the producer for this track
361                 mlt_producer producer = this->list[ index ];
362
363                 // Obtain the current position
364                 mlt_position position = mlt_producer_frame( parent );
365
366                 // Make sure we're at the same point
367                 mlt_producer_seek( producer, position );
368
369                 // Get the frame from the producer
370                 mlt_service_get_frame( mlt_producer_service( producer ), frame, 0 );
371
372                 // Indicate speed of this producer
373                 mlt_properties producer_properties = mlt_producer_properties( parent );
374                 double speed = mlt_properties_get_double( producer_properties, "speed" );
375                 mlt_properties properties = mlt_frame_properties( *frame );
376                 mlt_properties_set_double( properties, "speed", speed );
377         }
378         else
379         {
380                 // Generate a test frame
381                 *frame = mlt_frame_init( );
382
383                 // Update position on the frame we're creating
384                 mlt_frame_set_position( *frame, mlt_producer_position( parent ) );
385
386                 // Move on to the next frame
387                 if ( index >= this->count )
388                 {
389                         // Let tractor know if we've reached the end
390                         mlt_properties_set_int( mlt_frame_properties( *frame ), "last_track", 1 );
391
392                         // Move to the next frame
393                         mlt_producer_prepare_next( parent );
394                 }
395
396                 // Refresh our stats
397                 mlt_multitrack_refresh( this );
398         }
399
400         return 0;
401 }
402
403 /** Close this instance.
404 */
405
406 void mlt_multitrack_close( mlt_multitrack this )
407 {
408         // Close the producer
409         mlt_producer_close( &this->parent );
410
411         // Free the list
412         free( this->list );
413
414         // Free the object
415         free( this );
416 }