* [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, ¶ms.upscaler, s->upscaler));
- RET(find_scaler(avctx, ¶ms.downscaler, s->downscaler));
- RET(parse_fillcolor(avctx, ¶ms, s->fillcolor));
-
- pl_render_image(s->renderer, &image, &target, ¶ms);
+ 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, ¶ms.upscaler, s->upscaler));
> - RET(find_scaler(avctx, ¶ms.downscaler, s->downscaler));
> - RET(parse_fillcolor(avctx, ¶ms, s->fillcolor));
> -
> - pl_render_image(s->renderer, &image, &target, ¶ms);
> + 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