From: lilo_booter Date: Tue, 25 Jan 2005 12:31:08 +0000 (+0000) Subject: Transitions reworked (always_active capabilities); remaining audio handling switched... X-Git-Url: http://research.m1stereo.tv/gitweb?a=commitdiff_plain;h=da46016cc4e9c751ab346b9b8f451a59cd276b03;hp=85fef9bf91892619503967c8be84814945e0868c;p=melted Transitions reworked (always_active capabilities); remaining audio handling switched to stacks git-svn-id: https://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt@627 d19143bc-622f-0410-bfdd-b5b2a6649095 --- diff --git a/src/framework/mlt_consumer.c b/src/framework/mlt_consumer.c index 29f248e..99ca151 100644 --- a/src/framework/mlt_consumer.c +++ b/src/framework/mlt_consumer.c @@ -366,7 +366,6 @@ static void *consumer_read_ahead_thread( void *arg ) { samples = mlt_sample_calculator( fps, frequency, counter++ ); mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples ); - frame->get_audio = NULL; } mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); @@ -439,7 +438,6 @@ static void *consumer_read_ahead_thread( void *arg ) { samples = mlt_sample_calculator( fps, frequency, counter++ ); mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples ); - frame->get_audio = NULL; } // Increment the time take for this frame diff --git a/src/framework/mlt_frame.c b/src/framework/mlt_frame.c index 5a86627..660f3c1 100644 --- a/src/framework/mlt_frame.c +++ b/src/framework/mlt_frame.c @@ -92,12 +92,12 @@ int mlt_frame_is_test_card( mlt_frame this ) return mlt_deque_count( this->stack_image ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_image" ); } -/** Check if we have a way to derive something than test audio. +/** Check if we have a way to derive something other than test audio. */ int mlt_frame_is_test_audio( mlt_frame this ) { - return this->get_audio == NULL || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_audio" ); + return mlt_deque_count( this->stack_audio ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_audio" ); } /** Get the aspect ratio of the frame. @@ -205,6 +205,42 @@ mlt_deque mlt_frame_service_stack( mlt_frame this ) return this->stack_service; } +/** [EXPERIMENTAL] Replace image stack with the information provided. + + This might prove to be unreliable and restrictive - the idea is that a transition + which normally uses two images may decide to only use the b frame (ie: in the case + of a composite where the b frame completely obscures the a frame). + + The image must be writable and the destructor for the image itself must be taken + care of on another frame and that frame cannot have a replace applied to it... + Further it assumes that no alpha mask is in use. + + For these reasons, it can only be used in a specific situation - when you have + multiple tracks each with their own transition and these transitions are applied + in a strictly reversed order (ie: highest numbered [lowest track] is processed + first). + + More reliable approach - the cases should be detected during the process phase + and the upper tracks should simply not be invited to stack... +*/ + +void mlt_frame_replace_image( mlt_frame this, uint8_t *image, mlt_image_format format, int width, int height ) +{ + // Herein lies the potential problem for this function - it makes a potentially + // dangerous assumption that all content on the image stack can be removed without a destructor + while( mlt_deque_pop_back( this->stack_image ) ) ; + + // Update the information + mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "image", image, 0, NULL, NULL ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "width", width ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "height", height ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "format", format ); + this->get_alpha_mask = NULL; +} + +/** Get the image associated to the frame. +*/ + int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { mlt_properties properties = MLT_FRAME_PROPERTIES( this ); @@ -320,13 +356,14 @@ uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ) int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { + mlt_get_audio get_audio = mlt_frame_pop_audio( this ); mlt_properties properties = MLT_FRAME_PROPERTIES( this ); int hide = mlt_properties_get_int( properties, "test_audio" ); - if ( hide == 0 && this->get_audio != NULL ) + if ( hide == 0 && get_audio != NULL ) { mlt_position position = mlt_frame_get_position( this ); - this->get_audio( this, buffer, format, frequency, channels, samples ); + get_audio( this, buffer, format, frequency, channels, samples ); mlt_frame_set_position( this, position ); } else if ( mlt_properties_get_data( properties, "audio", NULL ) ) diff --git a/src/framework/mlt_frame.h b/src/framework/mlt_frame.h index 3a11daa..899b0d2 100644 --- a/src/framework/mlt_frame.h +++ b/src/framework/mlt_frame.h @@ -25,6 +25,7 @@ #include "mlt_deque.h" typedef int ( *mlt_get_image )( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); +typedef int ( *mlt_get_audio )( mlt_frame self, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); struct mlt_frame_s { @@ -32,7 +33,6 @@ struct mlt_frame_s struct mlt_properties_s parent; // Virtual methods - int ( *get_audio )( mlt_frame self, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); uint8_t * ( *get_alpha_mask )( mlt_frame self ); // Private properties @@ -52,6 +52,7 @@ extern double mlt_frame_get_aspect_ratio( mlt_frame self ); extern int mlt_frame_set_aspect_ratio( mlt_frame self, double value ); extern mlt_position mlt_frame_get_position( mlt_frame self ); extern int mlt_frame_set_position( mlt_frame self, mlt_position value ); +extern void mlt_frame_replace_image( mlt_frame self, uint8_t *image, mlt_image_format format, int width, int height ); extern int mlt_frame_get_image( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); extern uint8_t *mlt_frame_get_alpha_mask( mlt_frame self ); extern int mlt_frame_get_audio( mlt_frame self, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); diff --git a/src/framework/mlt_producer.c b/src/framework/mlt_producer.c index d84a0eb..55f85ad 100644 --- a/src/framework/mlt_producer.c +++ b/src/framework/mlt_producer.c @@ -503,6 +503,10 @@ static int producer_get_frame( mlt_service service, mlt_frame_ptr frame, int ind // We're done with the clone now mlt_properties_set_data( parent_properties, "use_clone", NULL, 0, NULL, NULL ); + // This is useful and required by always_active transitions to determine in/out points of the cut + if ( mlt_properties_get_data( MLT_FRAME_PROPERTIES( *frame ), "_producer", NULL ) == MLT_PRODUCER_SERVICE( parent ) ) + mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "_producer", this, 0, NULL, NULL ); + mlt_properties_set_double( MLT_FRAME_PROPERTIES( *frame ), "_speed", speed ); mlt_producer_prepare_next( this ); } diff --git a/src/framework/mlt_tractor.c b/src/framework/mlt_tractor.c index 4a26d0b..d41eba1 100644 --- a/src/framework/mlt_tractor.c +++ b/src/framework/mlt_tractor.c @@ -344,7 +344,7 @@ static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int tra if ( audio != NULL ) { mlt_frame_push_audio( *frame, audio ); - ( *frame )->get_audio = producer_get_audio; + mlt_frame_push_audio( *frame, producer_get_audio ); } if ( video != NULL ) diff --git a/src/framework/mlt_transition.c b/src/framework/mlt_transition.c index 435fdb6..0f1fceb 100644 --- a/src/framework/mlt_transition.c +++ b/src/framework/mlt_transition.c @@ -156,92 +156,150 @@ mlt_frame mlt_transition_process( mlt_transition this, mlt_frame a_frame, mlt_fr return this->process( this, a_frame, b_frame ); } -/** Get a frame from this filter. - - The logic is complex here. A transition is applied to frames on the a and b tracks - specified in the connect method above. Since all frames are obtained via this - method for all tracks, we have to take special care that we only obtain the a and - b frames once - we do this on the first call to get a frame from either a or b. - - After that, we have 2 cases to resolve: - - 1) if the track is the a_track and we're in the time zone, then we need to call the - process method to do the effect on the frame and remember we've passed it on - otherwise, we pass on the a_frame unmolested; - 2) For all other tracks, we get the frames on demand. +/** Get a frame from this transition. + + The logic is complex here. A transition is typically applied to frames on the a and + b tracks specified in the connect method above and only if both contain valid info + for the transition type (this is either audio or image). + + However, the fixed a_track may not always contain data of the correct type, eg: + + +---------+ +-------+ + |c1 | |c5 | <-- A(0,1) <-- B(0,2) <-- get frame + +---------+ +---------+-+-----+ | | + |c4 | <------+ | + +----------+-----------+-+---------+ | + |c2 |c3 | <-----------------+ + +----------+-------------+ + + During the overlap of c1 and c2, there is nothing for the A transition to do, so this + results in a no operation, but B is triggered. During the overlap of c2 and c3, again, + the A transition is inactive and because the B transition is pointing at track 0, + it too would be inactive. This isn't an ideal situation - it's better if the B + transition simply treats the frames from c3 as though they're the a track. + + For this to work, we cache all frames coming from all tracks between the a and b + tracks. Before we process, we determine that the b frame contains someting of the + right type and then we determine which frame to use as the a frame (selecting a + matching frame from a_track to b_track - 1). If both frames contain data of the + correct type, we process the transition. + + This method is invoked for each track and we return the cached frames as needed. + We clear the cache only when the requested frame is flagged as a 'last_track' frame. */ static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) { + int error = 0; mlt_transition this = service->child; mlt_properties properties = MLT_TRANSITION_PROPERTIES( this ); - int accepts_blanks = mlt_properties_get_int( properties, "_accepts_blanks" ); + int accepts_blanks = mlt_properties_get_int( properties, "accepts_blanks" ); int a_track = mlt_properties_get_int( properties, "a_track" ); int b_track = mlt_properties_get_int( properties, "b_track" ); mlt_position in = mlt_properties_get_position( properties, "in" ); mlt_position out = mlt_properties_get_position( properties, "out" ); int always_active = mlt_properties_get_int( properties, "always_active" ); + int type = mlt_properties_get_int( properties, "_transition_type" ); + int reverse_order = 0; - if ( ( index == a_track || index == b_track ) && !( this->a_held || this->b_held ) ) + // Ensure that we have the correct order + if ( a_track > b_track ) { - mlt_service_get_frame( this->producer, &this->a_frame, a_track ); - mlt_service_get_frame( this->producer, &this->b_frame, b_track ); - this->a_held = 1; - this->b_held = 1; + reverse_order = 1; + a_track = b_track; + b_track = mlt_properties_get_int( properties, "a_track" ); } - - // Special case track processing - if ( index == a_track ) + + // Only act on this operation once per multitrack iteration from the tractor + if ( !this->held ) { - // Determine if we're in the right time zone - mlt_position position = mlt_frame_get_position( this->a_frame ); - if ( always_active || ( position >= in && position <= out ) ) + int active = 0; + int i = 0; + int a_frame = a_track; + int b_frame = b_track; + mlt_position position; + int ( *invalid )( mlt_frame ) = type == 1 ? mlt_frame_is_test_card : mlt_frame_is_test_audio; + + // Initialise temporary store + if ( this->frames == NULL ) + this->frames = calloc( sizeof( mlt_frame ), b_track + 1 ); + + // Get all frames between a and b + for( i = a_track; i <= b_track; i ++ ) + mlt_service_get_frame( this->producer, &this->frames[ i ], i ); + + // We're holding these frames until the last_track frame property is received + this->held = 1; + + // When we need to locate the a_frame + switch( type ) { - if ( !accepts_blanks && ( this->b_frame == NULL || ( mlt_frame_is_test_card( this->b_frame ) && mlt_frame_is_test_audio( this->b_frame ) ) ) ) - { - *frame = this->a_frame; - } - else if ( !accepts_blanks && ( this->a_frame == NULL || ( mlt_frame_is_test_card( this->a_frame ) && mlt_frame_is_test_audio( this->a_frame ) ) ) ) - { - mlt_frame t = this->a_frame; - this->a_frame = this->b_frame; - this->b_frame = t; - *frame = this->a_frame; - } - else - { - int hide = 0; - *frame = mlt_transition_process( this, this->a_frame, this->b_frame ); - if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this->a_frame ), "test_image" ) ) - hide = 1; - if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this->a_frame ), "test_audio" ) ) - hide |= 2; - mlt_properties_set_int( MLT_FRAME_PROPERTIES( this->b_frame ), "hide", hide ); - } - this->a_held = 0; + case 1: + case 2: + // Some transitions (esp. audio) may accept blank frames + active = accepts_blanks; + + // If we're not active then... + if ( !active ) + { + // Hunt for the a_frame + while( a_frame <= b_frame && invalid( this->frames[ a_frame ] ) ) + a_frame ++; + + // Determine if we're active now + active = a_frame != b_frame && !invalid( this->frames[ b_frame ] ); + } + break; + + default: + fprintf( stderr, "invalid transition type\n" ); + break; } - else + + // Now handle the non-always active case + if ( active && !always_active ) { - // Pass on the 'a frame' and remember that we've done it - *frame = this->a_frame; - this->a_held = 0; + // For non-always-active transitions, we need the current position of the a frame + position = mlt_frame_get_position( this->frames[ a_frame ] ); + + // If a is in range, we're active + active = position >= in && position <= out; + } + + // Finally, process the a and b frames + if ( active ) + { + mlt_frame a_frame_ptr = this->frames[ !reverse_order ? a_frame : b_frame ]; + mlt_frame b_frame_ptr = this->frames[ !reverse_order ? b_frame : a_frame ]; + int a_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide" ); + int b_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide" ); + + // Process the transition + *frame = mlt_transition_process( this, a_frame_ptr, b_frame_ptr ); + + // We need to ensure that the tractor doesn't consider this frame for output + if ( *frame == a_frame_ptr ) + b_hide |= type; + else + a_hide |= type; + + mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide", a_hide ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide", b_hide ); } - return 0; - } - if ( index == b_track ) - { - // Pass on the 'b frame' and remember that we've done it - *frame = this->b_frame; - this->b_held = 0; - return 0; } + + // Obtain the frame from the cache or the producer we're attached to + if ( index >= a_track && index <= b_track ) + *frame = this->frames[ index ]; else - { - // Pass through - return mlt_service_get_frame( this->producer, frame, index ); - } + error = mlt_service_get_frame( this->producer, frame, index ); + + // Determine if that was the last track + this->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" ); + + return error; } /** Close the transition. @@ -259,6 +317,7 @@ void mlt_transition_close( mlt_transition this ) else { mlt_service_close( &this->parent ); + free( this->frames ); free( this ); } } diff --git a/src/framework/mlt_transition.h b/src/framework/mlt_transition.h index c6912db..9db26b3 100644 --- a/src/framework/mlt_transition.h +++ b/src/framework/mlt_transition.h @@ -44,10 +44,8 @@ struct mlt_transition_s mlt_service producer; // Private - mlt_frame a_frame; - mlt_frame b_frame; - int a_held; - int b_held; + mlt_frame *frames; + int held; }; /** Public final methods