From 9f5dfd6e9cabd3d419a3a58f7bfa3b3c1e179638 Mon Sep 17 00:00:00 2001
From: Miroslav Slugen <thunder.m@email.cz>
Date: Sun, 12 Feb 2017 20:29:34 +0100
Subject: [PATCH 1/1] cuvid: add resize and crop futures

---
 ffmpeg.h           |  2 ++
 ffmpeg_opt.c       | 12 +++++++
 libavcodec/cuvid.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 96 insertions(+), 13 deletions(-)

diff --git a/ffmpeg.h b/ffmpeg.h
index 85a8f18..0374f11 100644
--- a/ffmpeg.h
+++ b/ffmpeg.h
@@ -132,6 +132,8 @@ typedef struct OptionsContext {
     int        nb_hwaccel_output_formats;
     SpecifierOpt *autorotate;
     int        nb_autorotate;
+    SpecifierOpt *resize;
+    int        nb_resize;
 
     /* output options */
     StreamMap *stream_maps;
diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c
index 6a47d32..fcf4792 100644
--- a/ffmpeg_opt.c
+++ b/ffmpeg_opt.c
@@ -659,6 +659,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
         char *codec_tag = NULL;
         char *next;
         char *discard_str = NULL;
+        char *resize_str = NULL;
         const AVClass *cc = avcodec_get_class();
         const AVOption *discard_opt = av_opt_find(&cc, "skip_frame", NULL, 0, 0);
 
@@ -722,6 +723,14 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
         case AVMEDIA_TYPE_VIDEO:
             if(!ist->dec)
                 ist->dec = avcodec_find_decoder(par->codec_id);
+
+            MATCH_PER_STREAM_OPT(resize, str, resize_str, ic, st);
+            if (resize_str) {
+                av_parse_video_size(&ist->dec_ctx->width, &ist->dec_ctx->height, resize_str);
+                ist->dec_ctx->coded_width  = ist->dec_ctx->width;
+                ist->dec_ctx->coded_height = ist->dec_ctx->height;
+            }
+
 #if FF_API_EMU_EDGE
             if (av_codec_get_lowres(st->codec)) {
                 av_codec_set_lowres(ist->dec_ctx, av_codec_get_lowres(st->codec));
@@ -3591,6 +3600,9 @@ const OptionDef options[] = {
     { "hwaccel_output_format", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
                           OPT_SPEC | OPT_INPUT,                                  { .off = OFFSET(hwaccel_output_formats) },
         "select output format used with HW accelerated decoding", "format" },
+    { "resize",         OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
+                        OPT_SPEC | OPT_INPUT | OPT_OUTPUT,                       { .off = OFFSET(resize) },
+        "resizer builtin input or output" },
 #if CONFIG_VDA || CONFIG_VIDEOTOOLBOX
     { "videotoolbox_pixfmt", HAS_ARG | OPT_STRING | OPT_EXPERT, { &videotoolbox_pixfmt}, "" },
 #endif
diff --git a/libavcodec/cuvid.c b/libavcodec/cuvid.c
index a2e125d..7370ed1 100644
--- a/libavcodec/cuvid.c
+++ b/libavcodec/cuvid.c
@@ -21,6 +21,7 @@
 
 #include "compat/cuda/dynlink_loader.h"
 
+#include "libavutil/avstring.h"
 #include "libavutil/buffer.h"
 #include "libavutil/mathematics.h"
 #include "libavutil/hwcontext.h"
@@ -43,6 +44,15 @@ typedef struct CuvidContext
     char *cu_gpu;
     int nb_surfaces;
     int drop_second_field;
+    char *crop;
+    char *resize;
+
+    struct {
+        short left;
+        short top;
+        short right;
+        short bottom;
+    } offset;
 
     AVBufferRef *hwdevice;
     AVBufferRef *hwframe;
@@ -57,6 +67,10 @@ typedef struct CuvidContext
     int internal_error;
     int decoder_flushing;
 
+    int width;
+    int height;
+    int coded_width;
+    int coded_height;
     cudaVideoCodec codec_type;
     cudaVideoChromaFormat chroma_format;
 
@@ -105,6 +119,7 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
     AVHWFramesContext *hwframe_ctx = (AVHWFramesContext*)ctx->hwframe->data;
     CUVIDDECODECREATEINFO cuinfo;
     int surface_fmt;
+    int width, height;
 
     enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA,
                                        AV_PIX_FMT_NONE,  // Will be updated below
@@ -144,8 +159,8 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
 
     avctx->pix_fmt = surface_fmt;
 
-    avctx->width = format->display_area.right;
-    avctx->height = format->display_area.bottom;
+    width = format->display_area.right - format->display_area.left;
+    height = format->display_area.bottom - format->display_area.top;
 
     ff_set_sar(avctx, av_div_q(
         (AVRational){ format->display_aspect_ratio.x, format->display_aspect_ratio.y },
@@ -174,8 +189,10 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
     }
 
     if (ctx->cudecoder
-            && avctx->coded_width == format->coded_width
-            && avctx->coded_height == format->coded_height
+            && ctx->width == width
+            && ctx->height == height
+            && ctx->coded_width == format->coded_width
+            && ctx->coded_height == format->coded_height
             && ctx->chroma_format == format->chroma_format
             && ctx->codec_type == format->codec)
         return 1;
@@ -204,11 +221,15 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
         return 0;
     }
 
-    avctx->coded_width = format->coded_width;
-    avctx->coded_height = format->coded_height;
-
+    ctx->width = width;
+    ctx->height = height;
+    ctx->coded_width = format->coded_width;
+    ctx->coded_height = format->coded_height;
     ctx->chroma_format = format->chroma_format;
 
+    avctx->coded_width = avctx->width;
+    avctx->coded_height = avctx->height;
+
     memset(&cuinfo, 0, sizeof(cuinfo));
 
     cuinfo.CodecType = ctx->codec_type = format->codec;
@@ -228,15 +249,24 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
         return 0;
     }
 
-    cuinfo.ulWidth = avctx->coded_width;
-    cuinfo.ulHeight = avctx->coded_height;
-    cuinfo.ulTargetWidth = cuinfo.ulWidth;
-    cuinfo.ulTargetHeight = cuinfo.ulHeight;
+    cuinfo.ulWidth = ctx->coded_width;
+    cuinfo.ulHeight = ctx->coded_height;
+
+    /* cropping depends on original resolution */
+    cuinfo.display_area.left = ctx->offset.left;
+    cuinfo.display_area.top = ctx->offset.top;
+    cuinfo.display_area.right = cuinfo.ulWidth - ctx->offset.right;
+    cuinfo.display_area.bottom = cuinfo.ulHeight - ctx->offset.bottom;
 
+    /* scaling to requested resolution */
+    cuinfo.ulTargetWidth = avctx->width;
+    cuinfo.ulTargetHeight = avctx->height;
+
+    /* aspect ratio conversion, 1:1, depends on scaled resolution */
     cuinfo.target_rect.left = 0;
     cuinfo.target_rect.top = 0;
-    cuinfo.target_rect.right = cuinfo.ulWidth;
-    cuinfo.target_rect.bottom = cuinfo.ulHeight;
+    cuinfo.target_rect.right = cuinfo.ulTargetWidth;
+    cuinfo.target_rect.bottom = cuinfo.ulTargetHeight;
 
     cuinfo.ulNumDecodeSurfaces = ctx->nb_surfaces;
     cuinfo.ulNumOutputSurfaces = 1;
@@ -636,6 +666,11 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx,
     cuinfo.ulTargetWidth = cuinfo.ulWidth;
     cuinfo.ulTargetHeight = cuinfo.ulHeight;
 
+    cuinfo.display_area.left = 0;
+    cuinfo.display_area.top = 0;
+    cuinfo.display_area.right = cuinfo.ulWidth;
+    cuinfo.display_area.bottom = cuinfo.ulHeight;
+
     cuinfo.target_rect.left = 0;
     cuinfo.target_rect.top = 0;
     cuinfo.target_rect.right = cuinfo.ulWidth;
@@ -822,6 +857,38 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
                FFMIN(sizeof(ctx->cuparse_ext.raw_seqhdr_data), avctx->extradata_size));
     }
 
