From 5e83d9aced2fc2b2e1360452794c58aba55d497c Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 18 Feb 2013 16:32:18 +0100 Subject: [PATCH] h264: fully support cropping. Based on a patch by Vittorio Giovara Fixes Bug 378. --- doc/APIchanges | 3 ++ libavcodec/avcodec.h | 5 +++ libavcodec/h264.c | 87 +++++++++++++++++++++++++++++--------- libavcodec/h264.h | 3 ++ libavcodec/h264_ps.c | 46 +++++++++++--------- libavcodec/options_table.h | 1 + libavcodec/version.h | 2 +- 7 files changed, 108 insertions(+), 39 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 4fc2022697..89ad2ae085 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,9 @@ libavutil: 2012-10-22 API changes, most recent first: +2013-03-xx - xxxxxxx - lavc 55.2.0 - avcodec.h + Add CODEC_FLAG_UNALIGNED to allow decoders to produce unaligned output. + 2013-xx-xx - lavfi 3.8.0 Move all content from avfiltergraph.h to avfilter.h. Deprecate avfilterhraph.h, user applications should include just avfilter.h diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 84a685986b..c36d976a52 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -609,6 +609,11 @@ typedef struct RcOverride{ Note: Not everything is supported yet. */ +/** + * Allow decoders to produce frames with data planes that are not aligned + * to CPU requirements (e.g. due to cropping). + */ +#define CODEC_FLAG_UNALIGNED 0x0001 #define CODEC_FLAG_QSCALE 0x0002 ///< Use fixed qscale. #define CODEC_FLAG_4MV 0x0004 ///< 4 MV per MB allowed / advanced prediction for H.263. #define CODEC_FLAG_QPEL 0x0010 ///< Use qpel MC. diff --git a/libavcodec/h264.c b/libavcodec/h264.c index 4a3d2e7512..d8bc824d5c 100644 --- a/libavcodec/h264.c +++ b/libavcodec/h264.c @@ -1426,9 +1426,6 @@ av_cold int ff_h264_decode_init(AVCodecContext *avctx) h->avctx = avctx; - h->width = h->avctx->width; - h->height = h->avctx->height; - h->bit_depth_luma = 8; h->chroma_format_idc = 1; @@ -2978,14 +2975,49 @@ static enum AVPixelFormat get_pixel_format(H264Context *h) } } +/* export coded and cropped frame dimensions to AVCodecContext */ +static int init_dimensions(H264Context *h) +{ + int width = h->width - (h->sps.crop_right + h->sps.crop_left); + int height = h->height - (h->sps.crop_top + h->sps.crop_bottom); + + /* handle container cropping */ + if (!h->sps.crop && + FFALIGN(h->avctx->width, 16) == h->width && + FFALIGN(h->avctx->height, 16) == h->height) { + width = h->avctx->width; + height = h->avctx->height; + } + + if (width <= 0 || height <= 0) { + av_log(h->avctx, AV_LOG_ERROR, "Invalid cropped dimensions: %dx%d.\n", + width, height); + if (h->avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + + av_log(h->avctx, AV_LOG_WARNING, "Ignoring cropping information.\n"); + h->sps.crop_bottom = h->sps.crop_top = h->sps.crop_right = h->sps.crop_left = 0; + h->sps.crop = 0; + + width = h->width; + height = h->height; + } + + h->avctx->coded_width = h->width; + h->avctx->coded_height = h->height; + h->avctx->width = width; + h->avctx->height = height; + + return 0; +} + static int h264_slice_header_init(H264Context *h, int reinit) { int nb_slices = (HAVE_THREADS && h->avctx->active_thread_type & FF_THREAD_SLICE) ? h->avctx->thread_count : 1; - int i; + int i, ret; - avcodec_set_dimensions(h->avctx, h->width, h->height); h->avctx->sample_aspect_ratio = h->sps.sar; av_assert0(h->avctx->sample_aspect_ratio.den); av_pix_fmt_get_chroma_sub_sample(h->avctx->pix_fmt, @@ -3196,17 +3228,12 @@ static int decode_slice_header(H264Context *h, H264Context *h0) h->chroma_y_shift = h->sps.chroma_format_idc <= 1; // 400 uses yuv420p - h->width = 16 * h->mb_width - (2 >> CHROMA444(h)) * FFMIN(h->sps.crop_right, (8 << CHROMA444(h)) - 1); - if (h->sps.frame_mbs_only_flag) - h->height = 16 * h->mb_height - (1 << h->chroma_y_shift) * FFMIN(h->sps.crop_bottom, (16 >> h->chroma_y_shift) - 1); - else - h->height = 16 * h->mb_height - (2 << h->chroma_y_shift) * FFMIN(h->sps.crop_bottom, (16 >> h->chroma_y_shift) - 1); + h->width = 16 * h->mb_width; + h->height = 16 * h->mb_height; - if (FFALIGN(h->avctx->width, 16) == h->width && - FFALIGN(h->avctx->height, 16) == h->height) { - h->width = h->avctx->width; - h->height = h->avctx->height; - } + ret = init_dimensions(h); + if (ret < 0) + return ret; if (h->sps.video_signal_type_present_flag) { h->avctx->color_range = h->sps.full_range ? AVCOL_RANGE_JPEG @@ -3221,8 +3248,8 @@ static int decode_slice_header(H264Context *h, H264Context *h0) } if (h->context_initialized && - (h->width != h->avctx->width || - h->height != h->avctx->height || + (h->width != h->avctx->coded_width || + h->height != h->avctx->coded_height || needs_reinit)) { if (h != h0) { @@ -4611,6 +4638,26 @@ static int get_consumed_bytes(int pos, int buf_size) return pos; } +static int output_frame(H264Context *h, AVFrame *dst, AVFrame *src) +{ + int i; + int ret = av_frame_ref(dst, src); + if (ret < 0) + return ret; + + if (!h->sps.crop) + return 0; + + for (i = 0; i < 3; i++) { + int hshift = (i > 0) ? h->chroma_x_shift : 0; + int vshift = (i > 0) ? h->chroma_y_shift : 0; + int off = ((h->sps.crop_left >> hshift) << h->pixel_shift) + + (h->sps.crop_top >> vshift) * dst->linesize[i]; + dst->data[i] += off; + } + return 0; +} + static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt) { @@ -4648,7 +4695,8 @@ out: h->delayed_pic[i] = h->delayed_pic[i + 1]; if (out) { - if ((ret = av_frame_ref(pict, &out->f)) < 0) + ret = output_frame(h, pict, &out->f); + if (ret < 0) return ret; *got_frame = 1; } @@ -4683,7 +4731,8 @@ out: /* Wait for second field. */ *got_frame = 0; } else { - if ((ret = av_frame_ref(pict, &h->next_output_pic->f)) < 0) + ret = output_frame(h, pict, &h->next_output_pic->f); + if (ret < 0) return ret; *got_frame = 1; } diff --git a/libavcodec/h264.h b/libavcodec/h264.h index f5246febc8..484c9d3cfc 100644 --- a/libavcodec/h264.h +++ b/libavcodec/h264.h @@ -164,6 +164,8 @@ typedef struct SPS { int mb_aff; ///< mb_adaptive_frame_field_flag int direct_8x8_inference_flag; int crop; ///< frame_cropping_flag + + /* those 4 are already in luma samples */ unsigned int crop_left; ///< frame_cropping_rect_left_offset unsigned int crop_right; ///< frame_cropping_rect_right_offset unsigned int crop_top; ///< frame_cropping_rect_top_offset @@ -272,6 +274,7 @@ typedef struct H264Context { int qp_thresh; ///< QP threshold to skip loopfilter + /* coded dimensions -- 16 * mb w/h */ int width, height; int linesize, uvlinesize; int chroma_x_shift, chroma_y_shift; diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c index 6c895a46ea..c6587c791b 100644 --- a/libavcodec/h264_ps.c +++ b/libavcodec/h264_ps.c @@ -413,37 +413,45 @@ int ff_h264_decode_seq_parameter_set(H264Context *h){ #endif sps->crop= get_bits1(&h->gb); if(sps->crop){ - int crop_vertical_limit = sps->chroma_format_idc & 2 ? 16 : 8; - int crop_horizontal_limit = sps->chroma_format_idc == 3 ? 16 : 8; - sps->crop_left = get_ue_golomb(&h->gb); - sps->crop_right = get_ue_golomb(&h->gb); - sps->crop_top = get_ue_golomb(&h->gb); - sps->crop_bottom= get_ue_golomb(&h->gb); + int crop_left = get_ue_golomb(&h->gb); + int crop_right = get_ue_golomb(&h->gb); + int crop_top = get_ue_golomb(&h->gb); + int crop_bottom = get_ue_golomb(&h->gb); + if (h->avctx->flags2 & CODEC_FLAG2_IGNORE_CROP) { - av_log(h->avctx, AV_LOG_DEBUG, - "discarding sps cropping, " - "original values are l:%u r:%u t:%u b:%u\n", - sps->crop_left, - sps->crop_right, - sps->crop_top, - sps->crop_bottom); + av_log(h->avctx, AV_LOG_DEBUG, "discarding sps cropping, original " + "values are l:%u r:%u t:%u b:%u\n", crop_left, crop_right, + crop_top, crop_bottom); sps->crop_left = sps->crop_right = sps->crop_top = sps->crop_bottom = 0; - } - if(sps->crop_left || sps->crop_top){ - av_log(h->avctx, AV_LOG_ERROR, "insane cropping not completely supported, this could look slightly wrong ...\n"); - } - if(sps->crop_right >= crop_horizontal_limit || sps->crop_bottom >= crop_vertical_limit){ - av_log(h->avctx, AV_LOG_ERROR, "brainfart cropping not supported, this could look slightly wrong ...\n"); + } else { + int vsub = (sps->chroma_format_idc == 1) ? 1 : 0; + int hsub = (sps->chroma_format_idc == 1 || sps->chroma_format_idc == 2) ? 1 : 0; + int step_x = 1 << hsub; + int step_y = (2 - sps->frame_mbs_only_flag) << vsub; + + if (crop_left & (0x1F >> (sps->bit_depth_luma > 8)) && + !(h->avctx->flags & CODEC_FLAG_UNALIGNED)) { + crop_left &= ~(0x1F >> (sps->bit_depth_luma > 8)); + av_log(h->avctx, AV_LOG_WARNING, "Reducing left cropping to %d " + "chroma samples to preserve alignment.\n", + crop_left); + } + + sps->crop_left = crop_left * step_x; + sps->crop_right = crop_right * step_x; + sps->crop_top = crop_top * step_y; + sps->crop_bottom = crop_bottom * step_y; } }else{ sps->crop_left = sps->crop_right = sps->crop_top = sps->crop_bottom= 0; + sps->crop = 0; } sps->vui_parameters_present_flag= get_bits1(&h->gb); diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h index e1b124b31c..5595c0e4f5 100644 --- a/libavcodec/options_table.h +++ b/libavcodec/options_table.h @@ -46,6 +46,7 @@ static const AVOption options[]={ "to minimum/maximum bitrate. Lowering tolerance too much has an adverse effect on quality.", OFFSET(bit_rate_tolerance), AV_OPT_TYPE_INT, {.i64 = AV_CODEC_DEFAULT_BITRATE*20 }, 1, INT_MAX, V|E}, {"flags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, UINT_MAX, V|A|E|D, "flags"}, +{"unaligned", "allow decoders to produce unaligned output", 0, AV_OPT_TYPE_CONST, { .i64 = CODEC_FLAG_UNALIGNED }, INT_MIN, INT_MAX, V | D, "flags" }, {"mv4", "use four motion vectors per macroblock (MPEG-4)", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_4MV }, INT_MIN, INT_MAX, V|E, "flags"}, {"qpel", "use 1/4-pel motion compensation", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_QPEL }, INT_MIN, INT_MAX, V|E, "flags"}, {"loop", "use loop filter", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_LOOP_FILTER }, INT_MIN, INT_MAX, V|E, "flags"}, diff --git a/libavcodec/version.h b/libavcodec/version.h index 394bf1a386..cfcacb7f84 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -27,7 +27,7 @@ */ #define LIBAVCODEC_VERSION_MAJOR 55 -#define LIBAVCODEC_VERSION_MINOR 1 +#define LIBAVCODEC_VERSION_MINOR 2 #define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \