2 * \file mlt_multitrack.c
3 * \brief multitrack service class
5 * Copyright (C) 2003-2008 Ushodaya Enterprises Limited
6 * \author Charles Yates <charles.yates@pandora.be>
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.
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.
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
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 // Obtain stats on all connected services
106 for ( i
= 0; i
< this->count
; i
++ )
108 // Get the producer from this index
109 mlt_track track
= this->list
[ i
];
110 mlt_producer producer
= track
->producer
;
112 // If it's allocated then, update our stats
113 if ( producer
!= NULL
)
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" );
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
;
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 );
132 /** Listener for producers on the playlist.
135 static void mlt_multitrack_listener( mlt_producer producer
, mlt_multitrack
this )
137 mlt_multitrack_refresh( this );
140 /** Connect a producer to a given track.
142 Note that any producer can be connected here, but see special case treatment
143 of playlist in clip point determination below.
146 int mlt_multitrack_connect( mlt_multitrack
this, mlt_producer producer
, int track
)
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
);
153 // Resize the producer list if need be
154 if ( track
>= this->size
)
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;
163 if ( this->list
[ track
] != NULL
)
165 mlt_event_close( this->list
[ track
]->event
);
166 mlt_producer_close( this->list
[ track
]->producer
);
170 this->list
[ track
] = malloc( sizeof( struct mlt_track_s
) );
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
);
180 // Increment the track count if need be
181 if ( track
>= this->count
)
182 this->count
= track
+ 1;
185 mlt_multitrack_refresh( this );
191 /** Get the number of tracks.
194 int mlt_multitrack_count( mlt_multitrack
this )
199 /** Get an individual track as a producer.
202 mlt_producer
mlt_multitrack_track( mlt_multitrack
this, int track
)
204 mlt_producer producer
= NULL
;
206 if ( this->list
!= NULL
&& track
< this->count
)
207 producer
= this->list
[ track
]->producer
;
212 static int position_compare( const void *p1
, const void *p2
)
214 return *( mlt_position
* )p1
- *( mlt_position
* )p2
;
217 static int add_unique( mlt_position
*array
, int size
, mlt_position position
)
220 for ( i
= 0; i
< size
; i
++ )
221 if ( array
[ i
] == position
)
224 array
[ size
++ ] = position
;
228 /** Determine the clip point.
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
235 Additionally, it must locate clips in the correct order, for example, consider
236 the following track arrangement:
238 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
239 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
241 Note - b clips represent blanks. They are also reported as clip positions.
243 When extracting clip positions from these playlists, we should get a sequence of:
245 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
248 mlt_position
mlt_multitrack_clip( mlt_multitrack
this, mlt_whence whence
, int index
)
250 mlt_position position
= 0;
253 mlt_position
*map
= malloc( 1000 * sizeof( mlt_position
) );
256 for ( i
= 0; i
< this->count
; i
++ )
258 // Get the producer for this track
259 mlt_producer producer
= this->list
[ i
]->producer
;
261 // If it's assigned and not a hidden track
262 if ( producer
!= NULL
)
264 // Get the properties of this producer
265 mlt_properties properties
= MLT_PRODUCER_PROPERTIES( producer
);
267 // Determine if it's a playlist
268 mlt_playlist playlist
= mlt_properties_get_data( properties
, "playlist", NULL
);
270 // Special case consideration of playlists
271 if ( playlist
!= NULL
)
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 );
279 count
= add_unique( map
, count
, 0 );
280 count
= add_unique( map
, count
, mlt_producer_get_out( producer
) + 1 );
286 qsort( map
, count
, sizeof( mlt_position
), position_compare
);
288 // Now locate the requested index
291 case mlt_whence_relative_start
:
293 position
= map
[ index
];
295 position
= map
[ count
- 1 ];
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 ] )
304 if ( index
>= 0 && index
< count
)
305 position
= map
[ index
];
306 else if ( index
< 0 )
309 position
= map
[ count
- 1 ];
312 case mlt_whence_relative_end
:
314 position
= map
[ count
- index
- 1 ];
326 /** Get frame method.
328 Special case here: The multitrack must be used in a conjunction with a downstream
329 tractor-type service, ie:
332 Producer2 - multitrack - { filters/transitions } - tractor - consumer
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.
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.
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
348 See playlist logic too.
351 static int producer_get_frame( mlt_producer parent
, mlt_frame_ptr frame
, int index
)
353 // Get the mutiltrack object
354 mlt_multitrack
this = parent
->child
;
356 // Check if we have a track for this index
357 if ( index
< this->count
&& this->list
[ index
] != NULL
)
359 // Get the producer for this track
360 mlt_producer producer
= this->list
[ index
]->producer
;
362 // Get the track hide property
363 int hide
= mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer
) ), "hide" );
365 // Obtain the current position
366 mlt_position position
= mlt_producer_frame( parent
);
368 // Get the parent properties
369 mlt_properties producer_properties
= MLT_PRODUCER_PROPERTIES( parent
);
372 double speed
= mlt_properties_get_double( producer_properties
, "_speed" );
374 // Make sure we're at the same point
375 mlt_producer_seek( producer
, position
);
377 // Get the frame from the producer
378 mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer
), frame
, 0 );
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
);
388 // Generate a test frame
389 *frame
= mlt_frame_init( MLT_PRODUCER_SERVICE( parent
) );
391 // Update position on the frame we're creating
392 mlt_frame_set_position( *frame
, mlt_producer_position( parent
) );
394 // Move on to the next frame
395 if ( index
>= this->count
)
397 // Let tractor know if we've reached the end
398 mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame
), "last_track", 1 );
400 // Move to the next frame
401 mlt_producer_prepare_next( parent
);
408 /** Close this instance.
411 void mlt_multitrack_close( mlt_multitrack
this )
413 if ( this != NULL
&& mlt_properties_dec_ref( MLT_MULTITRACK_PROPERTIES( this ) ) <= 0 )
416 for ( i
= 0; i
< this->count
; i
++ )
418 if ( this->list
[ i
] != NULL
)
420 mlt_event_close( this->list
[ i
]->event
);
421 mlt_producer_close( this->list
[ i
]->producer
);
422 free( this->list
[ i
] );
426 // Close the producer
427 this->parent
.close
= NULL
;
428 mlt_producer_close( &this->parent
);