]> git.sesse.net Git - mlt/blob - src/framework/mlt_multitrack.c
Mlt Ref Counts and Playlist split/join
[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 != 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 }