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