2021-04-09 07:14:54 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2021 Niklas Haas
|
|
|
|
*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
|
|
|
* mpv is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* mpv is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2022-03-06 21:59:05 +00:00
|
|
|
#include <pthread.h>
|
2023-06-30 03:46:51 +00:00
|
|
|
#include <libplacebo/colorspace.h>
|
2021-04-09 07:14:54 +00:00
|
|
|
#include <libplacebo/renderer.h>
|
|
|
|
#include <libplacebo/shaders/lut.h>
|
2023-08-18 14:40:44 +00:00
|
|
|
#include <libplacebo/shaders/icc.h>
|
2022-04-01 21:43:47 +00:00
|
|
|
#include <libplacebo/utils/libav.h>
|
2021-04-09 07:14:54 +00:00
|
|
|
#include <libplacebo/utils/frame_queue.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "common/common.h"
|
|
|
|
#include "options/m_config.h"
|
|
|
|
#include "options/path.h"
|
|
|
|
#include "osdep/io.h"
|
|
|
|
#include "stream/stream.h"
|
|
|
|
#include "video/fmt-conversion.h"
|
2022-02-25 21:07:56 +00:00
|
|
|
#include "video/mp_image.h"
|
|
|
|
#include "video/out/placebo/ra_pl.h"
|
2021-04-09 07:14:54 +00:00
|
|
|
#include "placebo/utils.h"
|
|
|
|
#include "gpu/context.h"
|
2022-02-25 21:07:56 +00:00
|
|
|
#include "gpu/hwdec.h"
|
2021-04-09 07:14:54 +00:00
|
|
|
#include "gpu/video.h"
|
|
|
|
#include "gpu/video_shaders.h"
|
|
|
|
#include "sub/osd.h"
|
2021-11-20 19:59:37 +00:00
|
|
|
#include "gpu_next/context.h"
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2022-02-25 21:07:56 +00:00
|
|
|
#if HAVE_GL && defined(PL_HAVE_OPENGL)
|
|
|
|
#include <libplacebo/opengl.h>
|
|
|
|
#include "video/out/opengl/ra_gl.h"
|
|
|
|
#endif
|
|
|
|
|
2022-02-28 13:18:26 +00:00
|
|
|
#if HAVE_D3D11 && defined(PL_HAVE_D3D11)
|
|
|
|
#include <libplacebo/d3d11.h>
|
|
|
|
#include "video/out/d3d11/ra_d3d11.h"
|
|
|
|
#include "osdep/windows_utils.h"
|
|
|
|
#endif
|
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
#if PL_API_VER >= 309
|
|
|
|
#include <libplacebo/options.h>
|
|
|
|
#else
|
|
|
|
typedef struct pl_options_t {
|
|
|
|
// Backwards compatibility shim of this struct
|
|
|
|
struct pl_render_params params;
|
|
|
|
struct pl_deband_params deband_params;
|
|
|
|
struct pl_sigmoid_params sigmoid_params;
|
|
|
|
struct pl_color_adjustment color_adjustment;
|
|
|
|
struct pl_peak_detect_params peak_detect_params;
|
|
|
|
struct pl_color_map_params color_map_params;
|
|
|
|
struct pl_dither_params dither_params;
|
|
|
|
} *pl_options;
|
2023-08-27 11:47:34 +00:00
|
|
|
|
|
|
|
static inline pl_options pl_options_alloc(pl_log log)
|
|
|
|
{
|
|
|
|
struct pl_options_t *opts = talloc_ptrtype(NULL, opts);
|
|
|
|
opts->params = pl_render_default_params;
|
|
|
|
opts->deband_params = pl_deband_default_params;
|
|
|
|
opts->sigmoid_params = pl_sigmoid_default_params;
|
|
|
|
opts->color_adjustment = pl_color_adjustment_neutral;
|
|
|
|
opts->peak_detect_params = pl_peak_detect_default_params;
|
|
|
|
opts->color_map_params = pl_color_map_default_params;
|
|
|
|
opts->dither_params = pl_dither_default_params;
|
|
|
|
// Redirect always-enabled params structs to shim
|
|
|
|
opts->params.color_adjustment = &opts->color_adjustment;
|
|
|
|
opts->params.color_map_params = &opts->color_map_params;
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define pl_options_free TA_FREEP
|
2023-08-18 13:36:59 +00:00
|
|
|
#endif
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
struct osd_entry {
|
|
|
|
pl_tex tex;
|
|
|
|
struct pl_overlay_part *parts;
|
|
|
|
int num_parts;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct osd_state {
|
|
|
|
struct osd_entry entries[MAX_OSD_PARTS];
|
|
|
|
struct pl_overlay overlays[MAX_OSD_PARTS];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct scaler_params {
|
|
|
|
struct pl_filter_config config;
|
2023-08-06 16:09:04 +00:00
|
|
|
#if PL_API_VER < 303
|
2021-04-09 07:14:54 +00:00
|
|
|
struct pl_filter_function kernel;
|
|
|
|
struct pl_filter_function window;
|
2023-08-06 16:09:04 +00:00
|
|
|
#endif
|
2021-04-09 07:14:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct user_hook {
|
|
|
|
char *path;
|
|
|
|
const struct pl_hook *hook;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct user_lut {
|
|
|
|
char *opt;
|
|
|
|
char *path;
|
|
|
|
int type;
|
|
|
|
struct pl_custom_lut *lut;
|
|
|
|
};
|
|
|
|
|
2023-04-29 20:08:50 +00:00
|
|
|
struct frame_info {
|
|
|
|
int count;
|
|
|
|
struct pl_dispatch_info info[VO_PASS_PERF_MAX];
|
|
|
|
};
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
struct priv {
|
|
|
|
struct mp_log *log;
|
|
|
|
struct mpv_global *global;
|
|
|
|
struct ra_ctx *ra_ctx;
|
2021-11-20 19:59:37 +00:00
|
|
|
struct gpu_ctx *context;
|
2022-02-25 21:07:56 +00:00
|
|
|
struct ra_hwdec_ctx hwdec_ctx;
|
2022-04-09 21:58:59 +00:00
|
|
|
struct ra_hwdec_mapper *hwdec_mapper;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2022-03-06 21:59:05 +00:00
|
|
|
// Allocated DR buffers
|
|
|
|
pthread_mutex_t dr_lock;
|
|
|
|
pl_buf *dr_buffers;
|
|
|
|
int num_dr_buffers;
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
pl_log pllog;
|
|
|
|
pl_gpu gpu;
|
|
|
|
pl_renderer rr;
|
|
|
|
pl_queue queue;
|
|
|
|
pl_swapchain sw;
|
|
|
|
pl_fmt osd_fmt[SUBBITMAP_COUNT];
|
|
|
|
pl_tex *sub_tex;
|
|
|
|
int num_sub_tex;
|
|
|
|
|
2023-09-08 14:45:21 +00:00
|
|
|
#if PL_API_VER >= 320
|
|
|
|
pl_cache cache;
|
|
|
|
#endif
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
struct mp_rect src, dst;
|
|
|
|
struct mp_osd_res osd_res;
|
|
|
|
struct osd_state osd_state;
|
|
|
|
|
|
|
|
uint64_t last_id;
|
2022-02-15 11:47:53 +00:00
|
|
|
uint64_t osd_sync;
|
2021-04-09 07:14:54 +00:00
|
|
|
double last_pts;
|
|
|
|
bool is_interpolated;
|
|
|
|
bool want_reset;
|
2023-07-29 17:09:45 +00:00
|
|
|
bool frame_pending;
|
2023-09-28 18:07:42 +00:00
|
|
|
bool paused;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
pl_options pars;
|
2021-04-09 07:14:54 +00:00
|
|
|
struct m_config_cache *opts_cache;
|
|
|
|
struct mp_csp_equalizer_state *video_eq;
|
|
|
|
struct scaler_params scalers[SCALER_COUNT];
|
|
|
|
const struct pl_hook **hooks; // storage for `params.hooks`
|
2022-09-25 18:31:08 +00:00
|
|
|
enum mp_csp_levels output_levels;
|
2023-08-18 15:25:52 +00:00
|
|
|
char **raw_opts;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-09-14 18:26:06 +00:00
|
|
|
struct pl_icc_params icc_params;
|
2021-04-09 07:14:54 +00:00
|
|
|
char *icc_path;
|
2023-09-14 18:26:06 +00:00
|
|
|
#if PL_API_VER >= 327
|
|
|
|
pl_icc_object icc_profile;
|
|
|
|
#else
|
|
|
|
struct pl_icc_profile icc_profile;
|
|
|
|
#endif
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
struct user_lut image_lut;
|
|
|
|
struct user_lut target_lut;
|
|
|
|
struct user_lut lut;
|
|
|
|
|
|
|
|
// Cached shaders, preserved across options updates
|
|
|
|
struct user_hook *user_hooks;
|
|
|
|
int num_user_hooks;
|
|
|
|
|
|
|
|
// Performance data of last frame
|
2023-04-29 20:08:50 +00:00
|
|
|
struct frame_info perf_fresh;
|
|
|
|
struct frame_info perf_redraw;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-02-20 03:32:50 +00:00
|
|
|
bool delayed_peak;
|
|
|
|
bool inter_preserve;
|
|
|
|
bool target_hint;
|
2023-06-19 11:06:01 +00:00
|
|
|
|
|
|
|
float corner_rounding;
|
2023-08-29 08:23:26 +00:00
|
|
|
|
|
|
|
struct pl_hdr_metadata last_hdr_metadata;
|
2021-04-09 07:14:54 +00:00
|
|
|
};
|
|
|
|
|
2022-02-27 20:15:17 +00:00
|
|
|
static void update_render_options(struct vo *vo);
|
2021-04-09 07:14:54 +00:00
|
|
|
static void update_lut(struct priv *p, struct user_lut *lut);
|
|
|
|
|
2022-03-06 21:59:05 +00:00
|
|
|
static pl_buf get_dr_buf(struct priv *p, const uint8_t *ptr)
|
2021-04-09 07:14:54 +00:00
|
|
|
{
|
2022-03-06 21:59:05 +00:00
|
|
|
pthread_mutex_lock(&p->dr_lock);
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2022-03-06 21:59:05 +00:00
|
|
|
for (int i = 0; i < p->num_dr_buffers; i++) {
|
2022-03-08 12:49:03 +00:00
|
|
|
pl_buf buf = p->dr_buffers[i];
|
|
|
|
if (ptr >= buf->data && ptr < buf->data + buf->params.size) {
|
|
|
|
pthread_mutex_unlock(&p->dr_lock);
|
|
|
|
return buf;
|
|
|
|
}
|
2022-03-06 21:59:05 +00:00
|
|
|
}
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2022-03-06 21:59:05 +00:00
|
|
|
pthread_mutex_unlock(&p->dr_lock);
|
2022-03-08 12:49:03 +00:00
|
|
|
return NULL;
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void free_dr_buf(void *opaque, uint8_t *data)
|
|
|
|
{
|
2022-03-06 21:59:05 +00:00
|
|
|
struct priv *p = opaque;
|
|
|
|
pthread_mutex_lock(&p->dr_lock);
|
|
|
|
|
|
|
|
for (int i = 0; i < p->num_dr_buffers; i++) {
|
|
|
|
if (p->dr_buffers[i]->data == data) {
|
|
|
|
pl_buf_destroy(p->gpu, &p->dr_buffers[i]);
|
|
|
|
MP_TARRAY_REMOVE_AT(p->dr_buffers, p->num_dr_buffers, i);
|
|
|
|
pthread_mutex_unlock(&p->dr_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MP_ASSERT_UNREACHABLE();
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h,
|
2023-01-21 14:21:49 +00:00
|
|
|
int stride_align, int flags)
|
2021-04-09 07:14:54 +00:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
pl_gpu gpu = p->gpu;
|
|
|
|
if (!gpu->limits.thread_safe || !gpu->limits.max_mapped_size)
|
|
|
|
return NULL;
|
|
|
|
|
2023-01-21 14:48:57 +00:00
|
|
|
if ((flags & VO_DR_FLAG_HOST_CACHED) && !gpu->limits.host_cached)
|
|
|
|
return NULL;
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
int size = mp_image_get_alloc_size(imgfmt, w, h, stride_align);
|
|
|
|
if (size < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pl_buf buf = pl_buf_create(gpu, &(struct pl_buf_params) {
|
|
|
|
.memory_type = PL_BUF_MEM_HOST,
|
|
|
|
.host_mapped = true,
|
2022-03-14 14:43:46 +00:00
|
|
|
.size = size + stride_align,
|
2021-04-09 07:14:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
struct mp_image *mpi = mp_image_from_buffer(imgfmt, w, h, stride_align,
|
2022-03-14 14:43:46 +00:00
|
|
|
buf->data, buf->params.size,
|
|
|
|
p, free_dr_buf);
|
2021-04-09 07:14:54 +00:00
|
|
|
if (!mpi) {
|
|
|
|
pl_buf_destroy(gpu, &buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-03-06 21:59:05 +00:00
|
|
|
pthread_mutex_lock(&p->dr_lock);
|
|
|
|
MP_TARRAY_APPEND(p, p->dr_buffers, p->num_dr_buffers, buf);
|
|
|
|
pthread_mutex_unlock(&p->dr_lock);
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
return mpi;
|
|
|
|
}
|
|
|
|
|
2023-09-16 18:30:47 +00:00
|
|
|
static struct pl_color_space get_mpi_csp(struct vo *vo, struct mp_image *mpi);
|
|
|
|
|
|
|
|
static void update_overlays(struct vo *vo, struct mp_osd_res res,
|
vo_gpu_next: respect --blend-subtitles=<yes/no>
This brings vo_gpu_next's behavior more in line with vo_gpu. Currently,
the status quo was that `vo_gpu_next` generated subtitles with the full
resolution of the dst crop, even though practical limitations of
libplacebo meant that such subtitles inevitably got cut off at the video
boundaries. This was actually a worse status quo than `vo_gpu`, which
simply constrained subtitles to the video dimensions to prevent these
issues.
With this change, the behavior is the same as `vo_gpu`, which has the
exact same problem w.r.t interpolation. Users are back to choosing
between --blend-subtitles=yes and not having subtitles in margins,
and using --blend-subtitles=no and not having subtitle interpolation.
There are different pros and cons to each approach, and I still
ultimately plan on allowing libplacebo to interpolate subtitles even in
the black bars, to solve this issue once and for all. But for now, let's
not let perfect be the enemy of good.
No attempt is made to implement `--blend-subs=video`, which I consider
deprecated.
Fixes: https://github.com/mpv-player/mpv/issues/10272
2022-09-27 23:18:54 +00:00
|
|
|
int flags, enum pl_overlay_coords coords,
|
2023-09-16 18:30:47 +00:00
|
|
|
struct osd_state *state, struct pl_frame *frame,
|
|
|
|
struct mp_image *src)
|
2021-04-09 07:14:54 +00:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
static const bool subfmt_all[SUBBITMAP_COUNT] = {
|
|
|
|
[SUBBITMAP_LIBASS] = true,
|
2022-01-11 20:03:27 +00:00
|
|
|
[SUBBITMAP_BGRA] = true,
|
2021-04-09 07:14:54 +00:00
|
|
|
};
|
|
|
|
|
2023-09-16 18:30:47 +00:00
|
|
|
double pts = src ? src->pts : 0;
|
2021-04-09 07:14:54 +00:00
|
|
|
struct sub_bitmap_list *subs = osd_render(vo->osd, res, pts, flags, subfmt_all);
|
2023-09-28 18:07:42 +00:00
|
|
|
if (subs->num_items)
|
|
|
|
p->osd_sync++;
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
frame->overlays = state->overlays;
|
2022-02-15 11:47:53 +00:00
|
|
|
frame->num_overlays = 0;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
for (int n = 0; n < subs->num_items; n++) {
|
|
|
|
const struct sub_bitmaps *item = subs->items[n];
|
|
|
|
if (!item->num_parts || !item->packed)
|
|
|
|
continue;
|
|
|
|
struct osd_entry *entry = &state->entries[item->render_index];
|
|
|
|
pl_fmt tex_fmt = p->osd_fmt[item->format];
|
|
|
|
if (!entry->tex)
|
|
|
|
MP_TARRAY_POP(p->sub_tex, p->num_sub_tex, &entry->tex);
|
|
|
|
bool ok = pl_tex_recreate(p->gpu, &entry->tex, &(struct pl_tex_params) {
|
|
|
|
.format = tex_fmt,
|
|
|
|
.w = MPMAX(item->packed_w, entry->tex ? entry->tex->params.w : 0),
|
|
|
|
.h = MPMAX(item->packed_h, entry->tex ? entry->tex->params.h : 0),
|
|
|
|
.host_writable = true,
|
|
|
|
.sampleable = true,
|
|
|
|
});
|
|
|
|
if (!ok) {
|
|
|
|
MP_ERR(vo, "Failed recreating OSD texture!\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ok = pl_tex_upload(p->gpu, &(struct pl_tex_transfer_params) {
|
|
|
|
.tex = entry->tex,
|
|
|
|
.rc = { .x1 = item->packed_w, .y1 = item->packed_h, },
|
2023-03-13 18:55:04 +00:00
|
|
|
.row_pitch = item->packed->stride[0],
|
2021-04-09 07:14:54 +00:00
|
|
|
.ptr = item->packed->planes[0],
|
|
|
|
});
|
|
|
|
if (!ok) {
|
|
|
|
MP_ERR(vo, "Failed uploading OSD texture!\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->num_parts = 0;
|
|
|
|
for (int i = 0; i < item->num_parts; i++) {
|
|
|
|
const struct sub_bitmap *b = &item->parts[i];
|
|
|
|
uint32_t c = b->libass.color;
|
2021-11-20 20:27:46 +00:00
|
|
|
struct pl_overlay_part part = {
|
2021-04-09 07:14:54 +00:00
|
|
|
.src = { b->src_x, b->src_y, b->src_x + b->w, b->src_y + b->h },
|
|
|
|
.dst = { b->x, b->y, b->x + b->dw, b->y + b->dh },
|
|
|
|
.color = {
|
|
|
|
(c >> 24) / 255.0,
|
|
|
|
((c >> 16) & 0xFF) / 255.0,
|
|
|
|
((c >> 8) & 0xFF) / 255.0,
|
|
|
|
1.0 - (c & 0xFF) / 255.0,
|
|
|
|
}
|
2021-11-20 20:27:46 +00:00
|
|
|
};
|
|
|
|
MP_TARRAY_APPEND(p, entry->parts, entry->num_parts, part);
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct pl_overlay *ol = &state->overlays[frame->num_overlays++];
|
|
|
|
*ol = (struct pl_overlay) {
|
|
|
|
.tex = entry->tex,
|
|
|
|
.parts = entry->parts,
|
|
|
|
.num_parts = entry->num_parts,
|
2023-09-16 18:30:47 +00:00
|
|
|
.color = {
|
|
|
|
.primaries = PL_COLOR_PRIM_BT_709,
|
|
|
|
.transfer = PL_COLOR_TRC_SRGB,
|
|
|
|
},
|
vo_gpu_next: respect --blend-subtitles=<yes/no>
This brings vo_gpu_next's behavior more in line with vo_gpu. Currently,
the status quo was that `vo_gpu_next` generated subtitles with the full
resolution of the dst crop, even though practical limitations of
libplacebo meant that such subtitles inevitably got cut off at the video
boundaries. This was actually a worse status quo than `vo_gpu`, which
simply constrained subtitles to the video dimensions to prevent these
issues.
With this change, the behavior is the same as `vo_gpu`, which has the
exact same problem w.r.t interpolation. Users are back to choosing
between --blend-subtitles=yes and not having subtitles in margins,
and using --blend-subtitles=no and not having subtitle interpolation.
There are different pros and cons to each approach, and I still
ultimately plan on allowing libplacebo to interpolate subtitles even in
the black bars, to solve this issue once and for all. But for now, let's
not let perfect be the enemy of good.
No attempt is made to implement `--blend-subs=video`, which I consider
deprecated.
Fixes: https://github.com/mpv-player/mpv/issues/10272
2022-09-27 23:18:54 +00:00
|
|
|
.coords = coords,
|
2021-04-09 07:14:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
switch (item->format) {
|
2022-01-11 20:03:27 +00:00
|
|
|
case SUBBITMAP_BGRA:
|
2021-04-09 07:14:54 +00:00
|
|
|
ol->mode = PL_OVERLAY_NORMAL;
|
|
|
|
ol->repr.alpha = PL_ALPHA_PREMULTIPLIED;
|
2023-09-16 18:30:47 +00:00
|
|
|
// Infer bitmap colorspace from source
|
|
|
|
if (src) {
|
|
|
|
ol->color = get_mpi_csp(vo, src);
|
|
|
|
// Seems like HDR subtitles are targeting SDR white
|
|
|
|
if (pl_color_transfer_is_hdr(ol->color.transfer)) {
|
|
|
|
ol->color.hdr = (struct pl_hdr_metadata) {
|
|
|
|
.max_luma = PL_COLOR_SDR_WHITE,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2021-04-09 07:14:54 +00:00
|
|
|
break;
|
|
|
|
case SUBBITMAP_LIBASS:
|
|
|
|
ol->mode = PL_OVERLAY_MONOCHROME;
|
|
|
|
ol->repr.alpha = PL_ALPHA_INDEPENDENT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
talloc_free(subs);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct frame_priv {
|
|
|
|
struct vo *vo;
|
|
|
|
struct osd_state subs;
|
2022-02-15 11:47:53 +00:00
|
|
|
uint64_t osd_sync;
|
2022-04-09 21:58:59 +00:00
|
|
|
struct ra_hwdec *hwdec;
|
2021-04-09 07:14:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int plane_data_from_imgfmt(struct pl_plane_data out_data[4],
|
|
|
|
struct pl_bit_encoding *out_bits,
|
|
|
|
enum mp_imgfmt imgfmt)
|
|
|
|
{
|
|
|
|
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
|
|
|
|
if (!desc.num_planes || !(desc.flags & MP_IMGFLAG_HAS_COMPS))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (desc.flags & MP_IMGFLAG_HWACCEL)
|
|
|
|
return 0; // HW-accelerated frames need to be mapped differently
|
|
|
|
|
|
|
|
if (!(desc.flags & MP_IMGFLAG_NE))
|
|
|
|
return 0; // GPU endianness follows the host's
|
|
|
|
|
|
|
|
if (desc.flags & MP_IMGFLAG_PAL)
|
|
|
|
return 0; // Palette formats (currently) not supported in libplacebo
|
|
|
|
|
|
|
|
if ((desc.flags & MP_IMGFLAG_TYPE_FLOAT) && (desc.flags & MP_IMGFLAG_YUV))
|
|
|
|
return 0; // Floating-point YUV (currently) unsupported
|
|
|
|
|
2022-11-19 13:12:24 +00:00
|
|
|
bool has_bits = false;
|
2021-04-09 07:14:54 +00:00
|
|
|
bool any_padded = false;
|
2022-11-19 13:12:24 +00:00
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
for (int p = 0; p < desc.num_planes; p++) {
|
|
|
|
struct pl_plane_data *data = &out_data[p];
|
|
|
|
struct mp_imgfmt_comp_desc sorted[MP_NUM_COMPONENTS];
|
|
|
|
int num_comps = 0;
|
2022-09-12 13:58:13 +00:00
|
|
|
if (desc.bpp[p] % 8)
|
|
|
|
return 0; // Pixel size is not byte-aligned
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
for (int c = 0; c < mp_imgfmt_desc_get_num_comps(&desc); c++) {
|
|
|
|
if (desc.comps[c].plane != p)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
data->component_map[num_comps] = c;
|
|
|
|
sorted[num_comps] = desc.comps[c];
|
|
|
|
num_comps++;
|
|
|
|
|
|
|
|
// Sort components by offset order, while keeping track of the
|
|
|
|
// semantic mapping in `data->component_map`
|
|
|
|
for (int i = num_comps - 1; i > 0; i--) {
|
|
|
|
if (sorted[i].offset >= sorted[i - 1].offset)
|
|
|
|
break;
|
|
|
|
MPSWAP(struct mp_imgfmt_comp_desc, sorted[i], sorted[i - 1]);
|
|
|
|
MPSWAP(int, data->component_map[i], data->component_map[i - 1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t total_bits = 0;
|
|
|
|
|
|
|
|
// Fill in the pl_plane_data fields for each component
|
|
|
|
memset(data->component_size, 0, sizeof(data->component_size));
|
|
|
|
for (int c = 0; c < num_comps; c++) {
|
|
|
|
data->component_size[c] = sorted[c].size;
|
|
|
|
data->component_pad[c] = sorted[c].offset - total_bits;
|
|
|
|
total_bits += data->component_pad[c] + data->component_size[c];
|
|
|
|
any_padded |= sorted[c].pad;
|
|
|
|
|
|
|
|
// Ignore bit encoding of alpha channel
|
|
|
|
if (!out_bits || data->component_map[c] == PL_CHANNEL_A)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
struct pl_bit_encoding bits = {
|
|
|
|
.sample_depth = data->component_size[c],
|
|
|
|
.color_depth = sorted[c].size - abs(sorted[c].pad),
|
|
|
|
.bit_shift = MPMAX(sorted[c].pad, 0),
|
|
|
|
};
|
|
|
|
|
2022-11-19 13:12:24 +00:00
|
|
|
if (!has_bits) {
|
2021-04-09 07:14:54 +00:00
|
|
|
*out_bits = bits;
|
2022-11-19 13:12:24 +00:00
|
|
|
has_bits = true;
|
2021-04-09 07:14:54 +00:00
|
|
|
} else {
|
|
|
|
if (!pl_bit_encoding_equal(out_bits, &bits)) {
|
|
|
|
// Bit encoding differs between components/planes,
|
|
|
|
// cannot handle this
|
|
|
|
*out_bits = (struct pl_bit_encoding) {0};
|
|
|
|
out_bits = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-12 13:25:56 +00:00
|
|
|
data->pixel_stride = desc.bpp[p] / 8;
|
2021-04-09 07:14:54 +00:00
|
|
|
data->type = (desc.flags & MP_IMGFLAG_TYPE_FLOAT)
|
|
|
|
? PL_FMT_FLOAT
|
|
|
|
: PL_FMT_UNORM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (any_padded && !out_bits)
|
|
|
|
return 0; // can't handle padded components without `pl_bit_encoding`
|
|
|
|
|
|
|
|
return desc.num_planes;
|
|
|
|
}
|
|
|
|
|
2023-02-25 04:53:54 +00:00
|
|
|
#ifdef PL_HAVE_LAV_HDR
|
2023-02-11 17:11:32 +00:00
|
|
|
static inline void *get_side_data(const struct mp_image *mpi,
|
|
|
|
enum AVFrameSideDataType type)
|
|
|
|
{
|
|
|
|
for (int i = 0; i <mpi->num_ff_side_data; i++) {
|
|
|
|
if (mpi->ff_side_data[i].type == type)
|
|
|
|
return (void *) mpi->ff_side_data[i].buf->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2023-02-25 04:53:54 +00:00
|
|
|
#endif
|
2023-02-11 17:11:32 +00:00
|
|
|
|
2022-01-03 03:07:52 +00:00
|
|
|
static struct pl_color_space get_mpi_csp(struct vo *vo, struct mp_image *mpi)
|
|
|
|
{
|
|
|
|
struct pl_color_space csp = {
|
|
|
|
.primaries = mp_prim_to_pl(mpi->params.color.primaries),
|
|
|
|
.transfer = mp_trc_to_pl(mpi->params.color.gamma),
|
|
|
|
.hdr.max_luma = mpi->params.color.sig_peak * MP_REF_WHITE,
|
|
|
|
};
|
|
|
|
|
2023-02-11 17:11:32 +00:00
|
|
|
#ifdef PL_HAVE_LAV_HDR
|
|
|
|
pl_map_hdr_metadata(&csp.hdr, &(struct pl_av_hdr_metadata) {
|
|
|
|
.mdm = get_side_data(mpi, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA),
|
|
|
|
.clm = get_side_data(mpi, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL),
|
|
|
|
.dhp = get_side_data(mpi, AV_FRAME_DATA_DYNAMIC_HDR_PLUS),
|
|
|
|
});
|
|
|
|
#else // back-compat fallback for older libplacebo
|
2022-01-03 03:07:52 +00:00
|
|
|
for (int i = 0; i < mpi->num_ff_side_data; i++) {
|
|
|
|
void *data = mpi->ff_side_data[i].buf->data;
|
|
|
|
switch (mpi->ff_side_data[i].type) {
|
|
|
|
case AV_FRAME_DATA_CONTENT_LIGHT_LEVEL: {
|
|
|
|
const AVContentLightMetadata *clm = data;
|
|
|
|
csp.hdr.max_cll = clm->MaxCLL;
|
|
|
|
csp.hdr.max_fall = clm->MaxFALL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case AV_FRAME_DATA_MASTERING_DISPLAY_METADATA: {
|
|
|
|
const AVMasteringDisplayMetadata *mdm = data;
|
|
|
|
if (mdm->has_luminance) {
|
|
|
|
csp.hdr.min_luma = av_q2d(mdm->min_luminance);
|
|
|
|
csp.hdr.max_luma = av_q2d(mdm->max_luminance);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mdm->has_primaries) {
|
|
|
|
csp.hdr.prim.red.x = av_q2d(mdm->display_primaries[0][0]);
|
|
|
|
csp.hdr.prim.red.y = av_q2d(mdm->display_primaries[0][1]);
|
|
|
|
csp.hdr.prim.green.x = av_q2d(mdm->display_primaries[1][0]);
|
|
|
|
csp.hdr.prim.green.y = av_q2d(mdm->display_primaries[1][1]);
|
|
|
|
csp.hdr.prim.blue.x = av_q2d(mdm->display_primaries[2][0]);
|
|
|
|
csp.hdr.prim.blue.y = av_q2d(mdm->display_primaries[2][1]);
|
|
|
|
csp.hdr.prim.white.x = av_q2d(mdm->white_point[0]);
|
|
|
|
csp.hdr.prim.white.y = av_q2d(mdm->white_point[1]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
2023-02-11 17:11:32 +00:00
|
|
|
#endif // PL_HAVE_LAV_HDR
|
2022-01-03 03:07:52 +00:00
|
|
|
|
|
|
|
return csp;
|
|
|
|
}
|
|
|
|
|
2022-04-09 21:58:59 +00:00
|
|
|
static bool hwdec_reconfig(struct priv *p, struct ra_hwdec *hwdec,
|
|
|
|
const struct mp_image_params *par)
|
|
|
|
{
|
|
|
|
if (p->hwdec_mapper) {
|
|
|
|
if (mp_image_params_equal(par, &p->hwdec_mapper->src_params)) {
|
|
|
|
return p->hwdec_mapper;
|
|
|
|
} else {
|
|
|
|
ra_hwdec_mapper_free(&p->hwdec_mapper);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p->hwdec_mapper = ra_hwdec_mapper_create(hwdec, par);
|
|
|
|
if (!p->hwdec_mapper) {
|
|
|
|
MP_ERR(p, "Initializing texture for hardware decoding failed.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return p->hwdec_mapper;
|
|
|
|
}
|
|
|
|
|
2022-02-25 21:07:56 +00:00
|
|
|
// For RAs not based on ra_pl, this creates a new pl_tex wrapper
|
2022-04-09 21:58:59 +00:00
|
|
|
static pl_tex hwdec_get_tex(struct priv *p, int n)
|
2022-02-25 21:07:56 +00:00
|
|
|
{
|
2022-04-09 21:58:59 +00:00
|
|
|
struct ra_tex *ratex = p->hwdec_mapper->tex[n];
|
|
|
|
struct ra *ra = p->hwdec_mapper->ra;
|
2022-02-25 21:07:56 +00:00
|
|
|
if (ra_pl_get(ra))
|
|
|
|
return (pl_tex) ratex->priv;
|
|
|
|
|
|
|
|
#if HAVE_GL && defined(PL_HAVE_OPENGL)
|
|
|
|
if (ra_is_gl(ra) && pl_opengl_get(p->gpu)) {
|
|
|
|
struct pl_opengl_wrap_params par = {
|
|
|
|
.width = ratex->params.w,
|
|
|
|
.height = ratex->params.h,
|
|
|
|
};
|
|
|
|
|
|
|
|
ra_gl_get_format(ratex->params.format, &par.iformat,
|
|
|
|
&(GLenum){0}, &(GLenum){0});
|
|
|
|
ra_gl_get_raw_tex(ra, ratex, &par.texture, &par.target);
|
|
|
|
return pl_opengl_wrap(p->gpu, &par);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-02-28 13:18:26 +00:00
|
|
|
#if HAVE_D3D11 && defined(PL_HAVE_D3D11)
|
|
|
|
if (ra_is_d3d11(ra)) {
|
|
|
|
int array_slice = 0;
|
|
|
|
ID3D11Resource *res = ra_d3d11_get_raw_tex(ra, ratex, &array_slice);
|
|
|
|
pl_tex tex = pl_d3d11_wrap(p->gpu, pl_d3d11_wrap_params(
|
|
|
|
.tex = res,
|
|
|
|
.array_slice = array_slice,
|
|
|
|
.fmt = ra_d3d11_get_format(ratex->params.format),
|
|
|
|
.w = ratex->params.w,
|
|
|
|
.h = ratex->params.h,
|
|
|
|
));
|
|
|
|
SAFE_RELEASE(res);
|
|
|
|
return tex;
|
|
|
|
}
|
|
|
|
#endif
|
2022-02-25 21:07:56 +00:00
|
|
|
|
|
|
|
MP_ERR(p, "Failed mapping hwdec frame? Open a bug!\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-09 21:58:59 +00:00
|
|
|
static bool hwdec_acquire(pl_gpu gpu, struct pl_frame *frame)
|
2022-03-08 12:42:13 +00:00
|
|
|
{
|
2022-04-09 21:58:59 +00:00
|
|
|
struct mp_image *mpi = frame->user_data;
|
|
|
|
struct frame_priv *fp = mpi->priv;
|
|
|
|
struct priv *p = fp->vo->priv;
|
|
|
|
if (!hwdec_reconfig(p, fp->hwdec, &mpi->params))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (ra_hwdec_mapper_map(p->hwdec_mapper, mpi) < 0) {
|
|
|
|
MP_ERR(p, "Mapping hardware decoded surface failed.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int n = 0; n < frame->num_planes; n++) {
|
|
|
|
if (!(frame->planes[n].texture = hwdec_get_tex(p, n)))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2022-03-08 12:42:13 +00:00
|
|
|
}
|
|
|
|
|
2022-04-09 21:58:59 +00:00
|
|
|
static void hwdec_release(pl_gpu gpu, struct pl_frame *frame)
|
2022-03-08 12:42:13 +00:00
|
|
|
{
|
2022-04-09 21:58:59 +00:00
|
|
|
struct mp_image *mpi = frame->user_data;
|
2022-03-08 12:42:13 +00:00
|
|
|
struct frame_priv *fp = mpi->priv;
|
|
|
|
struct priv *p = fp->vo->priv;
|
2022-04-09 21:58:59 +00:00
|
|
|
if (!ra_pl_get(p->hwdec_mapper->ra)) {
|
|
|
|
for (int n = 0; n < frame->num_planes; n++)
|
|
|
|
pl_tex_destroy(p->gpu, &frame->planes[n].texture);
|
2022-03-08 12:42:13 +00:00
|
|
|
}
|
2022-04-09 21:58:59 +00:00
|
|
|
|
|
|
|
ra_hwdec_mapper_unmap(p->hwdec_mapper);
|
2022-03-08 12:42:13 +00:00
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src,
|
|
|
|
struct pl_frame *frame)
|
|
|
|
{
|
|
|
|
struct mp_image *mpi = src->frame_data;
|
2021-11-22 11:38:43 +00:00
|
|
|
const struct mp_image_params *par = &mpi->params;
|
2021-04-09 07:14:54 +00:00
|
|
|
struct frame_priv *fp = mpi->priv;
|
|
|
|
struct vo *vo = fp->vo;
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
2022-04-09 21:58:59 +00:00
|
|
|
fp->hwdec = ra_hwdec_get(&p->hwdec_ctx, mpi->imgfmt);
|
|
|
|
if (fp->hwdec) {
|
|
|
|
// Note: We don't actually need the mapper to map the frame yet, we
|
|
|
|
// only reconfig the mapper here (potentially creating it) to access
|
|
|
|
// `dst_params`. In practice, though, this should not matter unless the
|
|
|
|
// image format changes mid-stream.
|
|
|
|
if (!hwdec_reconfig(p, fp->hwdec, &mpi->params)) {
|
2022-03-08 12:42:13 +00:00
|
|
|
talloc_free(mpi);
|
2022-02-25 21:07:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-09 21:58:59 +00:00
|
|
|
par = &p->hwdec_mapper->dst_params;
|
2022-02-25 21:07:56 +00:00
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
*frame = (struct pl_frame) {
|
2022-01-03 03:07:52 +00:00
|
|
|
.color = get_mpi_csp(vo, mpi),
|
2021-04-09 07:14:54 +00:00
|
|
|
.repr = {
|
2021-11-22 11:38:43 +00:00
|
|
|
.sys = mp_csp_to_pl(par->color.space),
|
|
|
|
.levels = mp_levels_to_pl(par->color.levels),
|
|
|
|
.alpha = mp_alpha_to_pl(par->alpha),
|
2021-04-09 07:14:54 +00:00
|
|
|
},
|
|
|
|
.profile = {
|
|
|
|
.data = mpi->icc_profile ? mpi->icc_profile->data : NULL,
|
|
|
|
.len = mpi->icc_profile ? mpi->icc_profile->size : 0,
|
|
|
|
},
|
2021-11-22 11:38:43 +00:00
|
|
|
.rotation = par->rotate / 90,
|
2021-11-25 22:02:39 +00:00
|
|
|
.user_data = mpi,
|
2021-04-09 07:14:54 +00:00
|
|
|
};
|
|
|
|
|
2021-11-22 11:38:43 +00:00
|
|
|
// mp_image, like AVFrame, likes communicating RGB/XYZ/YCbCr status
|
|
|
|
// implicitly via the image format, rather than the actual tagging.
|
|
|
|
switch (mp_imgfmt_get_forced_csp(par->imgfmt)) {
|
|
|
|
case MP_CSP_RGB:
|
|
|
|
frame->repr.sys = PL_COLOR_SYSTEM_RGB;
|
|
|
|
frame->repr.levels = PL_COLOR_LEVELS_FULL;
|
|
|
|
break;
|
|
|
|
case MP_CSP_XYZ:
|
|
|
|
frame->repr.sys = PL_COLOR_SYSTEM_XYZ;
|
|
|
|
break;
|
|
|
|
case MP_CSP_AUTO:
|
2021-11-22 13:40:11 +00:00
|
|
|
if (!frame->repr.sys)
|
|
|
|
frame->repr.sys = pl_color_system_guess_ycbcr(par->w, par->h);
|
2021-11-22 11:38:43 +00:00
|
|
|
break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
2022-04-09 21:58:59 +00:00
|
|
|
if (fp->hwdec) {
|
2022-02-25 21:07:56 +00:00
|
|
|
|
|
|
|
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(par->imgfmt);
|
2022-04-09 21:58:59 +00:00
|
|
|
frame->acquire = hwdec_acquire;
|
|
|
|
frame->release = hwdec_release;
|
2022-02-25 21:07:56 +00:00
|
|
|
frame->num_planes = desc.num_planes;
|
|
|
|
for (int n = 0; n < frame->num_planes; n++) {
|
|
|
|
struct pl_plane *plane = &frame->planes[n];
|
|
|
|
int *map = plane->component_mapping;
|
|
|
|
for (int c = 0; c < mp_imgfmt_desc_get_num_comps(&desc); c++) {
|
|
|
|
if (desc.comps[c].plane != n)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Sort by component offset
|
|
|
|
uint8_t offset = desc.comps[c].offset;
|
|
|
|
int index = plane->components++;
|
|
|
|
while (index > 0 && desc.comps[map[index - 1]].offset > offset) {
|
|
|
|
map[index] = map[index - 1];
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
map[index] = c;
|
|
|
|
}
|
2022-02-11 04:04:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-25 21:07:56 +00:00
|
|
|
} else { // swdec
|
|
|
|
|
|
|
|
struct pl_plane_data data[4] = {0};
|
|
|
|
frame->num_planes = plane_data_from_imgfmt(data, &frame->repr.bits, mpi->imgfmt);
|
|
|
|
for (int n = 0; n < frame->num_planes; n++) {
|
|
|
|
struct pl_plane *plane = &frame->planes[n];
|
|
|
|
data[n].width = mp_image_plane_w(mpi, n);
|
|
|
|
data[n].height = mp_image_plane_h(mpi, n);
|
|
|
|
if (mpi->stride[n] < 0) {
|
|
|
|
data[n].pixels = mpi->planes[n] + (data[n].height - 1) * mpi->stride[n];
|
|
|
|
data[n].row_stride = -mpi->stride[n];
|
|
|
|
plane->flipped = true;
|
|
|
|
} else {
|
|
|
|
data[n].pixels = mpi->planes[n];
|
|
|
|
data[n].row_stride = mpi->stride[n];
|
|
|
|
}
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2022-03-06 21:59:05 +00:00
|
|
|
pl_buf buf = get_dr_buf(p, data[n].pixels);
|
2022-02-25 21:07:56 +00:00
|
|
|
if (buf) {
|
|
|
|
data[n].buf = buf;
|
|
|
|
data[n].buf_offset = (uint8_t *) data[n].pixels - buf->data;
|
|
|
|
data[n].pixels = NULL;
|
|
|
|
} else if (gpu->limits.callbacks) {
|
|
|
|
data[n].callback = talloc_free;
|
|
|
|
data[n].priv = mp_image_new_ref(mpi);
|
|
|
|
}
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2022-02-25 21:07:56 +00:00
|
|
|
if (!pl_upload_plane(gpu, plane, &tex[n], &data[n])) {
|
|
|
|
MP_ERR(vo, "Failed uploading frame!\n");
|
|
|
|
talloc_free(data[n].priv);
|
2022-04-09 21:58:59 +00:00
|
|
|
talloc_free(mpi);
|
2022-02-25 21:07:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
2022-02-11 04:04:27 +00:00
|
|
|
}
|
2022-02-25 21:07:56 +00:00
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
2022-02-25 21:07:56 +00:00
|
|
|
// Update chroma location, must be done after initializing planes
|
|
|
|
pl_frame_set_chroma_location(frame, mp_chroma_to_pl(par->chroma_location));
|
|
|
|
|
2023-02-14 02:28:40 +00:00
|
|
|
// Set the frame DOVI metadata
|
|
|
|
mp_map_dovi_metadata_to_pl(mpi, frame);
|
2022-01-06 07:39:51 +00:00
|
|
|
|
2022-04-01 20:26:44 +00:00
|
|
|
#ifdef PL_HAVE_LAV_FILM_GRAIN
|
|
|
|
if (mpi->film_grain)
|
|
|
|
pl_film_grain_from_av(&frame->film_grain, (AVFilmGrainParams *) mpi->film_grain->data);
|
|
|
|
#endif
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
// Compute a unique signature for any attached ICC profile. Wasteful in
|
|
|
|
// theory if the ICC profile is the same for multiple frames, but in
|
|
|
|
// practice ICC profiles are overwhelmingly going to be attached to
|
|
|
|
// still images so it shouldn't matter.
|
|
|
|
pl_icc_profile_compute_signature(&frame->profile);
|
|
|
|
|
|
|
|
// Update LUT attached to this frame
|
|
|
|
update_lut(p, &p->image_lut);
|
|
|
|
frame->lut = p->image_lut.lut;
|
|
|
|
frame->lut_type = p->image_lut.type;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-09 21:58:59 +00:00
|
|
|
static void unmap_frame(pl_gpu gpu, struct pl_frame *frame,
|
|
|
|
const struct pl_source_frame *src)
|
|
|
|
{
|
|
|
|
struct mp_image *mpi = src->frame_data;
|
|
|
|
struct frame_priv *fp = mpi->priv;
|
|
|
|
struct priv *p = fp->vo->priv;
|
|
|
|
for (int i = 0; i < MP_ARRAY_SIZE(fp->subs.entries); i++) {
|
|
|
|
pl_tex tex = fp->subs.entries[i].tex;
|
|
|
|
if (tex)
|
|
|
|
MP_TARRAY_APPEND(p, p->sub_tex, p->num_sub_tex, tex);
|
|
|
|
}
|
|
|
|
talloc_free(mpi);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void discard_frame(const struct pl_source_frame *src)
|
|
|
|
{
|
|
|
|
struct mp_image *mpi = src->frame_data;
|
|
|
|
talloc_free(mpi);
|
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
static void info_callback(void *priv, const struct pl_render_info *info)
|
|
|
|
{
|
|
|
|
struct vo *vo = priv;
|
|
|
|
struct priv *p = vo->priv;
|
2023-04-28 00:20:36 +00:00
|
|
|
if (info->index >= VO_PASS_PERF_MAX)
|
2022-09-26 19:14:34 +00:00
|
|
|
return; // silently ignore clipped passes, whatever
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-04-29 20:08:50 +00:00
|
|
|
struct frame_info *frame;
|
2021-04-09 07:14:54 +00:00
|
|
|
switch (info->stage) {
|
2023-04-29 20:08:50 +00:00
|
|
|
case PL_RENDER_STAGE_FRAME: frame = &p->perf_fresh; break;
|
|
|
|
case PL_RENDER_STAGE_BLEND: frame = &p->perf_redraw; break;
|
2021-04-09 07:14:54 +00:00
|
|
|
default: abort();
|
|
|
|
}
|
|
|
|
|
2023-04-29 20:08:50 +00:00
|
|
|
frame->count = info->index + 1;
|
|
|
|
pl_dispatch_info_move(&frame->info[info->index], info->pass);
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
2022-02-27 20:15:17 +00:00
|
|
|
static void update_options(struct vo *vo)
|
2021-04-09 07:14:54 +00:00
|
|
|
{
|
2022-02-27 20:15:17 +00:00
|
|
|
struct priv *p = vo->priv;
|
2023-08-18 13:36:59 +00:00
|
|
|
pl_options pars = p->pars;
|
2021-04-09 07:14:54 +00:00
|
|
|
if (m_config_cache_update(p->opts_cache))
|
2022-02-27 20:15:17 +00:00
|
|
|
update_render_options(vo);
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
update_lut(p, &p->lut);
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.lut = p->lut.lut;
|
|
|
|
pars->params.lut_type = p->lut.type;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
// Update equalizer state
|
|
|
|
struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
|
|
|
|
mp_csp_equalizer_state_get(p->video_eq, &cparams);
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->color_adjustment.brightness = cparams.brightness;
|
|
|
|
pars->color_adjustment.contrast = cparams.contrast;
|
|
|
|
pars->color_adjustment.hue = cparams.hue;
|
|
|
|
pars->color_adjustment.saturation = cparams.saturation;
|
|
|
|
pars->color_adjustment.gamma = cparams.gamma;
|
2022-09-25 18:31:08 +00:00
|
|
|
p->output_levels = cparams.levels_out;
|
2023-08-18 15:25:52 +00:00
|
|
|
|
|
|
|
#if PL_API_VER >= 309
|
|
|
|
for (char **kv = p->raw_opts; kv && kv[0]; kv += 2)
|
|
|
|
pl_options_set_str(pars, kv[0], kv[1]);
|
|
|
|
#endif
|
2021-11-25 21:00:30 +00:00
|
|
|
}
|
|
|
|
|
2023-06-30 03:46:51 +00:00
|
|
|
static void apply_target_contrast(struct priv *p, struct pl_color_space *color)
|
|
|
|
{
|
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
|
|
|
|
|
|
|
// Auto mode, leave as is
|
|
|
|
if (!opts->target_contrast)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Infinite contrast
|
|
|
|
if (opts->target_contrast == -1) {
|
2023-08-28 16:30:08 +00:00
|
|
|
color->hdr.min_luma = 1e-7;
|
2023-06-30 03:46:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Infer max_luma for current pl_color_space
|
|
|
|
pl_color_space_nominal_luma_ex(pl_nominal_luma_params(
|
|
|
|
.color = color,
|
|
|
|
// with HDR10 meta to respect value if already set
|
|
|
|
.metadata = PL_HDR_METADATA_HDR10,
|
|
|
|
.scaling = PL_HDR_NITS,
|
|
|
|
.out_max = &color->hdr.max_luma
|
|
|
|
));
|
|
|
|
|
|
|
|
color->hdr.min_luma = color->hdr.max_luma / opts->target_contrast;
|
|
|
|
}
|
|
|
|
|
2021-11-25 21:00:30 +00:00
|
|
|
static void apply_target_options(struct priv *p, struct pl_frame *target)
|
|
|
|
{
|
|
|
|
update_lut(p, &p->target_lut);
|
|
|
|
target->lut = p->target_lut.lut;
|
|
|
|
target->lut_type = p->target_lut.type;
|
|
|
|
|
|
|
|
// Colorspace overrides
|
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
2022-09-25 18:31:08 +00:00
|
|
|
if (p->output_levels)
|
|
|
|
target->repr.levels = mp_levels_to_pl(p->output_levels);
|
2021-11-25 21:00:30 +00:00
|
|
|
if (opts->target_prim)
|
|
|
|
target->color.primaries = mp_prim_to_pl(opts->target_prim);
|
|
|
|
if (opts->target_trc)
|
|
|
|
target->color.transfer = mp_trc_to_pl(opts->target_trc);
|
2023-02-22 21:10:55 +00:00
|
|
|
// If swapchain returned a value use this, override is used in hint
|
|
|
|
if (opts->target_peak && !target->color.hdr.max_luma)
|
2022-01-03 03:07:52 +00:00
|
|
|
target->color.hdr.max_luma = opts->target_peak;
|
2023-06-30 03:46:51 +00:00
|
|
|
if (!target->color.hdr.min_luma)
|
|
|
|
apply_target_contrast(p, &target->color);
|
2021-11-25 21:00:30 +00:00
|
|
|
if (opts->dither_depth > 0) {
|
|
|
|
struct pl_bit_encoding *tbits = &target->repr.bits;
|
|
|
|
tbits->color_depth += opts->dither_depth - tbits->sample_depth;
|
|
|
|
tbits->sample_depth = opts->dither_depth;
|
|
|
|
}
|
2023-04-23 20:49:33 +00:00
|
|
|
|
|
|
|
if (opts->icc_opts->icc_use_luma) {
|
2023-09-14 18:26:06 +00:00
|
|
|
p->icc_params.max_luma = 0.0f;
|
2023-04-23 20:49:33 +00:00
|
|
|
} else {
|
2023-09-14 18:26:06 +00:00
|
|
|
pl_color_space_nominal_luma_ex(pl_nominal_luma_params(
|
|
|
|
.color = &target->color,
|
|
|
|
.metadata = PL_HDR_METADATA_HDR10, // use only static HDR nits
|
|
|
|
.scaling = PL_HDR_NITS,
|
|
|
|
.out_max = &p->icc_params.max_luma,
|
|
|
|
));
|
2023-04-23 20:49:33 +00:00
|
|
|
}
|
2023-09-14 18:26:06 +00:00
|
|
|
|
|
|
|
#if PL_API_VER >= 327
|
|
|
|
pl_icc_update(p->pllog, &p->icc_profile, NULL, &p->icc_params);
|
|
|
|
target->icc = p->icc_profile;
|
|
|
|
#else
|
|
|
|
target->profile = p->icc_profile;
|
|
|
|
#endif
|
2021-11-25 21:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void apply_crop(struct pl_frame *frame, struct mp_rect crop,
|
|
|
|
int width, int height)
|
|
|
|
{
|
|
|
|
frame->crop = (struct pl_rect2df) {
|
|
|
|
.x0 = crop.x0,
|
|
|
|
.y0 = crop.y0,
|
|
|
|
.x1 = crop.x1,
|
|
|
|
.y1 = crop.y1,
|
|
|
|
};
|
|
|
|
|
|
|
|
// mpv gives us rotated/flipped rects, libplacebo expects unrotated
|
|
|
|
pl_rect2df_rotate(&frame->crop, -frame->rotation);
|
|
|
|
if (frame->crop.x1 < frame->crop.x0) {
|
|
|
|
frame->crop.x0 = width - frame->crop.x0;
|
|
|
|
frame->crop.x1 = width - frame->crop.x1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame->crop.y1 < frame->crop.y0) {
|
|
|
|
frame->crop.y0 = height - frame->crop.y0;
|
|
|
|
frame->crop.y1 = height - frame->crop.y1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-30 12:53:57 +00:00
|
|
|
static void update_tm_viz(struct pl_color_map_params *params,
|
2023-09-02 18:32:13 +00:00
|
|
|
const struct pl_frame *target)
|
2023-08-30 12:53:57 +00:00
|
|
|
{
|
|
|
|
if (!params->visualize_lut)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Use right half of sceen for TM visualization, constrain to 1:1 AR
|
|
|
|
const float out_w = fabsf(pl_rect_w(target->crop));
|
|
|
|
const float out_h = fabsf(pl_rect_h(target->crop));
|
|
|
|
const float size = MPMIN(out_w / 2.0f, out_h);
|
|
|
|
params->visualize_rect = (pl_rect2df) {
|
|
|
|
.x0 = 1.0f - size / out_w,
|
|
|
|
.x1 = 1.0f,
|
|
|
|
.y0 = 0.0f,
|
|
|
|
.y1 = size / out_h,
|
|
|
|
};
|
|
|
|
|
2023-09-02 18:32:13 +00:00
|
|
|
// Visualize red-blue plane
|
|
|
|
params->visualize_hue = M_PI / 4.0;
|
2023-08-30 12:53:57 +00:00
|
|
|
}
|
|
|
|
|
2021-11-25 21:00:30 +00:00
|
|
|
static void draw_frame(struct vo *vo, struct vo_frame *frame)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2023-08-18 13:36:59 +00:00
|
|
|
pl_options pars = p->pars;
|
2021-11-25 21:00:30 +00:00
|
|
|
pl_gpu gpu = p->gpu;
|
2022-02-27 20:15:17 +00:00
|
|
|
update_options(vo);
|
2023-08-27 11:54:49 +00:00
|
|
|
|
|
|
|
struct pl_render_params params = pars->params;
|
2023-08-27 12:00:39 +00:00
|
|
|
bool will_redraw = frame->display_synced && frame->num_vsyncs > 1;
|
|
|
|
bool cache_frame = will_redraw || frame->still;
|
2023-08-27 11:54:49 +00:00
|
|
|
params.info_callback = info_callback;
|
|
|
|
params.info_priv = vo;
|
2023-08-27 12:00:39 +00:00
|
|
|
params.skip_caching_single_frame = !cache_frame;
|
|
|
|
params.preserve_mixing_cache = p->inter_preserve && !frame->still;
|
|
|
|
if (frame->still)
|
|
|
|
params.frame_mixer = NULL;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
// Push all incoming frames into the frame queue
|
|
|
|
for (int n = 0; n < frame->num_frames; n++) {
|
|
|
|
int id = frame->frame_id + n;
|
|
|
|
if (id <= p->last_id)
|
|
|
|
continue; // ignore already seen frames
|
|
|
|
|
|
|
|
if (p->want_reset) {
|
|
|
|
pl_renderer_flush_cache(p->rr);
|
|
|
|
pl_queue_reset(p->queue);
|
|
|
|
p->last_pts = 0.0;
|
|
|
|
p->want_reset = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct mp_image *mpi = mp_image_new_ref(frame->frames[n]);
|
|
|
|
struct frame_priv *fp = talloc_zero(mpi, struct frame_priv);
|
|
|
|
mpi->priv = fp;
|
|
|
|
fp->vo = vo;
|
|
|
|
|
|
|
|
pl_queue_push(p->queue, &(struct pl_source_frame) {
|
|
|
|
.pts = mpi->pts,
|
2023-03-13 18:55:04 +00:00
|
|
|
.duration = frame->ideal_frame_duration,
|
2021-04-09 07:14:54 +00:00
|
|
|
.frame_data = mpi,
|
|
|
|
.map = map_frame,
|
|
|
|
.unmap = unmap_frame,
|
|
|
|
.discard = discard_frame,
|
|
|
|
});
|
|
|
|
|
|
|
|
p->last_id = id;
|
|
|
|
}
|
|
|
|
|
2022-01-03 03:07:52 +00:00
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
2021-11-07 22:47:55 +00:00
|
|
|
if (p->target_hint && frame->current) {
|
2022-01-03 03:07:52 +00:00
|
|
|
struct pl_color_space hint = get_mpi_csp(vo, frame->current);
|
|
|
|
if (opts->target_prim)
|
|
|
|
hint.primaries = mp_prim_to_pl(opts->target_prim);
|
|
|
|
if (opts->target_trc)
|
2022-09-03 00:33:40 +00:00
|
|
|
hint.transfer = mp_trc_to_pl(opts->target_trc);
|
2023-02-22 21:10:55 +00:00
|
|
|
if (opts->target_peak)
|
|
|
|
hint.hdr.max_luma = opts->target_peak;
|
2023-06-30 03:46:51 +00:00
|
|
|
apply_target_contrast(p, &hint);
|
2021-11-07 22:47:55 +00:00
|
|
|
pl_swapchain_colorspace_hint(p->sw, &hint);
|
|
|
|
} else if (!p->target_hint) {
|
|
|
|
pl_swapchain_colorspace_hint(p->sw, NULL);
|
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
struct pl_swapchain_frame swframe;
|
2021-11-20 18:20:52 +00:00
|
|
|
struct ra_swapchain *sw = p->ra_ctx->swapchain;
|
2022-01-03 03:07:52 +00:00
|
|
|
double vsync_offset = opts->interpolation ? frame->vsync_offset : 0;
|
2021-11-20 18:20:52 +00:00
|
|
|
bool should_draw = sw->fns->start_frame(sw, NULL); // for wayland logic
|
|
|
|
if (!should_draw || !pl_swapchain_start_frame(p->sw, &swframe)) {
|
2022-07-18 19:49:36 +00:00
|
|
|
if (frame->current) {
|
|
|
|
// Advance the queue state to the current PTS to discard unused frames
|
|
|
|
pl_queue_update(p->queue, NULL, pl_queue_params(
|
|
|
|
.pts = frame->current->pts + vsync_offset,
|
2023-08-27 11:54:49 +00:00
|
|
|
.radius = pl_frame_mix_radius(¶ms),
|
2022-07-18 19:49:36 +00:00
|
|
|
));
|
|
|
|
}
|
2021-04-09 07:14:54 +00:00
|
|
|
return;
|
2021-11-07 10:00:48 +00:00
|
|
|
}
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
bool valid = false;
|
|
|
|
p->is_interpolated = false;
|
|
|
|
|
|
|
|
// Calculate target
|
|
|
|
struct pl_frame target;
|
|
|
|
pl_frame_from_swapchain(&target, &swframe);
|
2021-11-25 21:00:30 +00:00
|
|
|
apply_target_options(p, &target);
|
2023-09-16 18:30:47 +00:00
|
|
|
update_overlays(vo, p->osd_res,
|
vo_gpu_next: respect --blend-subtitles=<yes/no>
This brings vo_gpu_next's behavior more in line with vo_gpu. Currently,
the status quo was that `vo_gpu_next` generated subtitles with the full
resolution of the dst crop, even though practical limitations of
libplacebo meant that such subtitles inevitably got cut off at the video
boundaries. This was actually a worse status quo than `vo_gpu`, which
simply constrained subtitles to the video dimensions to prevent these
issues.
With this change, the behavior is the same as `vo_gpu`, which has the
exact same problem w.r.t interpolation. Users are back to choosing
between --blend-subtitles=yes and not having subtitles in margins,
and using --blend-subtitles=no and not having subtitle interpolation.
There are different pros and cons to each approach, and I still
ultimately plan on allowing libplacebo to interpolate subtitles even in
the black bars, to solve this issue once and for all. But for now, let's
not let perfect be the enemy of good.
No attempt is made to implement `--blend-subs=video`, which I consider
deprecated.
Fixes: https://github.com/mpv-player/mpv/issues/10272
2022-09-27 23:18:54 +00:00
|
|
|
(frame->current && opts->blend_subs) ? OSD_DRAW_OSD_ONLY : 0,
|
2023-09-16 18:30:47 +00:00
|
|
|
PL_OVERLAY_COORDS_DST_FRAME, &p->osd_state, &target, frame->current);
|
2021-11-25 21:00:30 +00:00
|
|
|
apply_crop(&target, p->dst, swframe.fbo->params.w, swframe.fbo->params.h);
|
2023-09-02 18:32:13 +00:00
|
|
|
update_tm_viz(&pars->color_map_params, &target);
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2021-11-08 23:37:26 +00:00
|
|
|
struct pl_frame_mix mix = {0};
|
2021-04-09 07:14:54 +00:00
|
|
|
if (frame->current) {
|
|
|
|
// Update queue state
|
|
|
|
struct pl_queue_params qparams = {
|
2021-11-21 20:56:29 +00:00
|
|
|
.pts = frame->current->pts + vsync_offset,
|
2023-08-27 11:54:49 +00:00
|
|
|
.radius = pl_frame_mix_radius(¶ms),
|
2021-04-09 07:14:54 +00:00
|
|
|
.vsync_duration = frame->vsync_interval,
|
|
|
|
.interpolation_threshold = opts->interpolation_threshold,
|
|
|
|
};
|
|
|
|
|
|
|
|
// mpv likes to generate sporadically jumping PTS shortly after
|
|
|
|
// initialization, but pl_queue does not like these. Hard-clamp as
|
|
|
|
// a simple work-around.
|
|
|
|
qparams.pts = p->last_pts = MPMAX(qparams.pts, p->last_pts);
|
|
|
|
|
|
|
|
switch (pl_queue_update(p->queue, &mix, &qparams)) {
|
|
|
|
case PL_QUEUE_ERR:
|
|
|
|
MP_ERR(vo, "Failed updating frames!\n");
|
|
|
|
goto done;
|
|
|
|
case PL_QUEUE_EOF:
|
|
|
|
abort(); // we never signal EOF
|
|
|
|
case PL_QUEUE_MORE:
|
2022-02-27 20:15:17 +00:00
|
|
|
// This is expected to happen semi-frequently near the start and
|
|
|
|
// end of a file, so only log it at high verbosity and move on.
|
|
|
|
MP_DBG(vo, "Render queue underrun.\n");
|
|
|
|
break;
|
2021-04-09 07:14:54 +00:00
|
|
|
case PL_QUEUE_OK:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-02-15 11:47:53 +00:00
|
|
|
// Update source crop and overlays on all existing frames. We
|
|
|
|
// technically own the `pl_frame` struct so this is kosher. This could
|
|
|
|
// be partially avoided by instead flushing the queue on resizes, but
|
|
|
|
// doing it this way avoids unnecessarily re-uploading frames.
|
2021-04-09 07:14:54 +00:00
|
|
|
for (int i = 0; i < mix.num_frames; i++) {
|
2022-02-15 11:47:53 +00:00
|
|
|
struct pl_frame *image = (struct pl_frame *) mix.frames[i];
|
|
|
|
struct mp_image *mpi = image->user_data;
|
|
|
|
struct frame_priv *fp = mpi->priv;
|
|
|
|
apply_crop(image, p->src, vo->params->w, vo->params->h);
|
vo_gpu_next: respect --blend-subtitles=<yes/no>
This brings vo_gpu_next's behavior more in line with vo_gpu. Currently,
the status quo was that `vo_gpu_next` generated subtitles with the full
resolution of the dst crop, even though practical limitations of
libplacebo meant that such subtitles inevitably got cut off at the video
boundaries. This was actually a worse status quo than `vo_gpu`, which
simply constrained subtitles to the video dimensions to prevent these
issues.
With this change, the behavior is the same as `vo_gpu`, which has the
exact same problem w.r.t interpolation. Users are back to choosing
between --blend-subtitles=yes and not having subtitles in margins,
and using --blend-subtitles=no and not having subtitle interpolation.
There are different pros and cons to each approach, and I still
ultimately plan on allowing libplacebo to interpolate subtitles even in
the black bars, to solve this issue once and for all. But for now, let's
not let perfect be the enemy of good.
No attempt is made to implement `--blend-subs=video`, which I consider
deprecated.
Fixes: https://github.com/mpv-player/mpv/issues/10272
2022-09-27 23:18:54 +00:00
|
|
|
if (opts->blend_subs) {
|
2023-09-28 18:07:42 +00:00
|
|
|
if (p->paused || fp->osd_sync < p->osd_sync) {
|
vo_gpu_next: respect --blend-subtitles=<yes/no>
This brings vo_gpu_next's behavior more in line with vo_gpu. Currently,
the status quo was that `vo_gpu_next` generated subtitles with the full
resolution of the dst crop, even though practical limitations of
libplacebo meant that such subtitles inevitably got cut off at the video
boundaries. This was actually a worse status quo than `vo_gpu`, which
simply constrained subtitles to the video dimensions to prevent these
issues.
With this change, the behavior is the same as `vo_gpu`, which has the
exact same problem w.r.t interpolation. Users are back to choosing
between --blend-subtitles=yes and not having subtitles in margins,
and using --blend-subtitles=no and not having subtitle interpolation.
There are different pros and cons to each approach, and I still
ultimately plan on allowing libplacebo to interpolate subtitles even in
the black bars, to solve this issue once and for all. But for now, let's
not let perfect be the enemy of good.
No attempt is made to implement `--blend-subs=video`, which I consider
deprecated.
Fixes: https://github.com/mpv-player/mpv/issues/10272
2022-09-27 23:18:54 +00:00
|
|
|
float rx = pl_rect_w(p->dst) / pl_rect_w(image->crop);
|
|
|
|
float ry = pl_rect_h(p->dst) / pl_rect_h(image->crop);
|
|
|
|
struct mp_osd_res res = {
|
|
|
|
.w = pl_rect_w(p->dst),
|
|
|
|
.h = pl_rect_h(p->dst),
|
|
|
|
.ml = -image->crop.x0 * rx,
|
|
|
|
.mr = (image->crop.x1 - vo->params->w) * rx,
|
|
|
|
.mt = -image->crop.y0 * ry,
|
|
|
|
.mb = (image->crop.y1 - vo->params->h) * ry,
|
|
|
|
.display_par = 1.0,
|
|
|
|
};
|
2023-09-16 18:30:47 +00:00
|
|
|
update_overlays(vo, res, OSD_DRAW_SUB_ONLY,
|
vo_gpu_next: respect --blend-subtitles=<yes/no>
This brings vo_gpu_next's behavior more in line with vo_gpu. Currently,
the status quo was that `vo_gpu_next` generated subtitles with the full
resolution of the dst crop, even though practical limitations of
libplacebo meant that such subtitles inevitably got cut off at the video
boundaries. This was actually a worse status quo than `vo_gpu`, which
simply constrained subtitles to the video dimensions to prevent these
issues.
With this change, the behavior is the same as `vo_gpu`, which has the
exact same problem w.r.t interpolation. Users are back to choosing
between --blend-subtitles=yes and not having subtitles in margins,
and using --blend-subtitles=no and not having subtitle interpolation.
There are different pros and cons to each approach, and I still
ultimately plan on allowing libplacebo to interpolate subtitles even in
the black bars, to solve this issue once and for all. But for now, let's
not let perfect be the enemy of good.
No attempt is made to implement `--blend-subs=video`, which I consider
deprecated.
Fixes: https://github.com/mpv-player/mpv/issues/10272
2022-09-27 23:18:54 +00:00
|
|
|
PL_OVERLAY_COORDS_DST_CROP,
|
2023-09-16 18:30:47 +00:00
|
|
|
&fp->subs, image, mpi);
|
vo_gpu_next: respect --blend-subtitles=<yes/no>
This brings vo_gpu_next's behavior more in line with vo_gpu. Currently,
the status quo was that `vo_gpu_next` generated subtitles with the full
resolution of the dst crop, even though practical limitations of
libplacebo meant that such subtitles inevitably got cut off at the video
boundaries. This was actually a worse status quo than `vo_gpu`, which
simply constrained subtitles to the video dimensions to prevent these
issues.
With this change, the behavior is the same as `vo_gpu`, which has the
exact same problem w.r.t interpolation. Users are back to choosing
between --blend-subtitles=yes and not having subtitles in margins,
and using --blend-subtitles=no and not having subtitle interpolation.
There are different pros and cons to each approach, and I still
ultimately plan on allowing libplacebo to interpolate subtitles even in
the black bars, to solve this issue once and for all. But for now, let's
not let perfect be the enemy of good.
No attempt is made to implement `--blend-subs=video`, which I consider
deprecated.
Fixes: https://github.com/mpv-player/mpv/issues/10272
2022-09-27 23:18:54 +00:00
|
|
|
fp->osd_sync = p->osd_sync;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Disable overlays when blend_subs is disabled
|
|
|
|
image->num_overlays = 0;
|
|
|
|
fp->osd_sync = 0;
|
2022-02-15 11:47:53 +00:00
|
|
|
}
|
2022-11-23 13:58:16 +00:00
|
|
|
|
|
|
|
// Update the frame signature to include the current OSD sync
|
|
|
|
// value, in order to disambiguate between identical frames with
|
|
|
|
// modified OSD. Shift the OSD sync value by a lot to avoid
|
|
|
|
// collisions with low signature values.
|
|
|
|
//
|
|
|
|
// This is safe to do because `pl_frame_mix.signature` lives in
|
|
|
|
// temporary memory that is only valid for this `pl_queue_update`.
|
|
|
|
((uint64_t *) mix.signatures)[i] ^= fp->osd_sync << 48;
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render frame
|
2023-08-27 11:54:49 +00:00
|
|
|
if (!pl_render_image_mix(p->rr, &mix, &target, ¶ms)) {
|
2021-04-09 07:14:54 +00:00
|
|
|
MP_ERR(vo, "Failed rendering frame!\n");
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2023-08-29 08:23:26 +00:00
|
|
|
const struct pl_frame *cur_frame = NULL;
|
|
|
|
for (int i = 0; i < mix.num_frames; i++) {
|
|
|
|
if (mix.timestamps[i] > 0.0f)
|
|
|
|
break;
|
|
|
|
cur_frame = mix.frames[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur_frame) {
|
|
|
|
p->last_hdr_metadata = cur_frame->color.hdr;
|
|
|
|
#if PL_API_VER >= 314
|
|
|
|
// Augment metadata with peak detection max_pq_y / avg_pq_y
|
|
|
|
pl_renderer_get_hdr_metadata(p->rr, &p->last_hdr_metadata);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
p->last_hdr_metadata = (struct pl_hdr_metadata){0};
|
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
p->is_interpolated = mix.num_frames > 1;
|
|
|
|
valid = true;
|
|
|
|
// fall through
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (!valid) // clear with purple to indicate error
|
|
|
|
pl_tex_clear(gpu, swframe.fbo, (float[4]){ 0.5, 0.0, 1.0, 1.0 });
|
|
|
|
|
2023-07-29 15:09:59 +00:00
|
|
|
pl_gpu_flush(gpu);
|
2023-07-29 17:09:45 +00:00
|
|
|
p->frame_pending = true;
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void flip_page(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
struct ra_swapchain *sw = p->ra_ctx->swapchain;
|
2023-07-29 15:09:59 +00:00
|
|
|
|
2023-07-29 17:09:45 +00:00
|
|
|
if (p->frame_pending) {
|
|
|
|
if (!pl_swapchain_submit_frame(p->sw))
|
|
|
|
MP_ERR(vo, "Failed presenting frame!\n");
|
|
|
|
p->frame_pending = false;
|
|
|
|
}
|
2023-07-29 15:09:59 +00:00
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
sw->fns->swap_buffers(sw);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
struct ra_swapchain *sw = p->ra_ctx->swapchain;
|
|
|
|
if (sw->fns->get_vsync)
|
|
|
|
sw->fns->get_vsync(sw, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int query_format(struct vo *vo, int format)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2022-02-25 21:07:56 +00:00
|
|
|
if (ra_hwdec_get(&p->hwdec_ctx, format))
|
|
|
|
return true;
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
struct pl_bit_encoding bits;
|
2021-12-05 20:24:57 +00:00
|
|
|
struct pl_plane_data data[4] = {0};
|
2021-04-09 07:14:54 +00:00
|
|
|
int planes = plane_data_from_imgfmt(data, &bits, format);
|
|
|
|
if (!planes)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (int i = 0; i < planes; i++) {
|
|
|
|
if (!pl_plane_find_fmt(p->gpu, NULL, &data[i]))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void resize(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2022-02-15 11:47:53 +00:00
|
|
|
struct mp_rect src, dst;
|
|
|
|
struct mp_osd_res osd;
|
|
|
|
vo_get_src_dst_rects(vo, &src, &dst, &osd);
|
2022-04-22 20:06:20 +00:00
|
|
|
if (vo->dwidth && vo->dheight) {
|
|
|
|
gpu_ctx_resize(p->context, vo->dwidth, vo->dheight);
|
|
|
|
vo->want_redraw = true;
|
|
|
|
}
|
2022-02-15 11:47:53 +00:00
|
|
|
|
|
|
|
if (mp_rect_equals(&p->src, &src) &&
|
|
|
|
mp_rect_equals(&p->dst, &dst) &&
|
|
|
|
osd_res_equals(p->osd_res, osd))
|
|
|
|
return;
|
|
|
|
|
|
|
|
p->osd_sync++;
|
|
|
|
p->osd_res = osd;
|
|
|
|
p->src = src;
|
|
|
|
p->dst = dst;
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int reconfig(struct vo *vo, struct mp_image_params *params)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
if (!p->ra_ctx->fns->reconfig(p->ra_ctx))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
resize(vo);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-09-14 18:26:06 +00:00
|
|
|
// Takes over ownership of `icc`. Can be used to unload profile (icc.len == 0)
|
|
|
|
static bool update_icc(struct priv *p, struct bstr icc)
|
|
|
|
{
|
|
|
|
struct pl_icc_profile profile = {
|
|
|
|
.data = icc.start,
|
|
|
|
.len = icc.len,
|
|
|
|
};
|
|
|
|
|
|
|
|
pl_icc_profile_compute_signature(&profile);
|
|
|
|
|
|
|
|
#if PL_API_VER >= 327
|
|
|
|
bool ok = pl_icc_update(p->pllog, &p->icc_profile, &profile, &p->icc_params);
|
|
|
|
talloc_free(icc.start);
|
|
|
|
return ok;
|
|
|
|
#else
|
|
|
|
talloc_free((void *) p->icc_profile.data);
|
|
|
|
profile.data = talloc_steal(p, profile.data);
|
|
|
|
p->icc_profile = profile;
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns whether the ICC profile was updated (even on failure)
|
2021-04-09 07:14:54 +00:00
|
|
|
static bool update_auto_profile(struct priv *p, int *events)
|
|
|
|
{
|
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
|
|
|
if (!opts->icc_opts || !opts->icc_opts->profile_auto || p->icc_path)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
MP_VERBOSE(p, "Querying ICC profile...\n");
|
|
|
|
bstr icc = {0};
|
|
|
|
int r = p->ra_ctx->fns->control(p->ra_ctx, events, VOCTRL_GET_ICC_PROFILE, &icc);
|
|
|
|
|
|
|
|
if (r != VO_NOTAVAIL) {
|
|
|
|
if (r == VO_FALSE) {
|
|
|
|
MP_WARN(p, "Could not retrieve an ICC profile.\n");
|
|
|
|
} else if (r == VO_NOTIMPL) {
|
|
|
|
MP_ERR(p, "icc-profile-auto not implemented on this platform.\n");
|
|
|
|
}
|
|
|
|
|
2023-09-14 18:26:06 +00:00
|
|
|
update_icc(p, icc);
|
2021-04-09 07:14:54 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-11-25 22:02:39 +00:00
|
|
|
static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2023-08-19 14:03:09 +00:00
|
|
|
pl_options pars = p->pars;
|
2021-11-25 22:02:39 +00:00
|
|
|
pl_gpu gpu = p->gpu;
|
|
|
|
pl_tex fbo = NULL;
|
|
|
|
args->res = NULL;
|
|
|
|
|
2022-02-27 20:15:17 +00:00
|
|
|
update_options(vo);
|
2023-08-27 11:51:00 +00:00
|
|
|
struct pl_render_params params = pars->params;
|
|
|
|
params.info_callback = NULL;
|
|
|
|
params.skip_caching_single_frame = true;
|
|
|
|
params.preserve_mixing_cache = false;
|
|
|
|
params.frame_mixer = NULL;
|
|
|
|
|
|
|
|
struct pl_peak_detect_params peak_params;
|
|
|
|
if (params.peak_detect_params) {
|
|
|
|
peak_params = *params.peak_detect_params;
|
|
|
|
params.peak_detect_params = &peak_params;
|
|
|
|
peak_params.allow_delayed = false;
|
|
|
|
}
|
2023-03-06 20:30:29 +00:00
|
|
|
|
2021-11-25 22:02:39 +00:00
|
|
|
// Retrieve the current frame from the frame queue
|
|
|
|
struct pl_frame_mix mix;
|
|
|
|
enum pl_queue_status status;
|
|
|
|
status = pl_queue_update(p->queue, &mix, pl_queue_params(.pts = p->last_pts));
|
|
|
|
assert(status != PL_QUEUE_EOF);
|
|
|
|
if (status == PL_QUEUE_ERR) {
|
2023-03-27 20:42:17 +00:00
|
|
|
MP_ERR(vo, "Unknown error occurred while trying to take screenshot!\n");
|
2021-11-25 22:02:39 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-07-23 12:54:11 +00:00
|
|
|
if (!mix.num_frames) {
|
2023-08-21 11:42:47 +00:00
|
|
|
MP_ERR(vo, "No frames available to take screenshot of, is a file loaded?\n");
|
2021-11-25 22:02:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Passing an interpolation radius of 0 guarantees that the first frame in
|
|
|
|
// the resulting mix is the correct frame for this PTS
|
2022-06-05 18:00:10 +00:00
|
|
|
struct pl_frame image = *(struct pl_frame *) mix.frames[0];
|
|
|
|
struct mp_image *mpi = image.user_data;
|
2021-11-25 22:02:39 +00:00
|
|
|
struct mp_rect src = p->src, dst = p->dst;
|
|
|
|
struct mp_osd_res osd = p->osd_res;
|
|
|
|
if (!args->scaled) {
|
2023-08-08 02:01:26 +00:00
|
|
|
int w, h;
|
|
|
|
mp_image_params_get_dsize(&mpi->params, &w, &h);
|
2023-08-09 02:18:41 +00:00
|
|
|
if (w < 1 || h < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int src_w = mpi->params.w;
|
|
|
|
int src_h = mpi->params.h;
|
2023-09-07 18:50:43 +00:00
|
|
|
src = (struct mp_rect) {0, 0, src_w, src_h};
|
|
|
|
dst = (struct mp_rect) {0, 0, w, h};
|
|
|
|
|
|
|
|
if (mp_image_crop_valid(&mpi->params))
|
|
|
|
src = mpi->params.crop;
|
|
|
|
|
2023-08-09 02:18:41 +00:00
|
|
|
if (mpi->params.rotate % 180 == 90) {
|
2022-09-27 19:26:41 +00:00
|
|
|
MPSWAP(int, w, h);
|
2023-08-09 02:18:41 +00:00
|
|
|
MPSWAP(int, src_w, src_h);
|
|
|
|
}
|
2023-09-07 18:50:43 +00:00
|
|
|
mp_rect_rotate(&src, src_w, src_h, mpi->params.rotate);
|
|
|
|
mp_rect_rotate(&dst, w, h, mpi->params.rotate);
|
|
|
|
|
2021-11-25 22:02:39 +00:00
|
|
|
osd = (struct mp_osd_res) {
|
|
|
|
.display_par = 1.0,
|
2023-09-07 18:50:43 +00:00
|
|
|
.w = mp_rect_w(dst),
|
|
|
|
.h = mp_rect_h(dst),
|
2021-11-25 22:02:39 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create target FBO, try high bit depth first
|
|
|
|
int mpfmt;
|
|
|
|
for (int depth = args->high_bit_depth ? 16 : 8; depth; depth -= 8) {
|
|
|
|
mpfmt = depth == 16 ? IMGFMT_RGBA64 : IMGFMT_RGBA;
|
|
|
|
pl_fmt fmt = pl_find_fmt(gpu, PL_FMT_UNORM, 4, depth, depth,
|
|
|
|
PL_FMT_CAP_RENDERABLE | PL_FMT_CAP_HOST_READABLE);
|
|
|
|
if (!fmt)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fbo = pl_tex_create(gpu, pl_tex_params(
|
|
|
|
.w = osd.w,
|
|
|
|
.h = osd.h,
|
|
|
|
.format = fmt,
|
|
|
|
.blit_dst = true,
|
|
|
|
.renderable = true,
|
|
|
|
.host_readable = true,
|
|
|
|
.storable = fmt->caps & PL_FMT_CAP_STORABLE,
|
|
|
|
));
|
|
|
|
if (fbo)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fbo) {
|
|
|
|
MP_ERR(vo, "Failed creating target FBO for screenshot!\n");
|
2022-02-15 11:47:53 +00:00
|
|
|
return;
|
2021-11-25 22:02:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct pl_frame target = {
|
2022-12-12 20:37:34 +00:00
|
|
|
.repr = pl_color_repr_rgb,
|
2021-11-25 22:02:39 +00:00
|
|
|
.num_planes = 1,
|
|
|
|
.planes[0] = {
|
|
|
|
.texture = fbo,
|
|
|
|
.components = 4,
|
|
|
|
.component_mapping = {0, 1, 2, 3},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-12-12 20:38:23 +00:00
|
|
|
if (args->scaled) {
|
|
|
|
// Apply target LUT, ICC profile and CSP override only in window mode
|
|
|
|
apply_target_options(p, &target);
|
2022-12-12 21:38:46 +00:00
|
|
|
} else if (args->native_csp) {
|
|
|
|
target.color = image.color;
|
2022-12-12 20:41:15 +00:00
|
|
|
} else {
|
|
|
|
target.color = pl_color_space_srgb;
|
2022-12-12 20:38:23 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 18:00:10 +00:00
|
|
|
apply_crop(&image, src, mpi->params.w, mpi->params.h);
|
2021-11-25 22:02:39 +00:00
|
|
|
apply_crop(&target, dst, fbo->params.w, fbo->params.h);
|
2023-09-02 18:32:13 +00:00
|
|
|
update_tm_viz(&pars->color_map_params, &target);
|
2022-02-15 11:47:53 +00:00
|
|
|
|
|
|
|
int osd_flags = 0;
|
|
|
|
if (!args->subs)
|
|
|
|
osd_flags |= OSD_DRAW_OSD_ONLY;
|
|
|
|
if (!args->osd)
|
|
|
|
osd_flags |= OSD_DRAW_SUB_ONLY;
|
2023-09-07 20:03:10 +00:00
|
|
|
|
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
|
|
|
struct frame_priv *fp = mpi->priv;
|
|
|
|
if (opts->blend_subs) {
|
2023-09-28 18:07:42 +00:00
|
|
|
float rx = pl_rect_w(dst) / pl_rect_w(image.crop);
|
|
|
|
float ry = pl_rect_h(dst) / pl_rect_h(image.crop);
|
|
|
|
struct mp_osd_res res = {
|
|
|
|
.w = pl_rect_w(dst),
|
|
|
|
.h = pl_rect_h(dst),
|
|
|
|
.ml = -image.crop.x0 * rx,
|
|
|
|
.mr = (image.crop.x1 - vo->params->w) * rx,
|
|
|
|
.mt = -image.crop.y0 * ry,
|
|
|
|
.mb = (image.crop.y1 - vo->params->h) * ry,
|
|
|
|
.display_par = 1.0,
|
|
|
|
};
|
|
|
|
update_overlays(vo, res, osd_flags,
|
|
|
|
PL_OVERLAY_COORDS_DST_CROP,
|
|
|
|
&fp->subs, &image, mpi);
|
2023-09-07 20:03:10 +00:00
|
|
|
} else {
|
|
|
|
// Disable overlays when blend_subs is disabled
|
2023-09-16 18:30:47 +00:00
|
|
|
update_overlays(vo, osd, osd_flags, PL_OVERLAY_COORDS_DST_FRAME,
|
|
|
|
&p->osd_state, &target, mpi);
|
2023-09-07 20:03:10 +00:00
|
|
|
image.num_overlays = 0;
|
|
|
|
}
|
2021-11-25 22:02:39 +00:00
|
|
|
|
2023-08-27 11:51:00 +00:00
|
|
|
if (!pl_render_image(p->rr, &image, &target, ¶ms)) {
|
2021-11-25 22:02:39 +00:00
|
|
|
MP_ERR(vo, "Failed rendering frame!\n");
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
args->res = mp_image_alloc(mpfmt, fbo->params.w, fbo->params.h);
|
|
|
|
if (!args->res)
|
|
|
|
goto done;
|
|
|
|
|
2023-02-27 15:44:39 +00:00
|
|
|
args->res->params.color.primaries = mp_prim_from_pl(target.color.primaries);
|
|
|
|
args->res->params.color.gamma = mp_trc_from_pl(target.color.transfer);
|
|
|
|
args->res->params.color.levels = mp_levels_from_pl(target.repr.levels);
|
|
|
|
args->res->params.color.sig_peak = target.color.hdr.max_luma / MP_REF_WHITE;
|
|
|
|
if (args->scaled)
|
2022-12-12 20:41:15 +00:00
|
|
|
args->res->params.p_w = args->res->params.p_h = 1;
|
|
|
|
|
2021-11-25 22:02:39 +00:00
|
|
|
bool ok = pl_tex_download(gpu, pl_tex_transfer_params(
|
|
|
|
.tex = fbo,
|
|
|
|
.ptr = args->res->planes[0],
|
|
|
|
.row_pitch = args->res->stride[0],
|
|
|
|
));
|
|
|
|
|
|
|
|
if (!ok)
|
|
|
|
TA_FREEP(&args->res);
|
|
|
|
|
|
|
|
// fall through
|
|
|
|
done:
|
|
|
|
pl_tex_destroy(gpu, &fbo);
|
|
|
|
}
|
|
|
|
|
2023-04-29 20:08:50 +00:00
|
|
|
static inline void copy_frame_info_to_mp(struct frame_info *pl,
|
|
|
|
struct mp_frame_perf *mp) {
|
|
|
|
static_assert(MP_ARRAY_SIZE(pl->info) == MP_ARRAY_SIZE(mp->perf), "");
|
|
|
|
assert(pl->count <= VO_PASS_PERF_MAX);
|
|
|
|
mp->count = MPMIN(pl->count, VO_PASS_PERF_MAX);
|
|
|
|
|
|
|
|
for (int i = 0; i < mp->count; ++i) {
|
|
|
|
const struct pl_dispatch_info *pass = &pl->info[i];
|
|
|
|
|
|
|
|
static_assert(VO_PERF_SAMPLE_COUNT >= MP_ARRAY_SIZE(pass->samples), "");
|
|
|
|
assert(pass->num_samples <= MP_ARRAY_SIZE(pass->samples));
|
|
|
|
|
|
|
|
struct mp_pass_perf *perf = &mp->perf[i];
|
|
|
|
perf->count = MPMIN(pass->num_samples, VO_PERF_SAMPLE_COUNT);
|
|
|
|
memcpy(perf->samples, pass->samples, perf->count * sizeof(pass->samples[0]));
|
|
|
|
perf->last = pass->last;
|
|
|
|
perf->peak = pass->peak;
|
|
|
|
perf->avg = pass->average;
|
|
|
|
|
|
|
|
strncpy(mp->desc[i], pass->shader->description, sizeof(mp->desc[i]) - 1);
|
|
|
|
mp->desc[i][sizeof(mp->desc[i]) - 1] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
static int control(struct vo *vo, uint32_t request, void *data)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
switch (request) {
|
|
|
|
case VOCTRL_SET_PANSCAN:
|
|
|
|
resize(vo);
|
2022-02-15 11:47:53 +00:00
|
|
|
return VO_TRUE;
|
2021-04-09 07:14:54 +00:00
|
|
|
case VOCTRL_SET_EQUALIZER:
|
|
|
|
case VOCTRL_PAUSE:
|
2023-09-28 18:07:42 +00:00
|
|
|
p->paused = true;
|
2021-04-09 07:14:54 +00:00
|
|
|
if (p->is_interpolated)
|
|
|
|
vo->want_redraw = true;
|
|
|
|
return VO_TRUE;
|
2023-09-28 18:07:42 +00:00
|
|
|
case VOCTRL_RESUME:
|
|
|
|
p->paused = false;
|
2022-02-15 11:47:53 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
case VOCTRL_UPDATE_RENDER_OPTS: {
|
|
|
|
m_config_cache_update(p->opts_cache);
|
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
|
|
|
p->ra_ctx->opts.want_alpha = opts->alpha_mode == ALPHA_YES;
|
|
|
|
if (p->ra_ctx->fns->update_render_opts)
|
|
|
|
p->ra_ctx->fns->update_render_opts(p->ra_ctx);
|
2022-02-27 20:15:17 +00:00
|
|
|
update_render_options(vo);
|
2021-04-09 07:14:54 +00:00
|
|
|
vo->want_redraw = true;
|
|
|
|
|
|
|
|
// Also re-query the auto profile, in case `update_render_options`
|
|
|
|
// unloaded a manually specified icc profile in favor of
|
|
|
|
// icc-profile-auto
|
|
|
|
int events = 0;
|
|
|
|
update_auto_profile(p, &events);
|
|
|
|
vo_event(vo, events);
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VOCTRL_RESET:
|
|
|
|
// Defer until the first new frame (unique ID) actually arrives
|
|
|
|
p->want_reset = true;
|
|
|
|
return VO_TRUE;
|
|
|
|
|
2023-04-29 20:08:50 +00:00
|
|
|
case VOCTRL_PERFORMANCE_DATA: {
|
|
|
|
struct voctrl_performance_data *perf = data;
|
|
|
|
copy_frame_info_to_mp(&p->perf_fresh, &perf->fresh);
|
|
|
|
copy_frame_info_to_mp(&p->perf_redraw, &perf->redraw);
|
2021-04-09 07:14:54 +00:00
|
|
|
return true;
|
2023-04-29 20:08:50 +00:00
|
|
|
}
|
2021-11-25 22:02:39 +00:00
|
|
|
|
2023-08-29 08:23:26 +00:00
|
|
|
case VOCTRL_HDR_METADATA: {
|
|
|
|
struct mp_hdr_metadata *hdr = data;
|
|
|
|
hdr->min_luma = p->last_hdr_metadata.min_luma;
|
|
|
|
hdr->max_luma = p->last_hdr_metadata.max_luma;
|
|
|
|
hdr->max_cll = p->last_hdr_metadata.max_cll;
|
|
|
|
hdr->max_fall = p->last_hdr_metadata.max_fall;
|
|
|
|
hdr->scene_max[0] = p->last_hdr_metadata.scene_max[0];
|
|
|
|
hdr->scene_max[1] = p->last_hdr_metadata.scene_max[1];
|
|
|
|
hdr->scene_max[2] = p->last_hdr_metadata.scene_max[2];
|
|
|
|
hdr->scene_avg = p->last_hdr_metadata.scene_avg;
|
|
|
|
hdr->max_pq_y = p->last_hdr_metadata.max_pq_y;
|
|
|
|
hdr->avg_pq_y = p->last_hdr_metadata.avg_pq_y;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-25 22:02:39 +00:00
|
|
|
case VOCTRL_SCREENSHOT:
|
|
|
|
video_screenshot(vo, data);
|
|
|
|
return true;
|
2021-12-19 00:39:48 +00:00
|
|
|
|
|
|
|
case VOCTRL_EXTERNAL_RESIZE:
|
|
|
|
reconfig(vo, NULL);
|
|
|
|
return true;
|
2022-02-25 21:07:56 +00:00
|
|
|
|
|
|
|
case VOCTRL_LOAD_HWDEC_API:
|
2022-03-20 19:31:38 +00:00
|
|
|
ra_hwdec_ctx_load_fmt(&p->hwdec_ctx, vo->hwdec_devs, data);
|
2022-02-25 21:07:56 +00:00
|
|
|
return true;
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int events = 0;
|
|
|
|
int r = p->ra_ctx->fns->control(p->ra_ctx, &events, request, data);
|
|
|
|
if (events & VO_EVENT_ICC_PROFILE_CHANGED) {
|
|
|
|
if (update_auto_profile(p, &events))
|
|
|
|
vo->want_redraw = true;
|
|
|
|
}
|
|
|
|
if (events & VO_EVENT_RESIZE)
|
|
|
|
resize(vo);
|
|
|
|
if (events & VO_EVENT_EXPOSE)
|
|
|
|
vo->want_redraw = true;
|
|
|
|
vo_event(vo, events);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wakeup(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
if (p->ra_ctx && p->ra_ctx->fns->wakeup)
|
|
|
|
p->ra_ctx->fns->wakeup(p->ra_ctx);
|
|
|
|
}
|
|
|
|
|
2023-09-18 02:34:32 +00:00
|
|
|
static void wait_events(struct vo *vo, int64_t until_time_ns)
|
2021-04-09 07:14:54 +00:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
if (p->ra_ctx && p->ra_ctx->fns->wait_events) {
|
2023-09-18 02:34:32 +00:00
|
|
|
p->ra_ctx->fns->wait_events(p->ra_ctx, until_time_ns);
|
2021-04-09 07:14:54 +00:00
|
|
|
} else {
|
2023-09-18 02:34:32 +00:00
|
|
|
vo_wait_default(vo, until_time_ns);
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *get_cache_file(struct priv *p)
|
|
|
|
{
|
2023-07-05 20:11:40 +00:00
|
|
|
char *file = NULL;
|
2021-04-09 07:14:54 +00:00
|
|
|
struct gl_video_opts *opts = p->opts_cache->opts;
|
2023-05-03 00:42:23 +00:00
|
|
|
if (!opts->shader_cache)
|
2023-07-05 20:11:40 +00:00
|
|
|
goto done;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-05-03 00:42:23 +00:00
|
|
|
char *dir = opts->shader_cache_dir;
|
|
|
|
if (dir && dir[0]) {
|
|
|
|
dir = mp_get_user_path(NULL, p->global, dir);
|
|
|
|
} else {
|
|
|
|
dir = mp_find_user_file(NULL, p->global, "cache", "");
|
|
|
|
}
|
2023-07-05 20:11:40 +00:00
|
|
|
if (dir && dir[0]) {
|
|
|
|
file = mp_path_join(NULL, dir, "libplacebo.cache");
|
|
|
|
mp_mkdirp(dir);
|
|
|
|
}
|
2021-04-09 07:14:54 +00:00
|
|
|
talloc_free(dir);
|
2023-07-05 20:11:40 +00:00
|
|
|
done:
|
2021-04-09 07:14:54 +00:00
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uninit(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
pl_queue_destroy(&p->queue); // destroy this first
|
|
|
|
for (int i = 0; i < MP_ARRAY_SIZE(p->osd_state.entries); i++)
|
|
|
|
pl_tex_destroy(p->gpu, &p->osd_state.entries[i].tex);
|
|
|
|
for (int i = 0; i < p->num_sub_tex; i++)
|
|
|
|
pl_tex_destroy(p->gpu, &p->sub_tex[i]);
|
|
|
|
for (int i = 0; i < p->num_user_hooks; i++)
|
|
|
|
pl_mpv_user_shader_destroy(&p->user_hooks[i].hook);
|
|
|
|
|
2022-02-25 21:07:56 +00:00
|
|
|
if (vo->hwdec_devs) {
|
2022-04-09 21:58:59 +00:00
|
|
|
ra_hwdec_mapper_free(&p->hwdec_mapper);
|
2022-02-25 21:07:56 +00:00
|
|
|
ra_hwdec_ctx_uninit(&p->hwdec_ctx);
|
|
|
|
hwdec_devices_set_loader(vo->hwdec_devs, NULL, NULL);
|
|
|
|
hwdec_devices_destroy(vo->hwdec_devs);
|
|
|
|
}
|
|
|
|
|
2022-03-06 21:59:05 +00:00
|
|
|
assert(p->num_dr_buffers == 0);
|
|
|
|
pthread_mutex_destroy(&p->dr_lock);
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
char *cache_file = get_cache_file(p);
|
2022-10-14 13:33:47 +00:00
|
|
|
if (cache_file && p->rr) {
|
2021-04-09 07:14:54 +00:00
|
|
|
FILE *cache = fopen(cache_file, "wb");
|
|
|
|
if (cache) {
|
|
|
|
size_t size = pl_renderer_save(p->rr, NULL);
|
|
|
|
uint8_t *buf = talloc_size(NULL, size);
|
|
|
|
pl_renderer_save(p->rr, buf);
|
|
|
|
fwrite(buf, size, 1, cache);
|
|
|
|
talloc_free(buf);
|
|
|
|
fclose(cache);
|
|
|
|
}
|
|
|
|
talloc_free(cache_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
pl_renderer_destroy(&p->rr);
|
2021-11-20 19:59:37 +00:00
|
|
|
|
2023-04-29 20:08:50 +00:00
|
|
|
for (int i = 0; i < VO_PASS_PERF_MAX; ++i) {
|
|
|
|
pl_shader_info_deref(&p->perf_fresh.info[i].shader);
|
|
|
|
pl_shader_info_deref(&p->perf_redraw.info[i].shader);
|
|
|
|
}
|
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
pl_options_free(&p->pars);
|
|
|
|
|
2021-11-20 19:59:37 +00:00
|
|
|
p->ra_ctx = NULL;
|
|
|
|
p->pllog = NULL;
|
|
|
|
p->gpu = NULL;
|
|
|
|
p->sw = NULL;
|
|
|
|
gpu_ctx_destroy(&p->context);
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
2022-03-20 19:31:38 +00:00
|
|
|
static void load_hwdec_api(void *ctx, struct hwdec_imgfmt_request *params)
|
2022-02-25 21:07:56 +00:00
|
|
|
{
|
2022-03-20 19:31:38 +00:00
|
|
|
vo_control(ctx, VOCTRL_LOAD_HWDEC_API, params);
|
2022-02-25 21:07:56 +00:00
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
static int preinit(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
p->opts_cache = m_config_cache_alloc(p, vo->global, &gl_video_conf);
|
|
|
|
p->video_eq = mp_csp_equalizer_create(p, vo->global);
|
|
|
|
p->global = vo->global;
|
|
|
|
p->log = vo->log;
|
|
|
|
|
|
|
|
struct gl_video_opts *gl_opts = p->opts_cache->opts;
|
2021-11-20 19:59:37 +00:00
|
|
|
p->context = gpu_ctx_create(vo, gl_opts);
|
|
|
|
if (!p->context)
|
|
|
|
goto err_out;
|
|
|
|
// For the time being
|
|
|
|
p->ra_ctx = p->context->ra_ctx;
|
|
|
|
p->pllog = p->context->pllog;
|
|
|
|
p->gpu = p->context->gpu;
|
|
|
|
p->sw = p->context->swapchain;
|
2022-02-25 21:07:56 +00:00
|
|
|
p->hwdec_ctx = (struct ra_hwdec_ctx) {
|
|
|
|
.log = p->log,
|
|
|
|
.global = p->global,
|
2022-03-12 18:54:23 +00:00
|
|
|
.ra_ctx = p->ra_ctx,
|
2022-02-25 21:07:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
vo->hwdec_devs = hwdec_devices_create();
|
|
|
|
hwdec_devices_set_loader(vo->hwdec_devs, load_hwdec_api, vo);
|
|
|
|
ra_hwdec_ctx_init(&p->hwdec_ctx, vo->hwdec_devs, gl_opts->hwdec_interop, false);
|
2022-03-06 21:59:05 +00:00
|
|
|
pthread_mutex_init(&p->dr_lock, NULL);
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-09-08 14:45:21 +00:00
|
|
|
#if PL_API_VER >= 320
|
|
|
|
p->cache = pl_cache_create(pl_cache_params(
|
|
|
|
.log = p->pllog,
|
|
|
|
.max_object_size = 1 << 20, // 1 MB
|
|
|
|
.max_total_size = 10 << 20, // 10 MB
|
|
|
|
));
|
|
|
|
pl_gpu_set_cache(p->gpu, p->cache);
|
|
|
|
#endif
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
p->rr = pl_renderer_create(p->pllog, p->gpu);
|
|
|
|
p->queue = pl_queue_create(p->gpu);
|
|
|
|
p->osd_fmt[SUBBITMAP_LIBASS] = pl_find_named_fmt(p->gpu, "r8");
|
2022-01-11 20:06:33 +00:00
|
|
|
p->osd_fmt[SUBBITMAP_BGRA] = pl_find_named_fmt(p->gpu, "bgra8");
|
2022-02-15 11:47:53 +00:00
|
|
|
p->osd_sync = 1;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
char *cache_file = get_cache_file(p);
|
|
|
|
if (cache_file) {
|
|
|
|
if (stat(cache_file, &(struct stat){0}) == 0) {
|
|
|
|
bstr c = stream_read_file(cache_file, p, vo->global, 1000000000);
|
|
|
|
pl_renderer_load(p->rr, c.start);
|
|
|
|
talloc_free(c.start);
|
|
|
|
}
|
|
|
|
talloc_free(cache_file);
|
|
|
|
}
|
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
p->pars = pl_options_alloc(p->pllog);
|
2022-02-27 20:15:17 +00:00
|
|
|
update_render_options(vo);
|
2021-04-09 07:14:54 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_out:
|
|
|
|
uninit(vo);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct pl_filter_config *map_scaler(struct priv *p,
|
|
|
|
enum scaler_unit unit)
|
|
|
|
{
|
2023-04-24 00:51:28 +00:00
|
|
|
const struct pl_filter_preset fixed_scalers[] = {
|
2021-04-09 07:14:54 +00:00
|
|
|
{ "bilinear", &pl_filter_bilinear },
|
|
|
|
{ "bicubic_fast", &pl_filter_bicubic },
|
|
|
|
{ "nearest", &pl_filter_nearest },
|
|
|
|
{ "oversample", &pl_filter_oversample },
|
|
|
|
{0},
|
|
|
|
};
|
|
|
|
|
2023-04-24 00:51:28 +00:00
|
|
|
const struct pl_filter_preset fixed_frame_mixers[] = {
|
2021-04-09 07:14:54 +00:00
|
|
|
{ "linear", &pl_filter_bilinear },
|
|
|
|
{ "oversample", &pl_filter_oversample },
|
|
|
|
{0},
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct pl_filter_preset *fixed_presets =
|
|
|
|
unit == SCALER_TSCALE ? fixed_frame_mixers : fixed_scalers;
|
|
|
|
|
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
|
|
|
const struct scaler_config *cfg = &opts->scaler[unit];
|
2023-09-16 23:47:43 +00:00
|
|
|
if (unit == SCALER_DSCALE && (!cfg->kernel.name || !cfg->kernel.name[0]))
|
|
|
|
cfg = &opts->scaler[SCALER_SCALE];
|
|
|
|
if (unit == SCALER_CSCALE && (!cfg->kernel.name || !cfg->kernel.name[0]))
|
2021-04-09 07:14:54 +00:00
|
|
|
cfg = &opts->scaler[SCALER_SCALE];
|
|
|
|
|
|
|
|
for (int i = 0; fixed_presets[i].name; i++) {
|
|
|
|
if (strcmp(cfg->kernel.name, fixed_presets[i].name) == 0)
|
|
|
|
return fixed_presets[i].filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt loading filter preset first, fall back to raw filter function
|
|
|
|
struct scaler_params *par = &p->scalers[unit];
|
|
|
|
const struct pl_filter_preset *preset;
|
|
|
|
const struct pl_filter_function_preset *fpreset;
|
|
|
|
if ((preset = pl_find_filter_preset(cfg->kernel.name))) {
|
|
|
|
par->config = *preset->filter;
|
|
|
|
} else if ((fpreset = pl_find_filter_function_preset(cfg->kernel.name))) {
|
2023-08-06 16:09:04 +00:00
|
|
|
par->config = (struct pl_filter_config) {
|
|
|
|
.kernel = fpreset->function,
|
|
|
|
#if PL_API_VER >= 303
|
|
|
|
.params[0] = fpreset->function->params[0],
|
|
|
|
.params[1] = fpreset->function->params[1],
|
|
|
|
#endif
|
|
|
|
};
|
2021-04-09 07:14:54 +00:00
|
|
|
} else {
|
|
|
|
MP_ERR(p, "Failed mapping filter function '%s', no libplacebo analog?\n",
|
|
|
|
cfg->kernel.name);
|
|
|
|
return &pl_filter_bilinear;
|
|
|
|
}
|
|
|
|
|
2023-08-06 16:09:04 +00:00
|
|
|
const struct pl_filter_function_preset *wpreset;
|
|
|
|
if ((wpreset = pl_find_filter_function_preset(cfg->window.name))) {
|
|
|
|
par->config.window = wpreset->function;
|
|
|
|
#if PL_API_VER >= 303
|
|
|
|
par->config.wparams[0] = wpreset->function->params[0];
|
|
|
|
par->config.wparams[1] = wpreset->function->params[1];
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#if PL_API_VER < 303
|
|
|
|
par->kernel = *par->config.kernel;
|
2021-04-09 07:14:54 +00:00
|
|
|
par->config.kernel = &par->kernel;
|
|
|
|
if (par->config.window) {
|
|
|
|
par->window = *par->config.window;
|
|
|
|
par->config.window = &par->window;
|
|
|
|
}
|
2023-08-06 16:09:04 +00:00
|
|
|
#endif
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
2023-08-06 16:09:04 +00:00
|
|
|
#if PL_API_VER >= 303
|
|
|
|
if (!isnan(cfg->kernel.params[i]))
|
|
|
|
par->config.params[i] = cfg->kernel.params[i];
|
|
|
|
if (!isnan(cfg->window.params[i]))
|
|
|
|
par->config.wparams[i] = cfg->window.params[i];
|
|
|
|
#else
|
2021-04-09 07:14:54 +00:00
|
|
|
if (!isnan(cfg->kernel.params[i]))
|
|
|
|
par->kernel.params[i] = cfg->kernel.params[i];
|
|
|
|
if (!isnan(cfg->window.params[i]))
|
|
|
|
par->window.params[i] = cfg->window.params[i];
|
2023-08-06 16:09:04 +00:00
|
|
|
#endif
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
par->config.clamp = cfg->clamp;
|
2023-09-17 19:59:51 +00:00
|
|
|
if (cfg->kernel.blur > 0.0)
|
|
|
|
par->config.blur = cfg->kernel.blur;
|
|
|
|
if (cfg->kernel.taper > 0.0)
|
|
|
|
par->config.taper = cfg->kernel.taper;
|
2021-04-09 07:14:54 +00:00
|
|
|
if (cfg->radius > 0.0) {
|
2023-08-06 16:09:04 +00:00
|
|
|
if (par->config.kernel->resizable) {
|
|
|
|
#if PL_API_VER >= 303
|
|
|
|
par->config.radius = cfg->radius;
|
|
|
|
#else
|
2021-04-09 07:14:54 +00:00
|
|
|
par->kernel.radius = cfg->radius;
|
2023-08-06 16:09:04 +00:00
|
|
|
#endif
|
2021-04-09 07:14:54 +00:00
|
|
|
} else {
|
|
|
|
MP_WARN(p, "Filter radius specified but filter '%s' is not "
|
|
|
|
"resizable, ignoring\n", cfg->kernel.name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &par->config;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct pl_hook *load_hook(struct priv *p, const char *path)
|
|
|
|
{
|
|
|
|
if (!path || !path[0])
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (int i = 0; i < p->num_user_hooks; i++) {
|
|
|
|
if (strcmp(p->user_hooks[i].path, path) == 0)
|
|
|
|
return p->user_hooks[i].hook;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *fname = mp_get_user_path(NULL, p->global, path);
|
|
|
|
bstr shader = stream_read_file(fname, p, p->global, 1000000000); // 1GB
|
|
|
|
talloc_free(fname);
|
|
|
|
|
|
|
|
const struct pl_hook *hook = NULL;
|
|
|
|
if (shader.len)
|
|
|
|
hook = pl_mpv_user_shader_parse(p->gpu, shader.start, shader.len);
|
|
|
|
|
|
|
|
MP_TARRAY_APPEND(p, p->user_hooks, p->num_user_hooks, (struct user_hook) {
|
|
|
|
.path = talloc_strdup(p, path),
|
|
|
|
.hook = hook,
|
|
|
|
});
|
|
|
|
|
|
|
|
return hook;
|
|
|
|
}
|
|
|
|
|
2022-09-11 14:47:14 +00:00
|
|
|
static stream_t *icc_open_cache(struct priv *p, uint64_t sig, int flags)
|
|
|
|
{
|
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
2023-05-03 00:42:23 +00:00
|
|
|
if (!opts->icc_opts->cache)
|
2022-09-11 14:47:14 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
char cache_name[16+1];
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
|
|
cache_name[i] = "0123456789ABCDEF"[sig & 0xF];
|
|
|
|
sig >>= 4;
|
|
|
|
}
|
|
|
|
cache_name[16] = '\0';
|
|
|
|
|
2023-05-03 00:42:23 +00:00
|
|
|
char *cache_dir = opts->icc_opts->cache_dir;
|
|
|
|
if (cache_dir && cache_dir[0]) {
|
|
|
|
cache_dir = mp_get_user_path(NULL, p->global, cache_dir);
|
|
|
|
} else {
|
|
|
|
cache_dir = mp_find_user_file(NULL, p->global, "cache", "");
|
|
|
|
}
|
2022-09-11 14:47:14 +00:00
|
|
|
|
2023-07-05 20:11:40 +00:00
|
|
|
if (!cache_dir || !cache_dir[0])
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
char *path = mp_path_join(NULL, cache_dir, cache_name);
|
2022-09-11 14:47:14 +00:00
|
|
|
stream_t *stream = NULL;
|
|
|
|
if (flags & STREAM_WRITE) {
|
|
|
|
mp_mkdirp(cache_dir);
|
|
|
|
} else {
|
|
|
|
// Exit silently if the file does not exist
|
|
|
|
if (stat(path, &(struct stat) {0}) < 0)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags |= STREAM_ORIGIN_DIRECT | STREAM_LOCAL_FS_ONLY | STREAM_LESS_NOISE;
|
|
|
|
stream = stream_create(path, flags, NULL, p->global);
|
|
|
|
// fall through
|
|
|
|
done:
|
|
|
|
talloc_free(cache_dir);
|
|
|
|
talloc_free(path);
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void icc_save(void *priv, uint64_t sig, const uint8_t *cache, size_t size)
|
|
|
|
{
|
|
|
|
struct priv *p = priv;
|
|
|
|
stream_t *s = icc_open_cache(p, sig, STREAM_WRITE);
|
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
stream_write_buffer(s, (void *) cache, size);
|
|
|
|
free_stream(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool icc_load(void *priv, uint64_t sig, uint8_t *cache, size_t size)
|
|
|
|
{
|
|
|
|
struct priv *p = priv;
|
|
|
|
stream_t *s = icc_open_cache(p, sig, STREAM_READ);
|
|
|
|
if (!s)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int len = stream_read(s, cache, size);
|
|
|
|
free_stream(s);
|
|
|
|
return len == size;
|
|
|
|
}
|
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
static void update_icc_opts(struct priv *p, const struct mp_icc_opts *opts)
|
|
|
|
{
|
|
|
|
if (!opts)
|
|
|
|
return;
|
|
|
|
|
2023-09-14 18:26:06 +00:00
|
|
|
if (!opts->profile_auto && !p->icc_path) {
|
2021-04-09 07:14:54 +00:00
|
|
|
// Un-set any auto-loaded profiles if icc-profile-auto was disabled
|
2023-09-14 18:26:06 +00:00
|
|
|
update_icc(p, (bstr) {0});
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int s_r = 0, s_g = 0, s_b = 0;
|
|
|
|
gl_parse_3dlut_size(opts->size_str, &s_r, &s_g, &s_b);
|
2023-09-14 18:26:06 +00:00
|
|
|
p->icc_params = pl_icc_default_params;
|
|
|
|
p->icc_params.intent = opts->intent;
|
|
|
|
p->icc_params.size_r = s_r;
|
|
|
|
p->icc_params.size_g = s_g;
|
|
|
|
p->icc_params.size_b = s_b;
|
|
|
|
p->icc_params.cache_priv = p;
|
|
|
|
p->icc_params.cache_save = icc_save;
|
|
|
|
p->icc_params.cache_load = icc_load;
|
|
|
|
#if PL_API_VER < 327
|
|
|
|
p->pars->params.icc_params = &p->icc_params;
|
|
|
|
#endif
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
if (!opts->profile || !opts->profile[0]) {
|
|
|
|
// No profile enabled, un-load any existing profiles
|
2023-09-14 18:26:06 +00:00
|
|
|
update_icc(p, (bstr) {0});
|
|
|
|
TA_FREEP(&p->icc_path);
|
2021-04-09 07:14:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->icc_path && strcmp(opts->profile, p->icc_path) == 0)
|
|
|
|
return; // ICC profile hasn't changed
|
|
|
|
|
|
|
|
char *fname = mp_get_user_path(NULL, p->global, opts->profile);
|
|
|
|
MP_VERBOSE(p, "Opening ICC profile '%s'\n", fname);
|
|
|
|
struct bstr icc = stream_read_file(fname, p, p->global, 100000000); // 100 MB
|
|
|
|
talloc_free(fname);
|
2023-09-14 18:26:06 +00:00
|
|
|
update_icc(p, icc);
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
// Update cached path
|
|
|
|
talloc_free(p->icc_path);
|
|
|
|
p->icc_path = talloc_strdup(p, opts->profile);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_lut(struct priv *p, struct user_lut *lut)
|
|
|
|
{
|
|
|
|
if (!lut->opt) {
|
|
|
|
pl_lut_free(&lut->lut);
|
|
|
|
TA_FREEP(&lut->path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lut->path && strcmp(lut->path, lut->opt) == 0)
|
|
|
|
return; // no change
|
|
|
|
|
|
|
|
// Update cached path
|
|
|
|
pl_lut_free(&lut->lut);
|
|
|
|
talloc_free(lut->path);
|
|
|
|
lut->path = talloc_strdup(p, lut->opt);
|
|
|
|
|
|
|
|
// Load LUT file
|
|
|
|
char *fname = mp_get_user_path(NULL, p->global, lut->path);
|
|
|
|
MP_VERBOSE(p, "Loading custom LUT '%s'\n", fname);
|
|
|
|
struct bstr lutdata = stream_read_file(fname, p, p->global, 100000000); // 100 MB
|
|
|
|
lut->lut = pl_lut_parse_cube(p->pllog, lutdata.start, lutdata.len);
|
|
|
|
talloc_free(lutdata.start);
|
|
|
|
}
|
|
|
|
|
2022-11-03 15:16:59 +00:00
|
|
|
static void update_hook_opts(struct priv *p, char **opts, const char *shaderpath,
|
|
|
|
const struct pl_hook *hook)
|
|
|
|
{
|
|
|
|
if (!opts)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const char *basename = mp_basename(shaderpath);
|
|
|
|
struct bstr shadername;
|
|
|
|
if (!mp_splitext(basename, &shadername))
|
|
|
|
shadername = bstr0(basename);
|
|
|
|
|
|
|
|
for (int n = 0; opts[n * 2]; n++) {
|
|
|
|
struct bstr k = bstr0(opts[n * 2 + 0]);
|
|
|
|
struct bstr v = bstr0(opts[n * 2 + 1]);
|
|
|
|
int pos;
|
|
|
|
if ((pos = bstrchr(k, '/')) >= 0) {
|
|
|
|
if (!bstr_equals(bstr_splice(k, 0, pos), shadername))
|
|
|
|
continue;
|
|
|
|
k = bstr_cut(k, pos + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < hook->num_parameters; i++) {
|
|
|
|
const struct pl_hook_par *hp = &hook->parameters[i];
|
|
|
|
if (!bstr_equals0(k, hp->name) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m_option_t opt = {
|
|
|
|
.name = hp->name,
|
|
|
|
};
|
|
|
|
|
2023-08-07 16:50:04 +00:00
|
|
|
#if PL_API_VER >= 308
|
|
|
|
if (hp->names) {
|
|
|
|
for (int j = hp->minimum.i; j <= hp->maximum.i; j++) {
|
|
|
|
if (bstr_equals0(v, hp->names[j])) {
|
|
|
|
hp->data->i = j;
|
|
|
|
goto next_hook;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-11-03 15:16:59 +00:00
|
|
|
switch (hp->type) {
|
|
|
|
case PL_VAR_FLOAT:
|
|
|
|
opt.type = &m_option_type_float;
|
|
|
|
opt.min = hp->minimum.f;
|
|
|
|
opt.max = hp->maximum.f;
|
|
|
|
break;
|
|
|
|
case PL_VAR_SINT:
|
|
|
|
opt.type = &m_option_type_int;
|
|
|
|
opt.min = hp->minimum.i;
|
|
|
|
opt.max = hp->maximum.i;
|
|
|
|
break;
|
|
|
|
case PL_VAR_UINT:
|
|
|
|
opt.type = &m_option_type_int;
|
|
|
|
opt.min = MPMIN(hp->minimum.u, INT_MAX);
|
|
|
|
opt.max = MPMIN(hp->maximum.u, INT_MAX);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt.type->parse(p->log, &opt, k, v, hp->data);
|
2023-08-07 16:50:04 +00:00
|
|
|
goto next_hook;
|
2022-11-03 15:16:59 +00:00
|
|
|
}
|
2023-08-07 16:50:04 +00:00
|
|
|
|
|
|
|
next_hook:;
|
2022-11-03 15:16:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-27 20:15:17 +00:00
|
|
|
static void update_render_options(struct vo *vo)
|
2021-04-09 07:14:54 +00:00
|
|
|
{
|
2022-02-27 20:15:17 +00:00
|
|
|
struct priv *p = vo->priv;
|
2023-08-18 13:36:59 +00:00
|
|
|
pl_options pars = p->pars;
|
2021-04-09 07:14:54 +00:00
|
|
|
const struct gl_video_opts *opts = p->opts_cache->opts;
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.antiringing_strength = opts->scaler[0].antiring;
|
|
|
|
pars->params.background_color[0] = opts->background.r / 255.0;
|
|
|
|
pars->params.background_color[1] = opts->background.g / 255.0;
|
|
|
|
pars->params.background_color[2] = opts->background.b / 255.0;
|
|
|
|
pars->params.background_transparency = 1.0 - opts->background.a / 255.0;
|
|
|
|
pars->params.skip_anti_aliasing = !opts->correct_downscaling;
|
|
|
|
pars->params.disable_linear_scaling = !opts->linear_downscaling && !opts->linear_upscaling;
|
|
|
|
pars->params.disable_fbos = opts->dumb_mode == 1;
|
|
|
|
pars->params.blend_against_tiles = opts->alpha_mode == ALPHA_BLEND_TILES;
|
|
|
|
pars->params.corner_rounding = p->corner_rounding;
|
2023-09-14 16:24:25 +00:00
|
|
|
#if PL_API_VER >= 324
|
|
|
|
pars->params.correct_subpixel_offsets = !opts->scaler_resizes_only;
|
|
|
|
#endif
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
// Map scaler options as best we can
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.upscaler = map_scaler(p, SCALER_SCALE);
|
|
|
|
pars->params.downscaler = map_scaler(p, SCALER_DSCALE);
|
|
|
|
pars->params.plane_upscaler = map_scaler(p, SCALER_CSCALE);
|
2023-08-27 11:58:50 +00:00
|
|
|
pars->params.frame_mixer = opts->interpolation ? map_scaler(p, SCALER_TSCALE) : NULL;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2022-02-27 20:15:17 +00:00
|
|
|
// Request as many frames as required from the decoder
|
2023-08-27 11:35:52 +00:00
|
|
|
int req_frames = 2;
|
|
|
|
if (pars->params.frame_mixer)
|
|
|
|
req_frames += ceilf(pars->params.frame_mixer->kernel->radius);
|
|
|
|
vo_set_queue_params(vo, 0, req_frames);
|
2022-02-27 20:15:17 +00:00
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.deband_params = opts->deband ? &pars->deband_params : NULL;
|
|
|
|
pars->deband_params.iterations = opts->deband_opts->iterations;
|
|
|
|
pars->deband_params.radius = opts->deband_opts->range;
|
|
|
|
pars->deband_params.threshold = opts->deband_opts->threshold / 16.384;
|
|
|
|
pars->deband_params.grain = opts->deband_opts->grain / 8.192;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.sigmoid_params = opts->sigmoid_upscaling ? &pars->sigmoid_params : NULL;
|
|
|
|
pars->sigmoid_params.center = opts->sigmoid_center;
|
|
|
|
pars->sigmoid_params.slope = opts->sigmoid_slope;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.peak_detect_params = opts->tone_map.compute_peak >= 0 ? &pars->peak_detect_params : NULL;
|
|
|
|
pars->peak_detect_params.smoothing_period = opts->tone_map.decay_rate;
|
|
|
|
pars->peak_detect_params.scene_threshold_low = opts->tone_map.scene_threshold_low;
|
|
|
|
pars->peak_detect_params.scene_threshold_high = opts->tone_map.scene_threshold_high;
|
|
|
|
pars->peak_detect_params.percentile = opts->tone_map.peak_percentile;
|
2023-08-18 15:14:08 +00:00
|
|
|
pars->peak_detect_params.allow_delayed = p->delayed_peak;
|
2021-04-09 07:14:54 +00:00
|
|
|
|
2023-04-24 00:51:28 +00:00
|
|
|
const struct pl_tone_map_function * const tone_map_funs[] = {
|
2022-01-03 02:56:36 +00:00
|
|
|
[TONE_MAPPING_AUTO] = &pl_tone_map_auto,
|
|
|
|
[TONE_MAPPING_CLIP] = &pl_tone_map_clip,
|
|
|
|
[TONE_MAPPING_MOBIUS] = &pl_tone_map_mobius,
|
|
|
|
[TONE_MAPPING_REINHARD] = &pl_tone_map_reinhard,
|
|
|
|
[TONE_MAPPING_HABLE] = &pl_tone_map_hable,
|
|
|
|
[TONE_MAPPING_GAMMA] = &pl_tone_map_gamma,
|
|
|
|
[TONE_MAPPING_LINEAR] = &pl_tone_map_linear,
|
|
|
|
[TONE_MAPPING_SPLINE] = &pl_tone_map_spline,
|
|
|
|
[TONE_MAPPING_BT_2390] = &pl_tone_map_bt2390,
|
|
|
|
[TONE_MAPPING_BT_2446A] = &pl_tone_map_bt2446a,
|
2023-02-11 17:20:14 +00:00
|
|
|
[TONE_MAPPING_ST2094_40] = &pl_tone_map_st2094_40,
|
|
|
|
[TONE_MAPPING_ST2094_10] = &pl_tone_map_st2094_10,
|
2021-04-09 07:14:54 +00:00
|
|
|
};
|
|
|
|
|
2023-08-21 17:49:27 +00:00
|
|
|
const struct pl_gamut_map_function * const gamut_modes[] = {
|
|
|
|
[GAMUT_AUTO] = pl_color_map_default_params.gamut_mapping,
|
2023-05-24 19:24:48 +00:00
|
|
|
[GAMUT_CLIP] = &pl_gamut_map_clip,
|
2023-06-19 10:59:51 +00:00
|
|
|
[GAMUT_PERCEPTUAL] = &pl_gamut_map_perceptual,
|
|
|
|
[GAMUT_RELATIVE] = &pl_gamut_map_relative,
|
|
|
|
[GAMUT_SATURATION] = &pl_gamut_map_saturation,
|
|
|
|
[GAMUT_ABSOLUTE] = &pl_gamut_map_absolute,
|
2023-05-24 19:24:48 +00:00
|
|
|
[GAMUT_DESATURATE] = &pl_gamut_map_desaturate,
|
|
|
|
[GAMUT_DARKEN] = &pl_gamut_map_darken,
|
2023-06-19 10:59:51 +00:00
|
|
|
[GAMUT_WARN] = &pl_gamut_map_highlight,
|
|
|
|
[GAMUT_LINEAR] = &pl_gamut_map_linear,
|
2023-05-24 19:24:48 +00:00
|
|
|
};
|
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->color_map_params.tone_mapping_function = tone_map_funs[opts->tone_map.curve];
|
|
|
|
pars->color_map_params.tone_mapping_param = opts->tone_map.curve_param;
|
|
|
|
if (isnan(pars->color_map_params.tone_mapping_param)) // vo_gpu compatibility
|
|
|
|
pars->color_map_params.tone_mapping_param = 0.0;
|
2023-08-21 17:50:20 +00:00
|
|
|
pars->color_map_params.inverse_tone_mapping = opts->tone_map.inverse;
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->color_map_params.contrast_recovery = opts->tone_map.contrast_recovery;
|
|
|
|
pars->color_map_params.visualize_lut = opts->tone_map.visualize;
|
|
|
|
pars->color_map_params.contrast_smoothness = opts->tone_map.contrast_smoothness;
|
2023-08-21 17:49:27 +00:00
|
|
|
pars->color_map_params.gamut_mapping = gamut_modes[opts->tone_map.gamut_mode];
|
2021-04-09 07:14:54 +00:00
|
|
|
|
|
|
|
switch (opts->dither_algo) {
|
|
|
|
case DITHER_NONE:
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.dither_params = NULL;
|
2021-04-09 07:14:54 +00:00
|
|
|
break;
|
2022-09-20 12:59:00 +00:00
|
|
|
case DITHER_ERROR_DIFFUSION:
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.error_diffusion = pl_find_error_diffusion_kernel(opts->error_diffusion);
|
|
|
|
if (!pars->params.error_diffusion) {
|
2022-09-20 12:59:00 +00:00
|
|
|
MP_WARN(p, "Could not find error diffusion kernel '%s', falling "
|
|
|
|
"back to fruit.\n", opts->error_diffusion);
|
|
|
|
}
|
2023-01-29 12:28:08 +00:00
|
|
|
MP_FALLTHROUGH;
|
2021-04-09 07:14:54 +00:00
|
|
|
case DITHER_ORDERED:
|
|
|
|
case DITHER_FRUIT:
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.dither_params = &pars->dither_params;
|
|
|
|
pars->dither_params.method = opts->dither_algo == DITHER_ORDERED
|
2022-09-20 12:59:00 +00:00
|
|
|
? PL_DITHER_ORDERED_FIXED
|
|
|
|
: PL_DITHER_BLUE_NOISE;
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->dither_params.lut_size = opts->dither_size;
|
|
|
|
pars->dither_params.temporal = opts->temporal_dither;
|
2021-04-09 07:14:54 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-11-07 17:12:28 +00:00
|
|
|
if (opts->dither_depth < 0)
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.dither_params = NULL;
|
2021-11-07 17:12:28 +00:00
|
|
|
|
2021-04-09 07:14:54 +00:00
|
|
|
update_icc_opts(p, opts->icc_opts);
|
|
|
|
|
2023-08-19 21:39:25 +00:00
|
|
|
pars->params.num_hooks = 0;
|
2021-04-09 07:14:54 +00:00
|
|
|
const struct pl_hook *hook;
|
|
|
|
for (int i = 0; opts->user_shaders && opts->user_shaders[i]; i++) {
|
2022-11-03 15:16:59 +00:00
|
|
|
if ((hook = load_hook(p, opts->user_shaders[i]))) {
|
2023-08-18 13:36:59 +00:00
|
|
|
MP_TARRAY_APPEND(p, p->hooks, pars->params.num_hooks, hook);
|
2022-11-03 15:16:59 +00:00
|
|
|
update_hook_opts(p, opts->user_shader_opts, opts->user_shaders[i], hook);
|
|
|
|
}
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
2023-08-18 13:36:59 +00:00
|
|
|
pars->params.hooks = p->hooks;
|
2021-04-09 07:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define OPT_BASE_STRUCT struct priv
|
|
|
|
|
|
|
|
const struct m_opt_choice_alternatives lut_types[] = {
|
|
|
|
{"auto", PL_LUT_UNKNOWN},
|
|
|
|
{"native", PL_LUT_NATIVE},
|
|
|
|
{"normalized", PL_LUT_NORMALIZED},
|
|
|
|
{"conversion", PL_LUT_CONVERSION},
|
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct vo_driver video_out_gpu_next = {
|
|
|
|
.description = "Video output based on libplacebo",
|
|
|
|
.name = "gpu-next",
|
2022-04-03 12:17:43 +00:00
|
|
|
.caps = VO_CAP_ROTATE90 |
|
|
|
|
#ifdef PL_HAVE_LAV_FILM_GRAIN
|
|
|
|
VO_CAP_FILM_GRAIN |
|
|
|
|
#endif
|
|
|
|
0x0,
|
2021-04-09 07:14:54 +00:00
|
|
|
.preinit = preinit,
|
|
|
|
.query_format = query_format,
|
|
|
|
.reconfig = reconfig,
|
|
|
|
.control = control,
|
|
|
|
.get_image_ts = get_image,
|
|
|
|
.draw_frame = draw_frame,
|
|
|
|
.flip_page = flip_page,
|
|
|
|
.get_vsync = get_vsync,
|
|
|
|
.wait_events = wait_events,
|
|
|
|
.wakeup = wakeup,
|
|
|
|
.uninit = uninit,
|
|
|
|
.priv_size = sizeof(struct priv),
|
|
|
|
.priv_defaults = &(const struct priv) {
|
|
|
|
.delayed_peak = true,
|
|
|
|
.inter_preserve = true,
|
|
|
|
},
|
|
|
|
|
|
|
|
.options = (const struct m_option[]) {
|
2023-02-20 03:32:50 +00:00
|
|
|
{"allow-delayed-peak-detect", OPT_BOOL(delayed_peak)},
|
2023-06-19 11:06:01 +00:00
|
|
|
{"corner-rounding", OPT_FLOAT(corner_rounding), M_RANGE(0, 1)},
|
2023-02-20 03:32:50 +00:00
|
|
|
{"interpolation-preserve", OPT_BOOL(inter_preserve)},
|
2021-04-09 07:14:54 +00:00
|
|
|
{"lut", OPT_STRING(lut.opt), .flags = M_OPT_FILE},
|
|
|
|
{"lut-type", OPT_CHOICE_C(lut.type, lut_types)},
|
|
|
|
{"image-lut", OPT_STRING(image_lut.opt), .flags = M_OPT_FILE},
|
|
|
|
{"image-lut-type", OPT_CHOICE_C(image_lut.type, lut_types)},
|
|
|
|
{"target-lut", OPT_STRING(target_lut.opt), .flags = M_OPT_FILE},
|
2023-02-20 03:32:50 +00:00
|
|
|
{"target-colorspace-hint", OPT_BOOL(target_hint)},
|
2021-04-09 07:14:54 +00:00
|
|
|
// No `target-lut-type` because we don't support non-RGB targets
|
2023-08-18 15:25:52 +00:00
|
|
|
{"libplacebo-opts", OPT_KEYVALUELIST(raw_opts)},
|
2021-04-09 07:14:54 +00:00
|
|
|
{0}
|
|
|
|
},
|
|
|
|
};
|