From c520db3c7b2ed8fd6e7098b02c6150474dff4afc Mon Sep 17 00:00:00 2001 From: Dan Dennedy Date: Sat, 22 Mar 2014 16:05:26 -0700 Subject: [PATCH] Add mlt_playlist_mix_in() and mlt_playlist_mix_out(). These are new alternatives to mlt_playlist_mix() that Shotcut is using. --- src/framework/mlt.vers | 2 + src/framework/mlt_playlist.c | 208 +++++++++++++++++++++++++++++++++++ src/framework/mlt_playlist.h | 2 + src/mlt++/MltPlaylist.cpp | 10 ++ src/mlt++/MltPlaylist.h | 2 + src/mlt++/mlt++.vers | 2 + 6 files changed, 226 insertions(+) diff --git a/src/framework/mlt.vers b/src/framework/mlt.vers index a0069e41..244b090f 100644 --- a/src/framework/mlt.vers +++ b/src/framework/mlt.vers @@ -449,6 +449,8 @@ MLT_0.9.0 { } MLT_0.8.8; MLT_0.9.2 { + mlt_playlist_mix_in; + mlt_playlist_mix_out; mlt_properties_frames_to_time; mlt_properties_time_to_frames; } MLT_0.9.0; diff --git a/src/framework/mlt_playlist.c b/src/framework/mlt_playlist.c index b4cc7534..62a1f164 100644 --- a/src/framework/mlt_playlist.c +++ b/src/framework/mlt_playlist.c @@ -1151,6 +1151,10 @@ int mlt_playlist_join( mlt_playlist self, int clip, int count, int merge ) /** Mix consecutive clips for a specified length and apply transition if specified. * + * This version of the mix function does not utilize any frames beyond the out of + * clip A or before the in point of clip B. It takes the frames needed for the length + * of the transition by adjusting the duration of both clips - the out point for clip A + * and the in point for clip B. * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry @@ -1257,6 +1261,210 @@ int mlt_playlist_mix( mlt_playlist self, int clip, int length, mlt_transition tr return error; } +/** Mix consecutive clips for a specified length. + * + * This version of the mix function maintains the out point of the clip A by occupying the + * beginning of clip B before its current in point. Therefore, it ends up adjusting the in + * point and duration of clip B without affecting the duration of clip A. + * Also, therefore, there must be enough frames after the out point of clip A. + * \public \memberof mlt_playlist_s + * \param self a playlist + * \param clip the index of the playlist entry + * \param length the number of frames over which to create the mix + * \return true if there was an error + */ + +int mlt_playlist_mix_in( mlt_playlist self, int clip, int length ) +{ + int error = ( clip < 0 || clip + 1 >= self->count ); + if ( error == 0 ) + { + playlist_entry *clip_a = self->list[ clip ]; + playlist_entry *clip_b = self->list[ clip + 1 ]; + mlt_producer track_a = NULL; + mlt_producer track_b = NULL; + mlt_tractor tractor = mlt_tractor_new( ); + + mlt_service_set_profile( MLT_TRACTOR_SERVICE( tractor ), + mlt_service_profile( MLT_PLAYLIST_SERVICE( self ) ) ); + mlt_properties_set_lcnumeric( MLT_TRACTOR_PROPERTIES( tractor ), + mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); + mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); + + // Check length is valid for both clips and resize if necessary. + int max_size = ( clip_a->frame_out + 1 ) > clip_b->frame_count ? ( clip_a->frame_out + 1 ) : clip_b->frame_count; + length = length > max_size ? max_size : length; + + // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches + if ( length != clip_a->frame_out + 1 ) + track_a = mlt_producer_cut( clip_a->producer, clip_a->frame_out + 1, clip_a->frame_out + length ); + else + track_a = clip_a->producer; + + if ( length != clip_b->frame_count ) + track_b = mlt_producer_cut( clip_b->producer, clip_b->frame_in, clip_b->frame_in + length - 1 ); + else + track_b = clip_b->producer; + + // Set the tracks on the tractor + mlt_tractor_set_track( tractor, track_a, 0 ); + mlt_tractor_set_track( tractor, track_b, 1 ); + + // Insert the mix object into the playlist + mlt_playlist_insert( self, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL ); + + // Close our references to the tracks if we created new cuts above (the tracks can still be used here) + if ( track_a != clip_a->producer ) + mlt_producer_close( track_a ); + if ( track_b != clip_b->producer ) + mlt_producer_close( track_b ); + + // Check if we have anything left on the right hand clip + if ( track_b == clip_b->producer ) + { + clip_b->preservation_hack = 1; + mlt_playlist_remove( self, clip + 2 ); + } + else if ( clip_b->frame_out - clip_b->frame_in >= length ) + { + mlt_playlist_resize_clip( self, clip + 2, clip_b->frame_in + length, clip_b->frame_out ); + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_b->producer ), "mix_in", tractor, 0, NULL, NULL ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_out", clip_b->producer, 0, NULL, NULL ); + } + else + { + mlt_producer_clear( clip_b->producer ); + mlt_playlist_remove( self, clip + 2 ); + } + + // Check if we have anything left on the left hand clip + if ( track_a == clip_a->producer ) + { + clip_a->preservation_hack = 1; + mlt_playlist_remove( self, clip ); + } + else if ( clip_a->frame_out - clip_a->frame_in > 0 ) + { + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_a->producer ), "mix_out", tractor, 0, NULL, NULL ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_in", clip_a->producer, 0, NULL, NULL ); + } + else + { + mlt_producer_clear( clip_a->producer ); + mlt_playlist_remove( self, clip ); + } + + // Unblock and force a fire off of change events to listeners + mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); + mlt_playlist_virtual_refresh( self ); + mlt_tractor_close( tractor ); + } + return error; +} + +/** Mix consecutive clips for a specified length. + * + * This version of the mix function maintains the in point of the B clip by occupying the + * end of clip A before its current out point. Therefore, it ends up adjusting the out + * point and duration of clip A without affecting the duration or starting frame of clip B. + * Also, therefore, there must be enough frames before the in point of clip B. + * \public \memberof mlt_playlist_s + * \param self a playlist + * \param clip the index of the playlist entry + * \param length the number of frames over which to create the mix + * \return true if there was an error + */ + +int mlt_playlist_mix_out( mlt_playlist self, int clip, int length ) +{ + int error = ( clip < 0 || clip + 1 >= self->count ); + if ( error == 0 ) + { + playlist_entry *clip_a = self->list[ clip ]; + playlist_entry *clip_b = self->list[ clip + 1 ]; + mlt_producer track_a = NULL; + mlt_producer track_b = NULL; + mlt_tractor tractor = mlt_tractor_new( ); + + mlt_service_set_profile( MLT_TRACTOR_SERVICE( tractor ), + mlt_service_profile( MLT_PLAYLIST_SERVICE( self ) ) ); + mlt_properties_set_lcnumeric( MLT_TRACTOR_PROPERTIES( tractor ), + mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); + mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); + + // Check length is valid for both clips and resize if necessary. + int max_size = clip_a->frame_count > clip_b->frame_in ? clip_a->frame_count : clip_b->frame_in; + length = length > max_size ? max_size : length; + + // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches + if ( length != clip_a->frame_count ) + track_a = mlt_producer_cut( clip_a->producer, clip_a->frame_out - length + 1, clip_a->frame_out ); + else + track_a = clip_a->producer; + + if ( length != clip_b->frame_in ) + track_b = mlt_producer_cut( clip_b->producer, clip_b->frame_in - length + 1, clip_b->frame_in ); + else + track_b = clip_b->producer; + + // Set the tracks on the tractor + mlt_tractor_set_track( tractor, track_a, 0 ); + mlt_tractor_set_track( tractor, track_b, 1 ); + + // Insert the mix object into the playlist + mlt_playlist_insert( self, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL ); + + // Close our references to the tracks if we created new cuts above (the tracks can still be used here) + if ( track_a != clip_a->producer ) + mlt_producer_close( track_a ); + if ( track_b != clip_b->producer ) + mlt_producer_close( track_b ); + + // Check if we have anything left on the right hand clip + if ( track_b == clip_b->producer ) + { + clip_b->preservation_hack = 1; + mlt_playlist_remove( self, clip + 2 ); + } + else if ( clip_b->frame_out - clip_b->frame_in > 0 ) + { + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_b->producer ), "mix_in", tractor, 0, NULL, NULL ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_out", clip_b->producer, 0, NULL, NULL ); + } + else + { + mlt_producer_clear( clip_b->producer ); + mlt_playlist_remove( self, clip + 2 ); + } + + // Check if we have anything left on the left hand clip + if ( track_a == clip_a->producer ) + { + clip_a->preservation_hack = 1; + mlt_playlist_remove( self, clip ); + } + else if ( clip_a->frame_out - clip_a->frame_in >= length ) + { + mlt_playlist_resize_clip( self, clip, clip_a->frame_in, clip_a->frame_out - length ); + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_a->producer ), "mix_out", tractor, 0, NULL, NULL ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_in", clip_a->producer, 0, NULL, NULL ); + } + else + { + mlt_producer_clear( clip_a->producer ); + mlt_playlist_remove( self, clip ); + } + + // Unblock and force a fire off of change events to listeners + mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); + mlt_playlist_virtual_refresh( self ); + mlt_tractor_close( tractor ); + } + return error; +} + /** Add a transition to an existing mix. * * \public \memberof mlt_playlist_s diff --git a/src/framework/mlt_playlist.h b/src/framework/mlt_playlist.h index cc591294..04eeccb8 100644 --- a/src/framework/mlt_playlist.h +++ b/src/framework/mlt_playlist.h @@ -108,6 +108,8 @@ extern int mlt_playlist_split( mlt_playlist self, int clip, mlt_position positio extern int mlt_playlist_split_at( mlt_playlist self, mlt_position position, int left ); extern int mlt_playlist_join( mlt_playlist self, int clip, int count, int merge ); extern int mlt_playlist_mix( mlt_playlist self, int clip, int length, mlt_transition transition ); +extern int mlt_playlist_mix_in( mlt_playlist self, int clip, int length ); +extern int mlt_playlist_mix_out( mlt_playlist self, int clip, int length ); extern int mlt_playlist_mix_add( mlt_playlist self, int clip, mlt_transition transition ); extern mlt_producer mlt_playlist_get_clip( mlt_playlist self, int clip ); extern mlt_producer mlt_playlist_get_clip_at( mlt_playlist self, mlt_position position ); diff --git a/src/mlt++/MltPlaylist.cpp b/src/mlt++/MltPlaylist.cpp index 57c2c6ac..e6393d03 100644 --- a/src/mlt++/MltPlaylist.cpp +++ b/src/mlt++/MltPlaylist.cpp @@ -226,6 +226,16 @@ int Playlist::mix( int clip, int length, Transition *transition ) return mlt_playlist_mix( get_playlist( ), clip, length, transition == NULL ? NULL : transition->get_transition( ) ); } +int Playlist::mix_in(int clip, int length) +{ + return mlt_playlist_mix_in( get_playlist( ), clip, length ); +} + +int Playlist::mix_out(int clip, int length) +{ + return mlt_playlist_mix_out( get_playlist( ), clip, length ); +} + int Playlist::mix_add( int clip, Transition *transition ) { return mlt_playlist_mix_add( get_playlist( ), clip, transition == NULL ? NULL : transition->get_transition( ) ); diff --git a/src/mlt++/MltPlaylist.h b/src/mlt++/MltPlaylist.h index 924dc94c..a6fd44b5 100644 --- a/src/mlt++/MltPlaylist.h +++ b/src/mlt++/MltPlaylist.h @@ -86,6 +86,8 @@ namespace Mlt int split_at( int position, bool left = true ); int join( int clip, int count = 1, int merge = 1 ); int mix( int clip, int length, Transition *transition = NULL ); + int mix_in( int clip, int length ); + int mix_out( int clip, int length ); int mix_add( int clip, Transition *transition ); int repeat( int clip, int count ); Producer *get_clip( int clip ); diff --git a/src/mlt++/mlt++.vers b/src/mlt++/mlt++.vers index 17ec647d..4c58b12c 100644 --- a/src/mlt++/mlt++.vers +++ b/src/mlt++/mlt++.vers @@ -461,6 +461,8 @@ MLTPP_0.9.0 { MLTPP_0.9.2 { global: extern "C++" { + "Mlt::Playlist::mix_in(int, int)"; + "Mlt::Playlist::mix_out(int, int)"; "Mlt::Properties::frames_to_time(int, mlt_time_format)"; "Mlt::Properties::time_to_frames(char const*)"; }; -- 2.39.5