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