blob: b5acce653b00f05cce064f66358f788d14a2f8e7
1 | /* |
2 | * This file is part of FFmpeg. |
3 | * |
4 | * FFmpeg is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * FFmpeg is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with FFmpeg; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
17 | */ |
18 | |
19 | /** |
20 | * @file |
21 | * scale video filter |
22 | */ |
23 | |
24 | #include <nppi.h> |
25 | #include <stdio.h> |
26 | #include <string.h> |
27 | |
28 | #include "libavutil/avstring.h" |
29 | #include "libavutil/common.h" |
30 | #include "libavutil/hwcontext.h" |
31 | #include "libavutil/hwcontext_cuda_internal.h" |
32 | #include "libavutil/internal.h" |
33 | #include "libavutil/opt.h" |
34 | #include "libavutil/pixdesc.h" |
35 | |
36 | #include "avfilter.h" |
37 | #include "formats.h" |
38 | #include "internal.h" |
39 | #include "scale.h" |
40 | #include "video.h" |
41 | |
42 | static const enum AVPixelFormat supported_formats[] = { |
43 | AV_PIX_FMT_YUV420P, |
44 | AV_PIX_FMT_NV12, |
45 | AV_PIX_FMT_YUV444P, |
46 | }; |
47 | |
48 | static const enum AVPixelFormat deinterleaved_formats[][2] = { |
49 | { AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P }, |
50 | }; |
51 | |
52 | enum ScaleStage { |
53 | STAGE_DEINTERLEAVE, |
54 | STAGE_RESIZE, |
55 | STAGE_INTERLEAVE, |
56 | STAGE_NB, |
57 | }; |
58 | |
59 | typedef struct NPPScaleStageContext { |
60 | int stage_needed; |
61 | enum AVPixelFormat in_fmt; |
62 | enum AVPixelFormat out_fmt; |
63 | |
64 | struct { |
65 | int width; |
66 | int height; |
67 | } planes_in[3], planes_out[3]; |
68 | |
69 | AVBufferRef *frames_ctx; |
70 | AVFrame *frame; |
71 | } NPPScaleStageContext; |
72 | |
73 | typedef struct NPPScaleContext { |
74 | const AVClass *class; |
75 | |
76 | NPPScaleStageContext stages[STAGE_NB]; |
77 | AVFrame *tmp_frame; |
78 | int passthrough; |
79 | |
80 | int shift_width, shift_height; |
81 | |
82 | /** |
83 | * New dimensions. Special values are: |
84 | * 0 = original width/height |
85 | * -1 = keep original aspect |
86 | */ |
87 | int w, h; |
88 | |
89 | /** |
90 | * Output sw format. AV_PIX_FMT_NONE for no conversion. |
91 | */ |
92 | enum AVPixelFormat format; |
93 | |
94 | char *w_expr; ///< width expression string |
95 | char *h_expr; ///< height expression string |
96 | char *format_str; |
97 | |
98 | int interp_algo; |
99 | } NPPScaleContext; |
100 | |
101 | static int nppscale_init(AVFilterContext *ctx) |
102 | { |
103 | NPPScaleContext *s = ctx->priv; |
104 | int i; |
105 | |
106 | if (!strcmp(s->format_str, "same")) { |
107 | s->format = AV_PIX_FMT_NONE; |
108 | } else { |
109 | s->format = av_get_pix_fmt(s->format_str); |
110 | if (s->format == AV_PIX_FMT_NONE) { |
111 | av_log(ctx, AV_LOG_ERROR, "Unrecognized pixel format: %s\n", s->format_str); |
112 | return AVERROR(EINVAL); |
113 | } |
114 | } |
115 | |
116 | for (i = 0; i < FF_ARRAY_ELEMS(s->stages); i++) { |
117 | s->stages[i].frame = av_frame_alloc(); |
118 | if (!s->stages[i].frame) |
119 | return AVERROR(ENOMEM); |
120 | } |
121 | s->tmp_frame = av_frame_alloc(); |
122 | if (!s->tmp_frame) |
123 | return AVERROR(ENOMEM); |
124 | |
125 | return 0; |
126 | } |
127 | |
128 | static void nppscale_uninit(AVFilterContext *ctx) |
129 | { |
130 | NPPScaleContext *s = ctx->priv; |
131 | int i; |
132 | |
133 | for (i = 0; i < FF_ARRAY_ELEMS(s->stages); i++) { |
134 | av_frame_free(&s->stages[i].frame); |
135 | av_buffer_unref(&s->stages[i].frames_ctx); |
136 | } |
137 | av_frame_free(&s->tmp_frame); |
138 | } |
139 | |
140 | static int nppscale_query_formats(AVFilterContext *ctx) |
141 | { |
142 | static const enum AVPixelFormat pixel_formats[] = { |
143 | AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE, |
144 | }; |
145 | AVFilterFormats *pix_fmts = ff_make_format_list(pixel_formats); |
146 | |
147 | return ff_set_common_formats(ctx, pix_fmts); |
148 | } |
149 | |
150 | static int init_stage(NPPScaleStageContext *stage, AVBufferRef *device_ctx) |
151 | { |
152 | AVBufferRef *out_ref = NULL; |
153 | AVHWFramesContext *out_ctx; |
154 | int in_sw, in_sh, out_sw, out_sh; |
155 | int ret, i; |
156 | |
157 | av_pix_fmt_get_chroma_sub_sample(stage->in_fmt, &in_sw, &in_sh); |
158 | av_pix_fmt_get_chroma_sub_sample(stage->out_fmt, &out_sw, &out_sh); |
159 | if (!stage->planes_out[0].width) { |
160 | stage->planes_out[0].width = stage->planes_in[0].width; |
161 | stage->planes_out[0].height = stage->planes_in[0].height; |
162 | } |
163 | |
164 | for (i = 1; i < FF_ARRAY_ELEMS(stage->planes_in); i++) { |
165 | stage->planes_in[i].width = stage->planes_in[0].width >> in_sw; |
166 | stage->planes_in[i].height = stage->planes_in[0].height >> in_sh; |
167 | stage->planes_out[i].width = stage->planes_out[0].width >> out_sw; |
168 | stage->planes_out[i].height = stage->planes_out[0].height >> out_sh; |
169 | } |
170 | |
171 | out_ref = av_hwframe_ctx_alloc(device_ctx); |
172 | if (!out_ref) |
173 | return AVERROR(ENOMEM); |
174 | out_ctx = (AVHWFramesContext*)out_ref->data; |
175 | |
176 | out_ctx->format = AV_PIX_FMT_CUDA; |
177 | out_ctx->sw_format = stage->out_fmt; |
178 | out_ctx->width = FFALIGN(stage->planes_out[0].width, 32); |
179 | out_ctx->height = FFALIGN(stage->planes_out[0].height, 32); |
180 | |
181 | ret = av_hwframe_ctx_init(out_ref); |
182 | if (ret < 0) |
183 | goto fail; |
184 | |
185 | av_frame_unref(stage->frame); |
186 | ret = av_hwframe_get_buffer(out_ref, stage->frame, 0); |
187 | if (ret < 0) |
188 | goto fail; |
189 | |
190 | stage->frame->width = stage->planes_out[0].width; |
191 | stage->frame->height = stage->planes_out[0].height; |
192 | |
193 | av_buffer_unref(&stage->frames_ctx); |
194 | stage->frames_ctx = out_ref; |
195 | |
196 | return 0; |
197 | fail: |
198 | av_buffer_unref(&out_ref); |
199 | return ret; |
200 | } |
201 | |
202 | static int format_is_supported(enum AVPixelFormat fmt) |
203 | { |
204 | int i; |
205 | |
206 | for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) |
207 | if (supported_formats[i] == fmt) |
208 | return 1; |
209 | return 0; |
210 | } |
211 | |
212 | static enum AVPixelFormat get_deinterleaved_format(enum AVPixelFormat fmt) |
213 | { |
214 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); |
215 | int i, planes; |
216 | |
217 | planes = av_pix_fmt_count_planes(fmt); |
218 | if (planes == desc->nb_components) |
219 | return fmt; |
220 | for (i = 0; i < FF_ARRAY_ELEMS(deinterleaved_formats); i++) |
221 | if (deinterleaved_formats[i][0] == fmt) |
222 | return deinterleaved_formats[i][1]; |
223 | return AV_PIX_FMT_NONE; |
224 | } |
225 | |
226 | static int init_processing_chain(AVFilterContext *ctx, int in_width, int in_height, |
227 | int out_width, int out_height) |
228 | { |
229 | NPPScaleContext *s = ctx->priv; |
230 | |
231 | AVHWFramesContext *in_frames_ctx; |
232 | |
233 | enum AVPixelFormat in_format; |
234 | enum AVPixelFormat out_format; |
235 | enum AVPixelFormat in_deinterleaved_format; |
236 | enum AVPixelFormat out_deinterleaved_format; |
237 | |
238 | int i, ret, last_stage = -1; |
239 | |
240 | /* check that we have a hw context */ |
241 | if (!ctx->inputs[0]->hw_frames_ctx) { |
242 | av_log(ctx, AV_LOG_ERROR, "No hw context provided on input\n"); |
243 | return AVERROR(EINVAL); |
244 | } |
245 | in_frames_ctx = (AVHWFramesContext*)ctx->inputs[0]->hw_frames_ctx->data; |
246 | in_format = in_frames_ctx->sw_format; |
247 | out_format = (s->format == AV_PIX_FMT_NONE) ? in_format : s->format; |
248 | |
249 | if (!format_is_supported(in_format)) { |
250 | av_log(ctx, AV_LOG_ERROR, "Unsupported input format: %s\n", |
251 | av_get_pix_fmt_name(in_format)); |
252 | return AVERROR(ENOSYS); |
253 | } |
254 | if (!format_is_supported(out_format)) { |
255 | av_log(ctx, AV_LOG_ERROR, "Unsupported output format: %s\n", |
256 | av_get_pix_fmt_name(out_format)); |
257 | return AVERROR(ENOSYS); |
258 | } |
259 | |
260 | in_deinterleaved_format = get_deinterleaved_format(in_format); |
261 | out_deinterleaved_format = get_deinterleaved_format(out_format); |
262 | if (in_deinterleaved_format == AV_PIX_FMT_NONE || |
263 | out_deinterleaved_format == AV_PIX_FMT_NONE) |
264 | return AVERROR_BUG; |
265 | |
266 | /* figure out which stages need to be done */ |
267 | if (in_width != out_width || in_height != out_height || |
268 | in_deinterleaved_format != out_deinterleaved_format) { |
269 | s->stages[STAGE_RESIZE].stage_needed = 1; |
270 | |
271 | if (s->interp_algo == NPPI_INTER_SUPER && |
272 | (out_width > in_width && out_height > in_height)) { |
273 | s->interp_algo = NPPI_INTER_LANCZOS; |
274 | av_log(ctx, AV_LOG_WARNING, "super-sampling not supported for output dimensions, using lanczos instead.\n"); |
275 | } |
276 | if (s->interp_algo == NPPI_INTER_SUPER && |
277 | !(out_width < in_width && out_height < in_height)) { |
278 | s->interp_algo = NPPI_INTER_CUBIC; |
279 | av_log(ctx, AV_LOG_WARNING, "super-sampling not supported for output dimensions, using cubic instead.\n"); |
280 | } |
281 | } |
282 | |
283 | if (!s->stages[STAGE_RESIZE].stage_needed && in_format == out_format) |
284 | s->passthrough = 1; |
285 | |
286 | if (!s->passthrough) { |
287 | if (in_format != in_deinterleaved_format) |
288 | s->stages[STAGE_DEINTERLEAVE].stage_needed = 1; |
289 | if (out_format != out_deinterleaved_format) |
290 | s->stages[STAGE_INTERLEAVE].stage_needed = 1; |
291 | } |
292 | |
293 | s->stages[STAGE_DEINTERLEAVE].in_fmt = in_format; |
294 | s->stages[STAGE_DEINTERLEAVE].out_fmt = in_deinterleaved_format; |
295 | s->stages[STAGE_DEINTERLEAVE].planes_in[0].width = in_width; |
296 | s->stages[STAGE_DEINTERLEAVE].planes_in[0].height = in_height; |
297 | |
298 | s->stages[STAGE_RESIZE].in_fmt = in_deinterleaved_format; |
299 | s->stages[STAGE_RESIZE].out_fmt = out_deinterleaved_format; |
300 | s->stages[STAGE_RESIZE].planes_in[0].width = in_width; |
301 | s->stages[STAGE_RESIZE].planes_in[0].height = in_height; |
302 | s->stages[STAGE_RESIZE].planes_out[0].width = out_width; |
303 | s->stages[STAGE_RESIZE].planes_out[0].height = out_height; |
304 | |
305 | s->stages[STAGE_INTERLEAVE].in_fmt = out_deinterleaved_format; |
306 | s->stages[STAGE_INTERLEAVE].out_fmt = out_format; |
307 | s->stages[STAGE_INTERLEAVE].planes_in[0].width = out_width; |
308 | s->stages[STAGE_INTERLEAVE].planes_in[0].height = out_height; |
309 | |
310 | /* init the hardware contexts */ |
311 | for (i = 0; i < FF_ARRAY_ELEMS(s->stages); i++) { |
312 | if (!s->stages[i].stage_needed) |
313 | continue; |
314 | |
315 | ret = init_stage(&s->stages[i], in_frames_ctx->device_ref); |
316 | if (ret < 0) |
317 | return ret; |
318 | |
319 | last_stage = i; |
320 | } |
321 | |
322 | if (last_stage < 0) |
323 | return 0; |
324 | ctx->outputs[0]->hw_frames_ctx = av_buffer_ref(s->stages[last_stage].frames_ctx); |
325 | if (!ctx->outputs[0]->hw_frames_ctx) |
326 | return AVERROR(ENOMEM); |
327 | |
328 | return 0; |
329 | } |
330 | |
331 | static int nppscale_config_props(AVFilterLink *outlink) |
332 | { |
333 | AVFilterContext *ctx = outlink->src; |
334 | AVFilterLink *inlink = outlink->src->inputs[0]; |
335 | NPPScaleContext *s = ctx->priv; |
336 | int w, h; |
337 | int ret; |
338 | |
339 | if ((ret = ff_scale_eval_dimensions(s, |
340 | s->w_expr, s->h_expr, |
341 | inlink, outlink, |
342 | &w, &h)) < 0) |
343 | goto fail; |
344 | |
345 | if (((int64_t)h * inlink->w) > INT_MAX || |
346 | ((int64_t)w * inlink->h) > INT_MAX) |
347 | av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); |
348 | |
349 | outlink->w = w; |
350 | outlink->h = h; |
351 | |
352 | ret = init_processing_chain(ctx, inlink->w, inlink->h, w, h); |
353 | if (ret < 0) |
354 | return ret; |
355 | |
356 | av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d -> w:%d h:%d\n", |
357 | inlink->w, inlink->h, outlink->w, outlink->h); |
358 | |
359 | if (inlink->sample_aspect_ratio.num) |
360 | outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h*inlink->w, |
361 | outlink->w*inlink->h}, |
362 | inlink->sample_aspect_ratio); |
363 | else |
364 | outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; |
365 | |
366 | return 0; |
367 | |
368 | fail: |
369 | return ret; |
370 | } |
371 | |
372 | static int nppscale_deinterleave(AVFilterContext *ctx, NPPScaleStageContext *stage, |
373 | AVFrame *out, AVFrame *in) |
374 | { |
375 | AVHWFramesContext *in_frames_ctx = (AVHWFramesContext*)in->hw_frames_ctx->data; |
376 | NppStatus err; |
377 | |
378 | switch (in_frames_ctx->sw_format) { |
379 | case AV_PIX_FMT_NV12: |
380 | err = nppiYCbCr420_8u_P2P3R(in->data[0], in->linesize[0], |
381 | in->data[1], in->linesize[1], |
382 | out->data, out->linesize, |
383 | (NppiSize){ in->width, in->height }); |
384 | break; |
385 | default: |
386 | return AVERROR_BUG; |
387 | } |
388 | if (err != NPP_SUCCESS) { |
389 | av_log(ctx, AV_LOG_ERROR, "NPP deinterleave error: %d\n", err); |
390 | return AVERROR_UNKNOWN; |
391 | } |
392 | |
393 | return 0; |
394 | } |
395 | |
396 | static int nppscale_resize(AVFilterContext *ctx, NPPScaleStageContext *stage, |
397 | AVFrame *out, AVFrame *in) |
398 | { |
399 | NPPScaleContext *s = ctx->priv; |
400 | NppStatus err; |
401 | int i; |
402 | |
403 | for (i = 0; i < FF_ARRAY_ELEMS(in->data) && in->data[i]; i++) { |
404 | int iw = stage->planes_in[i].width; |
405 | int ih = stage->planes_in[i].height; |
406 | int ow = stage->planes_out[i].width; |
407 | int oh = stage->planes_out[i].height; |
408 | |
409 | err = nppiResizeSqrPixel_8u_C1R(in->data[i], (NppiSize){ iw, ih }, |
410 | in->linesize[i], (NppiRect){ 0, 0, iw, ih }, |
411 | out->data[i], out->linesize[i], |
412 | (NppiRect){ 0, 0, ow, oh }, |
413 | (double)ow / iw, (double)oh / ih, |
414 | 0.0, 0.0, s->interp_algo); |
415 | if (err != NPP_SUCCESS) { |
416 | av_log(ctx, AV_LOG_ERROR, "NPP resize error: %d\n", err); |
417 | return AVERROR_UNKNOWN; |
418 | } |
419 | } |
420 | |
421 | return 0; |
422 | } |
423 | |
424 | static int nppscale_interleave(AVFilterContext *ctx, NPPScaleStageContext *stage, |
425 | AVFrame *out, AVFrame *in) |
426 | { |
427 | AVHWFramesContext *out_frames_ctx = (AVHWFramesContext*)out->hw_frames_ctx->data; |
428 | NppStatus err; |
429 | |
430 | switch (out_frames_ctx->sw_format) { |
431 | case AV_PIX_FMT_NV12: |
432 | err = nppiYCbCr420_8u_P3P2R((const uint8_t**)in->data, |
433 | in->linesize, |
434 | out->data[0], out->linesize[0], |
435 | out->data[1], out->linesize[1], |
436 | (NppiSize){ in->width, in->height }); |
437 | break; |
438 | default: |
439 | return AVERROR_BUG; |
440 | } |
441 | if (err != NPP_SUCCESS) { |
442 | av_log(ctx, AV_LOG_ERROR, "NPP deinterleave error: %d\n", err); |
443 | return AVERROR_UNKNOWN; |
444 | } |
445 | |
446 | return 0; |
447 | } |
448 | |
449 | static int (*const nppscale_process[])(AVFilterContext *ctx, NPPScaleStageContext *stage, |
450 | AVFrame *out, AVFrame *in) = { |
451 | [STAGE_DEINTERLEAVE] = nppscale_deinterleave, |
452 | [STAGE_RESIZE] = nppscale_resize, |
453 | [STAGE_INTERLEAVE] = nppscale_interleave, |
454 | }; |
455 | |
456 | static int nppscale_scale(AVFilterContext *ctx, AVFrame *out, AVFrame *in) |
457 | { |
458 | NPPScaleContext *s = ctx->priv; |
459 | AVFrame *src = in; |
460 | int i, ret, last_stage = -1; |
461 | |
462 | for (i = 0; i < FF_ARRAY_ELEMS(s->stages); i++) { |
463 | if (!s->stages[i].stage_needed) |
464 | continue; |
465 | |
466 | ret = nppscale_process[i](ctx, &s->stages[i], s->stages[i].frame, src); |
467 | if (ret < 0) |
468 | return ret; |
469 | |
470 | src = s->stages[i].frame; |
471 | last_stage = i; |
472 | } |
473 | |
474 | if (last_stage < 0) |
475 | return AVERROR_BUG; |
476 | ret = av_hwframe_get_buffer(src->hw_frames_ctx, s->tmp_frame, 0); |
477 | if (ret < 0) |
478 | return ret; |
479 | |
480 | av_frame_move_ref(out, src); |
481 | av_frame_move_ref(src, s->tmp_frame); |
482 | |
483 | ret = av_frame_copy_props(out, in); |
484 | if (ret < 0) |
485 | return ret; |
486 | |
487 | return 0; |
488 | } |
489 | |
490 | static int nppscale_filter_frame(AVFilterLink *link, AVFrame *in) |
491 | { |
492 | AVFilterContext *ctx = link->dst; |
493 | NPPScaleContext *s = ctx->priv; |
494 | AVFilterLink *outlink = ctx->outputs[0]; |
495 | AVHWFramesContext *frames_ctx = (AVHWFramesContext*)outlink->hw_frames_ctx->data; |
496 | AVCUDADeviceContext *device_hwctx = frames_ctx->device_ctx->hwctx; |
497 | |
498 | AVFrame *out = NULL; |
499 | CUresult err; |
500 | CUcontext dummy; |
501 | int ret = 0; |
502 | |
503 | if (s->passthrough) |
504 | return ff_filter_frame(outlink, in); |
505 | |
506 | out = av_frame_alloc(); |
507 | if (!out) { |
508 | ret = AVERROR(ENOMEM); |
509 | goto fail; |
510 | } |
511 | |
512 | err = device_hwctx->internal->cuda_dl->cuCtxPushCurrent(device_hwctx->cuda_ctx); |
513 | if (err != CUDA_SUCCESS) { |
514 | ret = AVERROR_UNKNOWN; |
515 | goto fail; |
516 | } |
517 | |
518 | ret = nppscale_scale(ctx, out, in); |
519 | |
520 | device_hwctx->internal->cuda_dl->cuCtxPopCurrent(&dummy); |
521 | if (ret < 0) |
522 | goto fail; |
523 | |
524 | av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den, |
525 | (int64_t)in->sample_aspect_ratio.num * outlink->h * link->w, |
526 | (int64_t)in->sample_aspect_ratio.den * outlink->w * link->h, |
527 | INT_MAX); |
528 | |
529 | av_frame_free(&in); |
530 | return ff_filter_frame(outlink, out); |
531 | fail: |
532 | av_frame_free(&in); |
533 | av_frame_free(&out); |
534 | return ret; |
535 | } |
536 | |
537 | #define OFFSET(x) offsetof(NPPScaleContext, x) |
538 | #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM) |
539 | static const AVOption options[] = { |
540 | { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str = "iw" }, .flags = FLAGS }, |
541 | { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str = "ih" }, .flags = FLAGS }, |
542 | { "format", "Output pixel format", OFFSET(format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS }, |
543 | |
544 | { "interp_algo", "Interpolation algorithm used for resizing", OFFSET(interp_algo), AV_OPT_TYPE_INT, { .i64 = NPPI_INTER_CUBIC }, 0, INT_MAX, FLAGS, "interp_algo" }, |
545 | { "nn", "nearest neighbour", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_NN }, 0, 0, FLAGS, "interp_algo" }, |
546 | { "linear", "linear", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_LINEAR }, 0, 0, FLAGS, "interp_algo" }, |
547 | { "cubic", "cubic", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC }, 0, 0, FLAGS, "interp_algo" }, |
548 | { "cubic2p_bspline", "2-parameter cubic (B=1, C=0)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_BSPLINE }, 0, 0, FLAGS, "interp_algo" }, |
549 | { "cubic2p_catmullrom", "2-parameter cubic (B=0, C=1/2)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_CATMULLROM }, 0, 0, FLAGS, "interp_algo" }, |
550 | { "cubic2p_b05c03", "2-parameter cubic (B=1/2, C=3/10)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_B05C03 }, 0, 0, FLAGS, "interp_algo" }, |
551 | { "super", "supersampling", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_SUPER }, 0, 0, FLAGS, "interp_algo" }, |
552 | { "lanczos", "Lanczos", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_LANCZOS }, 0, 0, FLAGS, "interp_algo" }, |
553 | { NULL }, |
554 | }; |
555 | |
556 | static const AVClass nppscale_class = { |
557 | .class_name = "nppscale", |
558 | .item_name = av_default_item_name, |
559 | .option = options, |
560 | .version = LIBAVUTIL_VERSION_INT, |
561 | }; |
562 | |
563 | static const AVFilterPad nppscale_inputs[] = { |
564 | { |
565 | .name = "default", |
566 | .type = AVMEDIA_TYPE_VIDEO, |
567 | .filter_frame = nppscale_filter_frame, |
568 | }, |
569 | { NULL } |
570 | }; |
571 | |
572 | static const AVFilterPad nppscale_outputs[] = { |
573 | { |
574 | .name = "default", |
575 | .type = AVMEDIA_TYPE_VIDEO, |
576 | .config_props = nppscale_config_props, |
577 | }, |
578 | { NULL } |
579 | }; |
580 | |
581 | AVFilter ff_vf_scale_npp = { |
582 | .name = "scale_npp", |
583 | .description = NULL_IF_CONFIG_SMALL("NVIDIA Performance Primitives video " |
584 | "scaling and format conversion"), |
585 | |
586 | .init = nppscale_init, |
587 | .uninit = nppscale_uninit, |
588 | .query_formats = nppscale_query_formats, |
589 | |
590 | .priv_size = sizeof(NPPScaleContext), |
591 | .priv_class = &nppscale_class, |
592 | |
593 | .inputs = nppscale_inputs, |
594 | .outputs = nppscale_outputs, |
595 | |
596 | .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, |
597 | }; |
598 |