Skip to content

Commit

Permalink
from_lavc_vid_conv: handle full-range input
Browse files Browse the repository at this point in the history
If CS conversion is done, handle also full-range input. This shouldn't be
the case most of the time (we use always limited range) but the case that
is handled is namely a JPEG from a webcam or so, which is 601
limited. Full YCbCr->YCbCr is not handled by this commit.
  • Loading branch information
MartinPulec committed Oct 8, 2024
1 parent 14554b8 commit e868262
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 20 deletions.
21 changes: 15 additions & 6 deletions src/color.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,13 @@
#else
#define C_EPS 0.5
#define Y_LIMIT(out_depth) \
(219. * (1 << ((out_depth) - 8)) / ((1 << (out_depth)) - 1))
(out_depth == 0 \
? 1.0 \
: (219. * (1 << ((out_depth) - 8)) / ((1 << (out_depth)) - 1)))
#define CBCR_LIMIT(out_depth) \
(224. * (1 << ((out_depth) - 8)) / ((1 << (out_depth)) - 1))
(out_depth == 0 \
? 1.0 \
: (224. * (1 << ((out_depth) - 8)) / ((1 << (out_depth)) - 1)))
#endif // !defined YCBCR_FULL

#define Y_LIMIT_INV(in_depth) (1./Y_LIMIT(in_depth))
Expand Down Expand Up @@ -133,6 +137,7 @@ ADD_TO_PARAM("color-601", "* color-601\n"
*
* Using BT.709 by default.
*
* @param ycbcr_bit_depth limited YCbCr scale; 0 for full-range
*
* @note
* It is suggested to copy the result to a struct (not using the returned ptr
Expand All @@ -154,20 +159,24 @@ get_color_coeffs(enum colorspace cs, int ycbcr_bit_depth)
static const struct {
struct color_coeffs col_cfs[2];
} coeffs[] = {
{ { COEFFS_601(0), COEFFS_709(0) } },
{ { COEFFS_601(DEPTH8), COEFFS_709(DEPTH8) } },
{ { COEFFS_601(DEPTH10), COEFFS_709(DEPTH10) } },
{ { COEFFS_601(DEPTH12), COEFFS_709(DEPTH12) } },
{ { COEFFS_601(DEPTH16), COEFFS_709(DEPTH16) } }
};
if (ycbcr_bit_depth == 0) { // full-range
return &coeffs[0].col_cfs[cs_idx];
}
switch ((enum depth) ycbcr_bit_depth) {
case DEPTH8:
return &coeffs[0].col_cfs[cs_idx];
case DEPTH10:
return &coeffs[1].col_cfs[cs_idx];
case DEPTH12:
case DEPTH10:
return &coeffs[2].col_cfs[cs_idx];
case DEPTH16:
case DEPTH12:
return &coeffs[3].col_cfs[cs_idx];
case DEPTH16:
return &coeffs[4].col_cfs[cs_idx];
}

fprintf(stderr, "%s: Wrong depth %d!\n", __func__, ycbcr_bit_depth);
Expand Down
34 changes: 21 additions & 13 deletions src/libavcodec/from_lavc_vid_conv.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ struct av_conv_data {
size_t pitch;
int rgb_shift[3];
enum colorspace cs_coeffs;
int lmt_rng; // 1 (limited) or 0 for full range
};

static void
Expand Down Expand Up @@ -316,7 +317,7 @@ yuv444pXXle_to_r10k(struct av_conv_data d, int depth)
assert((uintptr_t) frame->linesize[1] % 2 == 0);
assert((uintptr_t) frame->linesize[2] % 2 == 0);

const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, depth);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, depth * d.lmt_rng);
for (int y = 0; y < height; ++y) {
uint16_t *src_y = (uint16_t *)(void *) (frame->data[0] + frame->linesize[0] * y);
uint16_t *src_cb = (uint16_t *)(void *) (frame->data[1] + frame->linesize[1] * y);
Expand Down Expand Up @@ -379,7 +380,7 @@ yuv444pXXle_to_r12l(struct av_conv_data d, int depth)
assert((uintptr_t) frame->linesize[1] % 2 == 0);
assert((uintptr_t) frame->linesize[2] % 2 == 0);

const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, depth);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, depth * d.lmt_rng);
for (int y = 0; y < height; ++y) {
uint16_t *src_y = (uint16_t *)(void *) (frame->data[0] + frame->linesize[0] * y);
uint16_t *src_cb = (uint16_t *)(void *) (frame->data[1] + frame->linesize[1] * y);
Expand Down Expand Up @@ -478,7 +479,7 @@ yuv444pXXle_to_rg48(struct av_conv_data d, int depth)
assert((uintptr_t) frame->linesize[1] % 2 == 0);
assert((uintptr_t) frame->linesize[2] % 2 == 0);

const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, depth);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, depth * d.lmt_rng);
for (int y = 0; y < height; ++y) {
uint16_t *src_y = (uint16_t *)(void *) (frame->data[0] + frame->linesize[0] * y);
uint16_t *src_cb = (uint16_t *)(void *) (frame->data[1] + frame->linesize[1] * y);
Expand Down Expand Up @@ -1148,7 +1149,7 @@ nv12_to_rgb(struct av_conv_data d, bool rgba)
const uint32_t alpha_mask = 0xFFFFFFFFU ^ (0xFFU << d.rgb_shift[R]) ^
(0xFFU << d.rgb_shift[G]) ^
(0xFFU << d.rgb_shift[B]);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH * d.lmt_rng);

