vo_opengl: handle chroma location

Use the video decoder chroma location flags and render chroma locations
other than centered. Until now, we've always used the intuitive and
obvious centered chroma location, but H.264 uses something else.

FFmpeg provides a small overview in libavcodec/avcodec.h:

-----------
/**
 *  X   X      3 4 X      X are luma samples,
 *             1 2        1-6 are possible chroma positions
 *  X   X      5 6 X      0 is undefined/unknown position
 */
enum AVChromaLocation{
    AVCHROMA_LOC_UNSPECIFIED = 0,
    AVCHROMA_LOC_LEFT        = 1, ///< mpeg2/4, h264 default
    AVCHROMA_LOC_CENTER      = 2, ///< mpeg1, jpeg, h263
    AVCHROMA_LOC_TOPLEFT     = 3, ///< DV
    AVCHROMA_LOC_TOP         = 4,
    AVCHROMA_LOC_BOTTOMLEFT  = 5,
    AVCHROMA_LOC_BOTTOM      = 6,
    AVCHROMA_LOC_NB             , ///< Not part of ABI
};
-----------

The visual difference is literally minimal, but since videophiles
apparently consider this detail as quality mark of a video renderer,
support it anyway. We don't bother with chroma locations other than
centered and left, though.

Not sure about correctness, but it's probably ok.
This commit is contained in:
wm4 2013-06-08 02:15:24 +02:00
parent 3382a6f6e4
commit ac266da658
10 changed files with 74 additions and 3 deletions

View File

@ -465,6 +465,10 @@ opengl
that when using FBO indirections (such as with ``opengl-hq``), a FBO
format with alpha must be specified with the ``fbo-format`` option.
chroma-location=<auto|center|left>
Set the YUV chroma sample location. auto means use the bitstream
flags (default: auto).
opengl-hq
Same as ``opengl``, but with default settings for high quality rendering.

View File

@ -100,6 +100,25 @@ enum mp_csp mp_csp_guess_colorspace(int width, int height)
return width >= 1280 || height > 576 ? MP_CSP_BT_709 : MP_CSP_BT_601;
}
enum mp_chroma_location avchroma_location_to_mp(enum AVChromaLocation loc)
{
switch (loc) {
case AVCHROMA_LOC_LEFT: return MP_CHROMA_LEFT;
case AVCHROMA_LOC_CENTER: return MP_CHROMA_CENTER;
default: return MP_CHROMA_AUTO;
}
}
// Return location of chroma samples relative to luma samples. 0/0 means
// centered. Other possible values are -1 (top/left) and +1 (right/bottom).
void mp_get_chroma_location(enum mp_chroma_location loc, int *x, int *y)
{
*x = 0;
*y = 0;
if (loc == MP_CHROMA_LEFT)
*x = -1;
}
/**
* \brief little helper function to create a lookup table for gamma
* \param map buffer to create map into

View File

@ -87,6 +87,12 @@ struct mp_csp_params {
.rgamma = 1, .ggamma = 1, .bgamma = 1, \
.texture_bits = 8, .input_bits = 8}
enum mp_chroma_location {
MP_CHROMA_AUTO,
MP_CHROMA_LEFT, // mpeg2/4, h264
MP_CHROMA_CENTER, // mpeg1, jpeg
};
enum mp_csp_equalizer_param {
MP_CSP_EQ_BRIGHTNESS,
MP_CSP_EQ_CONTRAST,
@ -136,6 +142,10 @@ enum AVColorRange mp_csp_levels_to_avcol_range(enum mp_csp_levels range);
enum mp_csp mp_csp_guess_colorspace(int width, int height);
enum mp_chroma_location avchroma_location_to_mp(enum AVChromaLocation loc);
void mp_get_chroma_location(enum mp_chroma_location loc, int *x, int *y);
void mp_gen_gamma_map(unsigned char *map, int size, float gamma);
#define ROW_R 0
#define ROW_G 1

View File

@ -446,6 +446,8 @@ static int init_vo(sh_video_t *sh, AVFrame *frame)
.d_h = height,
.colorspace = avcol_spc_to_mp_csp(ctx->avctx->colorspace),
.colorlevels = avcol_range_to_mp_csp_levels(ctx->avctx->color_range),
.chroma_location =
avchroma_location_to_mp(ctx->avctx->chroma_sample_location),
};
if (mpcodecs_reconfig_vo(sh, &ctx->image_params) < 0)
@ -701,6 +703,7 @@ static int decode(struct sh_video *sh, struct demux_packet *packet,
mpi->colorspace = ctx->image_params.colorspace;
mpi->levels = ctx->image_params.colorlevels;
mpi->chroma_location = ctx->image_params.chroma_location;
*out_image = mpi;
return 1;

View File

@ -477,6 +477,7 @@ int vf_next_config(struct vf_instance *vf,
.d_h = d_height,
.colorspace = vf->fmt_in.params.colorspace,
.colorlevels = vf->fmt_in.params.colorlevels,
.chroma_location = vf->fmt_in.params.chroma_location,
};
int r = vf_reconfig_wrapper(vf->next, &p, voflags);
return r < 0 ? 0 : 1;

View File

@ -324,6 +324,7 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src)
if ((dst->flags & MP_IMGFLAG_YUV) == (src->flags & MP_IMGFLAG_YUV)) {
dst->colorspace = src->colorspace;
dst->levels = src->levels;
dst->chroma_location = src->chroma_location;
}
if (dst->imgfmt == IMGFMT_PAL8 && src->imgfmt == IMGFMT_PAL8) {
if (dst->planes[1] && src->planes[1])

View File

@ -43,6 +43,7 @@ struct mp_image_params {
int d_w, d_h; // define display aspect ratio (never 0/0)
enum mp_csp colorspace;
enum mp_csp_levels colorlevels;
enum mp_chroma_location chroma_location;
};
/* Memory management:
@ -90,6 +91,8 @@ typedef struct mp_image {
enum mp_csp colorspace;
enum mp_csp_levels levels;
enum mp_chroma_location chroma_location;
/* only inside filter chain */
double pts;
/* memory management */

