Browse code

avcodec/cuvid: add support for cropping/resizing

Overhauled version, original patch by Miroslav Slugeň <thunder.m@email.cz>.

Timo Rothenpieler authored on 2017/03/05 23:32:36
Showing 1 changed files
... ...
@@ -43,6 +43,20 @@ typedef struct CuvidContext
43 43
     char *cu_gpu;
44 44
     int nb_surfaces;
45 45
     int drop_second_field;
46
+    char *crop_expr;
47
+    char *resize_expr;
48
+
49
+    struct {
50
+        int left;
51
+        int top;
52
+        int right;
53
+        int bottom;
54
+    } crop;
55
+
56
+    struct {
57
+        int width;
58
+        int height;
59
+    } resize;
46 60
 
47 61
     AVBufferRef *hwdevice;
48 62
     AVBufferRef *hwframe;
... ...
@@ -107,17 +121,46 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
107 107
     CUVIDDECODECREATEINFO cuinfo;
108 108
     int surface_fmt;
109 109
 
110
+    int old_width = avctx->width;
111
+    int old_height = avctx->height;
112
+
110 113
     enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA,
111 114
                                        AV_PIX_FMT_NONE,  // Will be updated below
112 115
                                        AV_PIX_FMT_NONE };
113 116
 
114 117
     av_log(avctx, AV_LOG_TRACE, "pfnSequenceCallback, progressive_sequence=%d\n", format->progressive_sequence);
115 118
 
119
+    memset(&cuinfo, 0, sizeof(cuinfo));
120
+
116 121
     ctx->internal_error = 0;
117 122
 
123
+    avctx->coded_width = cuinfo.ulWidth = format->coded_width;
124
+    avctx->coded_height = cuinfo.ulHeight = format->coded_height;
125
+
126
+    // apply cropping
127
+    cuinfo.display_area.left = format->display_area.left + ctx->crop.left;
128
+    cuinfo.display_area.top = format->display_area.top + ctx->crop.top;
129
+    cuinfo.display_area.right = format->display_area.right - ctx->crop.right;
130
+    cuinfo.display_area.bottom = format->display_area.bottom - ctx->crop.bottom;
131
+
118 132
     // width and height need to be set before calling ff_get_format
119
-    avctx->width = format->display_area.right;
120
-    avctx->height = format->display_area.bottom;
133
+    if (ctx->resize_expr) {
134
+        avctx->width = ctx->resize.width;
135
+        avctx->height = ctx->resize.height;
136
+    } else {
137
+        avctx->width = cuinfo.display_area.right - cuinfo.display_area.left;
138
+        avctx->height = cuinfo.display_area.bottom - cuinfo.display_area.top;
139
+    }
140
+
141
+    // target width/height need to be multiples of two
142
+    cuinfo.ulTargetWidth = avctx->width = (avctx->width + 1) & ~1;
143
+    cuinfo.ulTargetHeight = avctx->height = (avctx->height + 1) & ~1;
144
+
145
+    // aspect ratio conversion, 1:1, depends on scaled resolution
146
+    cuinfo.target_rect.left = 0;
147
+    cuinfo.target_rect.top = 0;
148
+    cuinfo.target_rect.right = cuinfo.ulTargetWidth;
149
+    cuinfo.target_rect.bottom = cuinfo.ulTargetHeight;
121 150
 
122 151
     switch (format->bit_depth_luma_minus8) {
123 152
     case 0: // 8-bit
... ...
@@ -195,6 +238,8 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
195 195
     if (ctx->cudecoder
196 196
             && avctx->coded_width == format->coded_width
197 197
             && avctx->coded_height == format->coded_height
198
+            && avctx->width == old_width
199
+            && avctx->height == old_height
198 200
             && ctx->chroma_format == format->chroma_format
199 201
             && ctx->codec_type == format->codec)
200 202
         return 1;
... ...
@@ -228,13 +273,8 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
228 228
         return 0;
229 229
     }
230 230
 
231
-    avctx->coded_width = format->coded_width;
232
-    avctx->coded_height = format->coded_height;
233
-
234 231
     ctx->chroma_format = format->chroma_format;
235 232
 