for(int y = 0; y < height; ++y) {
unsigned char *src_y = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y;
Expand Down Expand Up @@ -1213,7 +1214,7 @@ static inline void yuv8p_to_rgb(struct av_conv_data d, int subsampling, bool rgb
uint32_t alpha_mask = 0xFFFFFFFFU ^ (0xFFU << d.rgb_shift[R]) ^
(0xFFU << d.rgb_shift[G]) ^
(0xFFU << d.rgb_shift[B]);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH * d.lmt_rng);

for(int y = 0; y < height / 2; ++y) {
unsigned char *src_y1 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y * 2;
Expand Down Expand Up @@ -1331,7 +1332,7 @@ yuv444p_to_rgb(struct av_conv_data d, bool rgba)
uint32_t alpha_mask = 0xFFFFFFFFU ^ (0xFFU << d.rgb_shift[R]) ^
(0xFFU << d.rgb_shift[G]) ^
(0xFFU << d.rgb_shift[B]);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH * d.lmt_rng);

for(int y = 0; y < height; ++y) {
unsigned char *src_y = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y;
Expand Down Expand Up @@ -1722,7 +1723,7 @@ yuvp10le_to_rgb(struct av_conv_data d, int subsampling, int out_bit_depth)
(0xFFU << d.rgb_shift[G]) ^
(0xFFU << d.rgb_shift[B]);
const int bpp = out_bit_depth == 30 ? 10 : 8;
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH * d.lmt_rng);

for (int y = 0; y < height / 2; ++y) {
uint16_t * __restrict src_y1 = (uint16_t *)(void *) (frame->data[0] + frame->linesize[0] * 2 * y);
Expand Down Expand Up @@ -1832,7 +1833,7 @@ yuv444p10le_to_rgb(struct av_conv_data d, bool rgba)
const uint32_t alpha_mask = 0xFFFFFFFFU ^ (0xFFU << d.rgb_shift[R]) ^
(0xFFU << d.rgb_shift[G]) ^
(0xFFU << d.rgb_shift[B]);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH);
const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, S_DEPTH * d.lmt_rng);

for (int y = 0; y < height; y++) {
uint16_t *src_y = (uint16_t *)(void *)(in_frame->data[0] + in_frame->linesize[0] * y);
Expand Down Expand Up @@ -2927,7 +2928,8 @@ do_av_to_uv_convert(const av_to_uv_convert_t *s, char *__restrict dst_buffer,
inf,
pitch,
{ rgb_shift[0], rgb_shift[1], rgb_shift[2] },
cs_coeffs
cs_coeffs,
inf->color_range == AVCOL_RANGE_JPEG ? 0 : 1,
});
DEBUG_TIMER_STOP(lavd_av_to_uv);
return;
Expand All @@ -2941,7 +2943,8 @@ do_av_to_uv_convert(const av_to_uv_convert_t *s, char *__restrict dst_buffer,
inf,
src_linesize,
DEFAULT_RGB_SHIFT_INIT,
cs_coeffs
cs_coeffs,
inf->color_range == AVCOL_RANGE_JPEG ? 0 : 1,
});
DEBUG_TIMER_STOP(lavd_av_to_uv);
}
Expand Down Expand Up @@ -2987,11 +2990,11 @@ convert_task(void *arg)
* @return check color space/range if we can correctly convert
*/
static void
check_constraints(AVFrame *f)
check_constraints(AVFrame *f, bool dst_rgb)
{
const struct AVPixFmtDescriptor *avd = av_pix_fmt_desc_get(f->format);
const bool src_rgb = (avd->flags & AV_PIX_FMT_FLAG_RGB) != 0;
if (f->color_range == AVCOL_RANGE_JPEG && !src_rgb) {
if (f->color_range == AVCOL_RANGE_JPEG && !src_rgb && !dst_rgb) {
MSG_ONCE(WARNING, "Full-range YCbCr may be clipped!\n");
}
}
Expand Down Expand Up @@ -3052,7 +3055,7 @@ av_to_uv_convert(const av_to_uv_convert_t *convert,
char *dst, AVFrame *in, int pitch,
const int rgb_shift[3])
{
check_constraints(in);
check_constraints(in, codec_is_a_rgb(convert->dst_pixfmt));
if (convert->cuda_conv_state != NULL) {
av_to_uv_convert_cuda(convert->cuda_conv_state, dst, in,
in->width, in->height, pitch, rgb_shift);
Expand All @@ -3075,7 +3078,12 @@ av_to_uv_convert(const av_to_uv_convert_t *convert,
int row_height = (in->height / cpu_count) & ~1; // needs to be even
unsigned char *part_dst =
(unsigned char *) dst + (size_t) i * row_height * pitch;

// copy props - av_frame_copy_props() can be used as well
// (but unsure if there isn't higher overhead of that file)
memcpy(parts[i].linesize, in->linesize, sizeof in->linesize);
parts[i].color_range = in->color_range;

const AVPixFmtDescriptor *fmt_desc =
av_pix_fmt_desc_get(in->format);
for (int plane = 0; plane < AV_NUM_DATA_POINTERS; ++plane) {
Expand Down
2 changes: 1 addition & 1 deletion tools/benchmark_ff_convs.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ benchmark_from_lavc_convs()
timespec_get(&t0, TIME_UTC);
conv->convert((struct av_conv_data){
out, in, vc_get_linesize(W, conv->uv_codec),
DEFAULT_RGB_SHIFT_INIT, CS_DFL });
DEFAULT_RGB_SHIFT_INIT, CS_DFL, 1 });
timespec_get(&t1, TIME_UTC);
printf("%s->%s:\t%.2f ms\n",
av_get_pix_fmt_name(conv->av_codec),
Expand Down

0 comments on commit e868262

Please sign in to comment.