diff --git a/fluxer_media_proxy/src/media_process/tests/video.rs b/fluxer_media_proxy/src/media_process/tests/video.rs index 6c78dcf8d..e6683fda8 100644 --- a/fluxer_media_proxy/src/media_process/tests/video.rs +++ b/fluxer_media_proxy/src/media_process/tests/video.rs @@ -58,6 +58,42 @@ fn video_thumbnail_accepts_bt470bg_pal_primaries() { assert_eq!(png_dimensions(&thumb.bytes), Some((320, 240))); } +#[test] +fn a_bt709_thumbnail_keeps_the_source_shadow_luminance() { + let Some(video) = ffmpeg_gen_media( + "shadow.mkv", + &[ + "-f", + "lavfi", + "-i", + "color=c=0x181818:size=320x240:rate=10:duration=1", + "-vf", + "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=tv", + "-pix_fmt", + "yuv420p", + "-c:v", + "mpeg4", + "-f", + "matroska", + ], + ) else { + eprintln!("skipping: ffmpeg CLI not available"); + return; + }; + let thumb = extract_video_thumbnail(&video, OutputFormat::PNG, &test_media_limits()) + .expect("bt709 shadow video should thumbnail"); + let (_, _, pixels) = decode_rgba(&thumb.bytes); + let luma: Vec = pixels + .chunks_exact(4) + .map(|p| 0.2126 * f64::from(p[0]) + 0.7152 * f64::from(p[1]) + 0.0722 * f64::from(p[2])) + .collect(); + let mean = luma.iter().sum::() / luma.len() as f64; + assert!( + (mean - 24.0).abs() <= 8.0, + "a bt709 source must not be lifted by a transfer conversion: mean luminance {mean}" + ); +} + #[test] fn video_thumbnail_accepts_every_widened_sdr_transfer() { let transfers = ["smpte240m", "linear", "iec61966-2-1", "bt470m", "bt470bg"]; diff --git a/fluxer_media_proxy/src/native_shim/av_frame_rgba.c b/fluxer_media_proxy/src/native_shim/av_frame_rgba.c index fe7b5d3a3..23c705f11 100644 --- a/fluxer_media_proxy/src/native_shim/av_frame_rgba.c +++ b/fluxer_media_proxy/src/native_shim/av_frame_rgba.c @@ -4,11 +4,9 @@ enum ff_sdr_transfer { FF_SDR_TRANSFER_SRGB = 0, - FF_SDR_TRANSFER_BT709 = 1, - FF_SDR_TRANSFER_LINEAR = 2, + FF_SDR_TRANSFER_LINEAR = 1, }; -static uint8_t ff_bt709_to_srgb_lut[256]; static uint8_t ff_linear_to_srgb_lut[256]; static pthread_once_t ff_transfer_lut_once = PTHREAD_ONCE_INIT; @@ -25,18 +23,12 @@ static uint8_t ff_encode_srgb_byte(double linear) { static void ff_initialize_transfer_luts(void) { for (int index = 0; index < 256; index++) { double encoded = (double)index / 255.0; - double linear = encoded < 0.081 - ? encoded / 4.5 - : pow((encoded + 0.099) / 1.099, 1.0 / 0.45); - ff_bt709_to_srgb_lut[index] = ff_encode_srgb_byte(linear); ff_linear_to_srgb_lut[index] = ff_encode_srgb_byte(encoded); } } static const uint8_t *ff_transfer_to_srgb_lut(enum ff_sdr_transfer transfer) { switch (transfer) { - case FF_SDR_TRANSFER_BT709: - return ff_bt709_to_srgb_lut; case FF_SDR_TRANSFER_LINEAR: return ff_linear_to_srgb_lut; default: @@ -89,7 +81,7 @@ static int ff_frame_sdr_transfer( case AVCOL_TRC_BT709: case AVCOL_TRC_SMPTE170M: case AVCOL_TRC_SMPTE240M: - *out_transfer = FF_SDR_TRANSFER_BT709; + *out_transfer = FF_SDR_TRANSFER_SRGB; return 0; case AVCOL_TRC_LINEAR: *out_transfer = FF_SDR_TRANSFER_LINEAR;