[FFmpeg-devel] [PATCH]lavc/h264: Output pix_fmt GRAY for monochrome input.
James Almer
jamrial at gmail.com
Tue Aug 7 23:26:29 EEST 2018
On 8/7/2018 4:51 PM, Carl Eugen Hoyos wrote:
> Hi!
>
> Attached patch makes the h264 decoder output AV_PIX_FMT_GRAY for
> monochrome h264 streams.
> fate output is identical (and identical with the reference decoder) if
> compared with extractplanes=y.
>
> Please review, Carl Eugen
>
>
> 0001-lavc-h264-Output-pix_fmt-GRAY-for-monochrome-input.patch
>
>
> From 6434eff6c14698db192ec2f8777d2b4d2fdd3e8c Mon Sep 17 00:00:00 2001
> From: Carl Eugen Hoyos <ceffmpeg at gmail.com>
> Date: Tue, 7 Aug 2018 21:48:47 +0200
> Subject: [PATCH] lavc/h264: Output pix_fmt GRAY for monochrome input.
>
> fate output is identical when compared with extractplanes=y.
> ---
> libavcodec/h264_mb.c | 10 +-
> libavcodec/h264_mb_template.c | 11 +-
> libavcodec/h264_parser.c | 3 +
> libavcodec/h264_slice.c | 17 +-
> libavcodec/h264dec.h | 1 +
> .../fate/h264-conformance-frext-hpcamolq_brcm_b | 200 ++++++++++----------
> .../fate/h264-conformance-frext-hpcvmolq_brcm_b | 200 ++++++++++----------
> 7 files changed, 229 insertions(+), 213 deletions(-)
>
> diff --git a/libavcodec/h264_mb.c b/libavcodec/h264_mb.c
> index 3cd17b7..a269b5c 100644
> --- a/libavcodec/h264_mb.c
> +++ b/libavcodec/h264_mb.c
> @@ -250,7 +250,7 @@ static av_always_inline void mc_dir_part(const H264Context *h, H264SliceContext
> if (!square)
> qpix_op[luma_xy](dest_y + delta, src_y + delta, sl->mb_linesize);
>
> - if (CONFIG_GRAY && h->flags & AV_CODEC_FLAG_GRAY)
> + if (CHROMA400(h) || CONFIG_GRAY && h->flags & AV_CODEC_FLAG_GRAY)
> return;
>
> if (chroma_idc == 3 /* yuv444 */) {
> @@ -425,7 +425,7 @@ static av_always_inline void mc_part_weighted(const H264Context *h, H264SliceCon
> int weight1 = 64 - weight0;
> luma_weight_avg(dest_y, tmp_y, sl->mb_linesize,
> height, 5, weight0, weight1, 0);
> - if (!CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!CHROMA400(h) && (!CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> chroma_weight_avg(dest_cb, tmp_cb, sl->mb_uvlinesize,
> chroma_height, 5, weight0, weight1, 0);
> chroma_weight_avg(dest_cr, tmp_cr, sl->mb_uvlinesize,
> @@ -438,7 +438,7 @@ static av_always_inline void mc_part_weighted(const H264Context *h, H264SliceCon
> sl->pwt.luma_weight[refn1][1][0],
> sl->pwt.luma_weight[refn0][0][1] +
> sl->pwt.luma_weight[refn1][1][1]);
> - if (!CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!CHROMA400(h) && (!CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> chroma_weight_avg(dest_cb, tmp_cb, sl->mb_uvlinesize, chroma_height,
> sl->pwt.chroma_log2_weight_denom,
> sl->pwt.chroma_weight[refn0][0][0][0],
> @@ -465,7 +465,7 @@ static av_always_inline void mc_part_weighted(const H264Context *h, H264SliceCon
> sl->pwt.luma_log2_weight_denom,
> sl->pwt.luma_weight[refn][list][0],
> sl->pwt.luma_weight[refn][list][1]);
> - if (!CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!CHROMA400(h) && (!CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> if (sl->pwt.use_weight_chroma) {
> chroma_weight_op(dest_cb, sl->mb_uvlinesize, chroma_height,
> sl->pwt.chroma_log2_weight_denom,
> @@ -566,7 +566,7 @@ static av_always_inline void xchg_mb_border(const H264Context *h, H264SliceConte
> XCHG(sl->top_borders[top_idx][sl->mb_x + 1],
> src_y + (17 << pixel_shift), 1);
> }
> - if (simple || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!CHROMA400(h) && (simple || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> if (chroma444) {
> if (deblock_topleft) {
> XCHG(top_border_m1 + (24 << pixel_shift), src_cb - (7 << pixel_shift), 1);
> diff --git a/libavcodec/h264_mb_template.c b/libavcodec/h264_mb_template.c
> index d5ea26a..2abc864 100644
> --- a/libavcodec/h264_mb_template.c
> +++ b/libavcodec/h264_mb_template.c
> @@ -52,6 +52,7 @@ static av_noinline void FUNC(hl_decode_mb)(const H264Context *h, H264SliceContex
> void (*idct_add)(uint8_t *dst, int16_t *block, int stride);
> const int block_h = 16 >> h->chroma_y_shift;
> const int chroma422 = CHROMA422(h);
> + const int chroma400 = CHROMA400(h);
>
> dest_y = h->cur_pic.f->data[0] + ((mb_x << PIXEL_SHIFT) + mb_y * sl->linesize) * 16;
> dest_cb = h->cur_pic.f->data[1] + (mb_x << PIXEL_SHIFT) * 8 + mb_y * sl->uvlinesize * block_h;
> @@ -108,7 +109,7 @@ static av_noinline void FUNC(hl_decode_mb)(const H264Context *h, H264SliceContex
> for (j = 0; j < 16; j++)
> tmp_y[j] = get_bits(&gb, bit_depth);
> }
> - if (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!chroma400 && (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> if (!h->ps.sps->chroma_format_idc) {
I think this becomes dead code. Your change makes sure this chunk is
never reached if the stream is chroma400, so this check here will always
be false.
> for (i = 0; i < block_h; i++) {
> uint16_t *tmp_cb = (uint16_t *)(dest_cb + i * uvlinesize);
> @@ -133,7 +134,7 @@ static av_noinline void FUNC(hl_decode_mb)(const H264Context *h, H264SliceContex
> } else {
> for (i = 0; i < 16; i++)
> memcpy(dest_y + i * linesize, sl->intra_pcm_ptr + i * 16, 16);
> - if (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!chroma400 && (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> if (!h->ps.sps->chroma_format_idc) {
Same
> for (i = 0; i < 8; i++) {
> memset(dest_cb + i * uvlinesize, 1 << (bit_depth - 1), 8);
> @@ -155,7 +156,7 @@ static av_noinline void FUNC(hl_decode_mb)(const H264Context *h, H264SliceContex
> xchg_mb_border(h, sl, dest_y, dest_cb, dest_cr, linesize,
> uvlinesize, 1, 0, SIMPLE, PIXEL_SHIFT);
>
> - if (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!chroma400 && (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> h->hpc.pred8x8[sl->chroma_pred_mode](dest_cb, uvlinesize);
> h->hpc.pred8x8[sl->chroma_pred_mode](dest_cr, uvlinesize);
> }
> @@ -190,7 +191,7 @@ static av_noinline void FUNC(hl_decode_mb)(const H264Context *h, H264SliceContex
> hl_decode_mb_idct_luma(h, sl, mb_type, SIMPLE, transform_bypass,
> PIXEL_SHIFT, block_offset, linesize, dest_y, 0);
>
> - if ((SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) &&
> + if ((!CHROMA400(h) && (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) &&
> (sl->cbp & 0x30)) {
> uint8_t *dest[2] = { dest_cb, dest_cr };
> if (transform_bypass) {
> @@ -264,7 +265,7 @@ static av_noinline void FUNC(hl_decode_mb_444)(const H264Context *h, H264SliceCo
> int i, j, p;
> const int *block_offset = &h->block_offset[0];
> const int transform_bypass = !SIMPLE && (sl->qscale == 0 && h->ps.sps->transform_bypass);
> - const int plane_count = (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) ? 3 : 1;
> + const int plane_count = !CHROMA400(h) && (SIMPLE || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) ? 3 : 1;
>
> for (p = 0; p < plane_count; p++) {
> dest[p] = h->cur_pic.f->data[p] +
> diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c
> index 5f9a9c4..8670b78 100644
> --- a/libavcodec/h264_parser.c
> +++ b/libavcodec/h264_parser.c
> @@ -401,16 +401,19 @@ static inline int parse_nal_units(AVCodecParserContext *s,
> case 9:
> if (sps->chroma_format_idc == 3) s->format = AV_PIX_FMT_YUV444P9;
> else if (sps->chroma_format_idc == 2) s->format = AV_PIX_FMT_YUV422P9;
> + else if (sps->chroma_format_idc == 1) s->format = AV_PIX_FMT_GRAY9;
> else s->format = AV_PIX_FMT_YUV420P9;
> break;
> case 10:
> if (sps->chroma_format_idc == 3) s->format = AV_PIX_FMT_YUV444P10;
> else if (sps->chroma_format_idc == 2) s->format = AV_PIX_FMT_YUV422P10;
> + else if (sps->chroma_format_idc == 1) s->format = AV_PIX_FMT_GRAY10;
> else s->format = AV_PIX_FMT_YUV420P10;
> break;
> case 8:
> if (sps->chroma_format_idc == 3) s->format = AV_PIX_FMT_YUV444P;
> else if (sps->chroma_format_idc == 2) s->format = AV_PIX_FMT_YUV422P;
> + else if (sps->chroma_format_idc == 1) s->format = AV_PIX_FMT_GRAY8;
chroma_format_idc == 1 is yuv420p*, so use it here and put gray in the
last else.
> else s->format = AV_PIX_FMT_YUV420P;
> break;
> default:
> diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
> index ede9a1a..f98268a 100644
> --- a/libavcodec/h264_slice.c
> +++ b/libavcodec/h264_slice.c
> @@ -497,7 +497,7 @@ static int h264_frame_start(H264Context *h)
>
> if ((ret = alloc_picture(h, pic)) < 0)
> return ret;
> - if(!h->frame_recovered && !h->avctx->hwaccel)
> + if(!h->frame_recovered && !h->avctx->hwaccel && !CHROMA400(h))
> ff_color_frame(pic->f, c);
>
> h->cur_pic_ptr = pic;
> @@ -564,6 +564,7 @@ static av_always_inline void backup_mb_border(const H264Context *h, H264SliceCon
> const int pixel_shift = h->pixel_shift;
> int chroma444 = CHROMA444(h);
> int chroma422 = CHROMA422(h);
> + int chroma400 = CHROMA400(h);
>
> src_y -= linesize;
> src_cb -= uvlinesize;
> @@ -576,7 +577,7 @@ static av_always_inline void backup_mb_border(const H264Context *h, H264SliceCon
> AV_COPY128(top_border, src_y + 15 * linesize);
> if (pixel_shift)
> AV_COPY128(top_border + 16, src_y + 15 * linesize + 16);
> - if (simple || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!chroma400 && (simple || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> if (chroma444) {
> if (pixel_shift) {
> AV_COPY128(top_border + 32, src_cb + 15 * uvlinesize);
> @@ -619,7 +620,7 @@ static av_always_inline void backup_mb_border(const H264Context *h, H264SliceCon
> if (pixel_shift)
> AV_COPY128(top_border + 16, src_y + 16 * linesize + 16);
>
> - if (simple || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) {
> + if (!chroma400 && (simple || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY))) {
> if (chroma444) {
> if (pixel_shift) {
> AV_COPY128(top_border + 32, src_cb + 16 * linesize);
> @@ -772,6 +773,8 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
> *fmt++ = AV_PIX_FMT_GBRP9;
> } else
> *fmt++ = AV_PIX_FMT_YUV444P9;
> + } else if (CHROMA400(h)) {
> + *fmt++ = AV_PIX_FMT_GRAY9;
> } else if (CHROMA422(h))
> *fmt++ = AV_PIX_FMT_YUV422P9;
> else
> @@ -783,6 +786,8 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
> *fmt++ = AV_PIX_FMT_GBRP10;
> } else
> *fmt++ = AV_PIX_FMT_YUV444P10;
> + } else if (CHROMA400(h)) {
> + *fmt++ = AV_PIX_FMT_GRAY10;
> } else if (CHROMA422(h))
> *fmt++ = AV_PIX_FMT_YUV422P10;
> else
> @@ -794,6 +799,8 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
> *fmt++ = AV_PIX_FMT_GBRP12;
> } else
> *fmt++ = AV_PIX_FMT_YUV444P12;
> + } else if (CHROMA400(h)) {
> + *fmt++ = AV_PIX_FMT_GRAY12;
> } else if (CHROMA422(h))
> *fmt++ = AV_PIX_FMT_YUV422P12;
> else
> @@ -805,6 +812,8 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
> *fmt++ = AV_PIX_FMT_GBRP14;
> } else
> *fmt++ = AV_PIX_FMT_YUV444P14;
> + } else if (CHROMA400(h)) {
> + *fmt++ = AV_PIX_FMT_GRAY14;
> } else if (CHROMA422(h))
> *fmt++ = AV_PIX_FMT_YUV422P14;
> else
> @@ -824,6 +833,8 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
> *fmt++ = AV_PIX_FMT_YUVJ444P;
> else
> *fmt++ = AV_PIX_FMT_YUV444P;
> + } else if (CHROMA400(h)) {
> + *fmt++ = AV_PIX_FMT_GRAY8;
> } else if (CHROMA422(h)) {
> if (h->avctx->color_range == AVCOL_RANGE_JPEG)
> *fmt++ = AV_PIX_FMT_YUVJ422P;
> diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h
> index 1d97232..7e9fc72 100644
> --- a/libavcodec/h264dec.h
> +++ b/libavcodec/h264dec.h
> @@ -95,6 +95,7 @@
> #endif
>
> #define CHROMA(h) ((h)->ps.sps->chroma_format_idc)
> +#define CHROMA400(h) ((h)->ps.sps->chroma_format_idc == 0)
> #define CHROMA422(h) ((h)->ps.sps->chroma_format_idc == 2)
> #define CHROMA444(h) ((h)->ps.sps->chroma_format_idc == 3)
Seems to work, but wait for someone more familiar with h264dec to review
and confirm it's ok.
More information about the ffmpeg-devel
mailing list