X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Favformat%2Ffilter_avcolour_space.c;h=cce62c5ea2021e62b11db5f93290c1c2136c54f2;hb=4fd6c14958ac5bd739c8d2b66b86cbf99ae68c23;hp=e606d802fd52b2161b89b9fdd87e8947d00be91a;hpb=3c40b90364e6ecfe5cb9e2491bd2a5e77df53426;p=mlt diff --git a/src/modules/avformat/filter_avcolour_space.c b/src/modules/avformat/filter_avcolour_space.c index e606d802..cce62c5e 100644 --- a/src/modules/avformat/filter_avcolour_space.c +++ b/src/modules/avformat/filter_avcolour_space.c @@ -1,6 +1,6 @@ /* * filter_avcolour_space.c -- Colour space filter - * Copyright (C) 2004-2005 Ushodaya Enterprises Limited + * Copyright (C) 2004-2014 Ushodaya Enterprises Limited * Author: Charles Yates * * This library is free software; you can redistribute it and/or @@ -21,17 +21,12 @@ #include #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 @@ -65,25 +60,69 @@ static 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 avcolor_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 void av_convert_image( mlt_properties properties, uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, int width, int height ) +static int set_luma_transfer( struct SwsContext *context, int src_colorspace, int dst_colorspace, int full_range ) +{ + const int *src_coefficients = sws_getCoefficients( SWS_CS_DEFAULT ); + const int *dst_coefficients = sws_getCoefficients( SWS_CS_DEFAULT ); + int brightness = 0; + int contrast = 1 << 16; + int saturation = 1 << 16; + + switch ( src_colorspace ) + { + case 170: + case 470: + case 601: + case 624: + src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + break; + case 240: + src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + break; + case 709: + src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + break; + default: + break; + } + switch ( dst_colorspace ) + { + case 170: + case 470: + case 601: + case 624: + src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + break; + case 240: + src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + break; + case 709: + src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + break; + default: + break; + } + return sws_setColorspaceDetails( context, src_coefficients, full_range, dst_coefficients, full_range, + brightness, contrast, saturation ); +} + +// returns set_lumage_transfer result +static int av_convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, + int width, int height, int src_colorspace, int dst_colorspace, int use_full_range ) { AVPicture input; AVPicture output; - avpicture_fill( &input, in, in_fmt, width, height ); - avpicture_fill( &output, out, out_fmt, width, height ); - -#ifdef SWSCALE - int flags = SWS_BILINEAR | SWS_ACCURATE_RND; - struct SwsContext *context = mlt_properties_get_data( properties, "avcolorspace.swscale", NULL ); - struct SwsContext *new_context; + int flags = SWS_BICUBIC | SWS_ACCURATE_RND; + int error = -1; if ( out_fmt == PIX_FMT_YUYV422 ) flags |= SWS_FULL_CHR_H_INP; @@ -95,18 +134,24 @@ static void av_convert_image( mlt_properties properties, uint8_t *out, uint8_t * #ifdef USE_SSE flags |= SWS_CPU_CAPS_MMX2; #endif - new_context = sws_getCachedContext( context, width, height, in_fmt, + if ( out_fmt == PIX_FMT_YUV420P && use_full_range ) + out_fmt = PIX_FMT_YUVJ420P; + + avpicture_fill( &input, in, in_fmt, width, height ); + avpicture_fill( &output, out, out_fmt, width, height ); + struct SwsContext *context = sws_getContext( width, height, in_fmt, width, height, out_fmt, flags, NULL, NULL, NULL); -// Disable using cached context here due to crashing in libswscale. -// if ( new_context != context ) -// mlt_properties_set_data( properties, "avcolorspace.swscale", new_context, 0, NULL, NULL ); - sws_scale( new_context, input.data, input.linesize, 0, height, - output.data, output.linesize); - sws_freeContext( new_context ); - -#else - img_convert( &output, out_fmt, &input, in_fmt, width, height ); -#endif + if ( context ) + { + // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. + if ( out_fmt == PIX_FMT_RGB24 || out_fmt == PIX_FMT_RGBA ) + dst_colorspace = 601; + error = set_luma_transfer( context, src_colorspace, dst_colorspace, use_full_range ); + sws_scale( context, (const uint8_t* const*) input.data, input.linesize, 0, height, + output.data, output.linesize); + sws_freeContext( context ); + } + return error; } /** Do it :-). @@ -121,12 +166,19 @@ static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *fo if ( *format != output_format ) { - mlt_log_debug( NULL, "[filter avcolor_space] %s -> %s\n", - mlt_image_format_name( *format ), mlt_image_format_name( output_format ) ); + mlt_profile profile = mlt_service_profile( + MLT_PRODUCER_SERVICE( mlt_frame_get_original_producer( frame ) ) ); + int colorspace = mlt_properties_get_int( properties, "colorspace" ); + int force_full_luma = 0; + + mlt_log_debug( NULL, "[filter avcolor_space] %s -> %s @ %dx%d space %d->%d\n", + mlt_image_format_name( *format ), mlt_image_format_name( output_format ), + width, height, colorspace, profile->colorspace ); 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 ); + 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 ) @@ -154,38 +206,124 @@ static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *fo } while ( --n > 0 ); } - mlt_properties_set_data( properties, "alpha", alpha, len, mlt_pool_release, NULL ); - frame->get_alpha_mask = NULL; + mlt_frame_set_alpha( frame, alpha, len, mlt_pool_release ); } } // Update the output - mlt_producer producer = mlt_frame_get_original_producer( frame ); - mlt_properties prod_props = MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer ) ); - av_convert_image( prod_props, output, *image, out_fmt, in_fmt, width, height ); + if ( *format == mlt_image_yuv422 && mlt_properties_get( properties, "force_full_luma" ) + && ( output_format == mlt_image_rgb24 || output_format == mlt_image_rgb24a ) ) + { + // 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 ); + } + if ( !av_convert_image( output, *image, out_fmt, in_fmt, width, height, + colorspace, profile->colorspace, force_full_luma ) ) + { + // The new colorspace is only valid if destination is YUV. + if ( output_format == mlt_image_yuv422 || output_format == mlt_image_yuv420p ) + mlt_properties_set_int( properties, "colorspace", profile->colorspace ); + } *image = output; *format = output_format; - mlt_properties_set_data( properties, "image", output, size, mlt_pool_release, NULL ); + 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 ) + { + // 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 ) + { + 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 ); + } + } + } } return error; } +/* TODO: Enable this to force colorspace conversion. Cost is heavy due to RGB conversions. */ +#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; - return frame; -} + // 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 ); -static void filter_close( mlt_filter filter ) -{ -#ifdef SWSCALE - struct SwsContext *context = mlt_properties_get_data( MLT_FILTER_PROPERTIES(filter), "avcolorspace.swscale", NULL ); - sws_freeContext( context ); -#endif + 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; } /** Constructor for the filter. @@ -193,28 +331,22 @@ static void filter_close( mlt_filter filter ) mlt_filter filter_avcolour_space_init( void *arg ) { -#ifdef SWSCALE -#if (LIBSWSCALE_VERSION_INT >= ((0<<16)+(7<<8)+2)) // Test to see if swscale accepts the arg as resolution if ( arg ) { - int width = (int) arg; - 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; - } -#else - return NULL; -#endif -#endif - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) - { - this->process = filter_process; - this->close = filter_close; + 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; + } } - return this; + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + filter->process = filter_process; + return filter; }