From 4b4a949708c794c1b5377f16a284ad4600cbddfe Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 29 Feb 2024 15:12:04 +0100 Subject: [PATCH] testcard y4m: support for >8 bit non-444 y4m --- src/video_capture/testcard.c | 52 +++++++++++++++++------------ src/video_codec.c | 65 ++++++++++++++++++++++++++++++++++++ src/video_codec.h | 4 +++ 3 files changed, 99 insertions(+), 22 deletions(-) diff --git a/src/video_capture/testcard.c b/src/video_capture/testcard.c index d7417cd37b..a5f1318073 100644 --- a/src/video_capture/testcard.c +++ b/src/video_capture/testcard.c @@ -12,7 +12,7 @@ */ /* * Copyright (c) 2005-2006 University of Glasgow - * Copyright (c) 2005-2023 CESNET z.s.p.o. + * Copyright (c) 2005-2024 CESNET z.s.p.o. * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -349,42 +349,50 @@ static size_t testcard_load_from_file_y4m(const char *filename, struct video_des return 0; } if (info.bitdepth < DEPTH8 || - ((info.subsampling < Y4M_SUBS_420 || - info.subsampling > Y4M_SUBS_444) && - info.bitdepth == DEPTH8) || - (info.bitdepth > DEPTH8 && info.subsampling != Y4M_SUBS_444)) { - MSG(ERROR, "Only 3-channel 8-bit Y4M or >8 bit 4:4:4 " - "subsampled are supported.\n"); + (info.subsampling < Y4M_SUBS_420 || + info.subsampling > Y4M_SUBS_444)) { + MSG(ERROR, "Only 3-channel 8+ bit Y4M is supported.\n"); log_msg(LOG_LEVEL_INFO, MOD_NAME "Provided Y4M picture has subsampling %d and bit depth %d bits.\n", info.subsampling, info.bitdepth); return 0; } desc->width = info.width; desc->height = info.height; - desc->color_spec = info.bitdepth == 8 ? UYVY : Y416; + desc->color_spec = info.bitdepth == DEPTH8 ? UYVY : Y416; size_t data_len = vc_get_datalen(desc->width, desc->height, desc->color_spec); unsigned char *converted = malloc(data_len); - if (info.bitdepth == 8) { - switch (info.subsampling) { - case Y4M_SUBS_420: + switch (info.subsampling) { + case Y4M_SUBS_420: + if (info.bitdepth == DEPTH8) { i420_8_to_uyvy(desc->width, desc->height, data, converted); - break; - case Y4M_SUBS_422: + } else { + i420_16_to_y416((int) desc->width, (int) desc->height, + data, converted, info.bitdepth); + } + break; + case Y4M_SUBS_422: + if (info.bitdepth == DEPTH8) { i422_8_to_uyvy(desc->width, desc->height, data, converted); - break; - case Y4M_SUBS_444: + } else { + i422_16_to_y416((int) desc->width, (int) desc->height, + data, converted, info.bitdepth); + } + break; + case Y4M_SUBS_444: + if (info.bitdepth == DEPTH8) { i444_8_to_uyvy(desc->width, desc->height, data, converted); - break; - default: - MSG(ERROR, "Wrong Y4M subsampling: %d", info.subsampling); - free(converted); - return 0; + } else { + i444_16_to_y416((int) desc->width, (int) desc->height, + data, converted, info.bitdepth); } - } else { - i444_16_to_y416(desc->width, desc->height, data, converted, info.bitdepth); + break; + default: + MSG(ERROR, "Wrong Y4M subsampling: %d", info.subsampling); + free(converted); + return 0; } *in_file_contents = (char *) converted; free(data); diff --git a/src/video_codec.c b/src/video_codec.c index e4b8332bed..26f19e4884 100644 --- a/src/video_codec.c +++ b/src/video_codec.c @@ -994,6 +994,71 @@ i444_16_to_y416(int width, int height, const unsigned char *in, } } +void +i422_16_to_y416(int width, int height, const unsigned char *in, + unsigned char *out, int in_depth) +{ + enum { ALPHA16_OPAQUE = 0xFFFF }; + const uint16_t *in_y = (const uint16_t *) (const void *) in; + const uint16_t *in_cb = in_y + (ptrdiff_t) width * height; + const uint16_t *in_cr = in_cb + (ptrdiff_t) ((width + 1) / 2) * height; + uint16_t *outp = (void *) out; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < (width + 1) / 2; ++x) { + *outp++ = *in_cb << (DEPTH16 - in_depth); + *outp++ = *in_y++ << (DEPTH16 - in_depth); + *outp++ = *in_cr << (DEPTH16 - in_depth); + *outp++ = ALPHA16_OPAQUE; + *outp++ = *in_cb++ << (DEPTH16 - in_depth); + *outp++ = *in_y++ << (DEPTH16 - in_depth); + *outp++ = *in_cr++ << (DEPTH16 - in_depth); + *outp++ = ALPHA16_OPAQUE; + } + } +} + +void +i420_16_to_y416(int width, int height, const unsigned char *in, + unsigned char *out, int in_depth) +{ + enum { ALPHA16_OPAQUE = 0xFFFF }; + const uint16_t *in_y1 = (const void *) in; + const uint16_t *in_y2 = in_y1 + width; + const uint16_t *in_cb = + (const uint16_t *) (const void *) in + (ptrdiff_t) width * height; + const uint16_t *in_cr = + in_cb + (ptrdiff_t) ((width + 1) / 2) * ((height + 1) / 2); + const size_t out_line_len = vc_get_linesize(width, Y416) / 2; + uint16_t *outp1 = (void *) out; + uint16_t *outp2 = outp1 + out_line_len; + for (int y = 0; y < (height + 1) / 2; ++y) { + for (int x = 0; x < (width + 1) / 2; ++x) { + *outp1++ = *in_cb << (DEPTH16 - in_depth); + *outp1++ = *in_y1++ << (DEPTH16 - in_depth); + *outp1++ = *in_cr << (DEPTH16 - in_depth); + *outp1++ = ALPHA16_OPAQUE; + + *outp2++ = *in_cb << (DEPTH16 - in_depth); + *outp2++ = *in_y2++ << (DEPTH16 - in_depth); + *outp2++ = *in_cr << (DEPTH16 - in_depth); + *outp2++ = ALPHA16_OPAQUE; + + *outp1++ = *in_cb << (DEPTH16 - in_depth); + *outp1++ = *in_y1++ << (DEPTH16 - in_depth); + *outp1++ = *in_cr << (DEPTH16 - in_depth); + *outp1++ = ALPHA16_OPAQUE; + + *outp2++ = *in_cb++ << (DEPTH16 - in_depth); + *outp2++ = *in_y2++ << (DEPTH16 - in_depth); + *outp2++ = *in_cr++ << (DEPTH16 - in_depth); + *outp2++ = ALPHA16_OPAQUE; + } + outp1 += out_line_len; + outp2 += out_line_len; + in_y1 += width; + in_y2 += width; + } +} void i420_8_to_uyvy(int width, int height, const unsigned char *in, unsigned char *out) { diff --git a/src/video_codec.h b/src/video_codec.h index 28715b9f24..1caa56bd48 100644 --- a/src/video_codec.h +++ b/src/video_codec.h @@ -112,6 +112,10 @@ void y416_to_i444(int width, int height, const unsigned char *in, unsigned char *out, int depth); void i444_16_to_y416(int width, int height, const unsigned char *in, unsigned char *out, int in_depth); +void i422_16_to_y416(int width, int height, const unsigned char *in, + unsigned char *out, int in_depth); +void i420_16_to_y416(int width, int height, const unsigned char *in, + unsigned char *out, int in_depth); void i420_8_to_uyvy(int width, int height, const unsigned char *in, unsigned char *out); void i422_8_to_uyvy(int width, int height, const unsigned char *in,