| @@ -0,0 +1,717 @@ |
| | 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 | #include "libavutil/file.h" |
| | 20 | #include "libavutil/opt.h" |
| | 21 | #include "internal.h" |
| | 22 | #include "vulkan.h" |
| | 23 | #include "scale_eval.h" |
| | 24 | |
| | 25 | #include <libplacebo/renderer.h> |
| | 26 | #include <libplacebo/utils/libav.h> |
| | 27 | #include <libplacebo/vulkan.h> |
| | 28 | |
| | 29 | typedef struct LibplaceboContext { |
| | 30 | /* lavfi vulkan*/ |
| | 31 | FFVulkanContext vkctx; |
| | 32 | int initialized; |
| | 33 | |
| | 34 | /* libplacebo */ |
| | 35 | pl_log log; |
| | 36 | pl_vulkan vulkan; |
| | 37 | pl_gpu gpu; |
| | 38 | pl_renderer renderer; |
| | 39 | |
| | 40 | /* settings */ |
| | 41 | char *out_format_string; |
| | 42 | char *w_expr; |
| | 43 | char *h_expr; |
| | 44 | AVRational target_sar; |
| | 45 | float pad_crop_ratio; |
| | 46 | int force_original_aspect_ratio; |
| | 47 | int force_divisible_by; |
| | 48 | int normalize_sar; |
| | 49 | int apply_filmgrain; |
| | 50 | int colorspace; |
| | 51 | int color_range; |
| | 52 | int color_primaries; |
| | 53 | int color_trc; |
| | 54 | |
| | 55 | /* pl_render_params */ |
| | 56 | char *upscaler; |
| | 57 | char *downscaler; |
| | 58 | int lut_entries; |
| | 59 | float antiringing; |
| | 60 | int sigmoid; |
| | 61 | int skip_aa; |
| | 62 | float polar_cutoff; |
| | 63 | int disable_linear; |
| | 64 | int disable_builtin; |
| | 65 | int force_3dlut; |
| | 66 | int force_dither; |
| | 67 | int disable_fbos; |
| | 68 | |
| | 69 | /* pl_deband_params */ |
| | 70 | int deband; |
| | 71 | int deband_iterations; |
| | 72 | float deband_threshold; |
| | 73 | float deband_radius; |
| | 74 | float deband_grain; |
| | 75 | |
| | 76 | /* pl_color_adjustment */ |
| | 77 | float brightness; |
| | 78 | float contrast; |
| | 79 | float saturation; |
| | 80 | float hue; |
| | 81 | float gamma; |
| | 82 | |
| | 83 | /* pl_peak_detect_params */ |
| | 84 | int peakdetect; |
| | 85 | float smoothing; |
| | 86 | float min_peak; |
| | 87 | float scene_low; |
| | 88 | float scene_high; |
| | 89 | float overshoot; |
| | 90 | |
| | 91 | /* pl_color_map_params */ |
| | 92 | int intent; |
| | 93 | int tonemapping; |
| | 94 | float tonemapping_param; |
| | 95 | float desat_str; |
| | 96 | float desat_exp; |
| | 97 | float desat_base; |
| | 98 | float max_boost; |
| | 99 | int gamut_warning; |
| | 100 | int gamut_clipping; |
| | 101 | |
| | 102 | /* pl_dither_params */ |
| | 103 | int dithering; |
| | 104 | int dither_lut_size; |
| | 105 | int dither_temporal; |
| | 106 | |
| | 107 | /* pl_cone_params */ |
| | 108 | int cones; |
| | 109 | float cone_str; |
| | 110 | |
| | 111 | /* custom shaders */ |
| | 112 | char *shader_path; |
| | 113 | void *shader_bin; |
| | 114 | int shader_bin_len; |
| | 115 | const struct pl_hook *hooks[2]; |
| | 116 | int num_hooks; |
| | 117 | } LibplaceboContext; |
| | 118 | |
| | 119 | static void pl_av_log(void *log_ctx, enum pl_log_level level, const char *msg) |
| | 120 | { |
| | 121 | int av_lev; |
| | 122 | |
| | 123 | switch (level) { |
| | 124 | case PL_LOG_FATAL: av_lev = AV_LOG_FATAL; break; |
| | 125 | case PL_LOG_ERR: av_lev = AV_LOG_ERROR; break; |
| | 126 | case PL_LOG_WARN: av_lev = AV_LOG_WARNING; break; |
| | 127 | case PL_LOG_INFO: av_lev = AV_LOG_VERBOSE; break; |
| | 128 | case PL_LOG_DEBUG: av_lev = AV_LOG_DEBUG; break; |
| | 129 | case PL_LOG_TRACE: av_lev = AV_LOG_TRACE; break; |
| | 130 | default: return; |
| | 131 | } |
| | 132 | |
| | 133 | av_log(log_ctx, av_lev, "%s\n", msg); |
| | 134 | } |
| | 135 | |
| | 136 | static int parse_shader(AVFilterContext *avctx, const void *shader, size_t len) |
| | 137 | { |
| | 138 | LibplaceboContext *s = avctx->priv; |
| | 139 | const struct pl_hook *hook; |
| | 140 | |
| | 141 | hook = pl_mpv_user_shader_parse(s->gpu, shader, len); |
| | 142 | if (!hook) { |
| | 143 | av_log(s, AV_LOG_ERROR, "Failed parsing custom shader!\n"); |
| | 144 | return AVERROR(EINVAL); |
| | 145 | } |
| | 146 | |
| | 147 | s->hooks[s->num_hooks++] = hook; |
| | 148 | return 0; |
| | 149 | } |
| | 150 | |
| | 151 | static int find_scaler(AVFilterContext *avctx, |
| | 152 | const struct pl_filter_config **opt, |
| | 153 | const char *name) |
| | 154 | { |
| | 155 | const struct pl_filter_preset *preset; |
| | 156 | if (!strcmp(name, "help")) { |
| | 157 | av_log(avctx, AV_LOG_INFO, "Available scaler presets:\n"); |
| | 158 | for (preset = pl_scale_filters; preset->name; preset++) |
| | 159 | av_log(avctx, AV_LOG_INFO, " %s\n", preset->name); |
| | 160 | return AVERROR_EXIT; |
| | 161 | } |
| | 162 | |
| | 163 | for (preset = pl_scale_filters; preset->name; preset++) { |
| | 164 | if (!strcmp(name, preset->name)) { |
| | 165 | *opt = preset->filter; |
| | 166 | return 0; |
| | 167 | } |
| | 168 | } |
| | 169 | |
| | 170 | av_log(avctx, AV_LOG_ERROR, "No such scaler preset '%s'.\n", name); |
| | 171 | return AVERROR(EINVAL); |
| | 172 | } |
| | 173 | |
| | 174 | static int libplacebo_init(AVFilterContext *avctx) |
| | 175 | { |
| | 176 | LibplaceboContext *s = avctx->priv; |
| | 177 | |
| | 178 | /* Create libplacebo log context */ |
| | 179 | s->log = pl_log_create(PL_API_VER, pl_log_params( |
| | 180 | .log_level = PL_LOG_DEBUG, |
| | 181 | .log_cb = pl_av_log, |
| | 182 | .log_priv = s, |
| | 183 | )); |
| | 184 | |
| | 185 | if (!s->log) |
| | 186 | return AVERROR(ENOMEM); |
| | 187 | |
| | 188 | /* Note: s->vulkan etc. are initialized later, when hwctx is available */ |
| | 189 | return 0; |
| | 190 | } |
| | 191 | |
| | 192 | static int init_vulkan(AVFilterContext *avctx) |
| | 193 | { |
| | 194 | int err = 0; |
| | 195 | LibplaceboContext *s = avctx->priv; |
| | 196 | const AVVulkanDeviceContext *hwctx = s->vkctx.hwctx; |
| | 197 | uint8_t *buf = NULL; |
| | 198 | size_t buf_len; |
| | 199 | |
| | 200 | /* Import libavfilter vulkan context into libplacebo */ |
| | 201 | s->vulkan = pl_vulkan_import(s->log, pl_vulkan_import_params( |
| | 202 | .instance = hwctx->inst, |
| | 203 | .get_proc_addr = hwctx->get_proc_addr, |
| | 204 | .phys_device = hwctx->phys_dev, |
| | 205 | .device = hwctx->act_dev, |
| | 206 | .extensions = hwctx->enabled_dev_extensions, |
| | 207 | .num_extensions = hwctx->nb_enabled_dev_extensions, |
| | 208 | .features = &hwctx->device_features, |
| | 209 | .queue_graphics = { |
| | 210 | .index = hwctx->queue_family_index, |
| | 211 | .count = hwctx->nb_graphics_queues, |
| | 212 | }, |
| | 213 | .queue_compute = { |
| | 214 | .index = hwctx->queue_family_comp_index, |
| | 215 | .count = hwctx->nb_comp_queues, |
| | 216 | }, |
| | 217 | .queue_transfer = { |
| | 218 | .index = hwctx->queue_family_tx_index, |
| | 219 | .count = hwctx->nb_tx_queues, |
| | 220 | }, |
| | 221 | /* This is the highest version created by hwcontext_vulkan.c */ |
| | 222 | .max_api_version = VK_API_VERSION_1_2, |
| | 223 | )); |
| | 224 | |
| | 225 | if (!s->vulkan) { |
| | 226 | av_log(s, AV_LOG_ERROR, "Failed importing vulkan device to libplacebo!\n"); |
| | 227 | err = AVERROR_EXTERNAL; |
| | 228 | goto fail; |
| | 229 | } |
| | 230 | |
| | 231 | /* Create the renderer */ |
| | 232 | s->gpu = s->vulkan->gpu; |
| | 233 | s->renderer = pl_renderer_create(s->log, s->gpu); |
| | 234 | |
| | 235 | /* Parse the user shaders, if requested */ |
| | 236 | if (s->shader_bin_len) |
| | 237 | RET(parse_shader(avctx, s->shader_bin, s->shader_bin_len)); |
| | 238 | |
| | 239 | if (s->shader_path && s->shader_path[0]) { |
| | 240 | RET(av_file_map(s->shader_path, &buf, &buf_len, 0, s)); |
| | 241 | RET(parse_shader(avctx, buf, buf_len)); |
| | 242 | } |
| | 243 | |
| | 244 | /* fall through */ |
| | 245 | fail: |
| | 246 | if (buf) |
| | 247 | av_file_unmap(buf, buf_len); |
| | 248 | s->initialized = 1; |
| | 249 | return err; |
| | 250 | } |
| | 251 | |
| | 252 | static void libplacebo_uninit(AVFilterContext *avctx) |
| | 253 | { |
| | 254 | LibplaceboContext *s = avctx->priv; |
| | 255 | |
| | 256 | for (int i = 0; i < s->num_hooks; i++) |
| | 257 | pl_mpv_user_shader_destroy(&s->hooks[i]); |
| | 258 | pl_renderer_destroy(&s->renderer); |
| | 259 | pl_vulkan_destroy(&s->vulkan); |
| | 260 | pl_log_destroy(&s->log); |
| | 261 | ff_vk_filter_uninit(avctx); |
| | 262 | s->initialized = 0; |
| | 263 | s->gpu = NULL; |
| | 264 | } |
| | 265 | |
| | 266 | static int wrap_vkframe(pl_gpu gpu, const AVFrame *frame, int plane, pl_tex *tex) |
| | 267 | { |
| | 268 | AVVkFrame *vkf = (AVVkFrame *) frame->data[0]; |
| | 269 | const AVHWFramesContext *hwfc = (AVHWFramesContext *) frame->hw_frames_ctx->data; |
| | 270 | const AVVulkanFramesContext *vkfc = hwfc->hwctx; |
| | 271 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hwfc->sw_format); |
| | 272 | const VkFormat *vk_fmt = av_vkfmt_from_pixfmt(hwfc->sw_format); |
| | 273 | const int chroma = plane == 1 || plane == 2; |
| | 274 | |
| | 275 | *tex = pl_vulkan_wrap(gpu, pl_vulkan_wrap_params( |
| | 276 | .image = vkf->img[plane], |
| | 277 | .format = vk_fmt[plane], |
| | 278 | .width = AV_CEIL_RSHIFT(frame->width, chroma ? desc->log2_chroma_w : 0), |
| | 279 | .height = AV_CEIL_RSHIFT(frame->height, chroma ? desc->log2_chroma_h : 0), |
| | 280 | .usage = vkfc->usage, |
| | 281 | )); |
| | 282 | |
| | 283 | if (!*tex) |
| | 284 | return AVERROR(ENOMEM); |
| | 285 | |
| | 286 | pl_vulkan_release(gpu, *tex, vkf->layout[plane], (pl_vulkan_sem) { |
| | 287 | .sem = vkf->sem[plane], |
| | 288 | .value = vkf->sem_value[plane] |
| | 289 | }); |
| | 290 | return 0; |
| | 291 | } |
| | 292 | |
| | 293 | static int unwrap_vkframe(pl_gpu gpu, AVFrame *frame, int plane, pl_tex *tex) |
| | 294 | { |
| | 295 | AVVkFrame *vkf = (AVVkFrame *) frame->data[0]; |
| | 296 | int ok = pl_vulkan_hold_raw(gpu, *tex, &vkf->layout[plane], |
| | 297 | (pl_vulkan_sem) { vkf->sem[plane], vkf->sem_value[plane] + 1 }); |
| | 298 | vkf->access[plane] = 0; |
| | 299 | vkf->sem_value[plane] += !!ok; |
| | 300 | return ok ? 0 : AVERROR_EXTERNAL; |
| | 301 | } |
| | 302 | |
| | 303 | static void set_sample_depth(struct pl_frame *out_frame, const AVFrame *frame) |
| | 304 | { |
| | 305 | const AVHWFramesContext *hwfc = (AVHWFramesContext *) frame->hw_frames_ctx->data; |
| | 306 | pl_fmt fmt = out_frame->planes[0].texture->params.format; |
| | 307 | struct pl_bit_encoding *bits = &out_frame->repr.bits; |
| | 308 | bits->sample_depth = fmt->component_depth[0]; |
| | 309 | |
| | 310 | switch (hwfc->sw_format) { |
| | 311 | case AV_PIX_FMT_P010: bits->bit_shift = 6; break; |
| | 312 | default: break; |
| | 313 | } |
| | 314 | } |
| | 315 | |
| | 316 | static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in) |
| | 317 | { |
| | 318 | int err = 0; |
| | 319 | LibplaceboContext *s = avctx->priv; |
| | 320 | struct pl_render_params params; |
| | 321 | struct pl_frame image, target; |
| | 322 | pl_frame_from_avframe(&image, in); |
| | 323 | pl_frame_from_avframe(&target, out); |
| | 324 | |
| | 325 | if (!s->apply_filmgrain) |
| | 326 | image.film_grain.type = PL_FILM_GRAIN_NONE; |
| | 327 | |
| | 328 | if (s->target_sar.num) { |
| | 329 | float aspect = pl_rect2df_aspect(&target.crop) * av_q2d(s->target_sar); |
| | 330 | pl_rect2df_aspect_set(&target.crop, aspect, s->pad_crop_ratio); |
| | 331 | } |
| | 332 | |
| | 333 | /* Update render params */ |
| | 334 | params = (struct pl_render_params) { |
| | 335 | PL_RENDER_DEFAULTS |
| | 336 | .lut_entries = s->lut_entries, |
| | 337 | .antiringing_strength = s->antiringing, |
| | 338 | |
| | 339 | .deband_params = !s->deband ? NULL : pl_deband_params( |
| | 340 | .iterations = s->deband_iterations, |
| | 341 | .threshold = s->deband_threshold, |
| | 342 | .radius = s->deband_radius, |
| | 343 | .grain = s->deband_grain, |
| | 344 | ), |
| | 345 | |
| | 346 | .sigmoid_params = s->sigmoid ? &pl_sigmoid_default_params : NULL, |
| | 347 | |
| | 348 | .color_adjustment = &(struct pl_color_adjustment) { |
| | 349 | .brightness = s->brightness, |
| | 350 | .contrast = s->contrast, |
| | 351 | .saturation = s->saturation, |
| | 352 | .hue = s->hue, |
| | 353 | .gamma = s->gamma, |
| | 354 | }, |
| | 355 | |
| | 356 | .peak_detect_params = !s->peakdetect ? NULL : pl_peak_detect_params( |
| | 357 | .smoothing_period = s->smoothing, |
| | 358 | .minimum_peak = s->min_peak, |
| | 359 | .scene_threshold_low = s->scene_low, |
| | 360 | .scene_threshold_high = s->scene_high, |
| | 361 | .overshoot_margin = s->overshoot, |
| | 362 | ), |
| | 363 | |
| | 364 | .color_map_params = pl_color_map_params( |
| | 365 | .intent = s->intent, |
| | 366 | .tone_mapping_algo = s->tonemapping, |
| | 367 | .tone_mapping_param = s->tonemapping_param, |
| | 368 | .desaturation_strength = s->desat_str, |
| | 369 | .desaturation_exponent = s->desat_exp, |
| | 370 | .desaturation_base = s->desat_base, |
| | 371 | .max_boost = s->max_boost, |
| | 372 | .gamut_warning = s->gamut_warning, |
| | 373 | .gamut_clipping = s->gamut_clipping, |
| | 374 | ), |
| | 375 | |
| | 376 | .dither_params = s->dithering < 0 ? NULL : pl_dither_params( |
| | 377 | .method = s->dithering, |
| | 378 | .lut_size = s->dither_lut_size, |
| | 379 | .temporal = s->dither_temporal, |
| | 380 | ), |
| | 381 | |
| | 382 | .cone_params = !s->cones ? NULL : pl_cone_params( |
| | 383 | .cones = s->cones, |
| | 384 | .strength = s->cone_str, |
| | 385 | ), |
| | 386 | |
| | 387 | .hooks = s->hooks, |
| | 388 | .num_hooks = s->num_hooks, |
| | 389 | |
| | 390 | .skip_anti_aliasing = s->skip_aa, |
| | 391 | .polar_cutoff = s->polar_cutoff, |
| | 392 | .disable_linear_scaling = s->disable_linear, |
| | 393 | .disable_builtin_scalers = s->disable_builtin, |
| | 394 | .force_3dlut = s->force_3dlut, |
| | 395 | .force_dither = s->force_dither, |
| | 396 | .disable_fbos = s->disable_fbos, |
| | 397 | }; |
| | 398 | |
| | 399 | RET(find_scaler(avctx, ¶ms.upscaler, s->upscaler)); |
| | 400 | RET(find_scaler(avctx, ¶ms.downscaler, s->downscaler)); |
| | 401 | |
| | 402 | /* Ideally, we would persistently wrap all of these AVVkFrames into pl_tex |
| | 403 | * objects, but for now we'll just create and destroy a wrapper per frame. |
| | 404 | * Note that doing it this way is suboptimal, since it results in the |
| | 405 | * creation and destruction of a VkSampler and VkFramebuffer per frame. |
| | 406 | * |
| | 407 | * FIXME: Can we do better? */ |
| | 408 | for (int i = 0; i < image.num_planes; i++) |
| | 409 | RET(wrap_vkframe(s->gpu, in, i, &image.planes[i].texture)); |
| | 410 | for (int i = 0; i < target.num_planes; i++) |
| | 411 | RET(wrap_vkframe(s->gpu, out, i, &target.planes[i].texture)); |
| | 412 | |
| | 413 | /* Since we-re mapping vkframes manually, the pl_frame helpers don't know |
| | 414 | * about the mismatch between the sample format and the color depth. */ |
| | 415 | set_sample_depth(&image, in); |
| | 416 | set_sample_depth(&target, out); |
| | 417 | |
| | 418 | pl_render_image(s->renderer, &image, &target, ¶ms); |
| | 419 | |
| | 420 | for (int i = 0; i < image.num_planes; i++) |
| | 421 | RET(unwrap_vkframe(s->gpu, in, i, &image.planes[i].texture)); |
| | 422 | for (int i = 0; i < target.num_planes; i++) |
| | 423 | RET(unwrap_vkframe(s->gpu, out, i, &target.planes[i].texture)); |
| | 424 | |
| | 425 | /* Flush the command queues for performance */ |
| | 426 | pl_gpu_flush(s->gpu); |
| | 427 | |
| | 428 | /* fall through */ |
| | 429 | fail: |
| | 430 | for (int i = 0; i < image.num_planes; i++) |
| | 431 | pl_tex_destroy(s->gpu, &image.planes[i].texture); |
| | 432 | for (int i = 0; i < target.num_planes; i++) |
| | 433 | pl_tex_destroy(s->gpu, &target.planes[i].texture); |
| | 434 | return err; |
| | 435 | } |
| | 436 | |
| | 437 | static int filter_frame(AVFilterLink *link, AVFrame *in) |
| | 438 | { |
| | 439 | int err; |
| | 440 | AVFilterContext *ctx = link->dst; |
| | 441 | LibplaceboContext *s = ctx->priv; |
| | 442 | AVFilterLink *outlink = ctx->outputs[0]; |
| | 443 | |
| | 444 | AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
| | 445 | if (!out) { |
| | 446 | err = AVERROR(ENOMEM); |
| | 447 | goto fail; |
| | 448 | } |
| | 449 | |
| | 450 | if (!s->initialized) |
| | 451 | RET(init_vulkan(ctx)); |
| | 452 | |
| | 453 | RET(av_frame_copy_props(out, in)); |
| | 454 | out->width = outlink->w; |
| | 455 | out->height = outlink->h; |
| | 456 | |
| | 457 | if (s->colorspace >= 0) |
| | 458 | out->colorspace = s->colorspace; |
| | 459 | if (s->color_range >= 0) |
| | 460 | out->color_range = s->color_range; |
| | 461 | if (s->color_trc >= 0) |
| | 462 | out->color_trc = s->color_trc; |
| | 463 | if (s->color_primaries >= 0) |
| | 464 | out->color_primaries = s->color_primaries; |
| | 465 | |
| | 466 | RET(process_frames(ctx, out, in)); |
| | 467 | |
| | 468 | if (s->apply_filmgrain) |
| | 469 | av_frame_remove_side_data(out, AV_FRAME_DATA_FILM_GRAIN_PARAMS); |
| | 470 | |
| | 471 | av_frame_free(&in); |
| | 472 | |
| | 473 | return ff_filter_frame(outlink, out); |
| | 474 | |
| | 475 | fail: |
| | 476 | av_frame_free(&in); |
| | 477 | av_frame_free(&out); |
| | 478 | return err; |
| | 479 | } |
| | 480 | |
| | 481 | static int libplacebo_config_output(AVFilterLink *outlink) |
| | 482 | { |
| | 483 | int err; |
| | 484 | AVFilterContext *avctx = outlink->src; |
| | 485 | LibplaceboContext *s = avctx->priv; |
| | 486 | AVFilterLink *inlink = outlink->src->inputs[0]; |
| | 487 | AVHWFramesContext *hwfc; |
| | 488 | AVVulkanFramesContext *vkfc; |
| | 489 | AVRational scale_sar; |
| | 490 | int *out_w = &s->vkctx.output_width; |
| | 491 | int *out_h = &s->vkctx.output_height; |
| | 492 | |
| | 493 | RET(ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, |
| | 494 | out_w, out_h)); |
| | 495 | |
| | 496 | ff_scale_adjust_dimensions(inlink, out_w, out_h, |
| | 497 | s->force_original_aspect_ratio, |
| | 498 | s->force_divisible_by); |
| | 499 | |
| | 500 | scale_sar = (AVRational){outlink->h * inlink->w, *out_w * *out_h}; |
| | 501 | if (inlink->sample_aspect_ratio.num) |
| | 502 | scale_sar = av_mul_q(scale_sar, inlink->sample_aspect_ratio); |
| | 503 | |
| | 504 | if (s->normalize_sar) { |
| | 505 | /* Apply all SAR during scaling, so we don't need to set the out SAR */ |
| | 506 | s->target_sar = scale_sar; |
| | 507 | } else { |
| | 508 | /* This is consistent with other scale_* filters, which only |
| | 509 | * set the outlink SAR to be equal to the scale SAR iff the input SAR |
| | 510 | * was set to something nonzero */ |
| | 511 | if (inlink->sample_aspect_ratio.num) |
| | 512 | outlink->sample_aspect_ratio = scale_sar; |
| | 513 | } |
| | 514 | |
| | 515 | if (s->out_format_string) { |
| | 516 | s->vkctx.output_format = av_get_pix_fmt(s->out_format_string); |
| | 517 | if (s->vkctx.output_format == AV_PIX_FMT_NONE) { |
| | 518 | av_log(avctx, AV_LOG_ERROR, "Invalid output format.\n"); |
| | 519 | return AVERROR(EINVAL); |
| | 520 | } |
| | 521 | } else { |
| | 522 | /* Default to re-using the input format */ |
| | 523 | s->vkctx.output_format = s->vkctx.input_format; |
| | 524 | } |
| | 525 | |
| | 526 | RET(ff_vk_filter_config_output(outlink)); |
| | 527 | hwfc = (AVHWFramesContext *) outlink->hw_frames_ctx->data; |
| | 528 | vkfc = hwfc->hwctx; |
| | 529 | vkfc->usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| | 530 | |
| | 531 | return 0; |
| | 532 | |
| | 533 | fail: |
| | 534 | return err; |
| | 535 | } |
| | 536 | |
| | 537 | #define OFFSET(x) offsetof(LibplaceboContext, x) |
| | 538 | #define STATIC (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) |
| | 539 | #define DYNAMIC (STATIC | AV_OPT_FLAG_RUNTIME_PARAM) |
| | 540 | |
| | 541 | static const AVOption libplacebo_options[] = { |
| | 542 | { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = STATIC }, |
| | 543 | { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = STATIC }, |
| | 544 | { "format", "Output video format", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = STATIC }, |
| | 545 | { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, STATIC, "force_oar" }, |
| | 546 | { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, STATIC, "force_oar" }, |
| | 547 | { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, STATIC, "force_oar" }, |
| | 548 | { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, STATIC, "force_oar" }, |
| | 549 | { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 256, STATIC }, |
| | 550 | { "normalize_sar", "force SAR normalization to 1:1", OFFSET(normalize_sar), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, STATIC }, |
| | 551 | { "pad_crop_ratio", "ratio between padding and cropping when normalizing SAR (0=pad, 1=crop)", OFFSET(pad_crop_ratio), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, 1.0, DYNAMIC }, |
| | 552 | |
| | 553 | {"colorspace", "select colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_NB-1, DYNAMIC, "colorspace"}, |
| | 554 | {"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 555 | {"gbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_RGB}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 556 | {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 557 | {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 558 | {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT470BG}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 559 | {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 560 | {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE240M}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 561 | {"ycgco", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_YCGCO}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 562 | {"bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 563 | {"bt2020c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_CL}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 564 | {"ictcp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_ICTCP}, INT_MIN, INT_MAX, STATIC, "colorspace"}, |
| | 565 | |
| | 566 | {"range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_RANGE_NB-1, DYNAMIC, "range"}, |
| | 567 | {"auto", "keep the same color range", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, STATIC, "range"}, |
| | 568 | {"unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, STATIC, "range"}, |
| | 569 | {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, STATIC, "range"}, |
| | 570 | {"limited", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, "range"}, |
| | 571 | {"tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, "range"}, |
| | 572 | {"mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, "range"}, |
| | 573 | {"full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, "range"}, |
| | 574 | {"pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, "range"}, |
| | 575 | {"jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, "range"}, |
| | 576 | |
| | 577 | {"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_NB-1, DYNAMIC, "color_primaries"}, |
| | 578 | {"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 579 | {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 580 | {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 581 | {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470M}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 582 | {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 583 | {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 584 | {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE240M}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 585 | {"film", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_FILM}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 586 | {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 587 | {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE428}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 588 | {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 589 | {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 590 | {"jedec-p22", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_JEDEC_P22}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 591 | {"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, |
| | 592 | |
| | 593 | {"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB-1, DYNAMIC, "color_trc"}, |
| | 594 | {"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 595 | {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 596 | {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 597 | {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA22}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 598 | {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA28}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 599 | {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE170M}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 600 | {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE240M}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 601 | {"linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LINEAR}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 602 | {"iec61966-2-4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_4}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 603 | {"bt1361e", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT1361_ECG}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 604 | {"iec61966-2-1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_1}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 605 | {"bt2020-10", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_10}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 606 | {"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 607 | {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 608 | {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, STATIC, "color_trc"}, |
| | 609 | |
| | 610 | { "upscaler", "Upscaler function", OFFSET(upscaler), AV_OPT_TYPE_STRING, {.str = "spline36"}, .flags = DYNAMIC }, |
| | 611 | { "downscaler", "Downscaler function", OFFSET(downscaler), AV_OPT_TYPE_STRING, {.str = "mitchell"}, .flags = DYNAMIC }, |
| | 612 | { "lut_entries", "Number of scaler LUT entries", OFFSET(lut_entries), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 256, DYNAMIC }, |
| | 613 | { "antiringing", "Antiringing strength (for non-EWA filters)", OFFSET(antiringing), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 1.0, DYNAMIC }, |
| | 614 | { "sigmoid", "Enable sigmoid upscaling", OFFSET(sigmoid), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DYNAMIC }, |
| | 615 | { "apply_filmgrain", "Apply film grain metadata", OFFSET(apply_filmgrain), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DYNAMIC }, |
| | 616 | |
| | 617 | { "deband", "Enable debanding", OFFSET(deband), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, |
| | 618 | { "deband_iterations", "Deband iterations", OFFSET(deband_iterations), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 16, DYNAMIC }, |
| | 619 | { "deband_threshold", "Deband threshold", OFFSET(deband_threshold), AV_OPT_TYPE_FLOAT, {.dbl = 4.0}, 0.0, 1024.0, DYNAMIC }, |
| | 620 | { "deband_radius", "Deband radius", OFFSET(deband_radius), AV_OPT_TYPE_FLOAT, {.dbl = 16.0}, 0.0, 1024.0, DYNAMIC }, |
| | 621 | { "deband_grain", "Deband grain", OFFSET(deband_grain), AV_OPT_TYPE_FLOAT, {.dbl = 6.0}, 0.0, 1024.0, DYNAMIC }, |
| | 622 | |
| | 623 | { "brightness", "Brightness boost", OFFSET(brightness), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, -1.0, 1.0, DYNAMIC }, |
| | 624 | { "contrast", "Contrast gain", OFFSET(contrast), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 16.0, DYNAMIC }, |
| | 625 | { "saturation", "Saturation gain", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 16.0, DYNAMIC }, |
| | 626 | { "hue", "Hue shift", OFFSET(hue), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, -M_PI, M_PI, DYNAMIC }, |
| | 627 | { "gamma", "Gamma adjustment", OFFSET(gamma), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 16.0, DYNAMIC }, |
| | 628 | |
| | 629 | { "peak_detect", "Enable dynamic peak detection for HDR tone-mapping", OFFSET(peakdetect), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DYNAMIC }, |
| | 630 | { "smoothing_period", "Peak detection smoothing period", OFFSET(smoothing), AV_OPT_TYPE_FLOAT, {.dbl = 100.0}, 0.0, 1000.0, DYNAMIC }, |
| | 631 | { "minimum_peak", "Peak detection minimum peak", OFFSET(min_peak), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 100.0, DYNAMIC }, |
| | 632 | { "scene_threshold_low", "Scene change low threshold", OFFSET(scene_low), AV_OPT_TYPE_FLOAT, {.dbl = 5.5}, -1.0, 100.0, DYNAMIC }, |
| | 633 | { "scene_threshold_high", "Scene change high threshold", OFFSET(scene_high), AV_OPT_TYPE_FLOAT, {.dbl = 10.0}, -1.0, 100.0, DYNAMIC }, |
| | 634 | { "overshoot", "Tone-mapping overshoot margin", OFFSET(overshoot), AV_OPT_TYPE_FLOAT, {.dbl = 0.05}, 0.0, 1.0, DYNAMIC }, |
| | 635 | |
| | 636 | { "intent", "Rendering intent", OFFSET(intent), AV_OPT_TYPE_INT, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 3, DYNAMIC, "intent" }, |
| | 637 | { "perceptual", "Perceptual", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_PERCEPTUAL}, 0, 0, STATIC, "intent" }, |
| | 638 | { "relative", "Relative colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 0, STATIC, "intent" }, |
| | 639 | { "absolute", "Absolute colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_ABSOLUTE_COLORIMETRIC}, 0, 0, STATIC, "intent" }, |
| | 640 | { "saturation", "Saturation mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_SATURATION}, 0, 0, STATIC, "intent" }, |
| | 641 | { "tonemapping", "Tone-mapping algorithm", OFFSET(tonemapping), AV_OPT_TYPE_INT, {.i64 = PL_TONE_MAPPING_BT_2390}, 0, PL_TONE_MAPPING_ALGORITHM_COUNT - 1, DYNAMIC, "tonemap" }, |
| | 642 | { "clip", "Hard-clipping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_CLIP}, 0, 0, STATIC, "tonemap" }, |
| | 643 | { "mobius", "Mobius tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_MOBIUS}, 0, 0, STATIC, "tonemap" }, |
| | 644 | { "reinhard", "Reinhard tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_REINHARD}, 0, 0, STATIC, "tonemap" }, |
| | 645 | { "hable", "Hable/Filmic tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_HABLE}, 0, 0, STATIC, "tonemap" }, |
| | 646 | { "gamma", "Gamma tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_GAMMA}, 0, 0, STATIC, "tonemap" }, |
| | 647 | { "linear", "Linear tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_LINEAR}, 0, 0, STATIC, "tonemap" }, |
| | 648 | { "bt.2390", "ITU-R BT.2390 tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_BT_2390}, 0, 0, STATIC, "tonemap" }, |
| | 649 | { "tonemapping_param", "Tunable parameter for some tone-mapping functions", OFFSET(tonemapping_param), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 100.0, .flags = DYNAMIC }, |
| | 650 | { "desaturation_strength", "Desaturation strength", OFFSET(desat_str), AV_OPT_TYPE_FLOAT, {.dbl = 0.90}, 0.0, 1.0, DYNAMIC }, |
| | 651 | { "desaturation_exponent", "Desaturation exponent", OFFSET(desat_exp), AV_OPT_TYPE_FLOAT, {.dbl = 0.2}, 0.0, 10.0, DYNAMIC }, |
| | 652 | { "desaturation_base", "Desaturation base", OFFSET(desat_base), AV_OPT_TYPE_FLOAT, {.dbl = 0.18}, 0.0, 10.0, DYNAMIC }, |
| | 653 | { "max_boost", "Tone-mapping maximum boost", OFFSET(max_boost), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 1.0, 10.0, DYNAMIC }, |
| | 654 | { "gamut_warning", "Highlight out-of-gamut colors", OFFSET(gamut_warning), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, |
| | 655 | { "gamut_clipping", "Enable colorimetric gamut clipping", OFFSET(gamut_clipping), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DYNAMIC }, |
| | 656 | |
| | 657 | { "dithering", "Dither method to use", OFFSET(dithering), AV_OPT_TYPE_INT, {.i64 = PL_DITHER_BLUE_NOISE}, -1, PL_DITHER_METHOD_COUNT - 1, DYNAMIC, "dither" }, |
| | 658 | { "none", "Disable dithering", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, STATIC, "dither" }, |
| | 659 | { "blue", "Blue noise", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_BLUE_NOISE}, 0, 0, STATIC, "dither" }, |
| | 660 | { "ordered", "Ordered LUT", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_ORDERED_LUT}, 0, 0, STATIC, "dither" }, |
| | 661 | { "ordered_fixed", "Fixed function ordered", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_ORDERED_FIXED}, 0, 0, STATIC, "dither" }, |
| | 662 | { "white", "White noise", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_WHITE_NOISE}, 0, 0, STATIC, "dither" }, |
| | 663 | { "dither_lut_size", "Dithering LUT size", OFFSET(dither_lut_size), AV_OPT_TYPE_INT, {.i64 = 6}, 1, 8, STATIC }, |
| | 664 | { "dither_temporal", "Enable temporal dithering", OFFSET(dither_temporal), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, |
| | 665 | |
| | 666 | { "cones", "Colorblindness adaptation model", OFFSET(cones), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, PL_CONE_LMS, DYNAMIC, "cone" }, |
| | 667 | { "l", "L cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_L}, 0, 0, STATIC, "cone" }, |
| | 668 | { "m", "M cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_M}, 0, 0, STATIC, "cone" }, |
| | 669 | { "s", "S cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_S}, 0, 0, STATIC, "cone" }, |
| | 670 | { "cone-strength", "Colorblindness adaptation strength", OFFSET(cone_str), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 10.0, DYNAMIC }, |
| | 671 | |
| | 672 | { "custom_shader_path", "Path to custom user shader (mpv .hook format)", OFFSET(shader_path), AV_OPT_TYPE_STRING, .flags = STATIC }, |
| | 673 | { "custom_shader_bin", "Custom user shader as binary (mpv .hook format)", OFFSET(shader_bin), AV_OPT_TYPE_BINARY, .flags = STATIC }, |
| | 674 | |
| | 675 | /* Performance/quality tradeoff options */ |
| | 676 | { "skip_aa", "Skip anti-aliasing", OFFSET(skip_aa), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 0, DYNAMIC }, |
| | 677 | { "polar_cutoff", "Polar LUT cutoff", OFFSET(polar_cutoff), AV_OPT_TYPE_FLOAT, {.i64 = 0}, 0.0, 1.0, DYNAMIC }, |
| | 678 | { "disable_linear", "Disable linear scaling", OFFSET(disable_linear), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, |
| | 679 | { "disable_builtin", "Disable built-in scalers", OFFSET(disable_builtin), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, |
| | 680 | { "force_3dlut", "Force the use of a full 3DLUT", OFFSET(force_3dlut), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, |
| | 681 | { "force_dither", "Force dithering", OFFSET(force_dither), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, |
| | 682 | { "disable_fbos", "Force-disable FBOs", OFFSET(disable_fbos), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, |
| | 683 | { NULL }, |
| | 684 | }; |
| | 685 | |
| | 686 | AVFILTER_DEFINE_CLASS(libplacebo); |
| | 687 | |
| | 688 | static const AVFilterPad libplacebo_inputs[] = { |
| | 689 | { |
| | 690 | .name = "default", |
| | 691 | .type = AVMEDIA_TYPE_VIDEO, |
| | 692 | .filter_frame = &filter_frame, |
| | 693 | .config_props = &ff_vk_filter_config_input, |
| | 694 | }, |
| | 695 | }; |
| | 696 | |
| | 697 | static const AVFilterPad libplacebo_outputs[] = { |
| | 698 | { |
| | 699 | .name = "default", |
| | 700 | .type = AVMEDIA_TYPE_VIDEO, |
| | 701 | .config_props = &libplacebo_config_output, |
| | 702 | }, |
| | 703 | }; |
| | 704 | |
| | 705 | AVFilter ff_vf_libplacebo = { |
| | 706 | .name = "libplacebo", |
| | 707 | .description = NULL_IF_CONFIG_SMALL("Apply various GPU filters from libplacebo"), |
| | 708 | .priv_size = sizeof(LibplaceboContext), |
| | 709 | .init = &libplacebo_init, |
| | 710 | .uninit = &libplacebo_uninit, |
| | 711 | .process_command = &ff_filter_process_command, |
| | 712 | FILTER_INPUTS(libplacebo_inputs), |
| | 713 | FILTER_OUTPUTS(libplacebo_outputs), |
| | 714 | FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), |
| | 715 | .priv_class = &libplacebo_class, |
| | 716 | .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, |
| | 717 | }; |