236
-    memset(&cuinfo, 0, sizeof(cuinfo));
237
-
238 233
     cuinfo.CodecType = ctx->codec_type = format->codec;
239 234
     cuinfo.ChromaFormat = format->chroma_format;
240 235
 
... ...
@@ -252,16 +292,6 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
252 252
         return 0;
253 253
     }
254 254
 
255
-    cuinfo.ulWidth = avctx->coded_width;
256
-    cuinfo.ulHeight = avctx->coded_height;
257
-    cuinfo.ulTargetWidth = cuinfo.ulWidth;
258
-    cuinfo.ulTargetHeight = cuinfo.ulHeight;
259
-
260
-    cuinfo.target_rect.left = 0;
261
-    cuinfo.target_rect.top = 0;
262
-    cuinfo.target_rect.right = cuinfo.ulWidth;
263
-    cuinfo.target_rect.bottom = cuinfo.ulHeight;
264
-
265 255
     cuinfo.ulNumDecodeSurfaces = ctx->nb_surfaces;
266 256
     cuinfo.ulNumOutputSurfaces = 1;
267 257
     cuinfo.ulCreationFlags = cudaVideoCreate_PreferCUVID;
... ...
@@ -486,7 +516,7 @@ static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame)
486 486
                 if (ret < 0)
487 487
                     goto error;
488 488
 
489
-                offset += avctx->coded_height;
489
+                offset += avctx->height;
490 490
             }
491 491
         } else if (avctx->pix_fmt == AV_PIX_FMT_NV12 ||
492 492
                    avctx->pix_fmt == AV_PIX_FMT_P010 ||
... ...
@@ -502,7 +532,7 @@ static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame)
502 502
             tmp_frame->hw_frames_ctx = av_buffer_ref(ctx->hwframe);
503 503
             tmp_frame->data[0]       = (uint8_t*)mapped_frame;
504 504
             tmp_frame->linesize[0]   = pitch;
505
-            tmp_frame->data[1]       = (uint8_t*)(mapped_frame + avctx->coded_height * pitch);
505
+            tmp_frame->data[1]       = (uint8_t*)(mapped_frame + avctx->height * pitch);
506 506
             tmp_frame->linesize[1]   = pitch;
507 507
             tmp_frame->width         = avctx->width;
508 508
             tmp_frame->height        = avctx->height;
... ...
@@ -708,6 +738,21 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
708 708
     }
709 709
     avctx->pix_fmt = ret;
710 710
 
711
+    if (ctx->resize_expr && sscanf(ctx->resize_expr, "%dx%d",
712
+                                   &ctx->resize.width, &ctx->resize.height) != 2) {
713
+        av_log(avctx, AV_LOG_ERROR, "Invalid resize expressions\n");
714
+        ret = AVERROR(EINVAL);
715
+        goto error;
716
+    }
717
+
718
+    if (ctx->crop_expr && sscanf(ctx->crop_expr, "%dx%dx%dx%d",
719
+                                 &ctx->crop.top, &ctx->crop.bottom,
720
+                                 &ctx->crop.left, &ctx->crop.right) != 4) {
721
+        av_log(avctx, AV_LOG_ERROR, "Invalid cropping expressions\n");
722
+        ret = AVERROR(EINVAL);
723
+        goto error;
724
+    }
725
+
711 726
     ret = cuvid_load_functions(&ctx->cvdl);
712 727
     if (ret < 0) {
713 728
         av_log(avctx, AV_LOG_ERROR, "Failed loading nvcuvid.\n");
... ...
@@ -953,6 +998,8 @@ static const AVOption options[] = {
953 953
     { "gpu",      "GPU to be used for decoding", OFFSET(cu_gpu), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD },
954 954
     { "surfaces", "Maximum surfaces to be used for decoding", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, VD },
955 955
     { "drop_second_field", "Drop second field when deinterlacing", OFFSET(drop_second_field), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
956
+    { "crop",     "Crop (top)x(bottom)x(left)x(right)", OFFSET(crop_expr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD },
957
+    { "resize",   "Resize (width)x(height)", OFFSET(resize_expr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD },
956 958
     { NULL }
957 959
 };
958 960