Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand
@ 2023-05-10 13:55 Niklas Haas
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 2/6] lavfi/vf_libplacebo: split and refactor logic Niklas Haas
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Niklas Haas @ 2023-05-10 13:55 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

From: Niklas Haas <git@haasn.dev>

Only update this struct when it's expected to change, and cache it
otherwise. Partially motivated by a desire to make `process_frames`
smaller.
---
 libavfilter/vf_libplacebo.c | 228 ++++++++++++++++++++----------------
 1 file changed, 126 insertions(+), 102 deletions(-)

diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index 6fe3e0ea88..2ee9055c9d 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -143,6 +143,7 @@ typedef struct LibplaceboContext {
     int color_trc;
 
     /* pl_render_params */
+    struct pl_render_params params;
     char *upscaler;
     char *downscaler;
     int lut_entries;
@@ -156,6 +157,7 @@ typedef struct LibplaceboContext {
     int disable_fbos;
 
     /* pl_deband_params */
+    struct pl_deband_params deband_params;
     int deband;
     int deband_iterations;
     float deband_threshold;
@@ -163,6 +165,7 @@ typedef struct LibplaceboContext {
     float deband_grain;
 
     /* pl_color_adjustment */
+    struct pl_color_adjustment color_adjustment;
     float brightness;
     float contrast;
     float saturation;
@@ -170,6 +173,7 @@ typedef struct LibplaceboContext {
     float gamma;
 
     /* pl_peak_detect_params */
+    struct pl_peak_detect_params peak_detect_params;
     int peakdetect;
     float smoothing;
     float min_peak;
@@ -178,6 +182,7 @@ typedef struct LibplaceboContext {
     float overshoot;
 
     /* pl_color_map_params */
+    struct pl_color_map_params color_map_params;
     int intent;
     int gamut_mode;
     int tonemapping;
@@ -196,12 +201,14 @@ typedef struct LibplaceboContext {
     int force_icc_lut;
 #endif
 
-     /* pl_dither_params */
+    /* pl_dither_params */
+    struct pl_dither_params dither_params;
     int dithering;
     int dither_lut_size;
     int dither_temporal;
 
     /* pl_cone_params */
+    struct pl_cone_params cone_params;
     int cones;
     float cone_str;
 
@@ -280,18 +287,112 @@ static int find_scaler(AVFilterContext *avctx,
     return AVERROR(EINVAL);
 }
 
-static int parse_fillcolor(AVFilterContext *avctx,
-                           struct pl_render_params *params,
-                           const char *color_str)
+static int update_settings(AVFilterContext *ctx)
 {
     int err = 0;
+    LibplaceboContext *s = ctx->priv;
+    enum pl_tone_map_mode tonemapping_mode = s->tonemapping_mode;
+    enum pl_gamut_mode gamut_mode = s->gamut_mode;
     uint8_t color_rgba[4];
 
-    RET(av_parse_color(color_rgba, color_str, -1, avctx));
-    params->background_color[0] = (float) color_rgba[0] / UINT8_MAX;
-    params->background_color[1] = (float) color_rgba[1] / UINT8_MAX;
-    params->background_color[2] = (float) color_rgba[2] / UINT8_MAX;
-    params->background_transparency = 1.0f - (float) color_rgba[3] / UINT8_MAX;
+    RET(av_parse_color(color_rgba, s->fillcolor, -1, s));
+
+#if FF_API_LIBPLACEBO_OPTS
+    /* backwards compatibility with older API */
+    if (!tonemapping_mode && (s->desat_str >= 0.0f || s->desat_exp >= 0.0f)) {
+        float str = s->desat_str < 0.0f ? 0.9f : s->desat_str;
+        float exp = s->desat_exp < 0.0f ? 0.2f : s->desat_exp;
+        if (str >= 0.9f && exp <= 0.1f) {
+            tonemapping_mode = PL_TONE_MAP_RGB;
+        } else if (str > 0.1f) {
+            tonemapping_mode = PL_TONE_MAP_HYBRID;
+        } else {
+            tonemapping_mode = PL_TONE_MAP_LUMA;
+        }
+    }
+
+    if (s->gamut_warning)
+        gamut_mode = PL_GAMUT_WARN;
+    if (s->gamut_clipping)
+        gamut_mode = PL_GAMUT_DESATURATE;
+#endif
+
+    s->deband_params = *pl_deband_params(
+        .iterations = s->deband_iterations,
+        .threshold = s->deband_threshold,
+        .radius = s->deband_radius,
+        .grain = s->deband_grain,
+    );
+
+    s->color_adjustment = (struct pl_color_adjustment) {
+        .brightness = s->brightness,
+        .contrast = s->contrast,
+        .saturation = s->saturation,
+        .hue = s->hue,
+        .gamma = s->gamma,
+    };
+
+    s->peak_detect_params = *pl_peak_detect_params(
+        .smoothing_period = s->smoothing,
+        .minimum_peak = s->min_peak,
+        .scene_threshold_low = s->scene_low,
+        .scene_threshold_high = s->scene_high,
+        .overshoot_margin = s->overshoot,
+    );
+
+    s->color_map_params = *pl_color_map_params(
+        .intent = s->intent,
+        .gamut_mode = gamut_mode,
+        .tone_mapping_function = tonemapping_funcs[s->tonemapping],
+        .tone_mapping_param = s->tonemapping_param,
+        .tone_mapping_mode = tonemapping_mode,
+        .inverse_tone_mapping = s->inverse_tonemapping,
+        .tone_mapping_crosstalk = s->crosstalk,
+        .lut_size = s->tonemapping_lut_size,
+    );
+
+    s->dither_params = *pl_dither_params(
+        .method = s->dithering,
+        .lut_size = s->dither_lut_size,
+        .temporal = s->dither_temporal,
+    );
+
+    s->cone_params = *pl_cone_params(
+        .cones = s->cones,
+        .strength = s->cone_str,
+    );
+
+    s->params = *pl_render_params(
+        .lut_entries = s->lut_entries,
+        .antiringing_strength = s->antiringing,
+        .background_transparency = 1.0f - (float) color_rgba[3] / UINT8_MAX,
+        .background_color = {
+            (float) color_rgba[0] / UINT8_MAX,
+            (float) color_rgba[1] / UINT8_MAX,
+            (float) color_rgba[2] / UINT8_MAX,
+        },
+
+        .deband_params = s->deband ? &s->deband_params : NULL,
+        .sigmoid_params = s->sigmoid ? &pl_sigmoid_default_params : NULL,
+        .color_adjustment = &s->color_adjustment,
+        .peak_detect_params = s->peakdetect ? &s->peak_detect_params : NULL,
+        .color_map_params = &s->color_map_params,
+        .dither_params = s->dithering >= 0 ? &s->dither_params : NULL,
+        .cone_params = s->cones ? &s->cone_params : NULL,
+
+        .hooks = s->hooks,
+        .num_hooks = s->num_hooks,
+
+        .skip_anti_aliasing = s->skip_aa,
+        .polar_cutoff = s->polar_cutoff,
+        .disable_linear_scaling = s->disable_linear,
+        .disable_builtin_scalers = s->disable_builtin,
+        .force_dither = s->force_dither,
+        .disable_fbos = s->disable_fbos,
+    );
+
+    RET(find_scaler(ctx, &s->params.upscaler, s->upscaler));
+    RET(find_scaler(ctx, &s->params.downscaler, s->downscaler));
     return 0;
 
 fail:
@@ -327,6 +428,7 @@ static int libplacebo_init(AVFilterContext *avctx)
         s->out_format = AV_PIX_FMT_NONE;
     }
 
+    RET(update_settings(avctx));
     RET(av_expr_parse(&s->crop_x_pexpr, s->crop_x_expr, var_names,
                       NULL, NULL, NULL, NULL, 0, s));
     RET(av_expr_parse(&s->crop_y_pexpr, s->crop_y_expr, var_names,
@@ -449,14 +551,24 @@ static void libplacebo_uninit(AVFilterContext *avctx)
     av_expr_free(s->pos_h_pexpr);
 }
 
+static int libplacebo_process_command(AVFilterContext *ctx, const char *cmd,
+                                      const char *arg, char *res, int res_len,
+                                      int flags)
+{
+    int err = 0;
+    RET(ff_filter_process_command(ctx, cmd, arg, res, res_len, flags));
+    RET(update_settings(ctx));
+    return 0;
+
+fail:
+    return err;
+}
+
 static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
 {
     int err = 0, ok;
     LibplaceboContext *s = avctx->priv;
-    struct pl_render_params params;
-    enum pl_tone_map_mode tonemapping_mode = s->tonemapping_mode;
     const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->format);
-    enum pl_gamut_mode gamut_mode = s->gamut_mode;
     struct pl_frame image, target;
     ok = pl_map_avframe_ex(s->gpu, &image, pl_avframe_params(
         .frame    = in,
@@ -505,95 +617,7 @@ static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
         pl_rect2df_aspect_set(&target.crop, aspect, s->pad_crop_ratio);
     }
 
-#if FF_API_LIBPLACEBO_OPTS
-    /* backwards compatibility with older API */
-    if (!tonemapping_mode && (s->desat_str >= 0.0f || s->desat_exp >= 0.0f)) {
-        float str = s->desat_str < 0.0f ? 0.9f : s->desat_str;
-        float exp = s->desat_exp < 0.0f ? 0.2f : s->desat_exp;
-        if (str >= 0.9f && exp <= 0.1f) {
-            tonemapping_mode = PL_TONE_MAP_RGB;
-        } else if (str > 0.1f) {
-            tonemapping_mode = PL_TONE_MAP_HYBRID;
-        } else {
-            tonemapping_mode = PL_TONE_MAP_LUMA;
-        }
-    }
-
-    if (s->gamut_warning)
-        gamut_mode = PL_GAMUT_WARN;
-    if (s->gamut_clipping)
-        gamut_mode = PL_GAMUT_DESATURATE;
-#endif
-
-    /* Update render params */
-    params = (struct pl_render_params) {
-        PL_RENDER_DEFAULTS
-        .lut_entries = s->lut_entries,
-        .antiringing_strength = s->antiringing,
-
-        .deband_params = !s->deband ? NULL : pl_deband_params(
-            .iterations = s->deband_iterations,
-            .threshold = s->deband_threshold,
-            .radius = s->deband_radius,
-            .grain = s->deband_grain,
-        ),
-
-        .sigmoid_params = s->sigmoid ? &pl_sigmoid_default_params : NULL,
-
-        .color_adjustment = &(struct pl_color_adjustment) {
-            .brightness = s->brightness,
-            .contrast = s->contrast,
-            .saturation = s->saturation,
-            .hue = s->hue,
-            .gamma = s->gamma,
-        },
-
-        .peak_detect_params = !s->peakdetect ? NULL : pl_peak_detect_params(
-            .smoothing_period = s->smoothing,
-            .minimum_peak = s->min_peak,
-            .scene_threshold_low = s->scene_low,
-            .scene_threshold_high = s->scene_high,
-            .overshoot_margin = s->overshoot,
-        ),
-
-        .color_map_params = pl_color_map_params(
-            .intent = s->intent,
-            .gamut_mode = gamut_mode,
-            .tone_mapping_function = tonemapping_funcs[s->tonemapping],
-            .tone_mapping_param = s->tonemapping_param,
-            .tone_mapping_mode = tonemapping_mode,
-            .inverse_tone_mapping = s->inverse_tonemapping,
-            .tone_mapping_crosstalk = s->crosstalk,
-            .lut_size = s->tonemapping_lut_size,
-        ),
-
-        .dither_params = s->dithering < 0 ? NULL : pl_dither_params(
-            .method = s->dithering,
-            .lut_size = s->dither_lut_size,
-            .temporal = s->dither_temporal,
-        ),
-
-        .cone_params = !s->cones ? NULL : pl_cone_params(
-            .cones = s->cones,
-            .strength = s->cone_str,
-        ),
-
-        .hooks = s->hooks,
-        .num_hooks = s->num_hooks,
-
-        .skip_anti_aliasing = s->skip_aa,
-        .polar_cutoff = s->polar_cutoff,
-        .disable_linear_scaling = s->disable_linear,
-        .disable_builtin_scalers = s->disable_builtin,
-        .force_dither = s->force_dither,
-        .disable_fbos = s->disable_fbos,
-    };
-
-    RET(find_scaler(avctx, &params.upscaler, s->upscaler));
-    RET(find_scaler(avctx, &params.downscaler, s->downscaler));
-    RET(parse_fillcolor(avctx, &params, s->fillcolor));
-
-    pl_render_image(s->renderer, &image, &target, &params);
+    pl_render_image(s->renderer, &image, &target, &s->params);
     pl_unmap_avframe(s->gpu, &image);
 
     if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
@@ -1049,7 +1073,7 @@ const AVFilter ff_vf_libplacebo = {
     .priv_size      = sizeof(LibplaceboContext),
     .init           = &libplacebo_init,
     .uninit         = &libplacebo_uninit,
-    .process_command = &ff_filter_process_command,
+    .process_command = &libplacebo_process_command,
     FILTER_INPUTS(libplacebo_inputs),
     FILTER_OUTPUTS(libplacebo_outputs),
     FILTER_QUERY_FUNC(libplacebo_query_format),
-- 
2.40.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [FFmpeg-devel] [PATCH 2/6] lavfi/vf_libplacebo: split and refactor logic
  2023-05-10 13:55 [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
@ 2023-05-10 13:55 ` Niklas Haas
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 3/6] lavfi/vf_libplacebo: switch to pl_queue-based design Niklas Haas
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Niklas Haas @ 2023-05-10 13:55 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

From: Niklas Haas <git@haasn.dev>

This commit contains no functional change. The goal is merely to
separate the highly intertwined `filter_frame` and `process_frames`
functions into their separate concerns, specifically to separate frame
uploading (which is now done directly in `filter_frame`) from emitting a
frame (which is now done by a dedicated function `output_frame`).

The overall idea here is to be able to ultimately call `output_frame`
multiple times, to e.g. emit several output frames for a single input
frame.
---
 libavfilter/vf_libplacebo.c | 172 +++++++++++++++++++-----------------
 1 file changed, 93 insertions(+), 79 deletions(-)

diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index 2ee9055c9d..e84f81c143 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -28,6 +28,14 @@
 #include <libplacebo/utils/libav.h>
 #include <libplacebo/vulkan.h>
 
+/* Backwards compatibility with older libplacebo */
+#if PL_API_VER < 276
+static inline AVFrame *pl_get_mapped_avframe(const struct pl_frame *frame) 
+{
+    return frame->user_data;
+}
+#endif
+
 enum {
     TONE_MAP_AUTO,
     TONE_MAP_CLIP,
@@ -564,35 +572,27 @@ fail:
     return err;
 }
 
-static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
+static void update_crops(AVFilterContext *ctx,
+                         struct pl_frame *image,
+                         struct pl_frame *target,
+                         const double target_pts)
 {
-    int err = 0, ok;
-    LibplaceboContext *s = avctx->priv;
-    const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->format);
-    struct pl_frame image, target;
-    ok = pl_map_avframe_ex(s->gpu, &image, pl_avframe_params(
-        .frame    = in,
-        .tex      = s->tex,
-        .map_dovi = s->apply_dovi,
-    ));
-
-    if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
-        ok &= pl_map_avframe_ex(s->gpu, &target, pl_avframe_params(
-            .frame    = out,
-            .map_dovi = false,
-        ));
-    } else {
-        ok &= pl_frame_recreate_from_avframe(s->gpu, &target, s->tex + 4, out);
-    }
+    LibplaceboContext *s = ctx->priv;
+    const AVFrame *in = pl_get_mapped_avframe(image);
+    const AVFilterLink *inlink = ctx->inputs[0];
+    const double in_pts = in->pts * av_q2d(inlink->time_base);
 
-    if (!ok) {
-        err = AVERROR_EXTERNAL;
-        goto fail;
-    }
+    s->var_values[VAR_IN_T]   = s->var_values[VAR_T]  = in_pts;
+    s->var_values[VAR_OUT_T]  = s->var_values[VAR_OT] = target_pts;
+    s->var_values[VAR_N]      = inlink->frame_count_out;
 
-    if (!s->apply_filmgrain)
-        image.film_grain.type = PL_FILM_GRAIN_NONE;
+    /* Clear these explicitly to avoid leaking previous frames' state */
+    s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = NAN;
+    s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] = NAN;
+    s->var_values[VAR_POS_W]  = s->var_values[VAR_PW] = NAN;
+    s->var_values[VAR_POS_H]  = s->var_values[VAR_PH] = NAN;
 
+    /* Evaluate crop/pos dimensions first, and placement second */
     s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] =
         av_expr_eval(s->crop_w_pexpr, s->var_values, NULL);
     s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] =
@@ -602,72 +602,39 @@ static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
     s->var_values[VAR_POS_H]  = s->var_values[VAR_PH] =
         av_expr_eval(s->pos_h_pexpr, s->var_values, NULL);
 
-    image.crop.x0 = av_expr_eval(s->crop_x_pexpr, s->var_values, NULL);
-    image.crop.y0 = av_expr_eval(s->crop_y_pexpr, s->var_values, NULL);
-    image.crop.x1 = image.crop.x0 + s->var_values[VAR_CROP_W];
-    image.crop.y1 = image.crop.y0 + s->var_values[VAR_CROP_H];
+    image->crop.x0 = av_expr_eval(s->crop_x_pexpr, s->var_values, NULL);
+    image->crop.y0 = av_expr_eval(s->crop_y_pexpr, s->var_values, NULL);
+    image->crop.x1 = image->crop.x0 + s->var_values[VAR_CROP_W];
+    image->crop.y1 = image->crop.y0 + s->var_values[VAR_CROP_H];
 
-    target.crop.x0 = av_expr_eval(s->pos_x_pexpr, s->var_values, NULL);
-    target.crop.y0 = av_expr_eval(s->pos_y_pexpr, s->var_values, NULL);
-    target.crop.x1 = target.crop.x0 + s->var_values[VAR_POS_W];
-    target.crop.y1 = target.crop.y0 + s->var_values[VAR_POS_H];
+    target->crop.x0 = av_expr_eval(s->pos_x_pexpr, s->var_values, NULL);
+    target->crop.y0 = av_expr_eval(s->pos_y_pexpr, s->var_values, NULL);
+    target->crop.x1 = target->crop.x0 + s->var_values[VAR_POS_W];
+    target->crop.y1 = target->crop.y0 + s->var_values[VAR_POS_H];
 
     if (s->target_sar.num) {
-        float aspect = pl_rect2df_aspect(&target.crop) * av_q2d(s->target_sar);
-        pl_rect2df_aspect_set(&target.crop, aspect, s->pad_crop_ratio);
-    }
-
-    pl_render_image(s->renderer, &image, &target, &s->params);
-    pl_unmap_avframe(s->gpu, &image);
-
-    if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
-        pl_unmap_avframe(s->gpu, &target);
-    } else if (!pl_download_avframe(s->gpu, &target, out)) {
-        err = AVERROR_EXTERNAL;
-        goto fail;
+        float aspect = pl_rect2df_aspect(&target->crop) * av_q2d(s->target_sar);
+        pl_rect2df_aspect_set(&target->crop, aspect, s->pad_crop_ratio);
     }
-
-    /* Flush the command queues for performance */
-    pl_gpu_flush(s->gpu);
-    return 0;
-
-fail:
-    pl_unmap_avframe(s->gpu, &image);
-    pl_unmap_avframe(s->gpu, &target);
-    return err;
 }
 
-static int filter_frame(AVFilterLink *link, AVFrame *in)
+/* Construct and emit an output frame for `image` */
+static int output_frame(AVFilterContext *ctx, struct pl_frame *image)
 {
-    int err, changed_csp;
-    AVFilterContext *ctx = link->dst;
+    int err = 0, ok, changed_csp;
     LibplaceboContext *s = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[0];
-
+    const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(outlink->format);
     AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
-    if (!out) {
-        err = AVERROR(ENOMEM);
-        goto fail;
-    }
-
-    pl_log_level_update(s->log, get_log_level());
+    const AVFrame *in = pl_get_mapped_avframe(image);
+    struct pl_frame target;
+    if (!out)
+        return AVERROR(ENOMEM);
 
     RET(av_frame_copy_props(out, in));
     out->width = outlink->w;
     out->height = outlink->h;
 
-    /* Dynamic variables */
-    s->var_values[VAR_IN_T]   = s->var_values[VAR_T] =
-        in->pts == AV_NOPTS_VALUE ? NAN : in->pts * av_q2d(link->time_base);
-    s->var_values[VAR_OUT_T]  = s->var_values[VAR_OT] =
-        out->pts == AV_NOPTS_VALUE ? NAN : out->pts * av_q2d(outlink->time_base);
-    s->var_values[VAR_N]      = link->frame_count_out;
-    /* Will be evaluated/set by `process_frames` */
-    s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = NAN;
-    s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] = NAN;
-    s->var_values[VAR_POS_W]  = s->var_values[VAR_PW] = NAN;
-    s->var_values[VAR_POS_H]  = s->var_values[VAR_PH] = NAN;
-
     if (s->apply_dovi && av_frame_get_side_data(in, AV_FRAME_DATA_DOVI_METADATA)) {
         /* Output of dovi reshaping is always BT.2020+PQ, so infer the correct
          * output colorspace defaults */
@@ -703,18 +670,65 @@ static int filter_frame(AVFilterLink *link, AVFrame *in)
     if (s->apply_filmgrain)
         av_frame_remove_side_data(out, AV_FRAME_DATA_FILM_GRAIN_PARAMS);
 
-    RET(process_frames(ctx, out, in));
+    /* Map, render and unmap output frame */
+    if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
+        ok = pl_map_avframe_ex(s->gpu, &target, pl_avframe_params(
+            .frame    = out,
+            .map_dovi = false,
+        ));
+    } else {
+        ok = pl_frame_recreate_from_avframe(s->gpu, &target, s->tex + 4, out);
+    }
+    if (!ok) {
+        err = AVERROR_EXTERNAL;
+        goto fail;
+    }
 
-    av_frame_free(&in);
+    update_crops(ctx, image, &target, out->pts * av_q2d(outlink->time_base));
+    pl_render_image(s->renderer, image, &target, &s->params);
 
+    if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
+        pl_unmap_avframe(s->gpu, &target);
+    } else if (!pl_download_avframe(s->gpu, &target, out)) {
+        err = AVERROR_EXTERNAL;
+        goto fail;
+    }
     return ff_filter_frame(outlink, out);
 
 fail:
-    av_frame_free(&in);
     av_frame_free(&out);
     return err;
 }
 
+static int filter_frame(AVFilterLink *link, AVFrame *in)
+{
+    int ret, ok;
+    AVFilterContext *ctx = link->dst;
+    LibplaceboContext *s = ctx->priv;
+    struct pl_frame image;
+
+    pl_log_level_update(s->log, get_log_level());
+
+    /* Map input frame */
+    ok = pl_map_avframe_ex(s->gpu, &image, pl_avframe_params(
+        .frame      = in,
+        .tex        = s->tex,
+        .map_dovi   = s->apply_dovi,
+    ));
+    av_frame_free(&in);
+
+    if (!s->apply_filmgrain)
+        image.film_grain.type = PL_FILM_GRAIN_NONE;
+
+    if (!ok)
+        return AVERROR_EXTERNAL;
+
+    ret = output_frame(ctx, &image);
+
+    pl_unmap_avframe(s->gpu, &image);
+    return ret;
+}
+
 static int libplacebo_query_format(AVFilterContext *ctx)
 {
     int err;
-- 
2.40.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [FFmpeg-devel] [PATCH 3/6] lavfi/vf_libplacebo: switch to pl_queue-based design
  2023-05-10 13:55 [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 2/6] lavfi/vf_libplacebo: split and refactor logic Niklas Haas
@ 2023-05-10 13:55 ` Niklas Haas
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 4/6] lavfi/vf_libplacebo: switch to activate() Niklas Haas
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Niklas Haas @ 2023-05-10 13:55 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

From: Niklas Haas <git@haasn.dev>

This does not leverage any immediate benefits, but refactors and
prepares the codebase for upcoming changes, which will include the
ability to do deinterlacing and resampling (frame mixing).
---
 libavfilter/vf_libplacebo.c | 214 ++++++++++++++++++++++++------------
 1 file changed, 144 insertions(+), 70 deletions(-)

diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index e84f81c143..afb4e6a914 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -16,6 +16,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/avassert.h"
 #include "libavutil/eval.h"
 #include "libavutil/file.h"
 #include "libavutil/opt.h"
@@ -26,6 +27,7 @@
 
 #include <libplacebo/renderer.h>
 #include <libplacebo/utils/libav.h>
+#include <libplacebo/utils/frame_queue.h>
 #include <libplacebo/vulkan.h>
 
 /* Backwards compatibility with older libplacebo */
@@ -122,7 +124,8 @@ typedef struct LibplaceboContext {
     pl_vulkan vulkan;
     pl_gpu gpu;
     pl_renderer renderer;
-    pl_tex tex[8];
+    pl_queue queue;
+    pl_tex tex[4];
 
     /* settings */
     char *out_format_string;
@@ -392,6 +395,7 @@ static int update_settings(AVFilterContext *ctx)
         .num_hooks = s->num_hooks,
 
         .skip_anti_aliasing = s->skip_aa,
+        .skip_caching_single_frame = true,
         .polar_cutoff = s->polar_cutoff,
         .disable_linear_scaling = s->disable_linear,
         .disable_builtin_scalers = s->disable_builtin,
@@ -518,6 +522,7 @@ static int init_vulkan(AVFilterContext *avctx)
     /* Create the renderer */
     s->gpu = s->vulkan->gpu;
     s->renderer = pl_renderer_create(s->log, s->gpu);
+    s->queue = pl_queue_create(s->gpu);
 
     /* Parse the user shaders, if requested */
     if (s->shader_bin_len)
@@ -544,6 +549,7 @@ static void libplacebo_uninit(AVFilterContext *avctx)
     for (int i = 0; i < s->num_hooks; i++)
         pl_mpv_user_shader_destroy(&s->hooks[i]);
     pl_renderer_destroy(&s->renderer);
+    pl_queue_destroy(&s->queue);
     pl_vulkan_destroy(&s->vulkan);
     pl_log_destroy(&s->log);
     ff_vk_uninit(&s->vkctx);
@@ -573,69 +579,92 @@ fail:
 }
 
 static void update_crops(AVFilterContext *ctx,
-                         struct pl_frame *image,
-                         struct pl_frame *target,
-                         const double target_pts)
+                         struct pl_frame_mix *mix, struct pl_frame *target,
+                         uint64_t ref_sig, double base_pts)
 {
     LibplaceboContext *s = ctx->priv;
-    const AVFrame *in = pl_get_mapped_avframe(image);
-    const AVFilterLink *inlink = ctx->inputs[0];
-    const double in_pts = in->pts * av_q2d(inlink->time_base);
-
-    s->var_values[VAR_IN_T]   = s->var_values[VAR_T]  = in_pts;
-    s->var_values[VAR_OUT_T]  = s->var_values[VAR_OT] = target_pts;
-    s->var_values[VAR_N]      = inlink->frame_count_out;
-
-    /* Clear these explicitly to avoid leaking previous frames' state */
-    s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = NAN;
-    s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] = NAN;
-    s->var_values[VAR_POS_W]  = s->var_values[VAR_PW] = NAN;
-    s->var_values[VAR_POS_H]  = s->var_values[VAR_PH] = NAN;
-
-    /* Evaluate crop/pos dimensions first, and placement second */
-    s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] =
-        av_expr_eval(s->crop_w_pexpr, s->var_values, NULL);
-    s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] =
-        av_expr_eval(s->crop_h_pexpr, s->var_values, NULL);
-    s->var_values[VAR_POS_W]  = s->var_values[VAR_PW] =
-        av_expr_eval(s->pos_w_pexpr, s->var_values, NULL);
-    s->var_values[VAR_POS_H]  = s->var_values[VAR_PH] =
-        av_expr_eval(s->pos_h_pexpr, s->var_values, NULL);
-
-    image->crop.x0 = av_expr_eval(s->crop_x_pexpr, s->var_values, NULL);
-    image->crop.y0 = av_expr_eval(s->crop_y_pexpr, s->var_values, NULL);
-    image->crop.x1 = image->crop.x0 + s->var_values[VAR_CROP_W];
-    image->crop.y1 = image->crop.y0 + s->var_values[VAR_CROP_H];
-
-    target->crop.x0 = av_expr_eval(s->pos_x_pexpr, s->var_values, NULL);
-    target->crop.y0 = av_expr_eval(s->pos_y_pexpr, s->var_values, NULL);
-    target->crop.x1 = target->crop.x0 + s->var_values[VAR_POS_W];
-    target->crop.y1 = target->crop.y0 + s->var_values[VAR_POS_H];
-
-    if (s->target_sar.num) {
-        float aspect = pl_rect2df_aspect(&target->crop) * av_q2d(s->target_sar);
-        pl_rect2df_aspect_set(&target->crop, aspect, s->pad_crop_ratio);
+
+    for (int i = 0; i < mix->num_frames; i++) {
+        // Mutate the `pl_frame.crop` fields in-place. This is fine because we
+        // own the entire pl_queue, and hence, the pointed-at frames.
+        struct pl_frame *image = (struct pl_frame *) mix->frames[i];
+        double image_pts = base_pts + mix->timestamps[i];
+
+        /* Update dynamic variables */
+        s->var_values[VAR_IN_T]   = s->var_values[VAR_T]  = image_pts;
+        s->var_values[VAR_OUT_T]  = s->var_values[VAR_OT] = base_pts;
+        s->var_values[VAR_N]      = ctx->outputs[0]->frame_count_out;
+
+        /* Clear these explicitly to avoid leaking previous frames' state */
+        s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = NAN;
+        s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] = NAN;
+        s->var_values[VAR_POS_W]  = s->var_values[VAR_PW] = NAN;
+        s->var_values[VAR_POS_H]  = s->var_values[VAR_PH] = NAN;
+
+        /* Compute dimensions first and placement second */
+        s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] =
+            av_expr_eval(s->crop_w_pexpr, s->var_values, NULL);
+        s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] =
+            av_expr_eval(s->crop_h_pexpr, s->var_values, NULL);
+        s->var_values[VAR_POS_W]  = s->var_values[VAR_PW] =
+            av_expr_eval(s->pos_w_pexpr, s->var_values, NULL);
+        s->var_values[VAR_POS_H]  = s->var_values[VAR_PH] =
+            av_expr_eval(s->pos_h_pexpr, s->var_values, NULL);
+
+        image->crop.x0 = av_expr_eval(s->crop_x_pexpr, s->var_values, NULL);
+        image->crop.y0 = av_expr_eval(s->crop_y_pexpr, s->var_values, NULL);
+        image->crop.x1 = image->crop.x0 + s->var_values[VAR_CROP_W];
+        image->crop.y1 = image->crop.y0 + s->var_values[VAR_CROP_H];
+
+        if (mix->signatures[i] == ref_sig) {
+            /* Only update the target crop once, for the 'reference' frame */
+            target->crop.x0 = av_expr_eval(s->pos_x_pexpr, s->var_values, NULL);
+            target->crop.y0 = av_expr_eval(s->pos_y_pexpr, s->var_values, NULL);
+            target->crop.x1 = target->crop.x0 + s->var_values[VAR_POS_W];
+            target->crop.y1 = target->crop.y0 + s->var_values[VAR_POS_H];
+
+            if (s->target_sar.num) {
+                float aspect = pl_rect2df_aspect(&target->crop) * av_q2d(s->target_sar);
+                pl_rect2df_aspect_set(&target->crop, aspect, s->pad_crop_ratio);
+            }
+        }
     }
 }
 
-/* Construct and emit an output frame for `image` */
-static int output_frame(AVFilterContext *ctx, struct pl_frame *image)
+/* Construct and emit an output frame for a given frame mix */
+static int output_frame_mix(AVFilterContext *ctx,
+                            struct pl_frame_mix *mix,
+                            int64_t pts)
 {
     int err = 0, ok, changed_csp;
     LibplaceboContext *s = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[0];
     const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(outlink->format);
-    AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
-    const AVFrame *in = pl_get_mapped_avframe(image);
     struct pl_frame target;
+    const AVFrame *ref;
+    AVFrame *out;
+    uint64_t ref_sig;
+    if (!mix->num_frames)
+        return 0;
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     if (!out)
         return AVERROR(ENOMEM);
 
-    RET(av_frame_copy_props(out, in));
+    /* Use the last frame before current PTS value as reference */
+    for (int i = 0; i < mix->num_frames; i++) {
+        if (i && mix->timestamps[i] > 0.0f)
+            break;
+        ref = pl_get_mapped_avframe(mix->frames[i]);
+        ref_sig = mix->signatures[i];
+    }
+
+    RET(av_frame_copy_props(out, ref));
+    out->pts = pts;
     out->width = outlink->w;
     out->height = outlink->h;
 
-    if (s->apply_dovi && av_frame_get_side_data(in, AV_FRAME_DATA_DOVI_METADATA)) {
+    if (s->apply_dovi && av_frame_get_side_data(ref, AV_FRAME_DATA_DOVI_METADATA)) {
         /* Output of dovi reshaping is always BT.2020+PQ, so infer the correct
          * output colorspace defaults */
         out->colorspace = AVCOL_SPC_BT2020_NCL;
@@ -652,10 +681,10 @@ static int output_frame(AVFilterContext *ctx, struct pl_frame *image)
     if (s->color_primaries >= 0)
         out->color_primaries = s->color_primaries;
 
-    changed_csp = in->colorspace      != out->colorspace     ||
-                  in->color_range     != out->color_range    ||
-                  in->color_trc       != out->color_trc      ||
-                  in->color_primaries != out->color_primaries;
+    changed_csp = ref->colorspace      != out->colorspace     ||
+                  ref->color_range     != out->color_range    ||
+                  ref->color_trc       != out->color_trc      ||
+                  ref->color_primaries != out->color_primaries;
 
     /* Strip side data if no longer relevant */
     if (changed_csp) {
@@ -677,15 +706,15 @@ static int output_frame(AVFilterContext *ctx, struct pl_frame *image)
             .map_dovi = false,
         ));
     } else {
-        ok = pl_frame_recreate_from_avframe(s->gpu, &target, s->tex + 4, out);
+        ok = pl_frame_recreate_from_avframe(s->gpu, &target, s->tex, out);
     }
     if (!ok) {
         err = AVERROR_EXTERNAL;
         goto fail;
     }
 
-    update_crops(ctx, image, &target, out->pts * av_q2d(outlink->time_base));
-    pl_render_image(s->renderer, image, &target, &s->params);
+    update_crops(ctx, mix, &target, ref_sig, out->pts * av_q2d(outlink->time_base));
+    pl_render_image_mix(s->renderer, mix, &target, &s->params);
 
     if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
         pl_unmap_avframe(s->gpu, &target);
@@ -700,33 +729,78 @@ fail:
     return err;
 }
 
+static bool map_frame(pl_gpu gpu, pl_tex *tex,
+                      const struct pl_source_frame *src,
+                      struct pl_frame *out)
+{
+    AVFrame *avframe = src->frame_data;
+    LibplaceboContext *s = avframe->opaque;
+    bool ok = pl_map_avframe_ex(gpu, out, pl_avframe_params(
+        .frame      = avframe,
+        .tex        = tex,
+        .map_dovi   = s->apply_dovi,
+    ));
+
+    if (!s->apply_filmgrain)
+        out->film_grain.type = PL_FILM_GRAIN_NONE;
+
+    av_frame_free(&avframe);
+    return ok;
+}
+
+static void unmap_frame(pl_gpu gpu, struct pl_frame *frame,
+                        const struct pl_source_frame *src)
+{
+    pl_unmap_avframe(gpu, frame);
+}
+
+static void discard_frame(const struct pl_source_frame *src)
+{
+    AVFrame *avframe = src->frame_data;
+    av_frame_free(&avframe);
+}
+
 static int filter_frame(AVFilterLink *link, AVFrame *in)
 {
-    int ret, ok;
     AVFilterContext *ctx = link->dst;
     LibplaceboContext *s = ctx->priv;
-    struct pl_frame image;
+    AVFilterLink *outlink = ctx->outputs[0];
+    enum pl_queue_status status;
+    struct pl_frame_mix mix;
 
     pl_log_level_update(s->log, get_log_level());
 
-    /* Map input frame */
-    ok = pl_map_avframe_ex(s->gpu, &image, pl_avframe_params(
-        .frame      = in,
-        .tex        = s->tex,
-        .map_dovi   = s->apply_dovi,
+    /* Push input frame */
+    in->opaque = s;
+    pl_queue_push(s->queue, &(struct pl_source_frame) {
+        .pts         = in->pts * av_q2d(link->time_base),
+        .duration    = in->duration * av_q2d(link->time_base),
+        .first_field = pl_field_from_avframe(in),
+        .frame_data  = in,
+        .map         = map_frame,
+        .unmap       = unmap_frame,
+        .discard     = discard_frame,
+    });
+
+    /* Immediately return an output frame for the same PTS */
+    av_assert1(!av_cmp_q(link->time_base, outlink->time_base));
+    status = pl_queue_update(s->queue, &mix, pl_queue_params(
+        .pts            = in->pts * av_q2d(outlink->time_base),
+        .radius         = pl_frame_mix_radius(&s->params),
+        .vsync_duration = av_q2d(av_inv_q(outlink->frame_rate)),
     ));
-    av_frame_free(&in);
-
-    if (!s->apply_filmgrain)
-        image.film_grain.type = PL_FILM_GRAIN_NONE;
 
-    if (!ok)
+    switch (status) {
+    case PL_QUEUE_MORE: // TODO: switch to activate() and handle properly
+    case PL_QUEUE_OK:
+        return output_frame_mix(ctx, &mix, in->pts);
+    case PL_QUEUE_EOF:
+        return 0;
+    case PL_QUEUE_ERR:
         return AVERROR_EXTERNAL;
+    }
 
-    ret = output_frame(ctx, &image);
-
-    pl_unmap_avframe(s->gpu, &image);
-    return ret;
+    return AVERROR_BUG;
 }
 
 static int libplacebo_query_format(AVFilterContext *ctx)
-- 
2.40.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [FFmpeg-devel] [PATCH 4/6] lavfi/vf_libplacebo: switch to activate()
  2023-05-10 13:55 [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 2/6] lavfi/vf_libplacebo: split and refactor logic Niklas Haas
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 3/6] lavfi/vf_libplacebo: switch to pl_queue-based design Niklas Haas
@ 2023-05-10 13:55 ` Niklas Haas
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 5/6] lavfi/vf_libplacebo: allow fps conversion Niklas Haas
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Niklas Haas @ 2023-05-10 13:55 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

From: Niklas Haas <git@haasn.dev>

To present compatibility with the current behavior, we keep track of a
FIFO of exact frame timestamps that we want to output to the user. In
practice, this is essentially equivalent to the current filter_frame()
code, but this design allows us to scale to more complicated use cases
in the future - for example, insertion of intermediate frames
(deinterlacing, frame doubling, conversion to fixed fps, ...)
---
 libavfilter/vf_libplacebo.c | 113 +++++++++++++++++++++++++-----------
 1 file changed, 80 insertions(+), 33 deletions(-)

diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index afb4e6a914..d8128351c8 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -18,10 +18,12 @@
 
 #include "libavutil/avassert.h"
 #include "libavutil/eval.h"
+#include "libavutil/fifo.h"
 #include "libavutil/file.h"
 #include "libavutil/opt.h"
 #include "libavutil/parseutils.h"
 #include "internal.h"
+#include "filters.h"
 #include "vulkan_filter.h"
 #include "scale_eval.h"
 
@@ -127,6 +129,9 @@ typedef struct LibplaceboContext {
     pl_queue queue;
     pl_tex tex[4];
 
+    /* filter state */
+    AVFifo *out_pts; ///< timestamps of wanted output frames
+
     /* settings */
     char *out_format_string;
     enum AVPixelFormat out_format;
@@ -458,6 +463,9 @@ static int libplacebo_init(AVFilterContext *avctx)
     RET(av_expr_parse(&s->pos_h_pexpr, s->pos_h_expr, var_names,
                       NULL, NULL, NULL, NULL, 0, s));
 
+    /* Initialize dynamic filter state */
+    s->out_pts = av_fifo_alloc2(1, sizeof(int64_t), AV_FIFO_FLAG_AUTO_GROW);
+
     /* Note: s->vulkan etc. are initialized later, when hwctx is available */
     return 0;
 
@@ -563,6 +571,7 @@ static void libplacebo_uninit(AVFilterContext *avctx)
     av_expr_free(s->pos_y_pexpr);
     av_expr_free(s->pos_w_pexpr);
     av_expr_free(s->pos_h_pexpr);
+    av_fifo_freep2(&s->out_pts);
 }
 
 static int libplacebo_process_command(AVFilterContext *ctx, const char *cmd,
@@ -760,47 +769,85 @@ static void discard_frame(const struct pl_source_frame *src)
     av_frame_free(&avframe);
 }
 
-static int filter_frame(AVFilterLink *link, AVFrame *in)
+static int libplacebo_activate(AVFilterContext *ctx)
 {
-    AVFilterContext *ctx = link->dst;
+    int ret, status;
     LibplaceboContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
     AVFilterLink *outlink = ctx->outputs[0];
-    enum pl_queue_status status;
-    struct pl_frame_mix mix;
+    AVFrame *in;
+    int64_t pts;
 
+    FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
     pl_log_level_update(s->log, get_log_level());
 
-    /* Push input frame */
-    in->opaque = s;
-    pl_queue_push(s->queue, &(struct pl_source_frame) {
-        .pts         = in->pts * av_q2d(link->time_base),
-        .duration    = in->duration * av_q2d(link->time_base),
-        .first_field = pl_field_from_avframe(in),
-        .frame_data  = in,
-        .map         = map_frame,
-        .unmap       = unmap_frame,
-        .discard     = discard_frame,
-    });
-
-    /* Immediately return an output frame for the same PTS */
-    av_assert1(!av_cmp_q(link->time_base, outlink->time_base));
-    status = pl_queue_update(s->queue, &mix, pl_queue_params(
-        .pts            = in->pts * av_q2d(outlink->time_base),
-        .radius         = pl_frame_mix_radius(&s->params),
-        .vsync_duration = av_q2d(av_inv_q(outlink->frame_rate)),
-    ));
+    while ((ret = ff_inlink_consume_frame(inlink, &in)) > 0) {
+        in->opaque = s;
+        pl_queue_push(s->queue, &(struct pl_source_frame) {
+            .pts         = in->pts * av_q2d(inlink->time_base),
+            .duration    = in->duration * av_q2d(inlink->time_base),
+            .first_field = pl_field_from_avframe(in),
+            .frame_data  = in,
+            .map         = map_frame,
+            .unmap       = unmap_frame,
+            .discard     = discard_frame,
+        });
+
+        /* Internally queue an output frame for the same PTS */
+        av_assert1(!av_cmp_q(link->time_base, outlink->time_base));
+        av_fifo_write(s->out_pts, &in->pts, 1);
+    }
 
-    switch (status) {
-    case PL_QUEUE_MORE: // TODO: switch to activate() and handle properly
-    case PL_QUEUE_OK:
-        return output_frame_mix(ctx, &mix, in->pts);
-    case PL_QUEUE_EOF:
-        return 0;
-    case PL_QUEUE_ERR:
-        return AVERROR_EXTERNAL;
+    if (ret < 0)
+        return ret;
+
+    if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
+        pts = av_rescale_q_rnd(pts, inlink->time_base, outlink->time_base,
+                               AV_ROUND_UP);
+        if (status == AVERROR_EOF) {
+            /* Signal EOF to pl_queue, and enqueue this output frame to
+             * make sure we see PL_QUEUE_EOF returned eventually */
+            pl_queue_push(s->queue, NULL);
+            av_fifo_write(s->out_pts, &pts, 1);
+        } else {
+            ff_outlink_set_status(outlink, status, pts);
+            return 0;
+        }
+    }
+
+    if (ff_outlink_frame_wanted(outlink)) {
+        struct pl_frame_mix mix;
+        enum pl_queue_status ret;
+
+        if (av_fifo_peek(s->out_pts, &pts, 1, 0) < 0) {
+            ff_inlink_request_frame(inlink);
+            return 0;
+        }
+
+        ret = pl_queue_update(s->queue, &mix, pl_queue_params(
+            .pts            = pts * av_q2d(outlink->time_base),
+            .radius         = pl_frame_mix_radius(&s->params),
+            .vsync_duration = av_q2d(av_inv_q(outlink->frame_rate)),
+        ));
+
+        switch (ret) {
+        case PL_QUEUE_MORE:
+            ff_inlink_request_frame(inlink);
+            return 0;
+        case PL_QUEUE_OK:
+            av_fifo_drain2(s->out_pts, 1);
+            return output_frame_mix(ctx, &mix, pts);
+        case PL_QUEUE_EOF:
+            ff_outlink_set_status(outlink, AVERROR_EOF, pts);
+            return 0;
+        case PL_QUEUE_ERR:
+            return AVERROR_EXTERNAL;
+        }
+
+        return AVERROR_BUG;
     }
 
-    return AVERROR_BUG;
+    return FFERROR_NOT_READY;
 }
 
 static int libplacebo_query_format(AVFilterContext *ctx)
@@ -1142,7 +1189,6 @@ static const AVFilterPad libplacebo_inputs[] = {
     {
         .name         = "default",
         .type         = AVMEDIA_TYPE_VIDEO,
-        .filter_frame = &filter_frame,
         .config_props = &libplacebo_config_input,
     },
 };
@@ -1161,6 +1207,7 @@ const AVFilter ff_vf_libplacebo = {
     .priv_size      = sizeof(LibplaceboContext),
     .init           = &libplacebo_init,
     .uninit         = &libplacebo_uninit,
+    .activate       = &libplacebo_activate,
     .process_command = &libplacebo_process_command,
     FILTER_INPUTS(libplacebo_inputs),
     FILTER_OUTPUTS(libplacebo_outputs),
-- 
2.40.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [FFmpeg-devel] [PATCH 5/6] lavfi/vf_libplacebo: allow fps conversion
  2023-05-10 13:55 [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
                   ` (2 preceding siblings ...)
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 4/6] lavfi/vf_libplacebo: switch to activate() Niklas Haas
@ 2023-05-10 13:55 ` Niklas Haas
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 6/6] lavfi/vf_libplacebo: add frame_mixer option Niklas Haas
  2023-05-14  9:22 ` [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
  5 siblings, 0 replies; 7+ messages in thread
From: Niklas Haas @ 2023-05-10 13:55 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

From: Niklas Haas <git@haasn.dev>

This exposes libplacebo's frame mixing functionality to vf_libplacebo,
by allowing users to specify a desired target fps to output at. Incoming
frames will be smoothly resampled (in a manner determined by the
`frame_mixer` option, to be added in the next commit).

To generate a consistently timed output stream, we directly use the
desired framerate as the timebase, and simply output frames in
sequential order (tracked by the number of frames output so far).
---
 doc/filters.texi            |  7 +++++++
 libavfilter/vf_libplacebo.c | 38 ++++++++++++++++++++++++++++++-------
 2 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 206fb23785..3e17ba1654 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -16034,6 +16034,13 @@ and @code{(oh-ph)/2}.
 Set the output placement width/height expressions, default values are @code{ow}
 and @code{oh}.
 
+@item fps
+Set the output frame rate. This can be rational, e.g. @code{60000/1001}. If
+set to the special string @code{none} (the default), input timestamps will
+instead be passed through to the output unmodified. Otherwise, the input video
+frames will be interpolated as necessary to rescale the video to the specified
+target framerate, in a manner as determined by the @option{frame_mixer} option.
+
 @item format
 Set the output format override. If unset (the default), frames will be output
 in the same format as the respective input frames. Otherwise, format conversion
diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index d8128351c8..2bec942358 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -139,6 +139,8 @@ typedef struct LibplaceboContext {
     double var_values[VAR_VARS_NB];
     char *w_expr;
     char *h_expr;
+    char *fps_string;
+    AVRational fps; ///< parsed FPS, or 0/0 for "none"
     char *crop_x_expr, *crop_y_expr;
     char *crop_w_expr, *crop_h_expr;
     char *pos_x_expr, *pos_y_expr;
@@ -166,6 +168,7 @@ typedef struct LibplaceboContext {
     float antiringing;
     int sigmoid;
     int skip_aa;
+    int skip_cache;
     float polar_cutoff;
     int disable_linear;
     int disable_builtin;
@@ -400,7 +403,7 @@ static int update_settings(AVFilterContext *ctx)
         .num_hooks = s->num_hooks,
 
         .skip_anti_aliasing = s->skip_aa,
-        .skip_caching_single_frame = true,
+        .skip_caching_single_frame = s->skip_cache,
         .polar_cutoff = s->polar_cutoff,
         .disable_linear_scaling = s->disable_linear,
         .disable_builtin_scalers = s->disable_builtin,
@@ -465,6 +468,8 @@ static int libplacebo_init(AVFilterContext *avctx)
 
     /* Initialize dynamic filter state */
     s->out_pts = av_fifo_alloc2(1, sizeof(int64_t), AV_FIFO_FLAG_AUTO_GROW);
+    if (strcmp(s->fps_string, "none") != 0)
+        RET(av_parse_video_rate(&s->fps, s->fps_string));
 
     /* Note: s->vulkan etc. are initialized later, when hwctx is available */
     return 0;
@@ -672,6 +677,8 @@ static int output_frame_mix(AVFilterContext *ctx,
     out->pts = pts;
     out->width = outlink->w;
     out->height = outlink->h;
+    if (s->fps.num)
+        out->duration = 1;
 
     if (s->apply_dovi && av_frame_get_side_data(ref, AV_FRAME_DATA_DOVI_METADATA)) {
         /* Output of dovi reshaping is always BT.2020+PQ, so infer the correct
@@ -793,9 +800,11 @@ static int libplacebo_activate(AVFilterContext *ctx)
             .discard     = discard_frame,
         });
 
-        /* Internally queue an output frame for the same PTS */
-        av_assert1(!av_cmp_q(link->time_base, outlink->time_base));
-        av_fifo_write(s->out_pts, &in->pts, 1);
+        if (!s->fps.num) {
+            /* Internally queue an output frame for the same PTS */
+            av_assert1(!av_cmp_q(link->time_base, outlink->time_base));
+            av_fifo_write(s->out_pts, &in->pts, 1);
+        }
     }
 
     if (ret < 0)
@@ -808,7 +817,8 @@ static int libplacebo_activate(AVFilterContext *ctx)
             /* Signal EOF to pl_queue, and enqueue this output frame to
              * make sure we see PL_QUEUE_EOF returned eventually */
             pl_queue_push(s->queue, NULL);
-            av_fifo_write(s->out_pts, &pts, 1);
+            if (!s->fps.num)
+                av_fifo_write(s->out_pts, &pts, 1);
         } else {
             ff_outlink_set_status(outlink, status, pts);
             return 0;
@@ -819,7 +829,9 @@ static int libplacebo_activate(AVFilterContext *ctx)
         struct pl_frame_mix mix;
         enum pl_queue_status ret;
 
-        if (av_fifo_peek(s->out_pts, &pts, 1, 0) < 0) {
+        if (s->fps.num) {
+            pts = outlink->frame_count_out;
+        } else if (av_fifo_peek(s->out_pts, &pts, 1, 0) < 0) {
             ff_inlink_request_frame(inlink);
             return 0;
         }
@@ -835,7 +847,8 @@ static int libplacebo_activate(AVFilterContext *ctx)
             ff_inlink_request_frame(inlink);
             return 0;
         case PL_QUEUE_OK:
-            av_fifo_drain2(s->out_pts, 1);
+            if (!s->fps.num)
+                av_fifo_drain2(s->out_pts, 1);
             return output_frame_mix(ctx, &mix, pts);
         case PL_QUEUE_EOF:
             ff_outlink_set_status(outlink, AVERROR_EOF, pts);
@@ -939,6 +952,7 @@ static int libplacebo_config_output(AVFilterLink *outlink)
     AVVulkanFramesContext *vkfc;
     AVRational scale_sar;
 
+    /* Frame dimensions */
     RET(ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink,
                                  &outlink->w, &outlink->h));
 
@@ -962,6 +976,15 @@ static int libplacebo_config_output(AVFilterLink *outlink)
             outlink->sample_aspect_ratio = scale_sar;
     }
 
+    /* Frame rate */
+    if (s->fps.num) {
+        outlink->frame_rate = s->fps;
+        outlink->time_base = av_inv_q(s->fps);
+        s->skip_cache = av_cmp_q(inlink->frame_rate, s->fps) > 0;
+    } else {
+        s->skip_cache = true;
+    }
+
     /* Static variables */
     s->var_values[VAR_IN_W]     = s->var_values[VAR_IW] = inlink->w;
     s->var_values[VAR_IN_H]     = s->var_values[VAR_IH] = inlink->h;
@@ -1006,6 +1029,7 @@ fail:
 static const AVOption libplacebo_options[] = {
     { "w", "Output video frame width",  OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = STATIC },
     { "h", "Output video frame height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = STATIC },
+    { "fps", "Output video frame rate", OFFSET(fps_string), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = STATIC },
     { "crop_x", "Input video crop x", OFFSET(crop_x_expr), AV_OPT_TYPE_STRING, {.str = "(iw-cw)/2"}, .flags = DYNAMIC },
     { "crop_y", "Input video crop y", OFFSET(crop_y_expr), AV_OPT_TYPE_STRING, {.str = "(ih-ch)/2"}, .flags = DYNAMIC },
     { "crop_w", "Input video crop w", OFFSET(crop_w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = DYNAMIC },
-- 
2.40.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [FFmpeg-devel] [PATCH 6/6] lavfi/vf_libplacebo: add frame_mixer option
  2023-05-10 13:55 [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
                   ` (3 preceding siblings ...)
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 5/6] lavfi/vf_libplacebo: allow fps conversion Niklas Haas
@ 2023-05-10 13:55 ` Niklas Haas
  2023-05-14  9:22 ` [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
  5 siblings, 0 replies; 7+ messages in thread
From: Niklas Haas @ 2023-05-10 13:55 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

From: Niklas Haas <git@haasn.dev>

Fairly straightforward. We just need to modify the scaler handling code
slightly to support looking at a different list of filter presets.
---
 doc/filters.texi            | 31 +++++++++++++++++++++++++++++++
 libavfilter/vf_libplacebo.c | 17 +++++++++++------
 2 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 3e17ba1654..42ce313743 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -16167,6 +16167,31 @@ Cubic BC spline with parameters recommended by Mitchell and Netravali. Very
 little ringing.
 @end table
 
+@item frame_mixer
+Controls the kernel used for mixing frames temporally. The default value is
+@code{none}, which disables frame mixing. For a full list of possible values,
+pass @code{help} to this option. The most important values are:
+@table @samp
+@item none
+Disables frame mixing, giving a result equivalent to "nearest neighbour"
+semantics.
+
+@item oversample
+Oversamples the input video to create a "Smooth Motion"-type effect: if an
+output frame would exactly fall on the transition between two video frames, it
+is blended according to the relative overlap. This is the recommended option
+whenever preserving the original subjective appearance is desired.
+
+@item mitchell_clamp
+Larger filter kernel that smoothly interpolates multiple frames in a manner
+designed to eliminate ringing and other artefacts as much as possible. This is
+the recommended option wherever maximum visual smoothness is desired.
+
+@item linear
+Linear blend/fade between frames. Especially useful for constructing e.g.
+slideshows.
+@end table
+
 @item lut_entries
 Configures the size of scaler LUTs, ranging from @code{1} to @code{256}. The
 default of @code{0} will pick libplacebo's internal default, typically
@@ -16524,6 +16549,12 @@ Rescale input to fit into standard 1080p, with high quality scaling:
 libplacebo=w=1920:h=1080:force_original_aspect_ratio=decrease:normalize_sar=true:upscaler=ewa_lanczos:downscaler=ewa_lanczos
 @end example
 
+@item
+Interpolate low FPS / VFR input to smoothed constant 60 fps output:
+@example
+libplacebo=fps=60:frame_mixer=mitchell_clamp
+@end example
+
 @item
 Convert input to standard sRGB JPEG:
 @example
diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index 2bec942358..9cbc1bac94 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -164,6 +164,7 @@ typedef struct LibplaceboContext {
     struct pl_render_params params;
     char *upscaler;
     char *downscaler;
+    char *frame_mixer;
     int lut_entries;
     float antiringing;
     int sigmoid;
@@ -285,17 +286,19 @@ static int parse_shader(AVFilterContext *avctx, const void *shader, size_t len)
 
 static int find_scaler(AVFilterContext *avctx,
                        const struct pl_filter_config **opt,
-                       const char *name)
+                       const char *name, int frame_mixing)
 {
-    const struct pl_filter_preset *preset;
+    const struct pl_filter_preset *preset, *presets_avail;
+    presets_avail = frame_mixing ? pl_frame_mixers : pl_scale_filters;
+
     if (!strcmp(name, "help")) {
         av_log(avctx, AV_LOG_INFO, "Available scaler presets:\n");
-        for (preset = pl_scale_filters; preset->name; preset++)
+        for (preset = presets_avail; preset->name; preset++)
             av_log(avctx, AV_LOG_INFO, "    %s\n", preset->name);
         return AVERROR_EXIT;
     }
 
-    for (preset = pl_scale_filters; preset->name; preset++) {
+    for (preset = presets_avail; preset->name; preset++) {
         if (!strcmp(name, preset->name)) {
             *opt = preset->filter;
             return 0;
@@ -411,8 +414,9 @@ static int update_settings(AVFilterContext *ctx)
         .disable_fbos = s->disable_fbos,
     );
 
-    RET(find_scaler(ctx, &s->params.upscaler, s->upscaler));
-    RET(find_scaler(ctx, &s->params.downscaler, s->downscaler));
+    RET(find_scaler(ctx, &s->params.upscaler, s->upscaler, 0));
+    RET(find_scaler(ctx, &s->params.downscaler, s->downscaler, 0));
+    RET(find_scaler(ctx, &s->params.frame_mixer, s->frame_mixer, 1));
     return 0;
 
 fail:
@@ -1107,6 +1111,7 @@ static const AVOption libplacebo_options[] = {
 
     { "upscaler", "Upscaler function", OFFSET(upscaler), AV_OPT_TYPE_STRING, {.str = "spline36"}, .flags = DYNAMIC },
     { "downscaler", "Downscaler function", OFFSET(downscaler), AV_OPT_TYPE_STRING, {.str = "mitchell"}, .flags = DYNAMIC },
+    { "frame_mixer", "Frame mixing function", OFFSET(frame_mixer), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = DYNAMIC },
     { "lut_entries", "Number of scaler LUT entries", OFFSET(lut_entries), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 256, DYNAMIC },
     { "antiringing", "Antiringing strength (for non-EWA filters)", OFFSET(antiringing), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 1.0, DYNAMIC },
     { "sigmoid", "Enable sigmoid upscaling", OFFSET(sigmoid), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DYNAMIC },
-- 
2.40.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand
  2023-05-10 13:55 [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
                   ` (4 preceding siblings ...)
  2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 6/6] lavfi/vf_libplacebo: add frame_mixer option Niklas Haas
@ 2023-05-14  9:22 ` Niklas Haas
  5 siblings, 0 replies; 7+ messages in thread
From: Niklas Haas @ 2023-05-14  9:22 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

On Wed, 10 May 2023 15:55:04 +0200 Niklas Haas <ffmpeg@haasn.xyz> wrote:
> From: Niklas Haas <git@haasn.dev>
> 
> Only update this struct when it's expected to change, and cache it
> otherwise. Partially motivated by a desire to make `process_frames`
> smaller.
> ---
>  libavfilter/vf_libplacebo.c | 228 ++++++++++++++++++++----------------
>  1 file changed, 126 insertions(+), 102 deletions(-)
> 
> diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
> index 6fe3e0ea88..2ee9055c9d 100644
> --- a/libavfilter/vf_libplacebo.c
> +++ b/libavfilter/vf_libplacebo.c
> @@ -143,6 +143,7 @@ typedef struct LibplaceboContext {
>      int color_trc;
>  
>      /* pl_render_params */
> +    struct pl_render_params params;
>      char *upscaler;
>      char *downscaler;
>      int lut_entries;
> @@ -156,6 +157,7 @@ typedef struct LibplaceboContext {
>      int disable_fbos;
>  
>      /* pl_deband_params */
> +    struct pl_deband_params deband_params;
>      int deband;
>      int deband_iterations;
>      float deband_threshold;
> @@ -163,6 +165,7 @@ typedef struct LibplaceboContext {
>      float deband_grain;
>  
>      /* pl_color_adjustment */
> +    struct pl_color_adjustment color_adjustment;
>      float brightness;
>      float contrast;
>      float saturation;
> @@ -170,6 +173,7 @@ typedef struct LibplaceboContext {
>      float gamma;
>  
>      /* pl_peak_detect_params */
> +    struct pl_peak_detect_params peak_detect_params;
>      int peakdetect;
>      float smoothing;
>      float min_peak;
> @@ -178,6 +182,7 @@ typedef struct LibplaceboContext {
>      float overshoot;
>  
>      /* pl_color_map_params */
> +    struct pl_color_map_params color_map_params;
>      int intent;
>      int gamut_mode;
>      int tonemapping;
> @@ -196,12 +201,14 @@ typedef struct LibplaceboContext {
>      int force_icc_lut;
>  #endif
>  
> -     /* pl_dither_params */
> +    /* pl_dither_params */
> +    struct pl_dither_params dither_params;
>      int dithering;
>      int dither_lut_size;
>      int dither_temporal;
>  
>      /* pl_cone_params */
> +    struct pl_cone_params cone_params;
>      int cones;
>      float cone_str;
>  
> @@ -280,18 +287,112 @@ static int find_scaler(AVFilterContext *avctx,
>      return AVERROR(EINVAL);
>  }
>  
> -static int parse_fillcolor(AVFilterContext *avctx,
> -                           struct pl_render_params *params,
> -                           const char *color_str)
> +static int update_settings(AVFilterContext *ctx)
>  {
>      int err = 0;
> +    LibplaceboContext *s = ctx->priv;
> +    enum pl_tone_map_mode tonemapping_mode = s->tonemapping_mode;
> +    enum pl_gamut_mode gamut_mode = s->gamut_mode;
>      uint8_t color_rgba[4];
>  
> -    RET(av_parse_color(color_rgba, color_str, -1, avctx));
> -    params->background_color[0] = (float) color_rgba[0] / UINT8_MAX;
> -    params->background_color[1] = (float) color_rgba[1] / UINT8_MAX;
> -    params->background_color[2] = (float) color_rgba[2] / UINT8_MAX;
> -    params->background_transparency = 1.0f - (float) color_rgba[3] / UINT8_MAX;
> +    RET(av_parse_color(color_rgba, s->fillcolor, -1, s));
> +
> +#if FF_API_LIBPLACEBO_OPTS
> +    /* backwards compatibility with older API */
> +    if (!tonemapping_mode && (s->desat_str >= 0.0f || s->desat_exp >= 0.0f)) {
> +        float str = s->desat_str < 0.0f ? 0.9f : s->desat_str;
> +        float exp = s->desat_exp < 0.0f ? 0.2f : s->desat_exp;
> +        if (str >= 0.9f && exp <= 0.1f) {
> +            tonemapping_mode = PL_TONE_MAP_RGB;
> +        } else if (str > 0.1f) {
> +            tonemapping_mode = PL_TONE_MAP_HYBRID;
> +        } else {
> +            tonemapping_mode = PL_TONE_MAP_LUMA;
> +        }
> +    }
> +
> +    if (s->gamut_warning)
> +        gamut_mode = PL_GAMUT_WARN;
> +    if (s->gamut_clipping)
> +        gamut_mode = PL_GAMUT_DESATURATE;
> +#endif
> +
> +    s->deband_params = *pl_deband_params(
> +        .iterations = s->deband_iterations,
> +        .threshold = s->deband_threshold,
> +        .radius = s->deband_radius,
> +        .grain = s->deband_grain,
> +    );
> +
> +    s->color_adjustment = (struct pl_color_adjustment) {
> +        .brightness = s->brightness,
> +        .contrast = s->contrast,
> +        .saturation = s->saturation,
> +        .hue = s->hue,
> +        .gamma = s->gamma,
> +    };
> +
> +    s->peak_detect_params = *pl_peak_detect_params(
> +        .smoothing_period = s->smoothing,
> +        .minimum_peak = s->min_peak,
> +        .scene_threshold_low = s->scene_low,
> +        .scene_threshold_high = s->scene_high,
> +        .overshoot_margin = s->overshoot,
> +    );
> +
> +    s->color_map_params = *pl_color_map_params(
> +        .intent = s->intent,
> +        .gamut_mode = gamut_mode,
> +        .tone_mapping_function = tonemapping_funcs[s->tonemapping],
> +        .tone_mapping_param = s->tonemapping_param,
> +        .tone_mapping_mode = tonemapping_mode,
> +        .inverse_tone_mapping = s->inverse_tonemapping,
> +        .tone_mapping_crosstalk = s->crosstalk,
> +        .lut_size = s->tonemapping_lut_size,
> +    );
> +
> +    s->dither_params = *pl_dither_params(
> +        .method = s->dithering,
> +        .lut_size = s->dither_lut_size,
> +        .temporal = s->dither_temporal,
> +    );
> +
> +    s->cone_params = *pl_cone_params(
> +        .cones = s->cones,
> +        .strength = s->cone_str,
> +    );
> +
> +    s->params = *pl_render_params(
> +        .lut_entries = s->lut_entries,
> +        .antiringing_strength = s->antiringing,
> +        .background_transparency = 1.0f - (float) color_rgba[3] / UINT8_MAX,
> +        .background_color = {
> +            (float) color_rgba[0] / UINT8_MAX,
> +            (float) color_rgba[1] / UINT8_MAX,
> +            (float) color_rgba[2] / UINT8_MAX,
> +        },
> +
> +        .deband_params = s->deband ? &s->deband_params : NULL,
> +        .sigmoid_params = s->sigmoid ? &pl_sigmoid_default_params : NULL,
> +        .color_adjustment = &s->color_adjustment,
> +        .peak_detect_params = s->peakdetect ? &s->peak_detect_params : NULL,
> +        .color_map_params = &s->color_map_params,
> +        .dither_params = s->dithering >= 0 ? &s->dither_params : NULL,
> +        .cone_params = s->cones ? &s->cone_params : NULL,
> +
> +        .hooks = s->hooks,
> +        .num_hooks = s->num_hooks,
> +
> +        .skip_anti_aliasing = s->skip_aa,
> +        .polar_cutoff = s->polar_cutoff,
> +        .disable_linear_scaling = s->disable_linear,
> +        .disable_builtin_scalers = s->disable_builtin,
> +        .force_dither = s->force_dither,
> +        .disable_fbos = s->disable_fbos,
> +    );
> +
> +    RET(find_scaler(ctx, &s->params.upscaler, s->upscaler));
> +    RET(find_scaler(ctx, &s->params.downscaler, s->downscaler));
>      return 0;
>  
>  fail:
> @@ -327,6 +428,7 @@ static int libplacebo_init(AVFilterContext *avctx)
>          s->out_format = AV_PIX_FMT_NONE;
>      }
>  
> +    RET(update_settings(avctx));
>      RET(av_expr_parse(&s->crop_x_pexpr, s->crop_x_expr, var_names,
>                        NULL, NULL, NULL, NULL, 0, s));
>      RET(av_expr_parse(&s->crop_y_pexpr, s->crop_y_expr, var_names,
> @@ -449,14 +551,24 @@ static void libplacebo_uninit(AVFilterContext *avctx)
>      av_expr_free(s->pos_h_pexpr);
>  }
>  
> +static int libplacebo_process_command(AVFilterContext *ctx, const char *cmd,
> +                                      const char *arg, char *res, int res_len,
> +                                      int flags)
> +{
> +    int err = 0;
> +    RET(ff_filter_process_command(ctx, cmd, arg, res, res_len, flags));
> +    RET(update_settings(ctx));
> +    return 0;
> +
> +fail:
> +    return err;
> +}
> +
>  static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
>  {
>      int err = 0, ok;
>      LibplaceboContext *s = avctx->priv;
> -    struct pl_render_params params;
> -    enum pl_tone_map_mode tonemapping_mode = s->tonemapping_mode;
>      const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->format);
> -    enum pl_gamut_mode gamut_mode = s->gamut_mode;
>      struct pl_frame image, target;
>      ok = pl_map_avframe_ex(s->gpu, &image, pl_avframe_params(
>          .frame    = in,
> @@ -505,95 +617,7 @@ static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
>          pl_rect2df_aspect_set(&target.crop, aspect, s->pad_crop_ratio);
>      }
>  
> -#if FF_API_LIBPLACEBO_OPTS
> -    /* backwards compatibility with older API */
> -    if (!tonemapping_mode && (s->desat_str >= 0.0f || s->desat_exp >= 0.0f)) {
> -        float str = s->desat_str < 0.0f ? 0.9f : s->desat_str;
> -        float exp = s->desat_exp < 0.0f ? 0.2f : s->desat_exp;
> -        if (str >= 0.9f && exp <= 0.1f) {
> -            tonemapping_mode = PL_TONE_MAP_RGB;
> -        } else if (str > 0.1f) {
> -            tonemapping_mode = PL_TONE_MAP_HYBRID;
> -        } else {
> -            tonemapping_mode = PL_TONE_MAP_LUMA;
> -        }
> -    }
> -
> -    if (s->gamut_warning)
> -        gamut_mode = PL_GAMUT_WARN;
> -    if (s->gamut_clipping)
> -        gamut_mode = PL_GAMUT_DESATURATE;
> -#endif
> -
> -    /* Update render params */
> -    params = (struct pl_render_params) {
> -        PL_RENDER_DEFAULTS
> -        .lut_entries = s->lut_entries,
> -        .antiringing_strength = s->antiringing,
> -
> -        .deband_params = !s->deband ? NULL : pl_deband_params(
> -            .iterations = s->deband_iterations,
> -            .threshold = s->deband_threshold,
> -            .radius = s->deband_radius,
> -            .grain = s->deband_grain,
> -        ),
> -
> -        .sigmoid_params = s->sigmoid ? &pl_sigmoid_default_params : NULL,
> -
> -        .color_adjustment = &(struct pl_color_adjustment) {
> -            .brightness = s->brightness,
> -            .contrast = s->contrast,
> -            .saturation = s->saturation,
> -            .hue = s->hue,
> -            .gamma = s->gamma,
> -        },
> -
> -        .peak_detect_params = !s->peakdetect ? NULL : pl_peak_detect_params(
> -            .smoothing_period = s->smoothing,
> -            .minimum_peak = s->min_peak,
> -            .scene_threshold_low = s->scene_low,
> -            .scene_threshold_high = s->scene_high,
> -            .overshoot_margin = s->overshoot,
> -        ),
> -
> -        .color_map_params = pl_color_map_params(
> -            .intent = s->intent,
> -            .gamut_mode = gamut_mode,
> -            .tone_mapping_function = tonemapping_funcs[s->tonemapping],
> -            .tone_mapping_param = s->tonemapping_param,
> -            .tone_mapping_mode = tonemapping_mode,
> -            .inverse_tone_mapping = s->inverse_tonemapping,
> -            .tone_mapping_crosstalk = s->crosstalk,
> -            .lut_size = s->tonemapping_lut_size,
> -        ),
> -
> -        .dither_params = s->dithering < 0 ? NULL : pl_dither_params(
> -            .method = s->dithering,
> -            .lut_size = s->dither_lut_size,
> -            .temporal = s->dither_temporal,
> -        ),
> -
> -        .cone_params = !s->cones ? NULL : pl_cone_params(
> -            .cones = s->cones,
> -            .strength = s->cone_str,
> -        ),
> -
> -        .hooks = s->hooks,
> -        .num_hooks = s->num_hooks,
> -
> -        .skip_anti_aliasing = s->skip_aa,
> -        .polar_cutoff = s->polar_cutoff,
> -        .disable_linear_scaling = s->disable_linear,
> -        .disable_builtin_scalers = s->disable_builtin,
> -        .force_dither = s->force_dither,
> -        .disable_fbos = s->disable_fbos,
> -    };
> -
> -    RET(find_scaler(avctx, &params.upscaler, s->upscaler));
> -    RET(find_scaler(avctx, &params.downscaler, s->downscaler));
> -    RET(parse_fillcolor(avctx, &params, s->fillcolor));
> -
> -    pl_render_image(s->renderer, &image, &target, &params);
> +    pl_render_image(s->renderer, &image, &target, &s->params);
>      pl_unmap_avframe(s->gpu, &image);
>  
>      if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
> @@ -1049,7 +1073,7 @@ const AVFilter ff_vf_libplacebo = {
>      .priv_size      = sizeof(LibplaceboContext),
>      .init           = &libplacebo_init,
>      .uninit         = &libplacebo_uninit,
> -    .process_command = &ff_filter_process_command,
> +    .process_command = &libplacebo_process_command,
>      FILTER_INPUTS(libplacebo_inputs),
>      FILTER_OUTPUTS(libplacebo_outputs),
>      FILTER_QUERY_FUNC(libplacebo_query_format),
> -- 
> 2.40.1
> 

Merged as 482d8137..30c71ef.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-05-14  9:22 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-10 13:55 [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas
2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 2/6] lavfi/vf_libplacebo: split and refactor logic Niklas Haas
2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 3/6] lavfi/vf_libplacebo: switch to pl_queue-based design Niklas Haas
2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 4/6] lavfi/vf_libplacebo: switch to activate() Niklas Haas
2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 5/6] lavfi/vf_libplacebo: allow fps conversion Niklas Haas
2023-05-10 13:55 ` [FFmpeg-devel] [PATCH 6/6] lavfi/vf_libplacebo: add frame_mixer option Niklas Haas
2023-05-14  9:22 ` [FFmpeg-devel] [PATCH 1/6] lavfi/vf_libplacebo: update render params on demand Niklas Haas

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git