+    ctx->offset.top = 0;
+    ctx->offset.bottom = 0;
+    ctx->offset.left = 0;
+    ctx->offset.right = 0;
+    if (ctx->crop) {
+        char *crop_str, *saveptr;
+        int crop_idx = 0;
+        crop_str = av_strdup(ctx->crop);
+        crop_str = av_strtok(crop_str, "x", &saveptr);
+        while (crop_str) {
+            switch (crop_idx++) {
+            case 0:
+                ctx->offset.top = atoi(crop_str);
+                break;
+            case 1:
+                ctx->offset.bottom = atoi(crop_str);
+                break;
+            case 2:
+                ctx->offset.left = atoi(crop_str);
+                break;
+            case 3:
+                ctx->offset.right = atoi(crop_str);
+                break;
+            default:
+                break;
+            }
+            crop_str = av_strtok(NULL, "x", &saveptr);
+        }
+        free(crop_str);
+    }
+
+
     ctx->cuparseinfo.ulMaxNumDecodeSurfaces = ctx->nb_surfaces;
     ctx->cuparseinfo.ulMaxDisplayDelay = 4;
     ctx->cuparseinfo.pUserData = avctx;
@@ -934,6 +1001,8 @@ static const AVOption options[] = {
     { "gpu",      "GPU to be used for decoding", OFFSET(cu_gpu), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD },
     { "surfaces", "Maximum surfaces to be used for decoding", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, VD },
     { "drop_second_field", "Drop second field when deinterlacing", OFFSET(drop_second_field), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VD },
+    { "crop",     "Crop (top)x(bottom)x(left)x(right)", OFFSET(crop), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD },
+    { "resize",   "Resize (width)x(height)", OFFSET(resize), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD },
     { NULL }
 };
 
-- 
2.1.4

