#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
+#include <string.h>
+#include <math.h>
/** Geometry struct.
*/
struct geometry_s
{
+ float position;
+ float mix;
int nw; // normalised width
int nh; // normalised height
int sw; // scaled width, not including consumer scale based upon w/nw
float y;
float w;
float h;
- float mix;
int halign; // horizontal alignment: 0=left, 1=center, 2=right
int valign; // vertical alignment: 0=top, 1=middle, 2=bottom
+ int distort;
+ struct geometry_s *next;
};
/** Parse a value from a geometry string.
geometry->y = defaults->y;
geometry->w = geometry->sw = defaults->w;
geometry->h = geometry->sh = defaults->h;
+ geometry->distort = defaults->distort;
geometry->mix = defaults->mix;
+ defaults->next = geometry;
}
else
{
}
// Parse the geomtry string
- if ( property != NULL )
+ if ( property != NULL && strcmp( property, "" ) )
{
char *ptr = property;
geometry->x = parse_value( &ptr, nw, ',', geometry->x );
geometry->y = parse_value( &ptr, nh, ':', geometry->y );
geometry->w = geometry->sw = parse_value( &ptr, nw, 'x', geometry->w );
geometry->h = geometry->sh = parse_value( &ptr, nh, ':', geometry->h );
+ if ( *ptr == '!' )
+ {
+ geometry->distort = 1;
+ ptr ++;
+ if ( *ptr == ':' )
+ ptr ++;
+ }
geometry->mix = parse_value( &ptr, 100, ' ', geometry->mix );
}
}
/** Calculate real geometry.
*/
-static void geometry_calculate( struct geometry_s *output, struct geometry_s *in, struct geometry_s *out, float position )
+static void geometry_calculate( struct geometry_s *output, struct geometry_s *in, float position )
{
+ // Search in for position
+ struct geometry_s *out = in->next;
+
+ if ( position >= 1.0 )
+ {
+ int section = floor( position );
+ position -= section;
+ if ( section % 2 == 1 )
+ position = 1.0 - position;
+ }
+
+ while ( out->next != NULL )
+ {
+ if ( position >= in->position && position < out->position )
+ break;
+
+ in = out;
+ out = in->next;
+ }
+
+ position = ( position - in->position ) / ( out->position - in->position );
+
// Calculate this frames geometry
output->nw = in->nw;
output->nh = in->nh;
output->w = in->w + ( out->w - in->w ) * position;
output->h = in->h + ( out->h - in->h ) * position;
output->mix = in->mix + ( out->mix - in->mix ) * position;
+ output->distort = in->distort;
+}
+
+void transition_destroy_keys( void *arg )
+{
+ struct geometry_s *ptr = arg;
+ struct geometry_s *next = NULL;
+
+ while ( ptr != NULL )
+ {
+ next = ptr->next;
+ free( ptr );
+ ptr = next;
+ }
+}
+
+static struct geometry_s *transition_parse_keys( mlt_transition this, int normalised_width, int normalised_height )
+{
+ // Loop variable for property interrogation
+ int i = 0;
+
+ // Get the properties of the transition
+ mlt_properties properties = mlt_transition_properties( this );
+
+ // Get the in and out position
+ mlt_position in = mlt_transition_get_in( this );
+ mlt_position out = mlt_transition_get_out( this );
+
+ // Create the start
+ struct geometry_s *start = calloc( 1, sizeof( struct geometry_s ) );
+
+ // Create the end (we always need two entries)
+ struct geometry_s *end = calloc( 1, sizeof( struct geometry_s ) );
+
+ // Pointer
+ struct geometry_s *ptr = start;
+
+ // Parse the start property
+ geometry_parse( start, NULL, mlt_properties_get( properties, "start" ), normalised_width, normalised_height );
+
+ // Parse the keys in between
+ for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+ {
+ // Get the name of the property
+ char *name = mlt_properties_get_name( properties, i );
+
+ // Check that it's valid
+ if ( !strncmp( name, "key[", 4 ) )
+ {
+ // Get the value of the property
+ char *value = mlt_properties_get_value( properties, i );
+
+ // Determine the frame number
+ int frame = atoi( name + 4 );
+
+ // Determine the position
+ float position = 0;
+
+ if ( frame >= 0 && frame < ( out - in ) )
+ position = ( float )frame / ( float )( out - in + 1 );
+ else if ( frame < 0 && - frame < ( out - in ) )
+ position = ( float )( out - in + frame ) / ( float )( out - in + 1 );
+
+ // For now, we'll exclude all keys received out of order
+ if ( position > ptr->position )
+ {
+ // Create a new geometry
+ struct geometry_s *temp = calloc( 1, sizeof( struct geometry_s ) );
+
+ // Parse and add to the list
+ geometry_parse( temp, ptr, value, normalised_width, normalised_height );
+
+ // Assign the position
+ temp->position = position;
+
+ // Allow the next to be appended after this one
+ ptr = temp;
+ }
+ else
+ {
+ fprintf( stderr, "Key out of order - skipping %s\n", name );
+ }
+ }
+ }
+
+ // Parse the end
+ geometry_parse( end, ptr, mlt_properties_get( properties, "end" ), normalised_width, normalised_height );
+ if ( out > 0 )
+ end->position = ( float )( out - in ) / ( float )( out - in + 1 );
+ else
+ end->position = 1;
+
+ // Assign to properties to ensure we get destroyed
+ mlt_properties_set_data( properties, "geometries", start, 0, transition_destroy_keys, NULL );
+
+ return start;
}
/** Parse the alignment properties into the geometry.
/** Calculate the position for this frame.
*/
-static float position_calculate( mlt_transition this, mlt_frame frame )
+static inline float position_calculate( mlt_transition this, mlt_frame frame )
{
// Get the in and out position
mlt_position in = mlt_transition_get_in( this );
mlt_position out = mlt_transition_get_out( this );
- // Get the position of the frame
+ // Get the position
mlt_position position = mlt_frame_get_position( frame );
// Now do the calcs
/** Calculate the field delta for this frame - position between two frames.
*/
-static float delta_calculate( mlt_transition this, mlt_frame frame )
+static inline float delta_calculate( mlt_transition this, mlt_frame frame )
{
// Get the in and out position
mlt_position in = mlt_transition_get_in( this );
// Now do the calcs
float x = ( float )( position - in ) / ( float )( out - in + 1 );
- position++;
- float y = ( float )( position - in ) / ( float )( out - in + 1 );
+ float y = ( float )( position + 1 - in ) / ( float )( out - in + 1 );
return ( y - x ) / 2.0;
}
static int composite_yuv( uint8_t *p_dest, int width_dest, int height_dest, int bpp, uint8_t *p_src, int width_src, int height_src, uint8_t *p_alpha, struct geometry_s geometry, int field )
{
int ret = 0;
- int i, j, k;
+ int i, j;
int x_src = 0, y_src = 0;
- float weight = geometry.mix / 100;
+ int32_t weight = ( 1 << 16 ) * ( geometry.mix / 100 );
int stride_src = width_src * bpp;
int stride_dest = width_dest * bpp;
uint8_t *z = p_alpha;
uint8_t a;
- float value;
+ int32_t value;
int step = ( field > -1 ) ? 2 : 1;
+ stride_src = stride_src * step;
+ int alpha_stride = stride_src / bpp;
+ stride_dest = stride_dest * step;
+
// now do the compositing only to cropped extents
for ( i = 0; i < height_src; i += step )
{
- p = &p_src[ i * stride_src ];
- q = &p_dest[ i * stride_dest ];
- o = &p_dest[ i * stride_dest ];
- if ( p_alpha )
- z = &p_alpha[ i * stride_src / bpp ];
+ p = p_src;
+ q = p_dest;
+ o = q;
+ z = p_alpha;
for ( j = 0; j < width_src; j ++ )
{
a = ( z == NULL ) ? 255 : *z ++;
- value = ( weight * ( float ) a / 255.0 );
- for ( k = 0; k < bpp; k ++ )
- *o ++ = (uint8_t)( *p++ * value + *q++ * ( 1 - value ) );
+ value = ( weight * ( a + 1 ) ) >> 8;
+ *o ++ = ( *p++ * value + *q++ * ( ( 1 << 16 ) - value ) ) >> 16;
+ *o ++ = ( *p++ * value + *q++ * ( ( 1 << 16 ) - value ) ) >> 16;
}
+
+ p_src += stride_src;
+ p_dest += stride_dest;
+ if ( p_alpha )
+ p_alpha += alpha_stride;
}
return ret;
/** Get the properly sized image from b_frame.
*/
-static int get_b_frame_image( mlt_frame b_frame, uint8_t **image, int *width, int *height, struct geometry_s *geometry )
+static int get_b_frame_image( mlt_transition this, mlt_frame b_frame, uint8_t **image, int *width, int *height, struct geometry_s *geometry )
{
int ret = 0;
mlt_image_format format = mlt_image_yuv422;
- // Initialise the scaled dimensions from the computed
- geometry->sw = geometry->w;
- geometry->sh = geometry->h;
-
- // Compute the dimensioning rectangle
+ // Get the properties objects
mlt_properties b_props = mlt_frame_properties( b_frame );
- mlt_transition this = mlt_properties_get_data( b_props, "transition_composite", NULL );
mlt_properties properties = mlt_transition_properties( this );
- if ( mlt_properties_get( properties, "distort" ) == NULL )
+ // ???: Not getting the logic of this...
+ geometry->sw = geometry->w;
+ geometry->sh = geometry->h;
+
+ if ( mlt_properties_get( properties, "distort" ) == NULL && geometry->distort == 0 )
{
// Adjust b_frame pixel aspect
int normalised_width = geometry->w;
int real_height = get_value( b_props, "real_height", "height" );
double input_ar = mlt_frame_get_aspect_ratio( b_frame );
double output_ar = mlt_properties_get_double( b_props, "consumer_aspect_ratio" );
- //int scaled_width = ( input_ar > output_ar ? input_ar / output_ar : output_ar / input_ar ) * real_width;
- //int scaled_height = ( input_ar > output_ar ? input_ar / output_ar : output_ar / input_ar ) * real_height;
int scaled_width = real_width;
int scaled_height = real_height;
double output_sar = ( double ) geometry->nw / geometry->nh / output_ar;
- // We always normalise pixel aspect by requesting a larger than normal
- // image in order to maximise usage of the bounding rectangle
-
- // These calcs are optimised by reducing factors in equations
-// This is disabled due to bad results on 480 wide MPEGs
-#if 0
- if ( output_sar < 1.0 )
- // If the output is skinny pixels (PAL) then stretch our input vertically
- // derived from: input_sar / output_sar * real_height
- scaled_height = ( double )real_width / input_ar / output_sar;
-
- else
-#endif
- // If the output is fat pixels (NTSC) then stretch our input horizontally
- // derived from: output_sar / input_sar * real_width
- scaled_width = output_sar * real_height * input_ar;
+ // If the output is fat pixels (NTSC) then stretch our input horizontally
+ // derived from: output_sar / input_sar * real_width
+ scaled_width = output_sar * real_height * input_ar;
-// fprintf( stderr, "composite: real %dx%d scaled %dx%d normalised %dx%d\n", real_width, real_height, scaled_width, scaled_height, normalised_width, normalised_height );
-
// Now ensure that our images fit in the normalised frame
if ( scaled_width > normalised_width )
{
scaled_height = normalised_height;
}
- // Now we need to align to the geometry
- if ( scaled_width <= geometry->w && scaled_height <= geometry->h )
+ // Now apply the fill
+ // TODO: Should combine fill/distort in one property
+ if ( mlt_properties_get( properties, "fill" ) != NULL )
{
- // Save the new scaled dimensions
- geometry->sw = scaled_width;
- geometry->sh = scaled_height;
+ scaled_width = ( geometry->w / scaled_width ) * scaled_width;
+ scaled_height = ( geometry->h / scaled_height ) * scaled_height;
}
+
+ // Save the new scaled dimensions
+ geometry->sw = scaled_width;
+ geometry->sh = scaled_height;
}
// We want to ensure that we bypass resize now...
x -= x % 2;
- //fprintf( stderr, "composite calculated %d,%d:%dx%d\n", x, y, *width, *height );
-
// optimization points - no work to do
if ( *width <= 0 || *height <= 0 )
return 1;
if ( ( x < 0 && -x >= *width ) || ( y < 0 && -y >= *height ) )
return 1;
- ret = mlt_frame_get_image( b_frame, image, &format, width, height, 1 /* writable */ );
+ ret = mlt_frame_get_image( b_frame, image, &format, width, height, 1 );
return ret;
}
// This compositer is yuv422 only
*format = mlt_image_yuv422;
+ // Get the transition from the a frame
+ mlt_transition this = mlt_frame_pop_service( a_frame );
+
// Get the image from the a frame
mlt_frame_get_image( a_frame, image, format, width, height, 1 );
// Get the properties of the b frame
mlt_properties b_props = mlt_frame_properties( b_frame );
- // Get the transition from the b frame
- mlt_transition this = mlt_properties_get_data( b_props, "transition_composite", NULL );
-
// Get the properties from the transition
mlt_properties properties = mlt_transition_properties( this );
// Structures for geometry
struct geometry_s result;
- struct geometry_s start;
- struct geometry_s end;
+ struct geometry_s *start = mlt_properties_get_data( properties, "geometries", NULL );
// Calculate the position
- float position = position_calculate( this, a_frame );
+ float position = mlt_properties_get_double( b_props, "relative_position" );
float delta = delta_calculate( this, a_frame );
- // Obtain the normalised width and height from the a_frame
- int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
- int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
-
// Now parse the geometries
- geometry_parse( &start, NULL, mlt_properties_get( properties, "start" ), normalised_width, normalised_height );
- geometry_parse( &end, &start, mlt_properties_get( properties, "end" ), normalised_width, normalised_height );
+ if ( start == NULL )
+ {
+ // Obtain the normalised width and height from the a_frame
+ int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
+ int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
- // Now parse the alignment
- result.halign = alignment_parse( mlt_properties_get( properties, "halign" ) );
- result.valign = alignment_parse( mlt_properties_get( properties, "valign" ) );
+ // Parse the transitions properties
+ start = transition_parse_keys( this, normalised_width, normalised_height );
+ }
// Since we are the consumer of the b_frame, we must pass along these
// consumer properties from the a_frame
mlt_properties_set_double( b_props, "consumer_scale", mlt_properties_get_double( a_props, "consumer_scale" ) );
// Do the calculation
- geometry_calculate( &result, &start, &end, position );
+ geometry_calculate( &result, start, position );
+
+ // Now parse the alignment
+ result.halign = alignment_parse( mlt_properties_get( properties, "halign" ) );
+ result.valign = alignment_parse( mlt_properties_get( properties, "valign" ) );
// Get the image from the b frame
- uint8_t *image_b;
+ uint8_t *image_b = NULL;
int width_b = *width;
int height_b = *height;
- if ( get_b_frame_image( b_frame, &image_b, &width_b, &height_b, &result ) == 0 )
+ if ( get_b_frame_image( this, b_frame, &image_b, &width_b, &height_b, &result ) == 0 )
{
uint8_t *dest = *image;
uint8_t *src = image_b;
mlt_properties_get_int( properties, "progressive" );
int field;
- // See if the alpha channel is our destination
- if ( mlt_properties_get( properties, "a_frame" ) != NULL )
- {
- bpp = 1;
-
- // Get or make the a_frame alpha channel
- dest = mlt_frame_get_alpha_mask( a_frame );
- if ( dest == NULL )
- {
- // Allocate the alpha
- dest = mlt_pool_alloc( *width * *height );
- mlt_properties_set_data( a_props, "alpha", dest, *width * *height, ( mlt_destructor )mlt_pool_release, NULL );
-
- // Set alpha call back
- a_frame->get_alpha_mask = transition_get_alpha_mask;
- }
-
- // If the source is an image, convert its YUV to an alpha channel
- if ( mlt_properties_get( properties, "b_frame" ) == NULL )
- {
- if ( alpha == NULL )
- {
- // Allocate the alpha
- alpha = mlt_pool_alloc( width_b * height_b );
- mlt_properties_set_data( b_props, "alpha", alpha, width_b * height_b, ( mlt_destructor )mlt_pool_release, NULL );
-
- // Set alpha call back
- b_frame->get_alpha_mask = transition_get_alpha_mask;
- }
-
- // Copy the Y values into alpha
- uint8_t *p = image_b;
- uint8_t *q = alpha;
- int i;
- for ( i = 0; i < width_b * height_b; i ++, p += 2 )
- *q ++ = *p;
-
- // Setup to composite from the alpha channel
- src = alpha;
- alpha = NULL;
- }
- }
-
- // See if the alpha channel is our source
- if ( mlt_properties_get( properties, "b_frame" ) != NULL )
- {
- // If we do not have an alpha channel fabricate it
- if ( alpha == NULL )
- {
- // Allocate the alpha
- alpha = mlt_pool_alloc( width_b * height_b );
- mlt_properties_set_data( b_props, "alpha", alpha, width_b * height_b, ( mlt_destructor )mlt_pool_release, NULL );
-
- // Set alpha call back
- b_frame->get_alpha_mask = transition_get_alpha_mask;
-
- // Copy the Y values into alpha
- uint8_t *p = image_b;
- uint8_t *q = alpha;
- int i;
- for ( i = 0; i < width_b * height_b; i ++, p += 2 )
- *q ++ = *p;
- }
-
- // If the destination is image, convert the alpha channel to YUV
- if ( mlt_properties_get( properties, "a_frame" ) == NULL )
- {
- uint8_t *p = alpha;
- uint8_t *q = image_b;
- int i;
-
- for ( i = 0; i < width_b * height_b; i ++, p ++ )
- {
- *q ++ = 16 + ( ( float )*p / 255 * 219 ); // 220 is the luma range from 16-235
- *q ++ = 128;
- }
- }
- else
- {
- // Setup to composite from the alpha channel
- src = alpha;
- bpp = 1;
- }
-
- // Never the apply the alpha channel to this type of operation
- alpha = NULL;
- }
-
for ( field = 0; field < ( progressive ? 1 : 2 ); field++ )
{
// Assume lower field (0) first
float field_position = position + field * delta;
- // Do the calculation
- geometry_calculate( &result, &start, &end, field_position );
+ // Do the calculation if we need to
+ geometry_calculate( &result, start, field_position );
// Align
alignment_calculate( &result );
static mlt_frame composite_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
{
// Propogate the transition properties to the b frame
- mlt_properties b_props = mlt_frame_properties( b_frame );
- mlt_properties_set_data( b_props, "transition_composite", this, 0, NULL, NULL );
+ mlt_properties_set_double( mlt_frame_properties( b_frame ), "relative_position", position_calculate( this, a_frame ) );
+ mlt_frame_push_service( a_frame, this );
mlt_frame_push_get_image( a_frame, transition_get_image );
mlt_frame_push_frame( a_frame, b_frame );
return a_frame;
{
this->process = composite_process;
mlt_properties_set( mlt_transition_properties( this ), "start", arg != NULL ? arg : "85%,5%:10%x10%" );
- mlt_properties_set( mlt_transition_properties( this ), "end", "" );
}
return this;
}