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