]> git.sesse.net Git - mlt/blob - src/framework/mlt_playlist.c
remove dead code (coverity-709334)
[mlt] / src / framework / mlt_playlist.c
1 /**
2  * \file mlt_playlist.c
3  * \brief playlist service class
4  * \see mlt_playlist_s
5  *
6  * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "mlt_playlist.h"
25 #include "mlt_tractor.h"
26 #include "mlt_multitrack.h"
27 #include "mlt_field.h"
28 #include "mlt_frame.h"
29 #include "mlt_transition.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 /** \brief Virtual playlist entry used by mlt_playlist_s
36 */
37
38 struct playlist_entry_s
39 {
40         mlt_producer producer;
41         mlt_position frame_in;
42         mlt_position frame_out;
43         mlt_position frame_count;
44         int repeat;
45         mlt_position producer_length;
46         mlt_event event;
47         int preservation_hack;
48 };
49
50 /* Forward declarations
51 */
52
53 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
54 static int mlt_playlist_unmix( mlt_playlist self, int clip );
55 static int mlt_playlist_resize_mix( mlt_playlist self, int clip, int in, int out );
56
57 /** Construct a playlist.
58  *
59  * Sets the resource property to "<playlist>".
60  * Set the mlt_type to property to "mlt_producer".
61  * \public \memberof mlt_playlist_s
62  * \return a new playlist
63  */
64
65 mlt_playlist mlt_playlist_init( )
66 {
67         mlt_playlist self = calloc( sizeof( struct mlt_playlist_s ), 1 );
68         if ( self != NULL )
69         {
70                 mlt_producer producer = &self->parent;
71
72                 // Construct the producer
73                 mlt_producer_init( producer, self );
74
75                 // Override the producer get_frame
76                 producer->get_frame = producer_get_frame;
77
78                 // Define the destructor
79                 producer->close = ( mlt_destructor )mlt_playlist_close;
80                 producer->close_object = self;
81
82                 // Initialise blank
83                 mlt_producer_init( &self->blank, NULL );
84                 mlt_properties_set( MLT_PRODUCER_PROPERTIES( &self->blank ), "mlt_service", "blank" );
85                 mlt_properties_set( MLT_PRODUCER_PROPERTIES( &self->blank ), "resource", "blank" );
86
87                 // Indicate that this producer is a playlist
88                 mlt_properties_set_data( MLT_PLAYLIST_PROPERTIES( self ), "playlist", self, 0, NULL, NULL );
89
90                 // Specify the eof condition
91                 mlt_properties_set( MLT_PLAYLIST_PROPERTIES( self ), "eof", "pause" );
92                 mlt_properties_set( MLT_PLAYLIST_PROPERTIES( self ), "resource", "<playlist>" );
93                 mlt_properties_set( MLT_PLAYLIST_PROPERTIES( self ), "mlt_type", "mlt_producer" );
94                 mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( self ), "in", 0 );
95                 mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( self ), "out", -1 );
96                 mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( self ), "length", 0 );
97
98                 self->size = 10;
99                 self->list = malloc( self->size * sizeof( playlist_entry * ) );
100         }
101
102         return self;
103 }
104
105 /** Construct a playlist with a profile.
106  *
107  * Sets the resource property to "<playlist>".
108  * Set the mlt_type to property to "mlt_producer".
109  * \public \memberof mlt_playlist_s
110  * \param profile the profile to use with the profile
111  * \return a new playlist
112  */
113
114 mlt_playlist mlt_playlist_new( mlt_profile profile )
115 {
116     mlt_playlist self = mlt_playlist_init();
117     if ( self )
118         mlt_properties_set_data( MLT_PLAYLIST_PROPERTIES( self ), "_profile", profile, 0, NULL, NULL );
119     return self;
120 }
121
122 /** Get the producer associated to this playlist.
123  *
124  * \public \memberof mlt_playlist_s
125  * \param self a playlist
126  * \return the producer interface
127  * \see MLT_PLAYLIST_PRODUCER
128  */
129
130 mlt_producer mlt_playlist_producer( mlt_playlist self )
131 {
132         return self != NULL ? &self->parent : NULL;
133 }
134
135 /** Get the service associated to this playlist.
136  *
137  * \public \memberof mlt_playlist_s
138  * \param self a playlist
139  * \return the service interface
140  * \see MLT_PLAYLIST_SERVICE
141  */
142
143 mlt_service mlt_playlist_service( mlt_playlist self )
144 {
145         return MLT_PRODUCER_SERVICE( &self->parent );
146 }
147
148 /** Get the properties associated to this playlist.
149  *
150  * \public \memberof mlt_playlist_s
151  * \param self a playlist
152  * \return the playlist's properties list
153  * \see MLT_PLAYLIST_PROPERTIES
154  */
155
156 mlt_properties mlt_playlist_properties( mlt_playlist self )
157 {
158         return MLT_PRODUCER_PROPERTIES( &self->parent );
159 }
160
161 /** Refresh the playlist after a clip has been changed.
162  *
163  * \private \memberof mlt_playlist_s
164  * \param self a playlist
165  * \return false
166  */
167
168 static int mlt_playlist_virtual_refresh( mlt_playlist self )
169 {
170         // Obtain the properties
171         mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
172         int i = 0;
173         mlt_position frame_count = 0;
174
175         for ( i = 0; i < self->count; i ++ )
176         {
177                 // Get the producer
178                 mlt_producer producer = self->list[ i ]->producer;
179                 if ( producer )
180                 {
181                         int current_length = mlt_producer_get_playtime( producer );
182
183                         // Check if the length of the producer has changed
184                         if ( self->list[ i ]->frame_in != mlt_producer_get_in( producer ) ||
185                                 self->list[ i ]->frame_out != mlt_producer_get_out( producer ) )
186                         {
187                                 // This clip should be removed...
188                                 if ( current_length < 1 )
189                                 {
190                                         self->list[ i ]->frame_in = 0;
191                                         self->list[ i ]->frame_out = -1;
192                                         self->list[ i ]->frame_count = 0;
193                                 }
194                                 else
195                                 {
196                                         self->list[ i ]->frame_in = mlt_producer_get_in( producer );
197                                         self->list[ i ]->frame_out = mlt_producer_get_out( producer );
198                                         self->list[ i ]->frame_count = current_length;
199                                 }
200
201                                 // Update the producer_length
202                                 self->list[ i ]->producer_length = current_length;
203                         }
204                 }
205
206                 // Calculate the frame_count
207                 self->list[ i ]->frame_count = ( self->list[ i ]->frame_out - self->list[ i ]->frame_in + 1 ) * self->list[ i ]->repeat;
208
209                 // Update the frame_count for self clip
210                 frame_count += self->list[ i ]->frame_count;
211         }
212
213         // Refresh all properties
214         mlt_events_block( properties, properties );
215         mlt_properties_set_position( properties, "length", frame_count );
216         mlt_events_unblock( properties, properties );
217         mlt_properties_set_position( properties, "out", frame_count - 1 );
218
219         return 0;
220 }
221
222 /** Listener for producers on the playlist.
223  *
224  * Refreshes the playlist whenever an entry receives producer-changed.
225  * \private \memberof mlt_playlist_s
226  * \param producer a producer
227  * \param self a playlist
228  */
229
230 static void mlt_playlist_listener( mlt_producer producer, mlt_playlist self )
231 {
232         mlt_playlist_virtual_refresh( self );
233 }
234
235 /** Append to the virtual playlist.
236  *
237  * \private \memberof mlt_playlist_s
238  * \param self a playlist
239  * \param source a producer
240  * \param in the producer's starting time
241  * \param out the producer's ending time
242  * \return true if there was an error
243  */
244
245 static int mlt_playlist_virtual_append( mlt_playlist self, mlt_producer source, mlt_position in, mlt_position out )
246 {
247         mlt_producer producer = NULL;
248         mlt_properties properties = NULL;
249         mlt_properties parent = NULL;
250
251         // If we have a cut, then use the in/out points from the cut
252         if ( mlt_producer_is_blank( source )  )
253         {
254                 mlt_position length = out - in + 1;
255
256                 // Make sure the blank is long enough to accomodate the length specified
257                 if ( length > mlt_producer_get_length( &self->blank ) )
258                 {
259                         mlt_properties blank_props = MLT_PRODUCER_PROPERTIES( &self->blank );
260                         mlt_events_block( blank_props, blank_props );
261                         mlt_producer_set_in_and_out( &self->blank, in, out );
262                         mlt_events_unblock( blank_props, blank_props );
263                 }
264
265                 // Now make sure the cut comes from this self->blank
266                 if ( source == NULL )
267                 {
268                         producer = mlt_producer_cut( &self->blank, in, out );
269                 }
270                 else if ( !mlt_producer_is_cut( source ) || mlt_producer_cut_parent( source ) != &self->blank )
271                 {
272                         producer = mlt_producer_cut( &self->blank, in, out );
273                 }
274                 else
275                 {
276                         producer = source;
277                         mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
278                 }
279
280                 properties = MLT_PRODUCER_PROPERTIES( producer );
281
282                 // Make sure this cut of blank is long enough
283                 if ( length > mlt_producer_get_length( producer ) )
284                         mlt_properties_set_int( properties, "length", length );
285         }
286         else if ( mlt_producer_is_cut( source ) )
287         {
288                 producer = source;
289                 if ( in < 0 )
290                         in = mlt_producer_get_in( producer );
291                 if ( out < 0 || out > mlt_producer_get_out( producer ) )
292                         out = mlt_producer_get_out( producer );
293                 properties = MLT_PRODUCER_PROPERTIES( producer );
294                 mlt_properties_inc_ref( properties );
295         }
296         else
297         {
298                 producer = mlt_producer_cut( source, in, out );
299                 if ( in < 0 || in < mlt_producer_get_in( producer ) )
300                         in = mlt_producer_get_in( producer );
301                 if ( out < 0 || out > mlt_producer_get_out( producer ) )
302                         out = mlt_producer_get_out( producer );
303                 properties = MLT_PRODUCER_PROPERTIES( producer );
304         }
305
306         // Fetch the cuts parent properties
307         parent = MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer ) );
308
309         // Remove loader normalisers for fx cuts
310         if ( mlt_properties_get_int( parent, "meta.fx_cut" ) )
311         {
312                 mlt_service service = MLT_PRODUCER_SERVICE( mlt_producer_cut_parent( producer ) );
313                 mlt_filter filter = mlt_service_filter( service, 0 );
314                 while ( filter != NULL && mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "_loader" ) )
315                 {
316                         mlt_service_detach( service, filter );
317                         filter = mlt_service_filter( service, 0 );
318                 }
319                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "meta.fx_cut", 1 );
320         }
321
322         // Check that we have room
323         if ( self->count >= self->size )
324         {
325                 int i;
326                 self->list = realloc( self->list, ( self->size + 10 ) * sizeof( playlist_entry * ) );
327                 for ( i = self->size; i < self->size + 10; i ++ ) self->list[ i ] = NULL;
328                 self->size += 10;
329         }
330
331         // Create the entry
332         self->list[ self->count ] = calloc( sizeof( playlist_entry ), 1 );
333         if ( self->list[ self->count ] != NULL )
334         {
335                 self->list[ self->count ]->producer = producer;
336                 self->list[ self->count ]->frame_in = in;
337                 self->list[ self->count ]->frame_out = out;
338                 self->list[ self->count ]->frame_count = out - in + 1;
339                 self->list[ self->count ]->repeat = 1;
340                 self->list[ self->count ]->producer_length = mlt_producer_get_playtime( producer );
341                 self->list[ self->count ]->event = mlt_events_listen( parent, self, "producer-changed", ( mlt_listener )mlt_playlist_listener );
342                 mlt_event_inc_ref( self->list[ self->count ]->event );
343                 mlt_properties_set( properties, "eof", "pause" );
344                 mlt_producer_set_speed( producer, 0 );
345                 self->count ++;
346         }
347
348         return mlt_playlist_virtual_refresh( self );
349 }
350
351 /** Locate a producer by index.
352  *
353  * \private \memberof mlt_playlist_s
354  * \param self a playlist
355  * \param[in, out] position the time at which to locate the producer, returns the time relative to the producer's starting point
356  * \param[out] clip the index of the playlist entry
357  * \param[out] total the duration of the playlist up to and including this producer
358  * \return a producer or NULL if not found
359  */
360
361 static mlt_producer mlt_playlist_locate( mlt_playlist self, mlt_position *position, int *clip, int *total )
362 {
363         // Default producer to NULL
364         mlt_producer producer = NULL;
365
366         // Loop for each producer until found
367         for ( *clip = 0; *clip < self->count; *clip += 1 )
368         {
369                 // Increment the total
370                 *total += self->list[ *clip ]->frame_count;
371
372                 // Check if the position indicates that we have found the clip
373                 // Note that 0 length clips get skipped automatically
374                 if ( *position < self->list[ *clip ]->frame_count )
375                 {
376                         // Found it, now break
377                         producer = self->list[ *clip ]->producer;
378                         break;
379                 }
380                 else
381                 {
382                         // Decrement position by length of self entry
383                         *position -= self->list[ *clip ]->frame_count;
384                 }
385         }
386
387         return producer;
388 }
389
390 /** Seek in the virtual playlist.
391  *
392  * This gets the producer at the current position and seeks on the producer
393  * while doing repeat and end-of-file handling. This is also responsible for
394  * closing producers previous to the preceding playlist if the autoclose
395  * property is set.
396  * \private \memberof mlt_playlist_s
397  * \param self a playlist
398  * \param[out] progressive true if the producer should be displayed progressively
399  * \return the service interface of the producer at the play head
400  * \see producer_get_frame
401  */
402
403 static mlt_service mlt_playlist_virtual_seek( mlt_playlist self, int *progressive )
404 {
405         // Map playlist position to real producer in virtual playlist
406         mlt_position position = mlt_producer_frame( &self->parent );
407
408         // Keep the original position since we change it while iterating through the list
409         mlt_position original = position;
410
411         // Clip index and total
412         int i = 0;
413         int total = 0;
414
415         // Locate the producer for the position
416         mlt_producer producer = mlt_playlist_locate( self, &position, &i, &total );
417
418         // Get the properties
419         mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
420
421         // Automatically close previous producers if requested
422         if ( i > 1 // keep immediate previous in case app wants to get info about what just finished
423                 && position < 2 // tolerate off-by-one error on going to next clip
424                 && mlt_properties_get_int( properties, "autoclose" ) )
425         {
426                 int j;
427                 // They might have jumped ahead!
428                 for ( j = 0; j < i - 1; j++ )
429                 {
430                         mlt_service_lock( MLT_PRODUCER_SERVICE( self->list[ j ]->producer ) );
431                         mlt_producer p = self->list[ j ]->producer;
432                         if ( p )
433                         {
434                                 self->list[ j ]->producer = NULL;
435                                 mlt_service_unlock( MLT_PRODUCER_SERVICE( p ) );
436                                 mlt_producer_close( p );
437                         }
438                         // If p is null, the lock will not have been "taken"
439                 }
440         }
441
442         // Get the eof handling
443         char *eof = mlt_properties_get( properties, "eof" );
444
445         // Seek in real producer to relative position
446         if ( producer != NULL )
447         {
448                 int count = self->list[ i ]->frame_count / self->list[ i ]->repeat;
449                 *progressive = count == 1;
450                 mlt_producer_seek( producer, (int)position % count );
451         }
452         else if ( !strcmp( eof, "pause" ) && total > 0 )
453         {
454                 playlist_entry *entry = self->list[ self->count - 1 ];
455                 int count = entry->frame_count / entry->repeat;
456                 mlt_producer self_producer = MLT_PLAYLIST_PRODUCER( self );
457                 mlt_producer_seek( self_producer, original - 1 );
458                 producer = entry->producer;
459                 mlt_producer_seek( producer, (int)entry->frame_out % count );
460                 mlt_producer_set_speed( self_producer, 0 );
461                 mlt_producer_set_speed( producer, 0 );
462                 *progressive = count == 1;
463         }
464         else if ( !strcmp( eof, "loop" ) && total > 0 )
465         {
466                 playlist_entry *entry = self->list[ 0 ];
467                 mlt_producer self_producer = MLT_PLAYLIST_PRODUCER( self );
468                 mlt_producer_seek( self_producer, 0 );
469                 producer = entry->producer;
470                 mlt_producer_seek( producer, 0 );
471         }
472         else
473         {
474                 producer = &self->blank;
475         }
476
477         return MLT_PRODUCER_SERVICE( producer );
478 }
479
480 /** Invoked when a producer indicates that it has prematurely reached its end.
481  *
482  * \private \memberof mlt_playlist_s
483  * \param self a playlist
484  * \return a producer
485  * \see producer_get_frame
486  */
487
488 static mlt_producer mlt_playlist_virtual_set_out( mlt_playlist self )
489 {
490         // Default producer to blank
491         mlt_producer producer = &self->blank;
492
493         // Map playlist position to real producer in virtual playlist
494         mlt_position position = mlt_producer_frame( &self->parent );
495
496         // Loop through the virtual playlist
497         int i = 0;
498
499         for ( i = 0; i < self->count; i ++ )
500         {
501                 if ( position < self->list[ i ]->frame_count )
502                 {
503                         // Found it, now break
504                         producer = self->list[ i ]->producer;
505                         break;
506                 }
507                 else
508                 {
509                         // Decrement position by length of this entry
510                         position -= self->list[ i ]->frame_count;
511                 }
512         }
513
514         // Seek in real producer to relative position
515         if ( i < self->count && self->list[ i ]->frame_out != position )
516         {
517                 // Update the frame_count for the changed clip (hmmm)
518                 self->list[ i ]->frame_out = position;
519                 self->list[ i ]->frame_count = self->list[ i ]->frame_out - self->list[ i ]->frame_in + 1;
520
521                 // Refresh the playlist
522                 mlt_playlist_virtual_refresh( self );
523         }
524
525         return producer;
526 }
527
528 /** Obtain the current clips index.
529  *
530  * \public \memberof mlt_playlist_s
531  * \param self a playlist
532  * \return the index of the playlist entry at the current position
533  */
534
535 int mlt_playlist_current_clip( mlt_playlist self )
536 {
537         // Map playlist position to real producer in virtual playlist
538         mlt_position position = mlt_producer_frame( &self->parent );
539
540         // Loop through the virtual playlist
541         int i = 0;
542
543         for ( i = 0; i < self->count; i ++ )
544         {
545                 if ( position < self->list[ i ]->frame_count )
546                 {
547                         // Found it, now break
548                         break;
549                 }
550                 else
551                 {
552                         // Decrement position by length of this entry
553                         position -= self->list[ i ]->frame_count;
554                 }
555         }
556
557         return i;
558 }
559
560 /** Obtain the current clips producer.
561  *
562  * \public \memberof mlt_playlist_s
563  * \param self a playlist
564  * \return the producer at the current position
565  */
566
567 mlt_producer mlt_playlist_current( mlt_playlist self )
568 {
569         int i = mlt_playlist_current_clip( self );
570         if ( i < self->count )
571                 return self->list[ i ]->producer;
572         else
573                 return &self->blank;
574 }
575
576 /** Get the position which corresponds to the start of the next clip.
577  *
578  * \public \memberof mlt_playlist_s
579  * \param self a playlist
580  * \param whence the location from which to make the index relative:
581  * start of playlist, end of playlist, or current position
582  * \param index the playlist entry index relative to whence
583  * \return the time at which the referenced clip starts
584  */
585
586 mlt_position mlt_playlist_clip( mlt_playlist self, mlt_whence whence, int index )
587 {
588         mlt_position position = 0;
589         int absolute_clip = index;
590         int i = 0;
591
592         // Determine the absolute clip
593         switch ( whence )
594         {
595                 case mlt_whence_relative_start:
596                         absolute_clip = index;
597                         break;
598
599                 case mlt_whence_relative_current:
600                         absolute_clip = mlt_playlist_current_clip( self ) + index;
601                         break;
602
603                 case mlt_whence_relative_end:
604                         absolute_clip = self->count - index;
605                         break;
606         }
607
608         // Check that we're in a valid range
609         if ( absolute_clip < 0 )
610                 absolute_clip = 0;
611         else if ( absolute_clip > self->count )
612                 absolute_clip = self->count;
613
614         // Now determine the position
615         for ( i = 0; i < absolute_clip; i ++ )
616                 position += self->list[ i ]->frame_count;
617
618         return position;
619 }
620
621 /** Get all the info about the clip specified.
622  *
623  * \public \memberof mlt_playlist_s
624  * \param self a playlist
625  * \param info a clip info struct
626  * \param index a playlist entry index
627  * \return true if there was an error
628  */
629
630 int mlt_playlist_get_clip_info( mlt_playlist self, mlt_playlist_clip_info *info, int index )
631 {
632         int error = index < 0 || index >= self->count || self->list[ index ]->producer == NULL;
633         memset( info, 0, sizeof( mlt_playlist_clip_info ) );
634         if ( !error )
635         {
636                 mlt_producer producer = mlt_producer_cut_parent( self->list[ index ]->producer );
637                 mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
638                 info->clip = index;
639                 info->producer = producer;
640                 info->cut = self->list[ index ]->producer;
641                 info->start = mlt_playlist_clip( self, mlt_whence_relative_start, index );
642                 info->resource = mlt_properties_get( properties, "resource" );
643                 info->frame_in = self->list[ index ]->frame_in;
644                 info->frame_out = self->list[ index ]->frame_out;
645                 info->frame_count = self->list[ index ]->frame_count;
646                 info->repeat = self->list[ index ]->repeat;
647                 info->length = mlt_producer_get_length( producer );
648                 info->fps = mlt_producer_get_fps( producer );
649         }
650
651         return error;
652 }
653
654 /** Get number of clips in the playlist.
655  *
656  * \public \memberof mlt_playlist_s
657  * \param self a playlist
658  * \return the number of playlist entries
659  */
660
661 int mlt_playlist_count( mlt_playlist self )
662 {
663         return self->count;
664 }
665
666 /** Clear the playlist.
667  *
668  * \public \memberof mlt_playlist_s
669  * \param self a playlist
670  * \return true if there was an error
671  */
672
673 int mlt_playlist_clear( mlt_playlist self )
674 {
675         int i;
676         for ( i = 0; i < self->count; i ++ )
677         {
678                 mlt_event_close( self->list[ i ]->event );
679                 mlt_producer_close( self->list[ i ]->producer );
680         }
681         self->count = 0;
682         return mlt_playlist_virtual_refresh( self );
683 }
684
685 /** Append a producer to the playlist.
686  *
687  * \public \memberof mlt_playlist_s
688  * \param self a playlist
689  * \param producer the producer to append
690  * \return true if there was an error
691  */
692
693 int mlt_playlist_append( mlt_playlist self, mlt_producer producer )
694 {
695         // Append to virtual list
696         return mlt_playlist_virtual_append( self, producer, 0, mlt_producer_get_playtime( producer ) - 1 );
697 }
698
699 /** Append a producer to the playlist with in/out points.
700  *
701  * \public \memberof mlt_playlist_s
702  * \param self a playlist
703  * \param producer the producer to append
704  * \param in the starting point on the producer; a negative value is the same as 0
705  * \param out the ending point on the producer; a negative value is the same as producer length - 1
706  * \return true if there was an error
707  */
708
709 int mlt_playlist_append_io( mlt_playlist self, mlt_producer producer, mlt_position in, mlt_position out )
710 {
711         // Append to virtual list
712         if ( in < 0 && out < 0 )
713                 return mlt_playlist_append( self, producer );
714         else
715                 return mlt_playlist_virtual_append( self, producer, in, out );
716 }
717
718 /** Append a blank to the playlist of a given length.
719  *
720  * \public \memberof mlt_playlist_s
721  * \param self a playlist
722  * \param out the ending time of the blank entry, not its duration
723  * \return true if there was an error
724  */
725
726 int mlt_playlist_blank( mlt_playlist self, mlt_position out )
727 {
728         // Append to the virtual list
729         if ( out >= 0 )
730                 return mlt_playlist_virtual_append( self, &self->blank, 0, out );
731         else
732                 return 1;
733 }
734
735 /** Append a blank item to the playlist with duration as a time string.
736  *
737  * \public \memberof mlt_playlist_s
738  * \param self a playlist
739  * \param length the duration of the blank entry as a time string
740  * \return true if there was an error
741  */
742
743 int mlt_playlist_blank_time( mlt_playlist self, const char* length )
744 {
745         if ( self && length )
746         {
747                 mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
748                 mlt_properties_set( properties , "_blank_time", length );
749                 mlt_position duration = mlt_properties_get_position( properties, "_blank_time" );
750                 return mlt_playlist_blank( self, duration - 1 );
751         }
752         else
753                 return 1;
754 }
755
756 /** Insert a producer into the playlist.
757  *
758  * \public \memberof mlt_playlist_s
759  * \param self a playlist
760  * \param producer the producer to insert
761  * \param where the producer's playlist entry index
762  * \param in the starting point on the producer
763  * \param out the ending point on the producer
764  * \return true if there was an error
765  */
766
767 int mlt_playlist_insert( mlt_playlist self, mlt_producer producer, int where, mlt_position in, mlt_position out )
768 {
769         // Append to end
770         mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self );
771         mlt_playlist_append_io( self, producer, in, out );
772
773         // Move to the position specified
774         mlt_playlist_move( self, self->count - 1, where );
775         mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self );
776
777         return mlt_playlist_virtual_refresh( self );
778 }
779
780 /** Remove an entry in the playlist.
781  *
782  * \public \memberof mlt_playlist_s
783  * \param self a playlist
784  * \param where the playlist entry index
785  * \return true if there was an error
786  */
787
788 int mlt_playlist_remove( mlt_playlist self, int where )
789 {
790         int error = where < 0 || where >= self->count;
791         if ( error == 0 && mlt_playlist_unmix( self, where ) != 0 )
792         {
793                 // We need to know the current clip and the position within the playlist
794                 int current = mlt_playlist_current_clip( self );
795                 mlt_position position = mlt_producer_position( MLT_PLAYLIST_PRODUCER( self ) );
796
797                 // We need all the details about the clip we're removing
798                 mlt_playlist_clip_info where_info;
799                 playlist_entry *entry = self->list[ where ];
800                 mlt_properties properties = MLT_PRODUCER_PROPERTIES( entry->producer );
801
802                 // Loop variable
803                 int i = 0;
804
805                 // Get the clip info
806                 mlt_playlist_get_clip_info( self, &where_info, where );
807
808                 // Reorganise the list
809                 for ( i = where + 1; i < self->count; i ++ )
810                         self->list[ i - 1 ] = self->list[ i ];
811                 self->count --;
812
813                 if ( entry->preservation_hack == 0 )
814                 {
815                         // Decouple from mix_in/out if necessary
816                         if ( mlt_properties_get_data( properties, "mix_in", NULL ) != NULL )
817                         {
818                                 mlt_properties mix = mlt_properties_get_data( properties, "mix_in", NULL );
819                                 mlt_properties_set_data( mix, "mix_out", NULL, 0, NULL, NULL );
820                         }
821                         if ( mlt_properties_get_data( properties, "mix_out", NULL ) != NULL )
822                         {
823                                 mlt_properties mix = mlt_properties_get_data( properties, "mix_out", NULL );
824                                 mlt_properties_set_data( mix, "mix_in", NULL, 0, NULL, NULL );
825                         }
826
827                         if ( mlt_properties_ref_count( MLT_PRODUCER_PROPERTIES( entry->producer ) ) == 1 )
828                                 mlt_producer_clear( entry->producer );
829                 }
830
831                 // Close the producer associated to the clip info
832                 mlt_event_close( entry->event );
833                 mlt_producer_close( entry->producer );
834
835                 // Correct position
836                 if ( where == current )
837                         mlt_producer_seek( MLT_PLAYLIST_PRODUCER( self ), where_info.start );
838                 else if ( where < current && self->count > 0 )
839                         mlt_producer_seek( MLT_PLAYLIST_PRODUCER( self ), position - where_info.frame_count );
840                 else if ( self->count == 0 )
841                         mlt_producer_seek( MLT_PLAYLIST_PRODUCER( self ), 0 );
842
843                 // Free the entry
844                 free( entry );
845
846                 // Refresh the playlist
847                 mlt_playlist_virtual_refresh( self );
848         }
849
850         return error;
851 }
852
853 /** Move an entry in the playlist.
854  *
855  * \public \memberof mlt_playlist_s
856  * \param self a playlist
857  * \param src an entry index
858  * \param dest an entry index
859  * \return false
860  */
861
862 int mlt_playlist_move( mlt_playlist self, int src, int dest )
863 {
864         int i;
865
866         /* We need to ensure that the requested indexes are valid and correct it as necessary */
867         if ( src < 0 )
868                 src = 0;
869         if ( src >= self->count )
870                 src = self->count - 1;
871
872         if ( dest < 0 )
873                 dest = 0;
874         if ( dest >= self->count )
875                 dest = self->count - 1;
876
877         if ( src != dest && self->count > 1 )
878         {
879                 int current = mlt_playlist_current_clip( self );
880                 mlt_position position = mlt_producer_position( MLT_PLAYLIST_PRODUCER( self ) );
881                 playlist_entry *src_entry = NULL;
882
883                 // We need all the details about the current clip
884                 mlt_playlist_clip_info current_info;
885
886                 mlt_playlist_get_clip_info( self, &current_info, current );
887                 position -= current_info.start;
888
889                 if ( current == src )
890                         current = dest;
891                 else if ( current > src && current < dest )
892                         current ++;
893                 else if ( current == dest )
894                         current = src;
895
896                 src_entry = self->list[ src ];
897                 if ( src > dest )
898                 {
899                         for ( i = src; i > dest; i -- )
900                                 self->list[ i ] = self->list[ i - 1 ];
901                 }
902                 else
903                 {
904                         for ( i = src; i < dest; i ++ )
905                                 self->list[ i ] = self->list[ i + 1 ];
906                 }
907                 self->list[ dest ] = src_entry;
908
909                 mlt_playlist_get_clip_info( self, &current_info, current );
910                 mlt_producer_seek( MLT_PLAYLIST_PRODUCER( self ), current_info.start + position );
911                 mlt_playlist_virtual_refresh( self );
912         }
913
914         return 0;
915 }
916
917 /** Repeat the specified clip n times.
918  *
919  * \public \memberof mlt_playlist_s
920  * \param self a playlist
921  * \param clip a playlist entry index
922  * \param repeat the number of times to repeat the clip
923  * \return true if there was an error
924  */
925
926 int mlt_playlist_repeat_clip( mlt_playlist self, int clip, int repeat )
927 {
928         int error = repeat < 1 || clip < 0 || clip >= self->count;
929         if ( error == 0 )
930         {
931                 playlist_entry *entry = self->list[ clip ];
932                 entry->repeat = repeat;
933                 mlt_playlist_virtual_refresh( self );
934         }
935         return error;
936 }
937
938 /** Resize the specified clip.
939  *
940  * \public \memberof mlt_playlist_s
941  * \param self a playlist
942  * \param clip the index of the playlist entry
943  * \param in the new starting time on the clip's producer;  a negative value is the same as 0
944  * \param out the new ending time on the clip's producer;  a negative value is the same as length - 1
945  * \return true if there was an error
946  */
947
948 int mlt_playlist_resize_clip( mlt_playlist self, int clip, mlt_position in, mlt_position out )
949 {
950         int error = clip < 0 || clip >= self->count;
951         if ( error == 0 && mlt_playlist_resize_mix( self, clip, in, out ) != 0 )
952         {
953                 playlist_entry *entry = self->list[ clip ];
954                 mlt_producer producer = entry->producer;
955                 mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
956
957                 mlt_events_block( properties, properties );
958
959                 if ( mlt_producer_is_blank( producer ) )
960                 {
961                         mlt_position length = out - in + 1;
962
963                         // Make sure the parent blank is long enough to accomodate the length specified
964                         if ( length > mlt_producer_get_length( &self->blank ) )
965                         {
966                                 mlt_properties blank_props = MLT_PRODUCER_PROPERTIES( &self->blank );
967                                 mlt_properties_set_int( blank_props, "length", length );
968                                 mlt_producer_set_in_and_out( &self->blank, 0, out - in );
969                         }
970                         // Make sure this cut of blank is long enough
971                         if ( length > mlt_producer_get_length( producer ) )
972                                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "length", length );
973                 }
974
975                 if ( in < 0 )
976                         in = 0;
977                 if ( out < 0 || out >= mlt_producer_get_length( producer ) )
978                         out = mlt_producer_get_length( producer ) - 1;
979
980                 if ( out < in )
981                 {
982                         mlt_position t = in;
983                         in = out;
984                         out = t;
985                 }
986
987                 mlt_producer_set_in_and_out( producer, in, out );
988                 mlt_events_unblock( properties, properties );
989                 mlt_playlist_virtual_refresh( self );
990         }
991         return error;
992 }
993
994 /** Split a clip on the playlist at the given position.
995  *
996  * This splits after the specified frame.
997  * \public \memberof mlt_playlist_s
998  * \param self a playlist
999  * \param clip the index of the playlist entry
1000  * \param position the time at which to split relative to the beginning of the clip or its end if negative
1001  * \return true if there was an error
1002  */
1003
1004 int mlt_playlist_split( mlt_playlist self, int clip, mlt_position position )
1005 {
1006         int error = clip < 0 || clip >= self->count;
1007         if ( error == 0 )
1008         {
1009                 playlist_entry *entry = self->list[ clip ];
1010                 position = position < 0 ? entry->frame_count + position - 1 : position;
1011                 if ( position >= 0 && position < entry->frame_count - 1 )
1012                 {
1013                         int in = entry->frame_in;
1014                         int out = entry->frame_out;
1015                         mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self );
1016                         mlt_playlist_resize_clip( self, clip, in, in + position );
1017                         if ( !mlt_producer_is_blank( entry->producer ) )
1018                         {
1019                                 int i = 0;
1020                                 mlt_properties entry_properties = MLT_PRODUCER_PROPERTIES( entry->producer );
1021                                 mlt_producer split = mlt_producer_cut( entry->producer, in + position + 1, out );
1022                                 mlt_properties split_properties = MLT_PRODUCER_PROPERTIES( split );
1023                                 mlt_playlist_insert( self, split, clip + 1, 0, -1 );
1024                                 mlt_properties_lock( entry_properties );
1025                                 for ( i = 0; i < mlt_properties_count( entry_properties ); i ++ )
1026                                 {
1027                                         char *name = mlt_properties_get_name( entry_properties, i );
1028                                         if ( name != NULL && !strncmp( name, "meta.", 5 ) )
1029                                                 mlt_properties_set( split_properties, name, mlt_properties_get_value( entry_properties, i ) );
1030                                 }
1031                                 mlt_properties_unlock( entry_properties );
1032                                 mlt_producer_close( split );
1033                         }
1034                         else
1035                         {
1036                                 mlt_playlist_insert( self, &self->blank, clip + 1, 0, out - position - 1 );
1037                         }
1038                         mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self );
1039                         mlt_playlist_virtual_refresh( self );
1040                 }
1041                 else
1042                 {
1043                         error = 1;
1044                 }
1045         }
1046         return error;
1047 }
1048
1049 /** Split the playlist at the absolute position.
1050  *
1051  * \public \memberof mlt_playlist_s
1052  * \param self a playlist
1053  * \param position the time at which to split relative to the beginning of the clip
1054  * \param left true to split before the frame starting at position
1055  * \return true if there was an error
1056  */
1057
1058 int mlt_playlist_split_at( mlt_playlist self, mlt_position position, int left )
1059 {
1060         int result = self == NULL ? -1 : 0;
1061         if ( !result )
1062         {
1063                 if ( position >= 0 && position < mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( self ) ) )
1064                 {
1065                         int clip = mlt_playlist_get_clip_index_at( self, position );
1066                         mlt_playlist_clip_info info;
1067                         mlt_playlist_get_clip_info( self, &info, clip );
1068                         if ( left && position != info.start )
1069                                 mlt_playlist_split( self, clip, position - info.start - 1 );
1070                         else if ( !left )
1071                                 mlt_playlist_split( self, clip, position - info.start );
1072                         result = position;
1073                 }
1074                 else if ( position <= 0 )
1075                 {
1076                         result = 0;
1077                 }
1078                 else
1079                 {
1080                         result = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( self ) );
1081                 }
1082         }
1083         return result;
1084 }
1085
1086 /** Join 1 or more consecutive clips.
1087  *
1088  * \public \memberof mlt_playlist_s
1089  * \param self a playlist
1090  * \param clip the starting playlist entry index
1091  * \param count the number of entries to merge
1092  * \param merge ignored
1093  * \return true if there was an error
1094  */
1095
1096 int mlt_playlist_join( mlt_playlist self, int clip, int count, int merge )
1097 {
1098         int error = clip < 0 || clip >= self->count;
1099         if ( error == 0 )
1100         {
1101                 int i = clip;
1102                 mlt_playlist new_clip = mlt_playlist_new( mlt_service_profile( MLT_PLAYLIST_SERVICE(self) ) );
1103                 mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self );
1104                 if ( clip + count >= self->count )
1105                         count = self->count - clip - 1;
1106                 for ( i = 0; i <= count; i ++ )
1107                 {
1108                         playlist_entry *entry = self->list[ clip ];
1109                         mlt_playlist_append( new_clip, entry->producer );
1110                         mlt_playlist_repeat_clip( new_clip, i, entry->repeat );
1111                         entry->preservation_hack = 1;
1112                         mlt_playlist_remove( self, clip );
1113                 }
1114                 mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self );
1115                 mlt_playlist_insert( self, MLT_PLAYLIST_PRODUCER( new_clip ), clip, 0, -1 );
1116                 mlt_playlist_close( new_clip );
1117         }
1118         return error;
1119 }
1120
1121 /** Mix consecutive clips for a specified length and apply transition if specified.
1122  *
1123  * \public \memberof mlt_playlist_s
1124  * \param self a playlist
1125  * \param clip the index of the playlist entry
1126  * \param length the number of frames over which to create the mix
1127  * \param transition the transition to use for the mix
1128  * \return true if there was an error
1129  */
1130
1131 int mlt_playlist_mix( mlt_playlist self, int clip, int length, mlt_transition transition )
1132 {
1133         int error = ( clip < 0 || clip + 1 >= self->count );
1134         if ( error == 0 )
1135         {
1136                 playlist_entry *clip_a = self->list[ clip ];
1137                 playlist_entry *clip_b = self->list[ clip + 1 ];
1138                 mlt_producer track_a = NULL;
1139                 mlt_producer track_b = NULL;
1140                 mlt_tractor tractor = mlt_tractor_new( );
1141                 mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self );
1142
1143                 // Check length is valid for both clips and resize if necessary.
1144                 int max_size = clip_a->frame_count > clip_b->frame_count ? clip_a->frame_count : clip_b->frame_count;
1145                 length = length > max_size ? max_size : length;
1146
1147                 // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches
1148                 if ( length != clip_a->frame_count )
1149                         track_a = mlt_producer_cut( clip_a->producer, clip_a->frame_out - length + 1, clip_a->frame_out );
1150                 else
1151                         track_a = clip_a->producer;
1152
1153                 if ( length != clip_b->frame_count )
1154                         track_b = mlt_producer_cut( clip_b->producer, clip_b->frame_in, clip_b->frame_in + length - 1 );
1155                 else
1156                         track_b = clip_b->producer;
1157
1158                 // Set the tracks on the tractor
1159                 mlt_tractor_set_track( tractor, track_a, 0 );
1160                 mlt_tractor_set_track( tractor, track_b, 1 );
1161
1162                 // Insert the mix object into the playlist
1163                 mlt_playlist_insert( self, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 );
1164                 mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL );
1165
1166                 // Attach the transition
1167                 if ( transition != NULL )
1168                 {
1169                         mlt_field field = mlt_tractor_field( tractor );
1170                         mlt_field_plant_transition( field, transition, 0, 1 );
1171                         mlt_transition_set_in_and_out( transition, 0, length - 1 );
1172                 }
1173
1174                 // Close our references to the tracks if we created new cuts above (the tracks can still be used here)
1175                 if ( track_a != clip_a->producer )
1176                         mlt_producer_close( track_a );
1177                 if ( track_b != clip_b->producer )
1178                         mlt_producer_close( track_b );
1179
1180                 // Check if we have anything left on the right hand clip
1181                 if ( track_b == clip_b->producer )
1182                 {
1183                         clip_b->preservation_hack = 1;
1184                         mlt_playlist_remove( self, clip + 2 );
1185                 }
1186                 else if ( clip_b->frame_out - clip_b->frame_in > length )
1187                 {
1188                         mlt_playlist_resize_clip( self, clip + 2, clip_b->frame_in + length, clip_b->frame_out );
1189                         mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_b->producer ), "mix_in", tractor, 0, NULL, NULL );
1190                         mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_out", clip_b->producer, 0, NULL, NULL );
1191                 }
1192                 else
1193                 {
1194                         mlt_producer_clear( clip_b->producer );
1195                         mlt_playlist_remove( self, clip + 2 );
1196                 }
1197
1198                 // Check if we have anything left on the left hand clip
1199                 if ( track_a == clip_a->producer )
1200                 {
1201                         clip_a->preservation_hack = 1;
1202                         mlt_playlist_remove( self, clip );
1203                 }
1204                 else if ( clip_a->frame_out - clip_a->frame_in > length )
1205                 {
1206                         mlt_playlist_resize_clip( self, clip, clip_a->frame_in, clip_a->frame_out - length );
1207                         mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_a->producer ), "mix_out", tractor, 0, NULL, NULL );
1208                         mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_in", clip_a->producer, 0, NULL, NULL );
1209                 }
1210                 else
1211                 {
1212                         mlt_producer_clear( clip_a->producer );
1213                         mlt_playlist_remove( self, clip );
1214                 }
1215
1216                 // Unblock and force a fire off of change events to listeners
1217                 mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self );
1218                 mlt_playlist_virtual_refresh( self );
1219                 mlt_tractor_close( tractor );
1220         }
1221         return error;
1222 }
1223
1224 /** Add a transition to an existing mix.
1225  *
1226  * \public \memberof mlt_playlist_s
1227  * \param self a playlist
1228  * \param clip the index of the playlist entry
1229  * \param transition a transition
1230  * \return true if there was an error
1231  */
1232
1233 int mlt_playlist_mix_add( mlt_playlist self, int clip, mlt_transition transition )
1234 {
1235         mlt_producer producer = mlt_producer_cut_parent( mlt_playlist_get_clip( self, clip ) );
1236         mlt_properties properties = producer != NULL ? MLT_PRODUCER_PROPERTIES( producer ) : NULL;
1237         mlt_tractor tractor = properties != NULL ? mlt_properties_get_data( properties, "mlt_mix", NULL ) : NULL;
1238         int error = transition == NULL || tractor == NULL;
1239         if ( error == 0 )
1240         {
1241                 mlt_field field = mlt_tractor_field( tractor );
1242                 mlt_field_plant_transition( field, transition, 0, 1 );
1243                 mlt_transition_set_in_and_out( transition, 0, self->list[ clip ]->frame_count - 1 );
1244         }
1245         return error;
1246 }
1247
1248 /** Return the clip at the clip index.
1249  *
1250  * \public \memberof mlt_playlist_s
1251  * \param self a playlist
1252  * \param clip the index of a playlist entry
1253  * \return a producer or NULL if there was an error
1254  */
1255
1256 mlt_producer mlt_playlist_get_clip( mlt_playlist self, int clip )
1257 {
1258         if ( clip >= 0 && clip < self->count )
1259                 return self->list[ clip ]->producer;
1260         return NULL;
1261 }
1262
1263 /** Return the clip at the specified position.
1264  *
1265  * \public \memberof mlt_playlist_s
1266  * \param self a playlist
1267  * \param position a time relative to the beginning of the playlist
1268  * \return a producer or NULL if not found
1269  */
1270
1271 mlt_producer mlt_playlist_get_clip_at( mlt_playlist self, mlt_position position )
1272 {
1273         int index = 0, total = 0;
1274         return mlt_playlist_locate( self, &position, &index, &total );
1275 }
1276
1277 /** Return the clip index of the specified position.
1278  *
1279  * \public \memberof mlt_playlist_s
1280  * \param self a playlist
1281  * \param position a time relative to the beginning of the playlist
1282  * \return the index of the playlist entry
1283  */
1284
1285 int mlt_playlist_get_clip_index_at( mlt_playlist self, mlt_position position )
1286 {
1287         int index = 0, total = 0;
1288         mlt_playlist_locate( self, &position, &index, &total );
1289         return index;
1290 }
1291
1292 /** Determine if the clip is a mix.
1293  *
1294  * \public \memberof mlt_playlist_s
1295  * \param self a playlist
1296  * \param clip the index of the playlist entry
1297  * \return true if the producer is a mix
1298  */
1299
1300 int mlt_playlist_clip_is_mix( mlt_playlist self, int clip )
1301 {
1302         mlt_producer producer = mlt_producer_cut_parent( mlt_playlist_get_clip( self, clip ) );
1303         mlt_properties properties = producer != NULL ? MLT_PRODUCER_PROPERTIES( producer ) : NULL;
1304         mlt_tractor tractor = properties != NULL ? mlt_properties_get_data( properties, "mlt_mix", NULL ) : NULL;
1305         return tractor != NULL;
1306 }
1307
1308 /** Remove a mixed clip - ensure that the cuts included in the mix find their way
1309  * back correctly on to the playlist.
1310  *
1311  * \private \memberof mlt_playlist_s
1312  * \param self a playlist
1313  * \param clip the index of the playlist entry
1314  * \return true if there was an error
1315  */
1316
1317 static int mlt_playlist_unmix( mlt_playlist self, int clip )
1318 {
1319         int error = ( clip < 0 || clip >= self->count );
1320
1321         // Ensure that the clip request is actually a mix
1322         if ( error == 0 )
1323         {
1324                 mlt_producer producer = mlt_producer_cut_parent( self->list[ clip ]->producer );
1325                 mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
1326                 error = mlt_properties_get_data( properties, "mlt_mix", NULL ) == NULL ||
1327                             self->list[ clip ]->preservation_hack;
1328         }
1329
1330         if ( error == 0 )
1331         {
1332                 playlist_entry *mix = self->list[ clip ];
1333                 mlt_tractor tractor = ( mlt_tractor )mlt_producer_cut_parent( mix->producer );
1334                 mlt_properties properties = MLT_TRACTOR_PROPERTIES( tractor );
1335                 mlt_producer clip_a = mlt_properties_get_data( properties, "mix_in", NULL );
1336                 mlt_producer clip_b = mlt_properties_get_data( properties, "mix_out", NULL );
1337                 int length = mlt_producer_get_playtime( MLT_TRACTOR_PRODUCER( tractor ) );
1338                 mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self );
1339
1340                 if ( clip_a != NULL )
1341                 {
1342                         mlt_producer_set_in_and_out( clip_a, mlt_producer_get_in( clip_a ), mlt_producer_get_out( clip_a ) + length );
1343                 }
1344                 else
1345                 {
1346                         mlt_producer cut = mlt_tractor_get_track( tractor, 0 );
1347                         mlt_playlist_insert( self, cut, clip, -1, -1 );
1348                         clip ++;
1349                 }
1350
1351                 if ( clip_b != NULL )
1352                 {
1353                         mlt_producer_set_in_and_out( clip_b, mlt_producer_get_in( clip_b ) - length, mlt_producer_get_out( clip_b ) );
1354                 }
1355                 else
1356                 {
1357                         mlt_producer cut = mlt_tractor_get_track( tractor, 1 );
1358                         mlt_playlist_insert( self, cut, clip + 1, -1, -1 );
1359                 }
1360
1361                 mlt_properties_set_data( properties, "mlt_mix", NULL, 0, NULL, NULL );
1362                 mlt_playlist_remove( self, clip );
1363                 mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self );
1364                 mlt_playlist_virtual_refresh( self );
1365         }
1366         return error;
1367 }
1368
1369 /** Resize a mix clip.
1370  *
1371  * \private \memberof mlt_playlist_s
1372  * \param self a playlist
1373  * \param clip the index of the playlist entry
1374  * \param in the new starting point
1375  * \param out the new ending point
1376  * \return true if there was an error
1377  */
1378
1379 static int mlt_playlist_resize_mix( mlt_playlist self, int clip, int in, int out )
1380 {
1381         int error = ( clip < 0 || clip >= self->count );
1382
1383         // Ensure that the clip request is actually a mix
1384         if ( error == 0 )
1385         {
1386                 mlt_producer producer = mlt_producer_cut_parent( self->list[ clip ]->producer );
1387                 mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
1388                 error = mlt_properties_get_data( properties, "mlt_mix", NULL ) == NULL;
1389         }
1390
1391         if ( error == 0 )
1392         {
1393                 playlist_entry *mix = self->list[ clip ];
1394                 mlt_tractor tractor = ( mlt_tractor )mlt_producer_cut_parent( mix->producer );
1395                 mlt_properties properties = MLT_TRACTOR_PROPERTIES( tractor );
1396                 mlt_producer clip_a = mlt_properties_get_data( properties, "mix_in", NULL );
1397                 mlt_producer clip_b = mlt_properties_get_data( properties, "mix_out", NULL );
1398                 mlt_producer track_a = mlt_tractor_get_track( tractor, 0 );
1399                 mlt_producer track_b = mlt_tractor_get_track( tractor, 1 );
1400                 int length = out - in + 1;
1401                 int length_diff = length - mlt_producer_get_playtime( MLT_TRACTOR_PRODUCER( tractor ) );
1402                 mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self );
1403
1404                 if ( clip_a != NULL )
1405                         mlt_producer_set_in_and_out( clip_a, mlt_producer_get_in( clip_a ), mlt_producer_get_out( clip_a ) - length_diff );
1406
1407                 if ( clip_b != NULL )
1408                         mlt_producer_set_in_and_out( clip_b, mlt_producer_get_in( clip_b ) + length_diff, mlt_producer_get_out( clip_b ) );
1409
1410                 mlt_producer_set_in_and_out( track_a, mlt_producer_get_in( track_a ) - length_diff, mlt_producer_get_out( track_a ) );
1411                 mlt_producer_set_in_and_out( track_b, mlt_producer_get_in( track_b ), mlt_producer_get_out( track_b ) + length_diff );
1412                 mlt_producer_set_in_and_out( MLT_MULTITRACK_PRODUCER( mlt_tractor_multitrack( tractor ) ), in, out );
1413                 mlt_producer_set_in_and_out( MLT_TRACTOR_PRODUCER( tractor ), in, out );
1414                 mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( mix->producer ), "length", out - in + 1 );
1415                 mlt_producer_set_in_and_out( mix->producer, in, out );
1416
1417                 mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self );
1418                 mlt_playlist_virtual_refresh( self );
1419         }
1420         return error;
1421 }
1422
1423 /** Consolidate adjacent blank producers.
1424  *
1425  * \public \memberof mlt_playlist_s
1426  * \param self a playlist
1427  * \param keep_length set false to remove the last entry if it is blank
1428  */
1429
1430 void mlt_playlist_consolidate_blanks( mlt_playlist self, int keep_length )
1431 {
1432         if ( self != NULL )
1433         {
1434                 int i = 0;
1435                 mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
1436
1437                 mlt_events_block( properties, properties );
1438                 for ( i = 1; i < self->count; i ++ )
1439                 {
1440                         playlist_entry *left = self->list[ i - 1 ];
1441                         playlist_entry *right = self->list[ i ];
1442
1443                         if ( mlt_producer_is_blank( left->producer ) && mlt_producer_is_blank( right->producer ) )
1444                         {
1445                                 mlt_playlist_resize_clip( self, i - 1, 0, left->frame_count + right->frame_count - 1 );
1446                                 mlt_playlist_remove( self, i -- );
1447                         }
1448                 }
1449
1450                 if ( !keep_length && self->count > 0 )
1451                 {
1452                         playlist_entry *last = self->list[ self->count - 1 ];
1453                         if ( mlt_producer_is_blank( last->producer ) )
1454                                 mlt_playlist_remove( self, self->count - 1 );
1455                 }
1456
1457                 mlt_events_unblock( properties, properties );
1458                 mlt_playlist_virtual_refresh( self );
1459         }
1460 }
1461
1462 /** Determine if the specified clip index is a blank.
1463  *
1464  * \public \memberof mlt_playlist_s
1465  * \param self a playlist
1466  * \param clip the index of the playlist entry
1467  * \return true if there was an error
1468  */
1469
1470 int mlt_playlist_is_blank( mlt_playlist self, int clip )
1471 {
1472         return self == NULL || mlt_producer_is_blank( mlt_playlist_get_clip( self, clip ) );
1473 }
1474
1475 /** Determine if the specified position is a blank.
1476  *
1477  * \public \memberof mlt_playlist_s
1478  * \param self a playlist
1479  * \param position a time relative to the start or end (negative) of the playlist
1480  * \return true if there was an error
1481  */
1482
1483 int mlt_playlist_is_blank_at( mlt_playlist self, mlt_position position )
1484 {
1485         return self == NULL || mlt_producer_is_blank( mlt_playlist_get_clip_at( self, position ) );
1486 }
1487
1488 /** Replace the specified clip with a blank and return the clip.
1489  *
1490  * \public \memberof mlt_playlist_s
1491  * \param self a playlist
1492  * \param clip the index of the playlist entry
1493  * \return a producer or NULL if there was an error
1494  */
1495
1496 mlt_producer mlt_playlist_replace_with_blank( mlt_playlist self, int clip )
1497 {
1498         mlt_producer producer = NULL;
1499         if ( !mlt_playlist_is_blank( self, clip ) )
1500         {
1501                 playlist_entry *entry = self->list[ clip ];
1502                 int in = entry->frame_in;
1503                 int out = entry->frame_out;
1504                 mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
1505                 producer = entry->producer;
1506                 mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
1507                 mlt_events_block( properties, properties );
1508                 mlt_playlist_remove( self, clip );
1509                 mlt_playlist_blank( self, out - in );
1510                 mlt_playlist_move( self, self->count - 1, clip );
1511                 mlt_events_unblock( properties, properties );
1512                 mlt_playlist_virtual_refresh( self );
1513                 mlt_producer_set_in_and_out( producer, in, out );
1514         }
1515         return producer;
1516 }
1517
1518 /** Insert blank space.
1519  *
1520  * \public \memberof mlt_playlist_s
1521  * \param self a playlist
1522  * \param clip the index of the new blank section
1523  * \param length the ending time of the new blank section (duration - 1)
1524  */
1525
1526 void mlt_playlist_insert_blank( mlt_playlist self, int clip, int length )
1527 {
1528         if ( self != NULL && length >= 0 )
1529         {
1530                 mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
1531                 mlt_events_block( properties, properties );
1532                 mlt_playlist_blank( self, length );
1533                 mlt_playlist_move( self, self->count - 1, clip );
1534                 mlt_events_unblock( properties, properties );
1535                 mlt_playlist_virtual_refresh( self );
1536         }
1537 }
1538
1539 /** Resize a blank entry.
1540  *
1541  * \public \memberof mlt_playlist_s
1542  * \param self a playlist
1543  * \param position the time at which the blank entry exists relative to the start or end (negative) of the playlist.
1544  * \param length the additional amount of blank frames to add
1545  * \param find true to fist locate the blank after the clip at position
1546  */
1547 void mlt_playlist_pad_blanks( mlt_playlist self, mlt_position position, int length, int find )
1548 {
1549         if ( self != NULL && length != 0 )
1550         {
1551                 int clip = mlt_playlist_get_clip_index_at( self, position );
1552                 mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
1553                 mlt_events_block( properties, properties );
1554                 if ( find && clip < self->count && !mlt_playlist_is_blank( self, clip ) )
1555                         clip ++;
1556                 if ( clip < self->count && mlt_playlist_is_blank( self, clip ) )
1557                 {
1558                         mlt_playlist_clip_info info;
1559                         mlt_playlist_get_clip_info( self, &info, clip );
1560                         if ( info.frame_out + length > info.frame_in )
1561                                 mlt_playlist_resize_clip( self, clip, info.frame_in, info.frame_out + length );
1562                         else
1563                                 mlt_playlist_remove( self, clip );
1564                 }
1565                 else if ( find && clip < self->count && length > 0  )
1566                 {
1567                         mlt_playlist_insert_blank( self, clip, length );
1568                 }
1569                 mlt_events_unblock( properties, properties );
1570                 mlt_playlist_virtual_refresh( self );
1571         }
1572 }
1573
1574 /** Insert a clip at a specific time.
1575  *
1576  * \public \memberof mlt_playlist_s
1577  * \param self a playlist
1578  * \param position the time at which to insert
1579  * \param producer the producer to insert
1580  * \param mode true if you want to overwrite any blank section
1581  * \return true if there was an error
1582  */
1583
1584 int mlt_playlist_insert_at( mlt_playlist self, mlt_position position, mlt_producer producer, int mode )
1585 {
1586         int ret = self == NULL || position < 0 || producer == NULL;
1587         if ( ret == 0 )
1588         {
1589                 mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
1590                 int length = mlt_producer_get_playtime( producer );
1591                 int clip = mlt_playlist_get_clip_index_at( self, position );
1592                 mlt_playlist_clip_info info;
1593                 mlt_playlist_get_clip_info( self, &info, clip );
1594                 mlt_events_block( properties, self );
1595                 if ( clip < self->count && mlt_playlist_is_blank( self, clip ) )
1596                 {
1597                         // Split and move to new clip if need be
1598                         if ( position != info.start && mlt_playlist_split( self, clip, position - info.start - 1 ) == 0 )
1599                                 mlt_playlist_get_clip_info( self, &info, ++ clip );
1600
1601                         // Split again if need be
1602                         if ( length < info.frame_count )
1603                                 mlt_playlist_split( self, clip, length - 1 );
1604
1605                         // Remove
1606                         mlt_playlist_remove( self, clip );
1607
1608                         // Insert
1609                         mlt_playlist_insert( self, producer, clip, -1, -1 );
1610                         ret = clip;
1611                 }
1612                 else if ( clip < self->count )
1613                 {
1614                         if ( position > info.start + info.frame_count / 2 )
1615                                 clip ++;
1616                         if ( mode == 1 && clip < self->count && mlt_playlist_is_blank( self, clip ) )
1617                         {
1618                                 mlt_playlist_get_clip_info( self, &info, clip );
1619                                 if ( length < info.frame_count )
1620                                         mlt_playlist_split( self, clip, length );
1621                                 mlt_playlist_remove( self, clip );
1622                         }
1623                         mlt_playlist_insert( self, producer, clip, -1, -1 );
1624                         ret = clip;
1625                 }
1626                 else
1627                 {
1628                         if ( mode == 1 ) {
1629                                 if ( position == info.start )
1630                                         mlt_playlist_remove( self, clip );
1631                                 else
1632                                         mlt_playlist_blank( self, position - mlt_properties_get_int( properties, "length" ) - 1 );
1633                         }
1634                         mlt_playlist_append( self, producer );
1635                         ret = self->count - 1;
1636                 }
1637                 mlt_events_unblock( properties, self );
1638                 mlt_playlist_virtual_refresh( self );
1639         }
1640         else
1641         {
1642                 ret = -1;
1643         }
1644         return ret;
1645 }
1646
1647 /** Get the time at which the clip starts relative to the playlist.
1648  *
1649  * \public \memberof mlt_playlist_s
1650  * \param self a playlist
1651  * \param clip the index of the playlist entry
1652  * \return the starting time
1653  */
1654
1655 int mlt_playlist_clip_start( mlt_playlist self, int clip )
1656 {
1657         mlt_playlist_clip_info info;
1658         if ( mlt_playlist_get_clip_info( self, &info, clip ) == 0 )
1659                 return info.start;
1660         return clip < 0 ? 0 : mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( self ) );
1661 }
1662
1663 /** Get the playable duration of the clip.
1664  *
1665  * \public \memberof mlt_playlist_s
1666  * \param self a playlist
1667  * \param clip the index of the playlist entry
1668  * \return the duration of the playlist entry
1669  */
1670
1671 int mlt_playlist_clip_length( mlt_playlist self, int clip )
1672 {
1673         mlt_playlist_clip_info info;
1674         if ( mlt_playlist_get_clip_info( self, &info, clip ) == 0 )
1675                 return info.frame_count;
1676         return 0;
1677 }
1678
1679 /** Get the duration of a blank space.
1680  *
1681  * \public \memberof mlt_playlist_s
1682  * \param self a playlist
1683  * \param clip the index of the playlist entry
1684  * \param bounded the maximum number of blank entries or 0 for all
1685  * \return the duration of a blank section
1686  */
1687
1688 int mlt_playlist_blanks_from( mlt_playlist self, int clip, int bounded )
1689 {
1690         int count = 0;
1691         mlt_playlist_clip_info info;
1692         if ( self != NULL && clip < self->count )
1693         {
1694                 mlt_playlist_get_clip_info( self, &info, clip );
1695                 if ( mlt_playlist_is_blank( self, clip ) )
1696                         count += info.frame_count;
1697                 if ( bounded == 0 )
1698                         bounded = self->count;
1699                 for ( clip ++; clip < self->count && bounded >= 0; clip ++ )
1700                 {
1701                         mlt_playlist_get_clip_info( self, &info, clip );
1702                         if ( mlt_playlist_is_blank( self, clip ) )
1703                                 count += info.frame_count;
1704                         else
1705                                 bounded --;
1706                 }
1707         }
1708         return count;
1709 }
1710
1711 /** Remove a portion of the playlist by time.
1712  *
1713  * \public \memberof mlt_playlist_s
1714  * \param self a playlist
1715  * \param position the starting time
1716  * \param length the duration of time to remove
1717  * \return the new entry index at the position
1718  */
1719
1720 int mlt_playlist_remove_region( mlt_playlist self, mlt_position position, int length )
1721 {
1722         int index = mlt_playlist_get_clip_index_at( self, position );
1723         if ( index >= 0 && index < self->count )
1724         {
1725                 mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self );
1726                 int clip_start = mlt_playlist_clip_start( self, index );
1727                 int list_length = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( self ) );
1728                 mlt_events_block( properties, self );
1729
1730                 if ( position + length > list_length )
1731                         length -= ( position + length - list_length );
1732
1733                 if ( clip_start < position )
1734                 {
1735                         mlt_playlist_split( self, index ++, position - clip_start - 1 );
1736                 }
1737
1738                 while( length > 0 )
1739                 {
1740                         if ( mlt_playlist_clip_length( self, index ) > length )
1741                                 mlt_playlist_split( self, index, length - 1 );
1742                         length -= mlt_playlist_clip_length( self, index );
1743                         mlt_playlist_remove( self, index );
1744                 }
1745
1746                 mlt_playlist_consolidate_blanks( self, 0 );
1747                 mlt_events_unblock( properties, self );
1748                 mlt_playlist_virtual_refresh( self );
1749
1750                 // Just to be sure, we'll get the clip index again...
1751                 index = mlt_playlist_get_clip_index_at( self, position );
1752         }
1753         return index;
1754 }
1755
1756 /** Not implemented
1757  *
1758  * \deprecated not implemented
1759  * \public \memberof mlt_playlist_s
1760  * \param self
1761  * \param position
1762  * \param length
1763  * \param new_position
1764  * \return
1765  */
1766
1767 int mlt_playlist_move_region( mlt_playlist self, mlt_position position, int length, int new_position )
1768 {
1769         if ( self != NULL )
1770         {
1771         }
1772         return 0;
1773 }
1774
1775 /** Get the current frame.
1776  *
1777  * The implementation of the get_frame virtual function.
1778  * \private \memberof mlt_playlist_s
1779  * \param producer a producer
1780  * \param frame a frame by reference
1781  * \param index the time at which to get the frame
1782  * \return false
1783  */
1784
1785 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
1786 {
1787         // Check that we have a producer
1788         if ( producer == NULL )
1789         {
1790                 *frame = NULL;
1791                 return -1;
1792         }
1793
1794         // Get this mlt_playlist
1795         mlt_playlist self = producer->child;
1796
1797         // Need to ensure the frame is deinterlaced when repeating 1 frame
1798         int progressive = 0;
1799
1800         // Get the real producer
1801         mlt_service real = mlt_playlist_virtual_seek( self, &progressive );
1802
1803         // Check that we have a producer
1804         if ( real == NULL )
1805         {
1806                 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
1807                 return 0;
1808         }
1809
1810         // Get the frame
1811         if ( !mlt_properties_get_int( MLT_SERVICE_PROPERTIES( real ), "meta.fx_cut" ) )
1812         {
1813                 mlt_service_get_frame( real, frame, index );
1814         }
1815         else
1816         {
1817                 mlt_producer parent = mlt_producer_cut_parent( ( mlt_producer )real );
1818                 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
1819                 mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "fx_cut", 1 );
1820                 mlt_frame_push_service( *frame, NULL );
1821                 mlt_frame_push_audio( *frame, NULL );
1822                 mlt_service_apply_filters( MLT_PRODUCER_SERVICE( parent ), *frame, 0 );
1823                 mlt_service_apply_filters( real, *frame, 0 );
1824                 mlt_deque_pop_front( MLT_FRAME_IMAGE_STACK( *frame ) );
1825                 mlt_deque_pop_front( MLT_FRAME_AUDIO_STACK( *frame ) );
1826         }
1827
1828         // Check if we're at the end of the clip
1829         mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
1830         if ( mlt_properties_get_int( properties, "end_of_clip" ) )
1831                 mlt_playlist_virtual_set_out( self );
1832
1833         // Set the consumer progressive property
1834         if ( progressive )
1835         {
1836                 mlt_properties_set_int( properties, "consumer_deinterlace", progressive );
1837                 mlt_properties_set_int( properties, "test_audio", 1 );
1838         }
1839
1840         // Check for notifier and call with appropriate argument
1841         mlt_properties playlist_properties = MLT_PRODUCER_PROPERTIES( producer );
1842         void ( *notifier )( void * ) = mlt_properties_get_data( playlist_properties, "notifier", NULL );
1843         if ( notifier != NULL )
1844         {
1845                 void *argument = mlt_properties_get_data( playlist_properties, "notifier_arg", NULL );
1846                 notifier( argument );
1847         }
1848
1849         // Update position on the frame we're creating
1850         mlt_frame_set_position( *frame, mlt_producer_frame( producer ) );
1851
1852         // Position ourselves on the next frame
1853         mlt_producer_prepare_next( producer );
1854
1855         return 0;
1856 }
1857
1858 /** Close the playlist.
1859  *
1860  * \public \memberof mlt_playlist_s
1861  * \param self a playlist
1862  */
1863
1864 void mlt_playlist_close( mlt_playlist self )
1865 {
1866         if ( self != NULL && mlt_properties_dec_ref( MLT_PLAYLIST_PROPERTIES( self ) ) <= 0 )
1867         {
1868                 int i = 0;
1869                 self->parent.close = NULL;
1870                 for ( i = 0; i < self->count; i ++ )
1871                 {
1872                         mlt_event_close( self->list[ i ]->event );
1873                         mlt_producer_close( self->list[ i ]->producer );
1874                         free( self->list[ i ] );
1875                 }
1876                 mlt_producer_close( &self->blank );
1877                 mlt_producer_close( &self->parent );
1878                 free( self->list );
1879                 free( self );
1880         }
1881 }