Commit 5e83d9ac authored by Anton Khirnov's avatar Anton Khirnov

h264: fully support cropping.

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

Fixes Bug 378.
parent a7f46586
...@@ -13,6 +13,9 @@ libavutil: 2012-10-22 ...@@ -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
......
...@@ -609,6 +609,11 @@ typedef struct RcOverride{ ...@@ -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.
......
...@@ -1426,9 +1426,6 @@ av_cold int ff_h264_decode_init(AVCodecContext *avctx) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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: ...@@ -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: ...@@ -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;
} }
......
...@@ -164,6 +164,8 @@ typedef struct SPS { ...@@ -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 { ...@@ -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;
......
...@@ -413,37 +413,45 @@ int ff_h264_decode_seq_parameter_set(H264Context *h){ ...@@ -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);
......
...@@ -46,6 +46,7 @@ static const AVOption options[]={ ...@@ -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"},
......
...@@ -27,7 +27,7 @@ ...@@ -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, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment