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