]> git.sesse.net Git - mlt/blob - mlt/src/framework/mlt_playlist.c
7a2ad658a01e5323faf42087d986a3148ecc555b
[mlt] / 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         mlt_timecode playtime = mlt_producer_get_playtime( mlt_playlist_producer( this ) ) + out - in;
117
118         // If fps is 0
119         if ( fps == 0 )
120         {
121                 // Inherit it from the producer
122                 fps = mlt_producer_get_fps( producer );
123         }
124         else if ( fps != mlt_properties_get_double( mlt_producer_properties( producer ), "fps" ) )
125         {
126                 // Generate a warning for now - the following attempt to fix may fail
127                 fprintf( stderr, "Warning: fps mismatch on playlist producer %d\n", this->count );
128
129                 // It should be safe to impose fps on an image producer, but not necessarily safe for video
130                 mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps );
131         }
132
133         // Check that we have room
134         if ( this->count >= this->size )
135         {
136                 int i;
137                 this->list = realloc( this->list, ( this->size + 10 ) * sizeof( playlist_entry * ) );
138                 for ( i = this->size; i < this->size + 10; i ++ )
139                         this->list[ i ] = NULL;
140                 this->size += 10;
141         }
142
143         this->list[ this->count ] = calloc( sizeof( playlist_entry ), 1 );
144         this->list[ this->count ]->producer = producer;
145         this->list[ this->count ]->in = in;
146         this->list[ this->count ]->playtime = out - in;
147
148         this->count ++;
149
150         mlt_properties_set_double( mlt_playlist_properties( this ), "first_fps", fps );
151         mlt_properties_set_double( mlt_playlist_properties( this ), "fps", fps );
152         mlt_properties_set_timecode( mlt_playlist_properties( this ), "length", playtime );
153         mlt_properties_set_timecode( mlt_playlist_properties( this ), "out", playtime );
154
155         return 0;
156 }
157
158 /** Seek in the virtual playlist.
159 */
160
161 static mlt_producer mlt_playlist_virtual_seek( mlt_playlist this )
162 {
163         // Default producer to blank
164         mlt_producer producer = &this->blank;
165
166         // Map playlist position to real producer in virtual playlist
167         mlt_timecode position = mlt_producer_position( &this->parent );
168
169         // Loop through the virtual playlist
170         int i = 0;
171
172         for ( i = 0; i < this->count; i ++ )
173         {
174                 if ( position < this->list[ i ]->playtime )
175                 {
176                         // Found it, now break
177                         producer = this->list[ i ]->producer;
178                         position += this->list[ i ]->in;
179                         break;
180                 }
181                 else
182                 {
183                         // Decrement position by length of this entry
184                         position -= this->list[ i ]->playtime;
185                 }
186         }
187
188         // Seek in real producer to relative position
189         mlt_producer_seek( producer, position );
190
191         return producer;
192 }
193
194 static mlt_producer mlt_playlist_virtual_set_out( mlt_playlist this )
195 {
196         // Default producer to blank
197         mlt_producer producer = &this->blank;
198
199         // Map playlist position to real producer in virtual playlist
200         mlt_timecode position = mlt_producer_position( &this->parent );
201
202         // Loop through the virtual playlist
203         int i = 0;
204
205         for ( i = 0; i < this->count; i ++ )
206         {
207                 if ( position < this->list[ i ]->playtime )
208                 {
209                         // Found it, now break
210                         producer = this->list[ i ]->producer;
211                         position += this->list[ i ]->in;
212                         break;
213                 }
214                 else
215                 {
216                         // Decrement position by length of this entry
217                         position -= this->list[ i ]->playtime;
218                 }
219         }
220
221         // Seek in real producer to relative position
222         if ( i < this->count )
223         {
224                 fprintf( stderr, "END OF CLIP %d AT %e\n", i, position );
225                 this->list[ i ]->playtime = position - this->list[ i ]->in;
226         }
227
228         return producer;
229 }
230
231 static int mlt_playlist_current_clip( mlt_playlist this )
232 {
233         // Map playlist position to real producer in virtual playlist
234         mlt_timecode position = mlt_producer_position( &this->parent );
235
236         // Loop through the virtual playlist
237         int i = 0;
238
239         for ( i = 0; i < this->count; i ++ )
240         {
241                 if ( position < this->list[ i ]->playtime )
242                 {
243                         // Found it, now break
244                         break;
245                 }
246                 else
247                 {
248                         // Decrement position by length of this entry
249                         position -= this->list[ i ]->playtime;
250                 }
251         }
252
253         return i;
254 }
255
256 /** Get the timecode which corresponds to the start of the next clip.
257 */
258
259 mlt_timecode mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index )
260 {
261         mlt_timecode position = 0;
262         int absolute_clip = index;
263         int i = 0;
264
265         // Determine the absolute clip
266         switch ( whence )
267         {
268                 case mlt_whence_relative_start:
269                         absolute_clip = index;
270                         break;
271
272                 case mlt_whence_relative_current:
273                         absolute_clip = mlt_playlist_current_clip( this ) + index;
274                         break;
275
276                 case mlt_whence_relative_end:
277                         absolute_clip = this->count - index;
278                         break;
279         }
280
281         // Check that we're in a valid range
282         if ( absolute_clip < 0 )
283                 absolute_clip = 0;
284         else if ( absolute_clip > this->count )
285                 absolute_clip = this->count;
286
287         // Now determine the timecode
288         for ( i = 0; i < absolute_clip; i ++ )
289                 position += this->list[ i ]->playtime;
290
291         return position;
292 }
293
294 /** Append a producer to the playlist.
295 */
296
297 int mlt_playlist_append( mlt_playlist this, mlt_producer producer )
298 {
299         // Append to virtual list
300         return mlt_playlist_virtual_append( this, producer, 0, mlt_producer_get_playtime( producer ) );
301 }
302
303 /** Append a blank to the playlist of a given length.
304 */
305
306 int mlt_playlist_blank( mlt_playlist this, mlt_timecode length )
307 {
308         // Append to the virtual list
309         return mlt_playlist_virtual_append( this, &this->blank, 0, length );
310 }
311
312 /** Get the current frame.
313 */
314
315 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
316 {
317         // Get this mlt_playlist
318         mlt_playlist this = producer->child;
319
320         // Get the real producer
321         mlt_producer real = mlt_playlist_virtual_seek( this );
322
323         // Get the frame
324         mlt_service_get_frame( mlt_producer_service( real ), frame, index );
325
326         // Check if we're at the end of the clip
327         mlt_properties properties = mlt_frame_properties( *frame );
328         if ( mlt_properties_get_int( properties, "end_of_clip" ) )
329                 mlt_playlist_virtual_set_out( this );
330
331         // Update timecode on the frame we're creating
332         mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) );
333
334         // Position ourselves on the next frame
335         mlt_producer_prepare_next( producer );
336
337         return 0;
338 }
339
340 /** Close the playlist.
341 */
342
343 void mlt_playlist_close( mlt_playlist this )
344 {
345         mlt_producer_close( &this->parent );
346         mlt_producer_close( &this->blank );
347         free( this );
348 }