]> git.sesse.net Git - mlt/blob - src/framework/mlt_playlist.c
miracle part 1
[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 static int mlt_playlist_virtual_refresh( mlt_playlist this )
109 {
110         int i = 0;
111
112         // Get the fps of the first producer
113         double fps = mlt_properties_get_double( mlt_playlist_properties( this ), "first_fps" );
114         mlt_timecode playtime = 0;
115
116         for ( i = 0; i < this->count; i ++ )
117         {
118                 // Get the producer
119                 mlt_producer producer = this->list[ i ]->producer;
120
121                 // If fps is 0
122                 if ( fps == 0 )
123                 {
124                         // Inherit it from the producer
125                         fps = mlt_producer_get_fps( producer );
126                 }
127                 else if ( fps != mlt_properties_get_double( mlt_producer_properties( producer ), "fps" ) )
128                 {
129                         // Generate a warning for now - the following attempt to fix may fail
130                         fprintf( stderr, "Warning: fps mismatch on playlist producer %d\n", this->count );
131
132                         // It should be safe to impose fps on an image producer, but not necessarily safe for video
133                         mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps );
134                 }
135
136                 // Update the playtime for this clip
137                 playtime += this->list[ i ]->playtime;
138         }
139
140         // Refresh all properties
141         mlt_properties_set_double( mlt_playlist_properties( this ), "first_fps", fps );
142         mlt_properties_set_double( mlt_playlist_properties( this ), "fps", fps );
143         mlt_properties_set_timecode( mlt_playlist_properties( this ), "length", playtime );
144         mlt_properties_set_timecode( mlt_playlist_properties( this ), "out", playtime );
145
146         return 0;
147 }
148
149 /** Append to the virtual playlist.
150 */
151
152 static int mlt_playlist_virtual_append( mlt_playlist this, mlt_producer producer, mlt_timecode in, mlt_timecode out )
153 {
154         // Check that we have room
155         if ( this->count >= this->size )
156         {
157                 int i;
158                 this->list = realloc( this->list, ( this->size + 10 ) * sizeof( playlist_entry * ) );
159                 for ( i = this->size; i < this->size + 10; i ++ )
160                         this->list[ i ] = NULL;
161                 this->size += 10;
162         }
163
164         this->list[ this->count ] = calloc( sizeof( playlist_entry ), 1 );
165         this->list[ this->count ]->producer = producer;
166         this->list[ this->count ]->in = in;
167         this->list[ this->count ]->playtime = out - in;
168
169         this->count ++;
170
171         return mlt_playlist_virtual_refresh( this );
172 }
173
174 /** Seek in the virtual playlist.
175 */
176
177 static mlt_producer mlt_playlist_virtual_seek( mlt_playlist this )
178 {
179         // Default producer to blank
180         mlt_producer producer = &this->blank;
181
182         // Map playlist position to real producer in virtual playlist
183         mlt_timecode position = mlt_producer_position( &this->parent );
184
185         // Loop through the virtual playlist
186         int i = 0;
187
188         for ( i = 0; i < this->count; i ++ )
189         {
190                 if ( position < this->list[ i ]->playtime )
191                 {
192                         // Found it, now break
193                         producer = this->list[ i ]->producer;
194                         position += this->list[ i ]->in;
195                         break;
196                 }
197                 else
198                 {
199                         // Decrement position by length of this entry
200                         position -= this->list[ i ]->playtime;
201                 }
202         }
203
204         // Seek in real producer to relative position
205         mlt_producer_seek( producer, position );
206
207         return producer;
208 }
209
210 static mlt_producer mlt_playlist_virtual_set_out( mlt_playlist this )
211 {
212         // Default producer to blank
213         mlt_producer producer = &this->blank;
214
215         // Map playlist position to real producer in virtual playlist
216         mlt_timecode position = mlt_producer_position( &this->parent );
217
218         // Loop through the virtual playlist
219         int i = 0;
220
221         for ( i = 0; i < this->count; i ++ )
222         {
223                 if ( position < this->list[ i ]->playtime )
224                 {
225                         // Found it, now break
226                         producer = this->list[ i ]->producer;
227                         position += this->list[ i ]->in;
228                         break;
229                 }
230                 else
231                 {
232                         // Decrement position by length of this entry
233                         position -= this->list[ i ]->playtime;
234                 }
235         }
236
237         // Seek in real producer to relative position
238         if ( i < this->count )
239         {
240                 // Update the playtime for the changed clip (hmmm)
241                 this->list[ i ]->playtime = position - this->list[ i ]->in;
242
243                 // Refresh the playlist
244                 mlt_playlist_virtual_refresh( this );
245         }
246
247         return producer;
248 }
249
250 int mlt_playlist_current_clip( mlt_playlist this )
251 {
252         // Map playlist position to real producer in virtual playlist
253         mlt_timecode position = mlt_producer_position( &this->parent );
254
255         // Loop through the virtual playlist
256         int i = 0;
257
258         for ( i = 0; i < this->count; i ++ )
259         {
260                 if ( position < this->list[ i ]->playtime )
261                 {
262                         // Found it, now break
263                         break;
264                 }
265                 else
266                 {
267                         // Decrement position by length of this entry
268                         position -= this->list[ i ]->playtime;
269                 }
270         }
271
272         return i;
273 }
274
275 mlt_producer mlt_playlist_current( mlt_playlist this )
276 {
277         int i = mlt_playlist_current_clip( this );
278         if ( i < this->count )
279                 return this->list[ i ]->producer;
280         else
281                 return &this->blank;
282 }
283
284 /** Get the timecode which corresponds to the start of the next clip.
285 */
286
287 mlt_timecode mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index )
288 {
289         mlt_timecode position = 0;
290         int absolute_clip = index;
291         int i = 0;
292
293         // Determine the absolute clip
294         switch ( whence )
295         {
296                 case mlt_whence_relative_start:
297                         absolute_clip = index;
298                         break;
299
300                 case mlt_whence_relative_current:
301                         absolute_clip = mlt_playlist_current_clip( this ) + index;
302                         break;
303
304                 case mlt_whence_relative_end:
305                         absolute_clip = this->count - index;
306                         break;
307         }
308
309         // Check that we're in a valid range
310         if ( absolute_clip < 0 )
311                 absolute_clip = 0;
312         else if ( absolute_clip > this->count )
313                 absolute_clip = this->count;
314
315         // Now determine the timecode
316         for ( i = 0; i < absolute_clip; i ++ )
317                 position += this->list[ i ]->playtime;
318
319         return position;
320 }
321
322 int mlt_playlist_get_clip_info( mlt_playlist this, mlt_playlist_clip_info *info, int index )
323 {
324         int error = index < 0 || index >= this->count;
325         if ( !error )
326         {
327                 mlt_producer producer = this->list[ index ]->producer;
328                 mlt_properties properties = mlt_producer_properties( producer );
329                 info->resource = mlt_properties_get( properties, "resource" );
330                 info->in = this->list[ index ]->in;
331                 info->out = this->list[ index ]->in + this->list[ index ]->playtime;
332                 info->playtime = this->list[ index ]->playtime;
333                 info->length = mlt_producer_get_length( producer );
334                 info->fps = mlt_producer_get_fps( producer );
335         }
336         return error;
337 }
338
339 /** Get number of clips in the playlist.
340 */
341
342 int mlt_playlist_count( mlt_playlist this )
343 {
344         return this->count;
345 }
346
347 /** Clear the playlist.
348 */
349
350 int mlt_playlist_clear( mlt_playlist this )
351 {
352         this->count = 0;
353         return mlt_playlist_virtual_refresh( this );
354 }
355
356 /** Append a producer to the playlist.
357 */
358
359 int mlt_playlist_append( mlt_playlist this, mlt_producer producer )
360 {
361         // Append to virtual list
362         return mlt_playlist_virtual_append( this, producer, 0, mlt_producer_get_playtime( producer ) );
363 }
364
365 /** Append a producer to the playlist with in/out points.
366 */
367
368 int mlt_playlist_append_io( mlt_playlist this, mlt_producer producer, double in, double out )
369 {
370         // Append to virtual list
371         if ( in != -1 && out != -1 )
372                 return mlt_playlist_virtual_append( this, producer, in, out - in );
373         else
374                 return mlt_playlist_virtual_append( this, producer, 0, mlt_producer_get_playtime( producer ) );
375 }
376
377 /** Append a blank to the playlist of a given length.
378 */
379
380 int mlt_playlist_blank( mlt_playlist this, mlt_timecode length )
381 {
382         // Append to the virtual list
383         return mlt_playlist_virtual_append( this, &this->blank, 0, length );
384 }
385
386 /** Get the current frame.
387 */
388
389 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
390 {
391         // Get this mlt_playlist
392         mlt_playlist this = producer->child;
393
394         // Get the real producer
395         mlt_producer real = mlt_playlist_virtual_seek( this );
396
397         // Get the frame
398         mlt_service_get_frame( mlt_producer_service( real ), frame, index );
399
400         // Check if we're at the end of the clip
401         mlt_properties properties = mlt_frame_properties( *frame );
402         if ( mlt_properties_get_int( properties, "end_of_clip" ) )
403                 mlt_playlist_virtual_set_out( this );
404
405         // Check for notifier and call with appropriate argument
406         mlt_properties playlist_properties = mlt_producer_properties( producer );
407         void ( *notifier )( void * ) = mlt_properties_get_data( playlist_properties, "notifier", NULL );
408         if ( notifier != NULL )
409         {
410                 void *argument = mlt_properties_get_data( playlist_properties, "notifier_arg", NULL );
411                 notifier( argument );
412         }
413
414         // Update timecode on the frame we're creating
415         mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) );
416
417         // Position ourselves on the next frame
418         mlt_producer_prepare_next( producer );
419
420         return 0;
421 }
422
423 /** Close the playlist.
424 */
425
426 void mlt_playlist_close( mlt_playlist this )
427 {
428         mlt_producer_close( &this->parent );
429         mlt_producer_close( &this->blank );
430         free( this );
431 }