1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-05-13 21:26:33 +02:00

h264: fully support cropping.

Based on a patch by Vittorio Giovara <vittorio.giovara@gmail.com>

Fixes Bug 378.
This commit is contained in:
Anton Khirnov 2013-02-18 16:32:18 +01:00
parent a7f46586bf
commit 5e83d9aced
7 changed files with 108 additions and 39 deletions

View File

@ -13,6 +13,9 @@ libavutil: 2012-10-22
API changes, most recent first: 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 2013-xx-xx - lavfi 3.8.0
Move all content from avfiltergraph.h to avfilter.h. Deprecate Move all content from avfiltergraph.h to avfilter.h. Deprecate
avfilterhraph.h, user applications should include just avfilter.h avfilterhraph.h, user applications should include just avfilter.h

View File

@ -609,6 +609,11 @@ typedef struct RcOverride{
Note: Not everything is supported yet. 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_QSCALE 0x0002 ///< Use fixed qscale.
#define CODEC_FLAG_4MV 0x0004 ///< 4 MV per MB allowed / advanced prediction for H.263. #define CODEC_FLAG_4MV 0x0004 ///< 4 MV per MB allowed / advanced prediction for H.263.
#define CODEC_FLAG_QPEL 0x0010 ///< Use qpel MC. #define CODEC_FLAG_QPEL 0x0010 ///< Use qpel MC.

View File

@ -1426,9 +1426,6 @@ av_cold int ff_h264_decode_init(AVCodecContext *avctx)
h->avctx = avctx; h->avctx = avctx;
h->width = h->avctx->width;
h->height = h->avctx->height;
h->bit_depth_luma = 8; h->bit_depth_luma = 8;
h->chroma_format_idc = 1; 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) static int h264_slice_header_init(H264Context *h, int reinit)
{ {
int nb_slices = (HAVE_THREADS && int nb_slices = (HAVE_THREADS &&
h->avctx->active_thread_type & FF_THREAD_SLICE) ? h->avctx->active_thread_type & FF_THREAD_SLICE) ?
h->avctx->thread_count : 1; 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; h->avctx->sample_aspect_ratio = h->sps.sar;
av_assert0(h->avctx->sample_aspect_ratio.den); av_assert0(h->avctx->sample_aspect_ratio.den);
av_pix_fmt_get_chroma_sub_sample(h->avctx->pix_fmt, 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->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); h->width = 16 * h->mb_width;
if (h->sps.frame_mbs_only_flag) h->height = 16 * h->mb_height;
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);
if (FFALIGN(h->avctx->width, 16) == h->width && ret = init_dimensions(h);
FFALIGN(h->avctx->height, 16) == h->height) { if (ret < 0)
h->width = h->avctx->width; return ret;
h->height = h->avctx->height;
}
if (h->sps.video_signal_type_present_flag) { if (h->sps.video_signal_type_present_flag) {
h->avctx->color_range = h->sps.full_range ? AVCOL_RANGE_JPEG 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 && if (h->context_initialized &&
(h->width != h->avctx->width || (h->width != h->avctx->coded_width ||
h->height != h->avctx->height || h->height != h->avctx->coded_height ||
needs_reinit)) { needs_reinit)) {
if (h != h0) { if (h != h0) {
@ -4611,6 +4638,26 @@ static int get_consumed_bytes(int pos, int buf_size)
return pos; 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, static int decode_frame(AVCodecContext *avctx, void *data,
int *got_frame, AVPacket *avpkt) int *got_frame, AVPacket *avpkt)
{ {
@ -4648,7 +4695,8 @@ out:
h->delayed_pic[i] = h->delayed_pic[i + 1]; h->delayed_pic[i] = h->delayed_pic[i + 1];
if (out) { if (out) {
if ((ret = av_frame_ref(pict, &out->f)) < 0) ret = output_frame(h, pict, &out->f);
if (ret < 0)
return ret; return ret;
*got_frame = 1; *got_frame = 1;
} }
@ -4683,7 +4731,8 @@ out:
/* Wait for second field. */ /* Wait for second field. */
*got_frame = 0; *got_frame = 0;
} else { } 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; return ret;
*got_frame = 1; *got_frame = 1;
} }

View File

@ -164,6 +164,8 @@ typedef struct SPS {
int mb_aff; ///< mb_adaptive_frame_field_flag int mb_aff; ///< mb_adaptive_frame_field_flag
int direct_8x8_inference_flag; int direct_8x8_inference_flag;
int crop; ///< frame_cropping_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_left; ///< frame_cropping_rect_left_offset
unsigned int crop_right; ///< frame_cropping_rect_right_offset unsigned int crop_right; ///< frame_cropping_rect_right_offset
unsigned int crop_top; ///< frame_cropping_rect_top_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 int qp_thresh; ///< QP threshold to skip loopfilter
/* coded dimensions -- 16 * mb w/h */
int width, height; int width, height;
int linesize, uvlinesize; int linesize, uvlinesize;
int chroma_x_shift, chroma_y_shift; int chroma_x_shift, chroma_y_shift;

View File

@ -413,37 +413,45 @@ int ff_h264_decode_seq_parameter_set(H264Context *h){
#endif #endif
sps->crop= get_bits1(&h->gb); sps->crop= get_bits1(&h->gb);
if(sps->crop){ if(sps->crop){
int crop_vertical_limit = sps->chroma_format_idc & 2 ? 16 : 8; int crop_left = get_ue_golomb(&h->gb);
int crop_horizontal_limit = sps->chroma_format_idc == 3 ? 16 : 8; int crop_right = get_ue_golomb(&h->gb);
sps->crop_left = get_ue_golomb(&h->gb); int crop_top = get_ue_golomb(&h->gb);
sps->crop_right = get_ue_golomb(&h->gb); int crop_bottom = get_ue_golomb(&h->gb);
sps->crop_top = get_ue_golomb(&h->gb);
sps->crop_bottom= get_ue_golomb(&h->gb);
if (h->avctx->flags2 & CODEC_FLAG2_IGNORE_CROP) { if (h->avctx->flags2 & CODEC_FLAG2_IGNORE_CROP) {
av_log(h->avctx, AV_LOG_DEBUG, av_log(h->avctx, AV_LOG_DEBUG, "discarding sps cropping, original "
"discarding sps cropping, " "values are l:%u r:%u t:%u b:%u\n", crop_left, crop_right,
"original values are l:%u r:%u t:%u b:%u\n", crop_top, crop_bottom);
sps->crop_left,
sps->crop_right,
sps->crop_top,
sps->crop_bottom);
sps->crop_left = sps->crop_left =
sps->crop_right = sps->crop_right =
sps->crop_top = sps->crop_top =
sps->crop_bottom = 0; sps->crop_bottom = 0;
} } else {
if(sps->crop_left || sps->crop_top){ int vsub = (sps->chroma_format_idc == 1) ? 1 : 0;
av_log(h->avctx, AV_LOG_ERROR, "insane cropping not completely supported, this could look slightly wrong ...\n"); int hsub = (sps->chroma_format_idc == 1 || sps->chroma_format_idc == 2) ? 1 : 0;
} int step_x = 1 << hsub;
if(sps->crop_right >= crop_horizontal_limit || sps->crop_bottom >= crop_vertical_limit){ int step_y = (2 - sps->frame_mbs_only_flag) << vsub;
av_log(h->avctx, AV_LOG_ERROR, "brainfart cropping not supported, this could look slightly wrong ...\n");
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{ }else{
sps->crop_left = sps->crop_left =
sps->crop_right = sps->crop_right =
sps->crop_top = sps->crop_top =
sps->crop_bottom= 0; sps->crop_bottom= 0;
sps->crop = 0;
} }
sps->vui_parameters_present_flag= get_bits1(&h->gb); sps->vui_parameters_present_flag= get_bits1(&h->gb);

View File

@ -46,6 +46,7 @@ static const AVOption options[]={
"to minimum/maximum bitrate. Lowering tolerance too much has an adverse effect on quality.", "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}, 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"}, {"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"}, {"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"}, {"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"}, {"loop", "use loop filter", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_LOOP_FILTER }, INT_MIN, INT_MAX, V|E, "flags"},

View File

@ -27,7 +27,7 @@
*/ */
#define LIBAVCODEC_VERSION_MAJOR 55 #define LIBAVCODEC_VERSION_MAJOR 55
#define LIBAVCODEC_VERSION_MINOR 1 #define LIBAVCODEC_VERSION_MINOR 2
#define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_MICRO 0
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \