| @@ -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 | +}; |