2 * mlt_multitrack.c -- multitrack service class
3 * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
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.
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.
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
23 #include "mlt_multitrack.h"
24 #include "mlt_playlist.h"
25 #include "mlt_frame.h"
30 /** Forward reference.
33 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
);
38 mlt_multitrack
mlt_multitrack_init( )
40 // Allocate the multitrack object
41 mlt_multitrack
this = calloc( sizeof( struct mlt_multitrack_s
), 1 );
45 mlt_producer producer
= &this->parent
;
46 if ( mlt_producer_init( producer
, this ) == 0 )
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
;
68 /** Get the producer associated to this multitrack.
71 mlt_producer
mlt_multitrack_producer( mlt_multitrack
this )
73 return this != NULL ?
&this->parent
: NULL
;
76 /** Get the service associated this multitrack.
79 mlt_service
mlt_multitrack_service( mlt_multitrack
this )
81 return MLT_MULTITRACK_SERVICE( this );
84 /** Get the properties associated this multitrack.
87 mlt_properties
mlt_multitrack_properties( mlt_multitrack
this )
89 return MLT_MULTITRACK_PROPERTIES( this );
92 /** Initialise position related information.
95 void mlt_multitrack_refresh( mlt_multitrack
this )
99 // Obtain the properties of this multitrack
100 mlt_properties properties
= MLT_MULTITRACK_PROPERTIES( this );
102 // We need to ensure that the multitrack reports the longest track as its length
103 mlt_position length
= 0;
105 // We need to ensure that fps are the same on all services
108 // Obtain stats on all connected services
109 for ( i
= 0; i
< this->count
; i
++ )
111 // Get the producer from this index
112 mlt_track track
= this->list
[ i
];
113 mlt_producer producer
= track
->producer
;
115 // If it's allocated then, update our stats
116 if ( producer
!= NULL
)
118 // If we have more than 1 track, we must be in continue mode
119 if ( this->count
> 1 )
120 mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer
), "eof", "continue" );
122 // Determine the longest length
123 //if ( !mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "hide" ) )
124 length
= mlt_producer_get_playtime( producer
) > length ?
mlt_producer_get_playtime( producer
) : length
;
129 // This is the first producer, so it controls the fps
130 fps
= mlt_producer_get_fps( producer
);
132 else if ( fps
!= mlt_producer_get_fps( producer
) )
134 // Generate a warning for now - the following attempt to fix may fail
135 fprintf( stderr
, "Warning: fps mismatch on track %d\n", i
);
137 // It should be safe to impose fps on an image producer, but not necessarily safe for video
138 mlt_properties_set_double( MLT_PRODUCER_PROPERTIES( producer
), "fps", fps
);
143 // Update multitrack properties now - we'll not destroy the in point here
144 mlt_events_block( properties
, properties
);
145 mlt_properties_set_position( properties
, "length", length
);
146 mlt_events_unblock( properties
, properties
);
147 mlt_properties_set_position( properties
, "out", length
- 1 );
148 mlt_properties_set_double( properties
, "fps", fps
);
151 /** Listener for producers on the playlist.
154 static void mlt_multitrack_listener( mlt_producer producer
, mlt_multitrack
this )
156 mlt_multitrack_refresh( this );
159 /** Connect a producer to a given track.
161 Note that any producer can be connected here, but see special case treatment
162 of playlist in clip point determination below.
165 int mlt_multitrack_connect( mlt_multitrack
this, mlt_producer producer
, int track
)
167 // Connect to the producer to ourselves at the specified track
168 int result
= mlt_service_connect_producer( MLT_MULTITRACK_SERVICE( this ), MLT_PRODUCER_SERVICE( producer
), track
);
172 // Resize the producer list if need be
173 if ( track
>= this->size
)
176 this->list
= realloc( this->list
, ( track
+ 10 ) * sizeof( mlt_track
) );
177 for ( i
= this->size
; i
< track
+ 10; i
++ )
178 this->list
[ i
] = NULL
;
179 this->size
= track
+ 10;
182 if ( this->list
[ track
] != NULL
)
184 mlt_event_close( this->list
[ track
]->event
);
185 mlt_producer_close( this->list
[ track
]->producer
);
189 this->list
[ track
] = malloc( sizeof( struct mlt_track_s
) );
192 // Assign the track in our list here
193 this->list
[ track
]->producer
= producer
;
194 this->list
[ track
]->event
= mlt_events_listen( MLT_PRODUCER_PROPERTIES( producer
), this,
195 "producer-changed", ( mlt_listener
)mlt_multitrack_listener
);
196 mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer
) );
197 mlt_event_inc_ref( this->list
[ track
]->event
);
199 // Increment the track count if need be
200 if ( track
>= this->count
)
201 this->count
= track
+ 1;
204 mlt_multitrack_refresh( this );
210 /** Get the number of tracks.
213 int mlt_multitrack_count( mlt_multitrack
this )
218 /** Get an individual track as a producer.
221 mlt_producer
mlt_multitrack_track( mlt_multitrack
this, int track
)
223 mlt_producer producer
= NULL
;
225 if ( this->list
!= NULL
&& track
< this->count
)
226 producer
= this->list
[ track
]->producer
;
231 static int position_compare( const void *p1
, const void *p2
)
233 return *( mlt_position
* )p1
- *( mlt_position
* )p2
;
236 static int add_unique( mlt_position
*array
, int size
, mlt_position position
)
239 for ( i
= 0; i
< size
; i
++ )
240 if ( array
[ i
] == position
)
243 array
[ size
++ ] = position
;
247 /** Determine the clip point.
249 Special case here: a 'producer' has no concept of multiple clips - only the
250 playlist and multitrack producers have clip functionality. Further to that a
251 multitrack determines clip information from any connected tracks that happen
254 Additionally, it must locate clips in the correct order, for example, consider
255 the following track arrangement:
257 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
258 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
260 Note - b clips represent blanks. They are also reported as clip positions.
262 When extracting clip positions from these playlists, we should get a sequence of:
264 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
267 mlt_position
mlt_multitrack_clip( mlt_multitrack
this, mlt_whence whence
, int index
)
269 mlt_position position
= 0;
272 mlt_position
*map
= malloc( 1000 * sizeof( mlt_position
) );
275 for ( i
= 0; i
< this->count
; i
++ )
277 // Get the producer for this track
278 mlt_producer producer
= this->list
[ i
]->producer
;
280 // If it's assigned and not a hidden track
281 if ( producer
!= NULL
)
283 // Get the properties of this producer
284 mlt_properties properties
= MLT_PRODUCER_PROPERTIES( producer
);
286 // Determine if it's a playlist
287 mlt_playlist playlist
= mlt_properties_get_data( properties
, "playlist", NULL
);
289 // Special case consideration of playlists
290 if ( playlist
!= NULL
)
292 for ( j
= 0; j
< mlt_playlist_count( playlist
); j
++ )
293 count
= add_unique( map
, count
, mlt_playlist_clip( playlist
, mlt_whence_relative_start
, j
) );
294 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
298 count
= add_unique( map
, count
, 0 );
299 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
305 qsort( map
, count
, sizeof( mlt_position
), position_compare
);
307 // Now locate the requested index
310 case mlt_whence_relative_start
:
312 position
= map
[ index
];
314 position
= map
[ count
- 1 ];
317 case mlt_whence_relative_current
:
318 position
= mlt_producer_position( MLT_MULTITRACK_PRODUCER( this ) );
319 for ( i
= 0; i
< count
- 2; i
++ )
320 if ( position
>= map
[ i
] && position
< map
[ i
+ 1 ] )
323 if ( index
>= 0 && index
< count
)
324 position
= map
[ index
];
325 else if ( index
< 0 )
328 position
= map
[ count
- 1 ];
331 case mlt_whence_relative_end
:
333 position
= map
[ count
- index
- 1 ];
345 /** Get frame method.
347 Special case here: The multitrack must be used in a conjunction with a downstream
348 tractor-type service, ie:
351 Producer2 - multitrack - { filters/transitions } - tractor - consumer
354 The get_frame of a tractor pulls frames from it's connected service on all tracks and
355 will terminate as soon as it receives a test card with a last_track property. The
356 important case here is that the mulitrack does not move to the next frame until all
357 tracks have been pulled.
359 Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
360 that all producers are positioned on the same frame. It uses the 'last track' logic
361 to determine when to move to the next frame.
363 Flaw: if a transition is configured to read from a b-track which happens to trigger
364 the last frame logic (ie: it's configured incorrectly), then things are going to go
367 See playlist logic too.
370 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
)
372 // Get the mutiltrack object
373 mlt_multitrack
this = parent
->child
;
375 // Check if we have a track for this index
376 if ( index
< this->count
&& this->list
[ index
] != NULL
)
378 // Get the producer for this track
379 mlt_producer producer
= this->list
[ index
]->producer
;
381 // Get the track hide property
382 int hide
= mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer
) ), "hide" );
384 // Obtain the current position
385 mlt_position position
= mlt_producer_frame( parent
);
387 // Get the parent properties
388 mlt_properties producer_properties
= MLT_PRODUCER_PROPERTIES( parent
);
391 double speed
= mlt_properties_get_double( producer_properties
, "_speed" );
393 // Make sure we're at the same point
394 mlt_producer_seek( producer
, position
);
396 // Get the frame from the producer
397 mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer
), frame
, 0 );
399 // Indicate speed of this producer
400 mlt_properties properties
= MLT_FRAME_PROPERTIES( *frame
);
401 mlt_properties_set_double( properties
, "_speed", speed
);
402 mlt_properties_set_position( properties
, "_position", position
);
403 mlt_properties_set_int( properties
, "hide", hide
);
407 // Generate a test frame
408 *frame
= mlt_frame_init( );
410 // Update position on the frame we're creating
411 mlt_frame_set_position( *frame
, mlt_producer_position( parent
) );
413 // Move on to the next frame
414 if ( index
>= this->count
)
416 // Let tractor know if we've reached the end
417 mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame
), "last_track", 1 );
419 // Move to the next frame
420 mlt_producer_prepare_next( parent
);
427 /** Close this instance.
430 void mlt_multitrack_close( mlt_multitrack
this )
432 if ( this != NULL
&& mlt_properties_dec_ref( MLT_MULTITRACK_PROPERTIES( this ) ) <= 0 )
435 for ( i
= 0; i
< this->count
; i
++ )
437 if ( this->list
[ i
] != NULL
)
439 mlt_event_close( this->list
[ i
]->event
);
440 mlt_producer_close( this->list
[ i
]->producer
);
441 free( this->list
[ i
] );
445 // Close the producer
446 this->parent
.close
= NULL
;
447 mlt_producer_close( &this->parent
);