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