From: Steinar H. Gunderson Date: Wed, 8 Jan 2014 00:38:35 +0000 (+0100) Subject: Make the Movit converter use the correct color primaries. X-Git-Url: https://git.sesse.net/?p=mlt;a=commitdiff_plain;h=424496d4cbce82ca24cc85536bd9d3fdd6593067 Make the Movit converter use the correct color primaries. We need to distinguish between the YUV primaries and the color space; for instance, my camera outputs Rec. 601/525 YUV but uses Rec. 709 color primaries. Also fix so that we read the correct full_luma flag, not just check force_full_luma. (Again, my camera outputs this.) Movit doesn't support all the exotic color spaces ffmpeg/libav does, but this should cover most of the common ones. --- diff --git a/src/modules/avformat/producer_avformat.c b/src/modules/avformat/producer_avformat.c index a0edb123..f2530308 100644 --- a/src/modules/avformat/producer_avformat.c +++ b/src/modules/avformat/producer_avformat.c @@ -102,7 +102,7 @@ struct producer_avformat_s unsigned int invalid_pts_counter; unsigned int invalid_dts_counter; mlt_cache image_cache; - int colorspace; + int yuv_colorspace, color_primaries; int full_luma; pthread_mutex_t video_mutex; pthread_mutex_t audio_mutex; @@ -1026,7 +1026,7 @@ static void get_audio_streams_info( producer_avformat self ) self->audio_streams, self->audio_max_stream, self->total_channels, self->max_channel ); } -static void set_luma_transfer( struct SwsContext *context, int colorspace, int use_full_range ) +static void set_luma_transfer( struct SwsContext *context, int yuv_colorspace, int use_full_range ) { int *coefficients; const int *new_coefficients; @@ -1039,7 +1039,7 @@ static void set_luma_transfer( struct SwsContext *context, int colorspace, int u // Don't change these from defaults unless explicitly told to. if ( use_full_range >= 0 ) full_range = use_full_range; - switch ( colorspace ) + switch ( yuv_colorspace ) { case 170: case 470: @@ -1159,7 +1159,7 @@ static void convert_image( producer_avformat self, AVFrame *frame, uint8_t *buff output.linesize[0] = width; output.linesize[1] = width >> 1; output.linesize[2] = width >> 1; - set_luma_transfer( context, self->colorspace, -1 ); + set_luma_transfer( context, self->yuv_colorspace, -1 ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); @@ -1170,7 +1170,7 @@ static void convert_image( producer_avformat self, AVFrame *frame, uint8_t *buff width, height, PIX_FMT_RGB24, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height ); - set_luma_transfer( context, self->colorspace, self->full_luma ); + set_luma_transfer( context, self->yuv_colorspace, self->full_luma ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); @@ -1181,7 +1181,7 @@ static void convert_image( producer_avformat self, AVFrame *frame, uint8_t *buff width, height, PIX_FMT_RGBA, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_RGBA, width, height ); - set_luma_transfer( context, self->colorspace, self->full_luma ); + set_luma_transfer( context, self->yuv_colorspace, self->full_luma ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); @@ -1192,7 +1192,7 @@ static void convert_image( producer_avformat self, AVFrame *frame, uint8_t *buff width, height, PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height ); - set_luma_transfer( context, self->colorspace, -1 ); + set_luma_transfer( context, self->yuv_colorspace, -1 ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); @@ -1822,31 +1822,47 @@ static int video_codec_init( producer_avformat self, int index, mlt_properties p mlt_properties_set_int( properties, "meta.media.frame_rate_den", frame_rate.den ); // Set the YUV colorspace from override or detect - self->colorspace = mlt_properties_get_int( properties, "force_colorspace" ); + self->yuv_colorspace = mlt_properties_get_int( properties, "force_colorspace" ); #if LIBAVCODEC_VERSION_INT > ((52<<16)+(28<<8)+0) - if ( ! self->colorspace ) + if ( ! self->yuv_colorspace ) { switch ( self->video_codec->colorspace ) { case AVCOL_SPC_SMPTE240M: - self->colorspace = 240; + self->yuv_colorspace = 240; break; case AVCOL_SPC_BT470BG: case AVCOL_SPC_SMPTE170M: - self->colorspace = 601; + self->yuv_colorspace = 601; break; case AVCOL_SPC_BT709: - self->colorspace = 709; + self->yuv_colorspace = 709; break; default: // This is a heuristic Charles Poynton suggests in "Digital Video and HDTV" - self->colorspace = self->video_codec->width * self->video_codec->height > 750000 ? 709 : 601; + self->yuv_colorspace = self->video_codec->width * self->video_codec->height > 750000 ? 709 : 601; break; } } #endif // Let apps get chosen colorspace - mlt_properties_set_int( properties, "meta.media.colorspace", self->colorspace ); + mlt_properties_set_int( properties, "meta.media.colorspace", self->yuv_colorspace ); + + switch ( self->video_codec->color_primaries ) + { + case AVCOL_PRI_BT470BG: + self->color_primaries = 601625; + break; + case AVCOL_PRI_SMPTE170M: + case AVCOL_PRI_SMPTE240M: + self->color_primaries = 601525; + break; + case AVCOL_PRI_BT709: + case AVCOL_PRI_UNSPECIFIED: + default: + self->color_primaries = 709; + break; + } self->full_luma = -1; #if LIBAVCODEC_VERSION_INT >= ((52<<16)+(72<<8)+2) @@ -1934,7 +1950,9 @@ static void producer_set_up_video( producer_avformat self, mlt_frame frame ) mlt_properties_set_int( properties, "meta.media.width", self->video_codec->width ); mlt_properties_set_int( properties, "meta.media.height", self->video_codec->height ); mlt_properties_set_double( frame_properties, "aspect_ratio", aspect_ratio ); - mlt_properties_set_int( frame_properties, "colorspace", self->colorspace ); + mlt_properties_set_int( frame_properties, "colorspace", self->yuv_colorspace ); + mlt_properties_set_int( frame_properties, "color_primaries", self->color_primaries ); + mlt_properties_set_int( frame_properties, "full_luma", self->full_luma ); // Workaround 1088 encodings missing cropping info. if ( self->video_codec->height == 1088 && mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ) == 16.0/9.0 ) diff --git a/src/modules/opengl/filter_movit_convert.cpp b/src/modules/opengl/filter_movit_convert.cpp index aaf43c94..2229aebc 100644 --- a/src/modules/opengl/filter_movit_convert.cpp +++ b/src/modules/opengl/filter_movit_convert.cpp @@ -65,6 +65,44 @@ static void delete_chain( EffectChain* chain ) delete chain; } +static void get_format_from_properties( mlt_properties properties, ImageFormat* image_format, YCbCrFormat* ycbcr_format ) +{ + switch ( mlt_properties_get_int( properties, "colorspace" ) ) { + case 601: + ycbcr_format->luma_coefficients = YCBCR_REC_601; + break; + case 709: + default: + ycbcr_format->luma_coefficients = YCBCR_REC_709; + break; + } + + switch ( mlt_properties_get_int( properties, "color_primaries" ) ) { + case 601625: + image_format->color_space = COLORSPACE_REC_601_625; + break; + case 601525: + image_format->color_space = COLORSPACE_REC_601_525; + break; + case 709: + default: + image_format->color_space = COLORSPACE_REC_709; + break; + } + + image_format->gamma_curve = GAMMA_REC_709; + + if ( mlt_properties_get_int( properties, "force_full_luma" ) ) { + ycbcr_format->full_range = true; + } else { + ycbcr_format->full_range = ( mlt_properties_get_int( properties, "full_luma" ) == 1 ); + } + + // TODO: make new frame properties set by producers + ycbcr_format->cb_x_position = ycbcr_format->cr_x_position = 0.0f; + ycbcr_format->cb_y_position = ycbcr_format->cr_y_position = 0.5f; +} + static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { // Nothing to do! @@ -140,59 +178,29 @@ static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *fo } } if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { + // TODO: Get the color space if available. input->useFlatInput( chain, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_rgb24 ) { + // TODO: Get the color space if available. input->useFlatInput( chain, FORMAT_RGB, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_yuv420p ) { ImageFormat image_format; YCbCrFormat ycbcr_format; - if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { - image_format.color_space = COLORSPACE_REC_709; - image_format.gamma_curve = GAMMA_REC_709; - ycbcr_format.luma_coefficients = YCBCR_REC_709; - } else if ( 576 == mlt_properties_get_int( properties, "height" ) ) { - image_format.color_space = COLORSPACE_REC_601_625; - image_format.gamma_curve = GAMMA_REC_601; - ycbcr_format.luma_coefficients = YCBCR_REC_601; - } else { - image_format.color_space = COLORSPACE_REC_601_525; - image_format.gamma_curve = GAMMA_REC_601; - ycbcr_format.luma_coefficients = YCBCR_REC_601; - } - ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); + get_format_from_properties( properties, &image_format, &ycbcr_format ); ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 2; - // TODO: make new frame properties set by producers - ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; - ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_yuv422 ) { ImageFormat image_format; YCbCrFormat ycbcr_format; - if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { - image_format.color_space = COLORSPACE_REC_709; - image_format.gamma_curve = GAMMA_REC_709; - ycbcr_format.luma_coefficients = YCBCR_REC_709; - } else if ( 576 == height ) { - image_format.color_space = COLORSPACE_REC_601_625; - image_format.gamma_curve = GAMMA_REC_601; - ycbcr_format.luma_coefficients = YCBCR_REC_601; - } else { - image_format.color_space = COLORSPACE_REC_601_525; - image_format.gamma_curve = GAMMA_REC_601; - ycbcr_format.luma_coefficients = YCBCR_REC_601; - } - ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); + get_format_from_properties( properties, &image_format, &ycbcr_format ); ycbcr_format.chroma_subsampling_x = 2; ycbcr_format.chroma_subsampling_y = 1; - // TODO: make new frame properties set by producers - ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; - ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); // convert chunky to planar