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 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.
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.
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.
23 #include "mlt_multitrack.h"
24 #include "mlt_playlist.h"
25 #include "mlt_frame.h"
32 mlt_producer producer
;
36 typedef struct mlt_track_s
*mlt_track
;
38 /** Private definition.
41 struct mlt_multitrack_s
43 // We're extending producer here
44 struct mlt_producer_s parent
;
50 /** Forward reference.
53 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
);
58 mlt_multitrack
mlt_multitrack_init( )
60 // Allocate the multitrack object
61 mlt_multitrack
this = calloc( sizeof( struct mlt_multitrack_s
), 1 );
65 mlt_producer producer
= &this->parent
;
66 if ( mlt_producer_init( producer
, this ) == 0 )
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 mlt_properties_set_int( properties
, "in", 0 );
74 mlt_properties_set_int( properties
, "out", -1 );
75 mlt_properties_set_int( properties
, "length", 0 );
76 producer
->close
= ( mlt_destructor
)mlt_multitrack_close
;
88 /** Get the producer associated to this multitrack.
91 mlt_producer
mlt_multitrack_producer( mlt_multitrack
this )
93 return this != NULL ?
&this->parent
: NULL
;
96 /** Get the service associated this multitrack.
99 mlt_service
mlt_multitrack_service( mlt_multitrack
this )
101 return mlt_producer_service( mlt_multitrack_producer( this ) );
104 /** Get the properties associated this multitrack.
107 mlt_properties
mlt_multitrack_properties( mlt_multitrack
this )
109 return mlt_service_properties( mlt_multitrack_service( this ) );
112 /** Initialise position related information.
115 void mlt_multitrack_refresh( mlt_multitrack
this )
119 // Obtain the properties of this multitrack
120 mlt_properties properties
= mlt_multitrack_properties( this );
122 // We need to ensure that the multitrack reports the longest track as its length
123 mlt_position length
= 0;
125 // We need to ensure that fps are the same on all services
128 // Obtain stats on all connected services
129 for ( i
= 0; i
< this->count
; i
++ )
131 // Get the producer from this index
132 mlt_track track
= this->list
[ i
];
133 mlt_producer producer
= track
->producer
;
135 // If it's allocated then, update our stats
136 if ( producer
!= NULL
)
138 // If we have more than 1 track, we must be in continue mode
139 if ( this->count
> 1 )
140 mlt_properties_set( mlt_producer_properties( producer
), "eof", "continue" );
142 // Determine the longest length
143 //if ( !mlt_properties_get_int( mlt_producer_properties( producer ), "hide" ) )
144 length
= mlt_producer_get_playtime( producer
) > length ?
mlt_producer_get_playtime( producer
) : length
;
149 // This is the first producer, so it controls the fps
150 fps
= mlt_producer_get_fps( producer
);
152 else if ( fps
!= mlt_producer_get_fps( producer
) )
154 // Generate a warning for now - the following attempt to fix may fail
155 fprintf( stderr
, "Warning: fps mismatch on track %d\n", i
);
157 // It should be safe to impose fps on an image producer, but not necessarily safe for video
158 mlt_properties_set_double( mlt_producer_properties( producer
), "fps", fps
);
163 // Update multitrack properties now - we'll not destroy the in point here
164 mlt_events_block( properties
, properties
);
165 mlt_properties_set_position( properties
, "length", length
);
166 mlt_events_unblock( properties
, properties
);
167 mlt_properties_set_position( properties
, "out", length
- 1 );
168 mlt_properties_set_double( properties
, "fps", fps
);
171 /** Listener for producers on the playlist.
174 static void mlt_multitrack_listener( mlt_producer producer
, mlt_multitrack
this )
176 mlt_multitrack_refresh( this );
179 /** Connect a producer to a given track.
181 Note that any producer can be connected here, but see special case treatment
182 of playlist in clip point determination below.
185 int mlt_multitrack_connect( mlt_multitrack
this, mlt_producer producer
, int track
)
187 // Connect to the producer to ourselves at the specified track
188 int result
= mlt_service_connect_producer( mlt_multitrack_service( this ), mlt_producer_service( producer
), track
);
192 // Resize the producer list if need be
193 if ( track
>= this->size
)
196 this->list
= realloc( this->list
, ( track
+ 10 ) * sizeof( mlt_track
) );
197 for ( i
= this->size
; i
< track
+ 10; i
++ )
198 this->list
[ i
] = NULL
;
199 this->size
= track
+ 10;
202 if ( this->list
[ track
] != NULL
)
204 mlt_event_close( this->list
[ track
]->event
);
205 mlt_producer_close( this->list
[ track
]->producer
);
209 this->list
[ track
] = malloc( sizeof( struct mlt_track_s
) );
212 // Assign the track in our list here
213 this->list
[ track
]->producer
= producer
;
214 this->list
[ track
]->event
= mlt_events_listen( mlt_producer_properties( producer
), this,
215 "producer-changed", ( mlt_listener
)mlt_multitrack_listener
);
216 mlt_properties_inc_ref( mlt_producer_properties( producer
) );
217 mlt_event_inc_ref( this->list
[ track
]->event
);
219 // Increment the track count if need be
220 if ( track
>= this->count
)
221 this->count
= track
+ 1;
224 mlt_multitrack_refresh( this );
230 /** Get the number of tracks.
233 int mlt_multitrack_count( mlt_multitrack
this )
238 /** Get an individual track as a producer.
241 mlt_producer
mlt_multitrack_track( mlt_multitrack
this, int track
)
243 mlt_producer producer
= NULL
;
245 if ( this->list
!= NULL
&& track
< this->count
)
246 producer
= this->list
[ track
]->producer
;
251 static int position_compare( const void *p1
, const void *p2
)
253 return *( mlt_position
* )p1
- *( mlt_position
* )p2
;
256 static int add_unique( mlt_position
*array
, int size
, mlt_position position
)
259 for ( i
= 0; i
< size
; i
++ )
260 if ( array
[ i
] == position
)
263 array
[ size
++ ] = position
;
267 /** Determine the clip point.
269 Special case here: a 'producer' has no concept of multiple clips - only the
270 playlist and multitrack producers have clip functionality. Further to that a
271 multitrack determines clip information from any connected tracks that happen
274 Additionally, it must locate clips in the correct order, for example, consider
275 the following track arrangement:
277 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
278 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
280 Note - b clips represent blanks. They are also reported as clip positions.
282 When extracting clip positions from these playlists, we should get a sequence of:
284 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
287 mlt_position
mlt_multitrack_clip( mlt_multitrack
this, mlt_whence whence
, int index
)
289 mlt_position position
= 0;
292 mlt_position
*map
= malloc( 1000 * sizeof( mlt_position
) );
295 for ( i
= 0; i
< this->count
; i
++ )
297 // Get the producer for this track
298 mlt_producer producer
= this->list
[ i
]->producer
;
300 // If it's assigned and not a hidden track
301 if ( producer
!= NULL
)
303 // Get the properties of this producer
304 mlt_properties properties
= mlt_producer_properties( producer
);
306 // Determine if it's a playlist
307 mlt_playlist playlist
= mlt_properties_get_data( properties
, "playlist", NULL
);
309 // Special case consideration of playlists
310 if ( playlist
!= NULL
)
312 for ( j
= 0; j
< mlt_playlist_count( playlist
); j
++ )
313 count
= add_unique( map
, count
, mlt_playlist_clip( playlist
, mlt_whence_relative_start
, j
) );
314 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
318 count
= add_unique( map
, count
, 0 );
319 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
325 qsort( map
, count
, sizeof( mlt_position
), position_compare
);
327 // Now locate the requested index
330 case mlt_whence_relative_start
:
332 position
= map
[ index
];
334 position
= map
[ count
- 1 ];
337 case mlt_whence_relative_current
:
338 position
= mlt_producer_position( mlt_multitrack_producer( this ) );
339 for ( i
= 0; i
< count
- 2; i
++ )
340 if ( position
>= map
[ i
] && position
< map
[ i
+ 1 ] )
343 if ( index
>= 0 && index
< count
)
344 position
= map
[ index
];
345 else if ( index
< 0 )
348 position
= map
[ count
- 1 ];
351 case mlt_whence_relative_end
:
353 position
= map
[ count
- index
- 1 ];
365 /** Get frame method.
367 Special case here: The multitrack must be used in a conjunction with a downstream
368 tractor-type service, ie:
371 Producer2 - multitrack - { filters/transitions } - tractor - consumer
374 The get_frame of a tractor pulls frames from it's connected service on all tracks and
375 will terminate as soon as it receives a test card with a last_track property. The
376 important case here is that the mulitrack does not move to the next frame until all
377 tracks have been pulled.
379 Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
380 that all producers are positioned on the same frame. It uses the 'last track' logic
381 to determine when to move to the next frame.
383 Flaw: if a transition is configured to read from a b-track which happens to trigger
384 the last frame logic (ie: it's configured incorrectly), then things are going to go
387 See playlist logic too.
390 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
)
392 // Get the mutiltrack object
393 mlt_multitrack
this = parent
->child
;
395 // Check if we have a track for this index
396 if ( index
< this->count
&& this->list
[ index
] != NULL
)
398 // Get the producer for this track
399 mlt_producer producer
= this->list
[ index
]->producer
;
401 // Get the track hide property
402 int hide
= mlt_properties_get_int( mlt_producer_properties( mlt_producer_cut_parent( producer
) ), "hide" );
404 // Obtain the current position
405 mlt_position position
= mlt_producer_frame( parent
);
407 // Get the parent properties
408 mlt_properties producer_properties
= mlt_producer_properties( parent
);
411 double speed
= mlt_properties_get_double( producer_properties
, "_speed" );
413 // Make sure we're at the same point
414 mlt_producer_seek( producer
, position
);
416 // Get the frame from the producer
417 mlt_service_get_frame( mlt_producer_service( producer
), frame
, 0 );
419 // Indicate speed of this producer
420 mlt_properties properties
= mlt_frame_properties( *frame
);
421 mlt_properties_set_double( properties
, "_speed", speed
);
422 mlt_properties_set_position( properties
, "_position", position
);
423 mlt_properties_set_int( properties
, "hide", hide
);
427 // Generate a test frame
428 *frame
= mlt_frame_init( );
430 // Update position on the frame we're creating
431 mlt_frame_set_position( *frame
, mlt_producer_position( parent
) );
433 // Move on to the next frame
434 if ( index
>= this->count
)
436 // Let tractor know if we've reached the end
437 mlt_properties_set_int( mlt_frame_properties( *frame
), "last_track", 1 );
439 // Move to the next frame
440 mlt_producer_prepare_next( parent
);
447 /** Close this instance.
450 void mlt_multitrack_close( mlt_multitrack
this )
452 if ( this != NULL
&& mlt_properties_dec_ref( mlt_multitrack_properties( this ) ) <= 0 )
455 for ( i
= 0; i
< this->count
; i
++ )
457 if ( this->list
[ i
] != NULL
)
459 mlt_event_close( this->list
[ i
]->event
);
460 mlt_producer_close( this->list
[ i
]->producer
);
461 free( this->list
[ i
] );
465 // Close the producer
466 this->parent
.close
= NULL
;
467 mlt_producer_close( &this->parent
);