incomplete next/prev clip behaviour
[melted] / mlt / src / framework / mlt_playlist.c
1 /*
2 * mlt_playlist.c -- playlist 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_playlist.h"
24 #include "mlt_frame.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 /** Virtual playlist entry.
30 */
31
32 typedef struct
33 {
34 mlt_producer producer;
35 mlt_timecode in;
36 mlt_timecode playtime;
37 }
38 playlist_entry;
39
40 /** Private definition.
41 */
42
43 struct mlt_playlist_s
44 {
45 struct mlt_producer_s parent;
46 struct mlt_producer_s blank;
47
48 int size;
49 int count;
50 playlist_entry **list;
51 };
52
53 /** Forward declarations
54 */
55
56 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
57
58 /** Constructor.
59 */
60
61 mlt_playlist mlt_playlist_init( )
62 {
63 mlt_playlist this = calloc( sizeof( struct mlt_playlist_s ), 1 );
64 if ( this != NULL )
65 {
66 mlt_producer producer = &this->parent;
67
68 // Construct the producer
69 mlt_producer_init( producer, this );
70
71 // Override the producer get_frame
72 producer->get_frame = producer_get_frame;
73
74 // Initialise blank
75 mlt_producer_init( &this->blank, NULL );
76
77 // Indicate that this producer is a playlist
78 mlt_properties_set_data( mlt_playlist_properties( this ), "playlist", this, 0, NULL, NULL );
79 }
80
81 return this;
82 }
83
84 /** Get the producer associated to this playlist.
85 */
86
87 mlt_producer mlt_playlist_producer( mlt_playlist this )
88 {
89 return &this->parent;
90 }
91
92 /** Get the service associated to this playlist.
93 */
94
95 mlt_service mlt_playlist_service( mlt_playlist this )
96 {
97 return mlt_producer_service( &this->parent );
98 }
99
100 /** Get the propertues associated to this playlist.
101 */
102
103 mlt_properties mlt_playlist_properties( mlt_playlist this )
104 {
105 return mlt_producer_properties( &this->parent );
106 }
107
108 /** Append to the virtual playlist.
109 */
110
111 static int mlt_playlist_virtual_append( mlt_playlist this, mlt_producer producer, mlt_timecode in, mlt_timecode out )
112 {
113 // Get the fps of the first producer
114 double fps = mlt_properties_get_double( mlt_playlist_properties( this ), "first_fps" );
115
116 // If fps is 0
117 if ( fps == 0 )
118 {
119 // Inherit it from the producer
120 fps = mlt_producer_get_fps( producer );
121 }
122 else if ( fps != mlt_properties_get_double( mlt_producer_properties( producer ), "fps" ) )
123 {
124 // Generate a warning for now - the following attempt to fix may fail
125 fprintf( stderr, "Warning: fps mismatch on playlist producer %d\n", this->count );
126
127 // It should be safe to impose fps on an image producer, but not necessarily safe for video
128 mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps );
129 }
130
131 // Check that we have room
132 if ( this->count >= this->size )
133 {
134 int i;
135 this->list = realloc( this->list, ( this->size + 10 ) * sizeof( playlist_entry * ) );
136 for ( i = this->size; i < this->size + 10; i ++ )
137 this->list[ i ] = NULL;
138 this->size += 10;
139 }
140
141 this->list[ this->count ] = calloc( sizeof( playlist_entry ), 1 );
142 this->list[ this->count ]->producer = producer;
143 this->list[ this->count ]->in = in;
144 this->list[ this->count ]->playtime = out - in;
145
146 this->count ++;
147
148 mlt_properties_set_double( mlt_playlist_properties( this ), "first_fps", fps );
149 mlt_properties_set_double( mlt_playlist_properties( this ), "fps", fps );
150
151 return 0;
152 }
153
154 /** Seek in the virtual playlist.
155 */
156
157 static mlt_producer mlt_playlist_virtual_seek( mlt_playlist this )
158 {
159 // Default producer to blank
160 mlt_producer producer = &this->blank;
161
162 // Map playlist position to real producer in virtual playlist
163 mlt_timecode position = mlt_producer_position( &this->parent );
164
165 // Loop through the virtual playlist
166 int i = 0;
167
168 for ( i = 0; i < this->count; i ++ )
169 {
170 if ( position < this->list[ i ]->playtime )
171 {
172 // Found it, now break
173 producer = this->list[ i ]->producer;
174 position += this->list[ i ]->in;
175 break;
176 }
177 else
178 {
179 // Decrement position by length of this entry
180 position -= this->list[ i ]->playtime;
181 }
182 }
183
184 // Seek in real producer to relative position
185 mlt_producer_seek( producer, position );
186
187 return producer;
188 }
189
190 static mlt_producer mlt_playlist_virtual_set_out( mlt_playlist this )
191 {
192 // Default producer to blank
193 mlt_producer producer = &this->blank;
194
195 // Map playlist position to real producer in virtual playlist
196 mlt_timecode position = mlt_producer_position( &this->parent );
197
198 // Loop through the virtual playlist
199 int i = 0;
200
201 for ( i = 0; i < this->count; i ++ )
202 {
203 if ( position < this->list[ i ]->playtime )
204 {
205 // Found it, now break
206 producer = this->list[ i ]->producer;
207 position += this->list[ i ]->in;
208 break;
209 }
210 else
211 {
212 // Decrement position by length of this entry
213 position -= this->list[ i ]->playtime;
214 }
215 }
216
217 // Seek in real producer to relative position
218 if ( i < this->count )
219 {
220 fprintf( stderr, "END OF CLIP %d AT %e\n", i, position );
221 this->list[ i ]->playtime = position - this->list[ i ]->in;
222 }
223
224 return producer;
225 }
226
227 static int mlt_playlist_current_clip( mlt_playlist this )
228 {
229 // Map playlist position to real producer in virtual playlist
230 mlt_timecode position = mlt_producer_position( &this->parent );
231
232 // Loop through the virtual playlist
233 int i = 0;
234
235 for ( i = 0; i < this->count; i ++ )
236 {
237 if ( position < this->list[ i ]->playtime )
238 {
239 // Found it, now break
240 break;
241 }
242 else
243 {
244 // Decrement position by length of this entry
245 position -= this->list[ i ]->playtime;
246 }
247 }
248
249 return i;
250 }
251
252 /** Get the timecode which corresponds to the start of the next clip.
253 */
254
255 mlt_timecode mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index )
256 {
257 mlt_timecode position = 0;
258 int absolute_clip = index;
259 int i = 0;
260
261 // Determine the absolute clip
262 switch ( whence )
263 {
264 case mlt_whence_relative_start:
265 absolute_clip = index;
266 break;
267
268 case mlt_whence_relative_current:
269 absolute_clip = mlt_playlist_current_clip( this ) + index;
270 break;
271
272 case mlt_whence_relative_end:
273 absolute_clip = this->count - index;
274 break;
275 }
276
277 // Check that we're in a valid range
278 if ( absolute_clip < 0 )
279 absolute_clip = 0;
280 else if ( absolute_clip > this->count )
281 absolute_clip = this->count;
282
283 // Now determine the timecode
284 for ( i = 0; i < absolute_clip; i ++ )
285 position += this->list[ i ]->playtime;
286
287 return position;
288 }
289
290 /** Append a producer to the playlist.
291 */
292
293 int mlt_playlist_append( mlt_playlist this, mlt_producer producer )
294 {
295 // Append to virtual list
296 return mlt_playlist_virtual_append( this, producer, 0, mlt_producer_get_playtime( producer ) );
297 }
298
299 /** Append a blank to the playlist of a given length.
300 */
301
302 int mlt_playlist_blank( mlt_playlist this, mlt_timecode length )
303 {
304 // Append to the virtual list
305 return mlt_playlist_virtual_append( this, &this->blank, 0, length );
306 }
307
308 /** Get the current frame.
309 */
310
311 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
312 {
313 // Get this mlt_playlist
314 mlt_playlist this = producer->child;
315
316 // Get the real producer
317 mlt_producer real = mlt_playlist_virtual_seek( this );
318
319 // Get the frame
320 mlt_service_get_frame( mlt_producer_service( real ), frame, index );
321
322 // Check if we're at the end of the clip
323 mlt_properties properties = mlt_frame_properties( *frame );
324 if ( mlt_properties_get_int( properties, "end_of_clip" ) )
325 mlt_playlist_virtual_set_out( this );
326
327 // Update timecode on the frame we're creating
328 mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) );
329
330 // Position ourselves on the next frame
331 mlt_producer_prepare_next( producer );
332
333 return 0;
334 }
335
336 /** Close the playlist.
337 */
338
339 void mlt_playlist_close( mlt_playlist this )
340 {
341 mlt_producer_close( &this->parent );
342 mlt_producer_close( &this->blank );
343 free( this );
344 }