Removal of timecodes, consumer libdv, serialisation of inigo
[melted] / 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 /** Private definition.
31 */
32
33 struct mlt_multitrack_s
34 {
35 // We're extending producer here
36 struct mlt_producer_s parent;
37 mlt_producer *list;
38 int size;
39 int count;
40 };
41
42 /** Forward reference.
43 */
44
45 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
46
47 /** Constructor.
48 */
49
50 mlt_multitrack mlt_multitrack_init( )
51 {
52 // Allocate the multitrack object
53 mlt_multitrack this = calloc( sizeof( struct mlt_multitrack_s ), 1 );
54
55 if ( this != NULL )
56 {
57 mlt_producer producer = &this->parent;
58 if ( mlt_producer_init( producer, this ) == 0 )
59 {
60 mlt_properties properties = mlt_multitrack_properties( this );
61 producer->get_frame = producer_get_frame;
62 mlt_properties_set_data( properties, "multitrack", this, 0, NULL, NULL );
63 mlt_properties_set( properties, "log_id", "multitrack" );
64 }
65 else
66 {
67 free( this );
68 this = NULL;
69 }
70 }
71
72 return this;
73 }
74
75 /** Get the producer associated to this multitrack.
76 */
77
78 mlt_producer mlt_multitrack_producer( mlt_multitrack this )
79 {
80 return &this->parent;
81 }
82
83 /** Get the service associated this multitrack.
84 */
85
86 mlt_service mlt_multitrack_service( mlt_multitrack this )
87 {
88 return mlt_producer_service( mlt_multitrack_producer( this ) );
89 }
90
91 /** Get the properties associated this multitrack.
92 */
93
94 mlt_properties mlt_multitrack_properties( mlt_multitrack this )
95 {
96 return mlt_service_properties( mlt_multitrack_service( this ) );
97 }
98
99 /** Initialise position related information.
100 */
101
102 void mlt_multitrack_refresh( mlt_multitrack this )
103 {
104 int i = 0;
105
106 // Obtain the properties of this multitrack
107 mlt_properties properties = mlt_multitrack_properties( this );
108
109 // We need to ensure that the multitrack reports the longest track as its length
110 mlt_position length = 0;
111
112 // We need to ensure that fps are the same on all services
113 double fps = 0;
114
115 // Obtain stats on all connected services
116 for ( i = 0; i < this->count; i ++ )
117 {
118 // Get the producer from this index
119 mlt_producer producer = this->list[ i ];
120
121 // If it's allocated then, update our stats
122 if ( producer != NULL )
123 {
124 // If we have more than 1 track, we must be in continue mode
125 if ( this->count > 1 )
126 mlt_properties_set( mlt_producer_properties( producer ), "eof", "continue" );
127
128 // Determine the longest length
129 length = mlt_producer_get_playtime( producer ) > length ? mlt_producer_get_playtime( producer ) : length;
130
131 // Handle fps
132 if ( fps == 0 )
133 {
134 // This is the first producer, so it controls the fps
135 fps = mlt_producer_get_fps( producer );
136 }
137 else if ( fps != mlt_producer_get_fps( producer ) )
138 {
139 // Generate a warning for now - the following attempt to fix may fail
140 fprintf( stderr, "Warning: fps mismatch on track %d\n", i );
141
142 // It should be safe to impose fps on an image producer, but not necessarily safe for video
143 mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps );
144 }
145 }
146 }
147
148 // Update multitrack properties now - we'll not destroy the in point here
149 mlt_properties_set_position( properties, "length", length );
150 mlt_properties_set_position( properties, "out", length );
151 mlt_properties_set_double( properties, "fps", fps );
152 }
153
154 /** Connect a producer to a given track.
155
156 Note that any producer can be connected here, but see special case treatment
157 of playlist in clip point determination below.
158 */
159
160 int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track )
161 {
162 // Connect to the producer to ourselves at the specified track
163 int result = mlt_service_connect_producer( mlt_multitrack_service( this ), mlt_producer_service( producer ), track );
164
165 if ( result == 0 )
166 {
167 // Resize the producer list if need be
168 if ( track >= this->size )
169 {
170 int i;
171 this->list = realloc( this->list, ( track + 10 ) * sizeof( mlt_producer ) );
172 for ( i = this->size; i < track + 10; i ++ )
173 this->list[ i ] = NULL;
174 this->size = track + 10;
175 }
176
177 // Assign the track in our list here
178 this->list[ track ] = producer;
179
180 // Increment the track count if need be
181 if ( track >= this->count )
182 this->count = track + 1;
183
184 // Refresh our stats
185 mlt_multitrack_refresh( this );
186 }
187
188 return result;
189 }
190
191 /** Determine the clip point.
192
193 Special case here: a 'producer' has no concept of multiple clips - only the
194 playlist and multitrack producers have clip functionality. Further to that a
195 multitrack determines clip information from any connected tracks that happen
196 to be playlists.
197
198 Additionally, it must locate clips in the correct order, for example, consider
199 the following track arrangement:
200
201 playlist1 |0.0 |b0.0 |0.1 |0.1 |0.2 |
202 playlist2 |b1.0 |1.0 |b1.1 |1.1 |
203
204 Note - b clips represent blanks. They are also reported as clip positions.
205
206 When extracting clip positions from these playlists, we should get a sequence of:
207
208 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
209 */
210
211 mlt_position mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index )
212 {
213 int first = 1;
214 mlt_position position = 0;
215 int i = 0;
216
217 // Loop through each of the tracks
218 for ( i = 0; i < this->count; i ++ )
219 {
220 // Get the producer for this track
221 mlt_producer producer = this->list[ i ];
222
223 // If it's assigned...
224 if ( producer != NULL )
225 {
226 // Get the properties of this producer
227 mlt_properties properties = mlt_producer_properties( producer );
228
229 // Determine if it's a playlist
230 mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
231
232 // We only consider playlists
233 if ( playlist != NULL )
234 {
235 // Locate the smallest position
236 if ( first )
237 {
238 // First position found
239 position = mlt_playlist_clip( playlist, whence, index );
240
241 // We're no longer first
242 first = 0;
243 }
244 else
245 {
246 // Obtain the clip position in this playlist
247 //mlt_position position2 = mlt_playlist_clip( playlist, whence, index );
248
249 // If this position is prior to the first, then use it
250 //if ( position2 < position )
251 //position = position2;
252 }
253 }
254 else
255 {
256 fprintf( stderr, "track %d isn't a playlist\n", index );
257 }
258 }
259 }
260
261 return position;
262 }
263
264 /** Get frame method.
265
266 Special case here: The multitrack must be used in a conjunction with a downstream
267 tractor-type service, ie:
268
269 Producer1 \
270 Producer2 - multitrack - { filters/transitions } - tractor - consumer
271 Producer3 /
272
273 The get_frame of a tractor pulls frames from it's connected service on all tracks and
274 will terminate as soon as it receives a test card with a last_track property. The
275 important case here is that the mulitrack does not move to the next frame until all
276 tracks have been pulled.
277
278 Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
279 that all producers are positioned on the same frame. It uses the 'last track' logic
280 to determine when to move to the next frame.
281
282 Flaw: if a transition is configured to read from a b-track which happens to trigger
283 the last frame logic (ie: it's configured incorrectly), then things are going to go
284 out of sync.
285
286 See playlist logic too.
287 */
288
289 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index )
290 {
291 // Get the mutiltrack object
292 mlt_multitrack this = parent->child;
293
294 // Check if we have a track for this index
295 if ( index < this->count && this->list[ index ] != NULL )
296 {
297 // Get the producer for this track
298 mlt_producer producer = this->list[ index ];
299
300 // Obtain the current position
301 uint64_t position = mlt_producer_frame( parent );
302
303 // Make sure we're at the same point
304 mlt_producer_seek( producer, position );
305
306 // Get the frame from the producer
307 mlt_service_get_frame( mlt_producer_service( producer ), frame, 0 );
308
309 // Indicate speed of this producer
310 mlt_properties producer_properties = mlt_producer_properties( parent );
311 double speed = mlt_properties_get_double( producer_properties, "speed" );
312 mlt_properties properties = mlt_frame_properties( *frame );
313 mlt_properties_set_double( properties, "speed", speed );
314 }
315 else
316 {
317 // Generate a test frame
318 *frame = mlt_frame_init( );
319
320 // Update position on the frame we're creating
321 mlt_frame_set_position( *frame, mlt_producer_position( parent ) );
322
323 // Move on to the next frame
324 if ( index >= this->count )
325 {
326 // Let tractor know if we've reached the end
327 mlt_properties_set_int( mlt_frame_properties( *frame ), "last_track", 1 );
328
329 // Move to the next frame
330 mlt_producer_prepare_next( parent );
331 }
332
333 // Refresh our stats
334 mlt_multitrack_refresh( this );
335 }
336
337 return 0;
338 }
339
340 /** Close this instance.
341 */
342
343 void mlt_multitrack_close( mlt_multitrack this )
344 {
345 // Close the producer
346 mlt_producer_close( &this->parent );
347
348 // Free the list
349 free( this->list );
350
351 // Free the object
352 free( this );
353 }