]> git.sesse.net Git - mlt/blob - src/framework/mlt_multitrack.c
7e16e04d95591fc8405380a63773d06ea1e5d1be
[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 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 }