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