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>" );
84 /** Get the producer associated to this multitrack.
87 mlt_producer
mlt_multitrack_producer( mlt_multitrack
this )
89 return this != NULL ?
&this->parent
: NULL
;
92 /** Get the service associated this multitrack.
95 mlt_service
mlt_multitrack_service( mlt_multitrack
this )
97 return mlt_producer_service( mlt_multitrack_producer( this ) );
100 /** Get the properties associated this multitrack.
103 mlt_properties
mlt_multitrack_properties( mlt_multitrack
this )
105 return mlt_service_properties( mlt_multitrack_service( this ) );
108 /** Initialise position related information.
111 void mlt_multitrack_refresh( mlt_multitrack
this )
115 // Obtain the properties of this multitrack
116 mlt_properties properties
= mlt_multitrack_properties( this );
118 // We need to ensure that the multitrack reports the longest track as its length
119 mlt_position length
= 0;
121 // We need to ensure that fps are the same on all services
124 // Obtain stats on all connected services
125 for ( i
= 0; i
< this->count
; i
++ )
127 // Get the producer from this index
128 mlt_track track
= this->list
[ i
];
129 mlt_producer producer
= track
->producer
;
131 // If it's allocated then, update our stats
132 if ( producer
!= NULL
)
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" );
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
;
145 // This is the first producer, so it controls the fps
146 fps
= mlt_producer_get_fps( producer
);
148 else if ( fps
!= mlt_producer_get_fps( producer
) )
150 // Generate a warning for now - the following attempt to fix may fail
151 fprintf( stderr
, "Warning: fps mismatch on track %d\n", i
);
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
);
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
);
167 /** Listener for producers on the playlist.
170 static void mlt_multitrack_listener( mlt_producer producer
, mlt_multitrack
this )
172 mlt_multitrack_refresh( this );
175 /** Connect a producer to a given track.
177 Note that any producer can be connected here, but see special case treatment
178 of playlist in clip point determination below.
181 int mlt_multitrack_connect( mlt_multitrack
this, mlt_producer producer
, int track
)
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
);
188 // Resize the producer list if need be
189 if ( track
>= this->size
)
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;
198 if ( this->list
[ track
] != NULL
)
200 mlt_event_close( this->list
[ track
]->event
);
201 mlt_producer_close( this->list
[ track
]->producer
);
205 this->list
[ track
] = malloc( sizeof( struct mlt_track_s
) );
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
);
215 // Increment the track count if need be
216 if ( track
>= this->count
)
217 this->count
= track
+ 1;
220 mlt_multitrack_refresh( this );
226 /** Get the number of tracks.
229 int mlt_multitrack_count( mlt_multitrack
this )
234 /** Get an individual track as a producer.
237 mlt_producer
mlt_multitrack_track( mlt_multitrack
this, int track
)
239 mlt_producer producer
= NULL
;
241 if ( this->list
!= NULL
&& track
< this->count
)
242 producer
= this->list
[ track
]->producer
;
247 static int position_compare( const void *p1
, const void *p2
)
249 return *( mlt_position
* )p1
- *( mlt_position
* )p2
;
252 static int add_unique( mlt_position
*array
, int size
, mlt_position position
)
255 for ( i
= 0; i
< size
; i
++ )
256 if ( array
[ i
] == position
)
259 array
[ size
++ ] = position
;
263 /** Determine the clip point.
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
270 Additionally, it must locate clips in the correct order, for example, consider
271 the following track arrangement:
273 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
274 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
276 Note - b clips represent blanks. They are also reported as clip positions.
278 When extracting clip positions from these playlists, we should get a sequence of:
280 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
283 mlt_position
mlt_multitrack_clip( mlt_multitrack
this, mlt_whence whence
, int index
)
285 mlt_position position
= 0;
288 mlt_position
*map
= malloc( 1000 * sizeof( mlt_position
) );
291 for ( i
= 0; i
< this->count
; i
++ )
293 // Get the producer for this track
294 mlt_producer producer
= this->list
[ i
]->producer
;
296 // If it's assigned and not a hidden track
297 if ( producer
!= NULL
)
299 // Get the properties of this producer
300 mlt_properties properties
= mlt_producer_properties( producer
);
302 // Determine if it's a playlist
303 mlt_playlist playlist
= mlt_properties_get_data( properties
, "playlist", NULL
);
305 // Special case consideration of playlists
306 if ( playlist
!= NULL
)
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 );
314 count
= add_unique( map
, count
, 0 );
315 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
321 qsort( map
, count
, sizeof( mlt_position
), position_compare
);
323 // Now locate the requested index
326 case mlt_whence_relative_start
:
328 position
= map
[ index
];
330 position
= map
[ count
- 1 ];
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 ] )
339 if ( index
>= 0 && index
< count
)
340 position
= map
[ index
];
341 else if ( index
< 0 )
344 position
= map
[ count
- 1 ];
347 case mlt_whence_relative_end
:
349 position
= map
[ count
- index
- 1 ];
361 /** Get frame method.
363 Special case here: The multitrack must be used in a conjunction with a downstream
364 tractor-type service, ie:
367 Producer2 - multitrack - { filters/transitions } - tractor - consumer
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.
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.
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
383 See playlist logic too.
386 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
)
388 // Get the mutiltrack object
389 mlt_multitrack
this = parent
->child
;
391 // Check if we have a track for this index
392 if ( index
< this->count
&& this->list
[ index
] != NULL
)
394 // Get the producer for this track
395 mlt_producer producer
= this->list
[ index
]->producer
;
397 // Get the track hide property
398 int hide
= mlt_properties_get_int( mlt_producer_properties( mlt_producer_cut_parent( producer
) ), "hide" );
400 // Obtain the current position
401 mlt_position position
= mlt_producer_frame( parent
);
403 // Get the parent properties
404 mlt_properties producer_properties
= mlt_producer_properties( parent
);
407 double speed
= mlt_properties_get_double( producer_properties
, "_speed" );
409 // Make sure we're at the same point
410 mlt_producer_seek( producer
, position
);
412 // Get the frame from the producer
413 mlt_service_get_frame( mlt_producer_service( producer
), frame
, 0 );
415 // Indicate speed of this producer
416 mlt_properties properties
= mlt_frame_properties( *frame
);
417 mlt_properties_set_double( properties
, "_speed", speed
);
418 mlt_properties_set_position( properties
, "_position", position
);
419 mlt_properties_set_int( properties
, "hide", hide
);
423 // Generate a test frame
424 *frame
= mlt_frame_init( );
426 // Update position on the frame we're creating
427 mlt_frame_set_position( *frame
, mlt_producer_position( parent
) );
429 // Move on to the next frame
430 if ( index
>= this->count
)
432 // Let tractor know if we've reached the end
433 mlt_properties_set_int( mlt_frame_properties( *frame
), "last_track", 1 );
435 // Move to the next frame
436 mlt_producer_prepare_next( parent
);
443 /** Close this instance.
446 void mlt_multitrack_close( mlt_multitrack
this )
448 if ( this != NULL
&& mlt_properties_dec_ref( mlt_multitrack_properties( this ) ) <= 0 )
451 for ( i
= 0; i
< this->count
; i
++ )
453 if ( this->list
[ i
] != NULL
)
455 mlt_event_close( this->list
[ i
]->event
);
456 mlt_producer_close( this->list
[ i
]->producer
);
457 free( this->list
[ i
] );
461 // Close the producer
462 mlt_producer_close( &this->parent
);