X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Favformat%2Ffilter_avcolour_space.c;h=2e5fc6244d597ad620ab315f67537e494687a71b;hb=c24a9369d821bc07e44c2d6b046d4ceb404e7541;hp=1cee5f9c2f72162bb09f24869e55cf9ccf9ce7c1;hpb=2657693db7ce32ddb0a48b677e4cec6101a35e20;p=mlt diff --git a/src/modules/avformat/filter_avcolour_space.c b/src/modules/avformat/filter_avcolour_space.c index 1cee5f9c..2e5fc624 100644 --- a/src/modules/avformat/filter_avcolour_space.c +++ b/src/modules/avformat/filter_avcolour_space.c @@ -21,29 +21,26 @@ #include #include #include +#include // ffmpeg Header files -#include -#ifdef SWSCALE -#include -#endif - -#if LIBAVUTIL_VERSION_INT < (50<<16) -#define PIX_FMT_YUYV422 PIX_FMT_YUV422 -#endif +#include +#include #include #include -static inline int is_big_endian( ) +#if 0 // This test might come in handy elsewhere someday. +static int is_big_endian( ) { union { int i; char c[ 4 ]; } big_endian_test; big_endian_test.i = 1; return big_endian_test.c[ 0 ] != 1; } +#endif -static inline int convert_mlt_to_av_cs( mlt_image_format format ) +static int convert_mlt_to_av_cs( mlt_image_format format ) { int value = 0; @@ -62,35 +59,85 @@ static inline int convert_mlt_to_av_cs( mlt_image_format format ) case mlt_image_yuv420p: value = PIX_FMT_YUV420P; break; - case mlt_image_none: - mlt_log_error( NULL, "[filter avcolour_space] Invalid format\n" ); + default: + mlt_log_error( NULL, "[filter avcolor_space] Invalid format %s\n", + mlt_image_format_name( format ) ); break; } return value; } -static inline void av_convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, int width, int height ) +static void set_luma_transfer( struct SwsContext *context, int colorspace, int use_full_range ) +{ + int *coefficients; + const int *new_coefficients; + int full_range; + int brightness, contrast, saturation; + + if ( sws_getColorspaceDetails( context, &coefficients, &full_range, &coefficients, &full_range, + &brightness, &contrast, &saturation ) != -1 ) + { + // Don't change these from defaults unless explicitly told to. + if ( use_full_range >= 0 ) + full_range = use_full_range; + switch ( colorspace ) + { + case 170: + case 470: + case 601: + case 624: + new_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + break; + case 240: + new_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + break; + case 709: + new_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + break; + default: + new_coefficients = coefficients; + break; + } + sws_setColorspaceDetails( context, new_coefficients, full_range, new_coefficients, full_range, + brightness, contrast, saturation ); + } +} + +static void av_convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, + int width, int height, int colorspace, int use_full_range ) { AVPicture input; AVPicture output; + int flags = SWS_BICUBIC | SWS_ACCURATE_RND; + + if ( out_fmt == PIX_FMT_YUYV422 ) + flags |= SWS_FULL_CHR_H_INP; + else + flags |= SWS_FULL_CHR_H_INT; +#ifdef USE_MMX + flags |= SWS_CPU_CAPS_MMX; +#endif +#ifdef USE_SSE + flags |= SWS_CPU_CAPS_MMX2; +#endif + avpicture_fill( &input, in, in_fmt, width, height ); avpicture_fill( &output, out, out_fmt, width, height ); -#ifdef SWSCALE struct SwsContext *context = sws_getContext( width, height, in_fmt, - width, height, out_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL); - sws_scale( context, input.data, input.linesize, 0, height, - output.data, output.linesize); - sws_freeContext( context ); -#else - img_convert( &output, out_fmt, &input, in_fmt, width, height ); -#endif + width, height, out_fmt, flags, NULL, NULL, NULL); + if ( context ) + { + set_luma_transfer( context, colorspace, use_full_range ); + sws_scale( context, (const uint8_t* const*) input.data, input.linesize, 0, height, + output.data, output.linesize); + sws_freeContext( context ); + } } /** Do it :-). */ -// static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); @@ -100,134 +147,156 @@ static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *fo if ( *format != output_format ) { - mlt_log_debug( NULL, "[filter avcolour_space] %s -> %s\n", - mlt_image_format_name( *format ), mlt_image_format_name( output_format ) ); - if ( output_format != mlt_image_opengl ) + int colorspace = mlt_properties_get_int( properties, "colorspace" ); + int force_full_luma = -1; + + mlt_log_debug( NULL, "[filter avcolor_space] %s -> %s @ %dx%d space %d\n", + mlt_image_format_name( *format ), mlt_image_format_name( output_format ), + width, height, colorspace ); + + int in_fmt = convert_mlt_to_av_cs( *format ); + int out_fmt = convert_mlt_to_av_cs( output_format ); + int size = FFMAX( avpicture_get_size( out_fmt, width, height ), + mlt_image_format_size( output_format, width, height, NULL ) ); + uint8_t *output = mlt_pool_alloc( size ); + + if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { - int in_fmt = convert_mlt_to_av_cs( *format ); - int out_fmt = convert_mlt_to_av_cs( output_format ); - int size = avpicture_get_size( out_fmt, width, height ); - uint8_t *output = mlt_pool_alloc( size ); - av_convert_image( output, *image, out_fmt, in_fmt, width, height ); - - // Special case for alpha rgb input - //if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) -if (0) + register int len = width * height; + uint8_t *alpha = mlt_pool_alloc( len ); + + if ( alpha ) { - register uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); - register int len = width * height; - register uint8_t *bits = *image; + // Extract the alpha mask from the RGBA image using Duff's Device + register uint8_t *s = *image + 3; // start on the alpha component + register uint8_t *d = alpha; register int n = ( len + 7 ) / 8; - - if( !is_big_endian( ) ) - bits += 3; - - // Extract alpha mask from the image using Duff's Device - switch( len % 8 ) + + switch ( len % 8 ) { - case 0: do { *alpha ++ = *bits; bits += 4; - case 7: *alpha ++ = *bits; bits += 4; - case 6: *alpha ++ = *bits; bits += 4; - case 5: *alpha ++ = *bits; bits += 4; - case 4: *alpha ++ = *bits; bits += 4; - case 3: *alpha ++ = *bits; bits += 4; - case 2: *alpha ++ = *bits; bits += 4; - case 1: *alpha ++ = *bits; bits += 4; + case 0: do { *d++ = *s; s += 4; + case 7: *d++ = *s; s += 4; + case 6: *d++ = *s; s += 4; + case 5: *d++ = *s; s += 4; + case 4: *d++ = *s; s += 4; + case 3: *d++ = *s; s += 4; + case 2: *d++ = *s; s += 4; + case 1: *d++ = *s; s += 4; } - while( --n ); - } - } - - // Update the output - *image = output; - *format = output_format; - mlt_properties_set_data( properties, "image", output, size, mlt_pool_release, NULL ); - mlt_properties_set_int( properties, "format", output_format ); - - // Special case for alpha rgb output -// if ( output_format == mlt_image_rgb24a ) -if (0) - { - // Fetch the alpha - register uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); - - if ( alpha != NULL ) - { - register uint8_t *bits = *image; - register int len = width * height; - register int n = ( len + 7 ) / 8; - - if( !is_big_endian( ) ) - bits += 3; - - // Merge the alpha mask into the RGB image using Duff's Device - switch( len % 8 ) - { - case 0: do { *bits = *alpha++; bits += 4; - case 7: *bits = *alpha++; bits += 4; - case 6: *bits = *alpha++; bits += 4; - case 5: *bits = *alpha++; bits += 4; - case 4: *bits = *alpha++; bits += 4; - case 3: *bits = *alpha++; bits += 4; - case 2: *bits = *alpha++; bits += 4; - case 1: *bits = *alpha++; bits += 4; - } - while( --n ); - } + while ( --n > 0 ); } + mlt_frame_set_alpha( frame, alpha, len, mlt_pool_release ); } } - else if ( *format == mlt_image_yuv422 ) // && output_format == mlt_image_opengl + + // Update the output + if ( *format == mlt_image_yuv422 && mlt_properties_get( properties, "force_full_luma" ) + && ( output_format == mlt_image_rgb24 || output_format == mlt_image_rgb24a ) ) { - int size = width * height * 4; - uint8_t *output = mlt_pool_alloc( size ); - int h = height; - int w = width; - uint8_t *o = output + size; - int ostride = w * 4; - uint8_t *p = *image; - uint8_t *alpha = mlt_frame_get_alpha_mask( frame ) + width * height; - int r, g, b; - - while( h -- ) + // By removing the frame property we only permit the luma to skip scaling once. + // Thereafter, we let swscale scale the luma range as it pleases since it seems + // we do not have control over the RGB to YUV conversion. + force_full_luma = mlt_properties_get_int( properties, "force_full_luma" ); + mlt_properties_set( properties, "force_full_luma", NULL ); + } + av_convert_image( output, *image, out_fmt, in_fmt, width, height, colorspace, force_full_luma ); + *image = output; + *format = output_format; + mlt_frame_set_image( frame, output, size, mlt_pool_release ); + mlt_properties_set_int( properties, "format", output_format ); + + if ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl ) + { + register int len = width * height; + int alpha_size = 0; + uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); + mlt_properties_get_data( properties, "alpha", &alpha_size ); + + if ( alpha && alpha_size >= len ) { - w = width; - o -= ostride; - alpha -= width; - while( w >= 2 ) + // Merge the alpha mask from into the RGBA image using Duff's Device + register uint8_t *s = alpha; + register uint8_t *d = *image + 3; // start on the alpha component + register int n = ( len + 7 ) / 8; + + switch ( len % 8 ) { - YUV2RGB( *p, *( p + 1 ), *( p + 3 ), r, g, b ); - *o ++ = r; - *o ++ = g; - *o ++ = b; - *o ++ = *alpha ++; - YUV2RGB( *( p + 2 ), *( p + 1 ), *( p + 3 ), r, g, b ); - *o ++ = r; - *o ++ = g; - *o ++ = b; - *o ++ = *alpha ++; - w -= 2; - p += 4; + case 0: do { *d = *s++; d += 4; + case 7: *d = *s++; d += 4; + case 6: *d = *s++; d += 4; + case 5: *d = *s++; d += 4; + case 4: *d = *s++; d += 4; + case 3: *d = *s++; d += 4; + case 2: *d = *s++; d += 4; + case 1: *d = *s++; d += 4; + } + while ( --n > 0 ); } - o -= ostride; - alpha -= width; } + } + } + return error; +} - mlt_properties_set_data( properties, "image", output, size, mlt_pool_release, NULL ); - mlt_properties_set_int( properties, "format", output_format ); - *image = output; - *format = output_format; +/* TODO: The below is not working because swscale does not have + * adjustable coefficients yet for RGB->YUV */ +#if 0 +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error = 0; + mlt_profile profile = (mlt_profile) mlt_frame_pop_get_image( frame ); + mlt_properties properties = MLT_FRAME_PROPERTIES(frame); + mlt_image_format format_from = *format; + mlt_image_format format_to = mlt_image_rgb24; + + error = mlt_frame_get_image( frame, image, format, width, height, writable ); + + int frame_colorspace = mlt_properties_get_int( properties, "colorspace" ); + + if ( !error && *format == mlt_image_yuv422 && profile->colorspace > 0 && + frame_colorspace > 0 && frame_colorspace != profile->colorspace ) + { + mlt_log_debug( NULL, "[filter avcolor_space] colorspace %d -> %d\n", + frame_colorspace, profile->colorspace ); + + // Convert to RGB using frame's colorspace + error = convert_image( frame, image, &format_from, format_to ); + + // Convert to YUV using profile's colorspace + if ( !error ) + { + *image = mlt_properties_get_data( properties, "image", NULL ); + format_from = mlt_image_rgb24; + format_to = *format; + mlt_properties_set_int( properties, "colorspace", profile->colorspace ); + error = convert_image( frame, image, &format_from, format_to ); + *image = mlt_properties_get_data( properties, "image", NULL ); } } + return error; } +#endif /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - frame->convert_image = convert_image; + // Set a default colorspace on the frame if not yet set by the producer. + // The producer may still change it during get_image. + // This way we do not have to modify each producer to set a valid colorspace. + mlt_properties properties = MLT_FRAME_PROPERTIES(frame); + if ( mlt_properties_get_int( properties, "colorspace" ) <= 0 ) + mlt_properties_set_int( properties, "colorspace", mlt_service_profile( MLT_FILTER_SERVICE(filter) )->colorspace ); + + if ( !frame->convert_image ) + frame->convert_image = convert_image; + +// Not working yet - see comment for get_image() above. +// mlt_frame_push_service( frame, mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ); +// mlt_frame_push_get_image( frame, get_image ); + return frame; } @@ -236,9 +305,22 @@ static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) mlt_filter filter_avcolour_space_init( void *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) - this->process = filter_process; - return this; + // Test to see if swscale accepts the arg as resolution + if ( arg ) + { + int *width = (int*) arg; + if ( *width > 0 ) + { + struct SwsContext *context = sws_getContext( *width, *width, PIX_FMT_RGB32, 64, 64, PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); + if ( context ) + sws_freeContext( context ); + else + return NULL; + } + } + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + filter->process = filter_process; + return filter; }