* commit '019ab88a95cb31b698506d90e8ce56695a7f1cc5': lavc: add an option for exporting cropping information to the caller Merged-by: James Almer <jamrial@gmail.com>tags/n3.4
| @@ -15,6 +15,10 @@ libavutil: 2015-08-28 | |||
| API changes, most recent first: | |||
| 2017-xx-xx - xxxxxxx - lavc 57.95.100 / 57.31.0 - avcodec.h | |||
| Add AVCodecContext.apply_cropping to control whether cropping | |||
| is handled by libavcodec or the caller. | |||
| 2017-xx-xx - xxxxxxx - lavu 55.62.100 / 55.30.0 - frame.h | |||
| Add AVFrame.crop_left/right/top/bottom fields for attaching cropping | |||
| information to video frames. | |||
| @@ -3644,6 +3644,33 @@ typedef struct AVCodecContext { | |||
| * AVCodecContext.get_format callback) | |||
| */ | |||
| int hwaccel_flags; | |||
| /** | |||
| * Video decoding only. Certain video codecs support cropping, meaning that | |||
| * only a sub-rectangle of the decoded frame is intended for display. This | |||
| * option controls how cropping is handled by libavcodec. | |||
| * | |||
| * When set to 1 (the default), libavcodec will apply cropping internally. | |||
| * I.e. it will modify the output frame width/height fields and offset the | |||
| * data pointers (only by as much as possible while preserving alignment, or | |||
| * by the full amount if the AV_CODEC_FLAG_UNALIGNED flag is set) so that | |||
| * the frames output by the decoder refer only to the cropped area. The | |||
| * crop_* fields of the output frames will be zero. | |||
| * | |||
| * When set to 0, the width/height fields of the output frames will be set | |||
| * to the coded dimensions and the crop_* fields will describe the cropping | |||
| * rectangle. Applying the cropping is left to the caller. | |||
| * | |||
| * @warning When hardware acceleration with opaque output frames is used, | |||
| * libavcodec is unable to apply cropping from the top/left border. | |||
| * | |||
| * @note when this option is set to zero, the width/height fields of the | |||
| * AVCodecContext and output AVFrames have different meanings. The codec | |||
| * context fields store display dimensions (with the coded dimensions in | |||
| * coded_width/height), while the frame fields store the coded dimensions | |||
| * (with the display dimensions being determined by the crop_* fields). | |||
| */ | |||
| int apply_cropping; | |||
| } AVCodecContext; | |||
| AVRational av_codec_get_pkt_timebase (const AVCodecContext *avctx); | |||
| @@ -35,6 +35,7 @@ | |||
| #include "libavutil/hwcontext.h" | |||
| #include "libavutil/imgutils.h" | |||
| #include "libavutil/internal.h" | |||
| #include "libavutil/intmath.h" | |||
| #include "avcodec.h" | |||
| #include "bytestream.h" | |||
| @@ -682,6 +683,112 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke | |||
| return 0; | |||
| } | |||
| static int calc_cropping_offsets(size_t offsets[4], const AVFrame *frame, | |||
| const AVPixFmtDescriptor *desc) | |||
| { | |||
| int i, j; | |||
| for (i = 0; frame->data[i]; i++) { | |||
| const AVComponentDescriptor *comp = NULL; | |||
| int shift_x = (i == 1 || i == 2) ? desc->log2_chroma_w : 0; | |||
| int shift_y = (i == 1 || i == 2) ? desc->log2_chroma_h : 0; | |||
| if (desc->flags & (AV_PIX_FMT_FLAG_PAL | AV_PIX_FMT_FLAG_PSEUDOPAL) && i == 1) { | |||
| offsets[i] = 0; | |||
| break; | |||
| } | |||
| /* find any component descriptor for this plane */ | |||
| for (j = 0; j < desc->nb_components; j++) { | |||
| if (desc->comp[j].plane == i) { | |||
| comp = &desc->comp[j]; | |||
| break; | |||
| } | |||
| } | |||
| if (!comp) | |||
| return AVERROR_BUG; | |||
| offsets[i] = (frame->crop_top >> shift_y) * frame->linesize[i] + | |||
| (frame->crop_left >> shift_x) * comp->step; | |||
| } | |||
| return 0; | |||
| } | |||
| static int apply_cropping(AVCodecContext *avctx, AVFrame *frame) | |||
| { | |||
| const AVPixFmtDescriptor *desc; | |||
| size_t offsets[4]; | |||
| int i; | |||
| /* make sure we are noisy about decoders returning invalid cropping data */ | |||
| if (frame->crop_left >= INT_MAX - frame->crop_right || | |||
| frame->crop_top >= INT_MAX - frame->crop_bottom || | |||
| (frame->crop_left + frame->crop_right) >= frame->width || | |||
| (frame->crop_top + frame->crop_bottom) >= frame->height) { | |||
| av_log(avctx, AV_LOG_WARNING, | |||
| "Invalid cropping information set by a decoder: " | |||
| "%"SIZE_SPECIFIER"/%"SIZE_SPECIFIER"/%"SIZE_SPECIFIER"/%"SIZE_SPECIFIER" " | |||
| "(frame size %dx%d). This is a bug, please report it\n", | |||
| frame->crop_left, frame->crop_right, frame->crop_top, frame->crop_bottom, | |||
| frame->width, frame->height); | |||
| frame->crop_left = 0; | |||
| frame->crop_right = 0; | |||
| frame->crop_top = 0; | |||
| frame->crop_bottom = 0; | |||
| return 0; | |||
| } | |||
| if (!avctx->apply_cropping) | |||
| return 0; | |||
| desc = av_pix_fmt_desc_get(frame->format); | |||
| if (!desc) | |||
| return AVERROR_BUG; | |||
| /* Apply just the right/bottom cropping for hwaccel formats. Bitstream | |||
| * formats cannot be easily handled here either (and corresponding decoders | |||
| * should not export any cropping anyway), so do the same for those as well. | |||
| * */ | |||
| if (desc->flags & (AV_PIX_FMT_FLAG_BITSTREAM | AV_PIX_FMT_FLAG_HWACCEL)) { | |||
| frame->width -= frame->crop_right; | |||
| frame->height -= frame->crop_bottom; | |||
| frame->crop_right = 0; | |||
| frame->crop_bottom = 0; | |||
| return 0; | |||
| } | |||
| /* calculate the offsets for each plane */ | |||
| calc_cropping_offsets(offsets, frame, desc); | |||
| /* adjust the offsets to avoid breaking alignment */ | |||
| if (!(avctx->flags & AV_CODEC_FLAG_UNALIGNED)) { | |||
| int min_log2_align = INT_MAX; | |||
| for (i = 0; frame->data[i]; i++) { | |||
| int log2_align = offsets[i] ? ff_ctz(offsets[i]) : INT_MAX; | |||
| min_log2_align = FFMIN(log2_align, min_log2_align); | |||
| } | |||
| if (min_log2_align < 5) { | |||
| frame->crop_left &= ~((1 << min_log2_align) - 1); | |||
| calc_cropping_offsets(offsets, frame, desc); | |||
| } | |||
| } | |||
| for (i = 0; frame->data[i]; i++) | |||
| frame->data[i] += offsets[i]; | |||
| frame->width -= (frame->crop_left + frame->crop_right); | |||
| frame->height -= (frame->crop_top + frame->crop_bottom); | |||
| frame->crop_left = 0; | |||
| frame->crop_right = 0; | |||
| frame->crop_top = 0; | |||
| frame->crop_bottom = 0; | |||
| return 0; | |||
| } | |||
| int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame) | |||
| { | |||
| AVCodecInternal *avci = avctx->internal; | |||
| @@ -704,6 +811,14 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr | |||
| return ret; | |||
| } | |||
| if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { | |||
| ret = apply_cropping(avctx, frame); | |||
| if (ret < 0) { | |||
| av_frame_unref(frame); | |||
| return ret; | |||
| } | |||
| } | |||
| avctx->frame_number++; | |||
| return 0; | |||
| @@ -1611,7 +1726,8 @@ static int get_buffer_internal(AVCodecContext *avctx, AVFrame *frame, int flags) | |||
| validate_avframe_allocation(avctx, frame); | |||
| end: | |||
| if (avctx->codec_type == AVMEDIA_TYPE_VIDEO && !override_dimensions) { | |||
| if (avctx->codec_type == AVMEDIA_TYPE_VIDEO && !override_dimensions && | |||
| !(avctx->codec->caps_internal & FF_CODEC_CAP_EXPORTS_CROPPING)) { | |||
| frame->width = avctx->width; | |||
| frame->height = avctx->height; | |||
| } | |||
| @@ -58,6 +58,12 @@ | |||
| * skipped due to the skip_frame setting. | |||
| */ | |||
| #define FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM (1 << 3) | |||
| /** | |||
| * The decoder sets the cropping fields in the output frames manually. | |||
| * If this cap is set, the generic code will initialize output frame | |||
| * dimensions to coded rather than display values. | |||
| */ | |||
| #define FF_CODEC_CAP_EXPORTS_CROPPING (1 << 4) | |||
| #ifdef TRACE | |||
| # define ff_tlog(ctx, ...) av_log(ctx, AV_LOG_TRACE, __VA_ARGS__) | |||
| @@ -561,6 +561,7 @@ static const AVOption avcodec_options[] = { | |||
| #if FF_API_SIDEDATA_ONLY_PKT | |||
| {"side_data_only_packets", NULL, OFFSET(side_data_only_packets), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, A|V|E }, | |||
| #endif | |||
| {"apply_cropping", NULL, OFFSET(apply_cropping), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, V | D }, | |||
| {"skip_alpha", "Skip processing alpha", OFFSET(skip_alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, V|D }, | |||
| {"field_order", "Field order", OFFSET(field_order), AV_OPT_TYPE_INT, {.i64 = AV_FIELD_UNKNOWN }, 0, 5, V|D|E, "field_order" }, | |||
| {"progressive", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_PROGRESSIVE }, 0, 0, V|D|E, "field_order" }, | |||
| @@ -28,8 +28,8 @@ | |||
| #include "libavutil/version.h" | |||
| #define LIBAVCODEC_VERSION_MAJOR 57 | |||
| #define LIBAVCODEC_VERSION_MINOR 94 | |||
| #define LIBAVCODEC_VERSION_MICRO 101 | |||
| #define LIBAVCODEC_VERSION_MINOR 95 | |||
| #define LIBAVCODEC_VERSION_MICRO 100 | |||
| #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ | |||
| LIBAVCODEC_VERSION_MINOR, \ | |||
| @@ -149,6 +149,7 @@ stream=0, decode=0 | |||
| sub_text_format=1 | |||
| refcounted_frames=false | |||
| side_data_only_packets=true | |||
| apply_cropping=true | |||
| skip_alpha=false | |||
| field_order=0 | |||
| dump_separator= | |||
| @@ -307,6 +308,7 @@ stream=0, decode=1 | |||
| sub_text_format=1 | |||
| refcounted_frames=false | |||
| side_data_only_packets=true | |||
| apply_cropping=true | |||
| skip_alpha=false | |||
| field_order=0 | |||
| dump_separator= | |||
| @@ -149,6 +149,7 @@ stream=0, decode=0 | |||
| sub_text_format=1 | |||
| refcounted_frames=false | |||
| side_data_only_packets=true | |||
| apply_cropping=true | |||
| skip_alpha=false | |||
| field_order=0 | |||
| dump_separator= | |||
| @@ -307,6 +308,7 @@ stream=0, decode=1 | |||
| sub_text_format=1 | |||
| refcounted_frames=false | |||
| side_data_only_packets=true | |||
| apply_cropping=true | |||
| skip_alpha=false | |||
| field_order=0 | |||
| dump_separator= | |||