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