View File

@ -185,6 +185,7 @@ struct gl_video {
struct mp_csp_details colorspace;
struct mp_csp_equalizer video_eq;
enum mp_chroma_location chroma_loc;
struct mp_rect src_rect; // displayed part of the source video
struct mp_rect dst_rect; // video rectangle on output window
@ -279,6 +280,10 @@ const struct m_sub_options gl_video_conf = {
({"fruit", 0}, {"ordered", 1}, {"no", -1})),
OPT_INTRANGE("dither-size-fruit", dither_size, 0, 2, 8),
OPT_FLAG("temporal-dither", temporal_dither, 0),
OPT_CHOICE("chroma-location", chroma_location, 0,
({"auto", MP_CHROMA_AUTO},
{"center", MP_CHROMA_CENTER},
{"left", MP_CHROMA_LEFT})),
OPT_FLAG("alpha", enable_alpha, 0),
{0}
},
@ -510,6 +515,27 @@ static void update_uniforms(struct gl_video *p, GLuint program)
p->image.planes[n].tex_w, p->image.planes[n].tex_h);
}
loc = gl->GetUniformLocation(program, "chroma_center_offset");
if (loc >= 0) {
int chr = p->opts.chroma_location;
if (!chr)
chr = p->chroma_loc;
int cx, cy;
mp_get_chroma_location(chr, &cx, &cy);
// By default texture coordinates are such that chroma is centered with
// any chroma subsampling. If a specific direction is given, make it
// so that the luma and chroma sample line up exactly.
// For 4:4:4, setting chroma location should have no effect at all.
// luma sample size (in chroma coord. space)
float ls_w = 1.0 / (1 << p->image_desc.chroma_xs);
float ls_h = 1.0 / (1 << p->image_desc.chroma_ys);
// move chroma center to luma center (in chroma coord. space)
float o_x = ls_w < 1 ? ls_w * -cx / 2 : 0;
float o_y = ls_h < 1 ? ls_h * -cy / 2 : 0;
gl->Uniform2f(loc, o_x / FFMAX(p->image.planes[1].w, 1),
o_y / FFMAX(p->image.planes[1].h, 1));
}
gl->Uniform2f(gl->GetUniformLocation(program, "dither_size"),
p->dither_size, p->dither_size);
@ -1861,6 +1887,7 @@ void gl_video_config(struct gl_video *p, struct mp_image_params *params)
}
p->image_dw = params->d_w;
p->image_dh = params->d_h;
p->chroma_loc = params->chroma_location;
}
void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b)

View File

@ -45,6 +45,7 @@ struct gl_video_opts {
int fbo_format;
int stereo_mode;
int enable_alpha;
int chroma_location;
};
extern const struct m_sub_options gl_video_conf;

View File

@ -114,6 +114,7 @@ void main() {
#!section frag_video
uniform sampler2D textures[4];
uniform vec2 textures_size[4];
uniform vec2 chroma_center_offset;
uniform sampler1D lut_c_1d;
uniform sampler1D lut_l_1d;
uniform sampler2D lut_c_2d;
@ -321,17 +322,18 @@ vec4 sample_sharpen5(sampler2D tex, vec2 texsize, vec2 texcoord) {
}
void main() {
vec2 chr_texcoord = texcoord + chroma_center_offset;
#ifndef USE_CONV
#define USE_CONV 0
#endif
#if USE_CONV == CONV_PLANAR
vec3 color = vec3(SAMPLE_L(textures[0], textures_size[0], texcoord).r,
SAMPLE_C(textures[1], textures_size[1], texcoord).r,
SAMPLE_C(textures[2], textures_size[2], texcoord).r);
SAMPLE_C(textures[1], textures_size[1], chr_texcoord).r,
SAMPLE_C(textures[2], textures_size[2], chr_texcoord).r);
float alpha = 1.0;
#elif USE_CONV == CONV_NV12
vec3 color = vec3(SAMPLE_L(textures[0], textures_size[0], texcoord).r,
SAMPLE_C(textures[1], textures_size[1], texcoord).rg);
SAMPLE_C(textures[1], textures_size[1], chr_texcoord).rg);
float alpha = 1.0;
#else
vec4 acolor = SAMPLE_L(textures[0], textures_size[0], texcoord);