mirror of https://github.com/mpv-player/mpv
209 lines
5.9 KiB
C
209 lines
5.9 KiB
C
#include "libmpv/render_gl.h"
|
|
#include "libmpv.h"
|
|
#include "sub/osd.h"
|
|
#include "video/sws_utils.h"
|
|
|
|
struct priv {
|
|
struct libmpv_gpu_context *context;
|
|
|
|
struct mp_sws_context *sws;
|
|
struct osd_state *osd;
|
|
|
|
struct mp_image_params src_params, dst_params;
|
|
struct mp_rect src_rc, dst_rc;
|
|
struct mp_osd_res osd_rc;
|
|
bool anything_changed;
|
|
};
|
|
|
|
static int init(struct render_backend *ctx, mpv_render_param *params)
|
|
{
|
|
ctx->priv = talloc_zero(NULL, struct priv);
|
|
struct priv *p = ctx->priv;
|
|
|
|
char *api = get_mpv_render_param(params, MPV_RENDER_PARAM_API_TYPE, NULL);
|
|
if (!api)
|
|
return MPV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (strcmp(api, MPV_RENDER_API_TYPE_SW) != 0)
|
|
return MPV_ERROR_NOT_IMPLEMENTED;
|
|
|
|
p->sws = mp_sws_alloc(p);
|
|
mp_sws_enable_cmdline_opts(p->sws, ctx->global);
|
|
|
|
p->anything_changed = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool check_format(struct render_backend *ctx, int imgfmt)
|
|
{
|
|
struct priv *p = ctx->priv;
|
|
|
|
// Note: we don't know the output format yet. Using an arbitrary supported
|
|
// format is fine, because we know that any supported input format can
|
|
// be converted to any supported output format.
|
|
return mp_sws_supports_formats(p->sws, IMGFMT_RGB0, imgfmt);
|
|
}
|
|
|
|
static int set_parameter(struct render_backend *ctx, mpv_render_param param)
|
|
{
|
|
return MPV_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static void reconfig(struct render_backend *ctx, struct mp_image_params *params)
|
|
{
|
|
struct priv *p = ctx->priv;
|
|
|
|
p->src_params = *params;
|
|
p->anything_changed = true;
|
|
}
|
|
|
|
static void reset(struct render_backend *ctx)
|
|
{
|
|
// stateless
|
|
}
|
|
|
|
static void update_external(struct render_backend *ctx, struct vo *vo)
|
|
{
|
|
struct priv *p = ctx->priv;
|
|
|
|
p->osd = vo ? vo->osd : NULL;
|
|
}
|
|
|
|
static void resize(struct render_backend *ctx, struct mp_rect *src,
|
|
struct mp_rect *dst, struct mp_osd_res *osd)
|
|
{
|
|
struct priv *p = ctx->priv;
|
|
|
|
p->src_rc = *src;
|
|
p->dst_rc = *dst;
|
|
p->osd_rc = *osd;
|
|
p->anything_changed = true;
|
|
}
|
|
|
|
static int get_target_size(struct render_backend *ctx, mpv_render_param *params,
|
|
int *out_w, int *out_h)
|
|
{
|
|
int *sz = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_SIZE, NULL);
|
|
if (!sz)
|
|
return MPV_ERROR_INVALID_PARAMETER;
|
|
|
|
*out_w = sz[0];
|
|
*out_h = sz[1];
|
|
return 0;
|
|
}
|
|
|
|
static int render(struct render_backend *ctx, mpv_render_param *params,
|
|
struct vo_frame *frame)
|
|
{
|
|
struct priv *p = ctx->priv;
|
|
|
|
int *sz = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_SIZE, NULL);
|
|
char *fmt = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_FORMAT, NULL);
|
|
size_t *stride = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_STRIDE, NULL);
|
|
void *ptr = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_POINTER, NULL);
|
|
|
|
if (!sz || !fmt || !stride || !ptr)
|
|
return MPV_ERROR_INVALID_PARAMETER;
|
|
|
|
char *prev_fmt = mp_imgfmt_to_name(p->dst_params.imgfmt);
|
|
if (strcmp(prev_fmt, fmt) != 0)
|
|
p->anything_changed = true;
|
|
|
|
if (sz[0] != p->dst_params.w || sz[1] != p->dst_params.h)
|
|
p->anything_changed = true;
|
|
|
|
if (p->anything_changed) {
|
|
p->dst_params = (struct mp_image_params){
|
|
.imgfmt = mp_imgfmt_from_name(bstr0(fmt)),
|
|
.w = sz[0],
|
|
.h = sz[1],
|
|
};
|
|
|
|
// Exclude "problematic" formats. In particular, reject multi-plane and
|
|
// hw formats. Exclude non-byte-aligned formats for easier stride
|
|
// checking.
|
|
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(p->dst_params.imgfmt);
|
|
if (!(desc.flags & MP_IMGFLAG_COLOR_RGB) ||
|
|
!(desc.flags & (MP_IMGFLAG_TYPE_UINT | MP_IMGFLAG_TYPE_FLOAT)) ||
|
|
(desc.flags & MP_IMGFLAG_TYPE_PAL8) ||
|
|
!(desc.flags & MP_IMGFLAG_BYTE_ALIGNED) ||
|
|
desc.num_planes != 1)
|
|
return MPV_ERROR_UNSUPPORTED;
|
|
|
|
mp_image_params_guess_csp(&p->dst_params);
|
|
|
|
// Can be unset if rendering before any video was loaded.
|
|
if (p->src_params.imgfmt) {
|
|
p->sws->src = p->src_params;
|
|
p->sws->src.w = mp_rect_w(p->src_rc);
|
|
p->sws->src.h = mp_rect_h(p->src_rc);
|
|
|
|
p->sws->dst = p->dst_params;
|
|
p->sws->dst.w = mp_rect_w(p->dst_rc);
|
|
p->sws->dst.h = mp_rect_h(p->dst_rc);
|
|
|
|
if (mp_sws_reinit(p->sws) < 0)
|
|
return MPV_ERROR_UNSUPPORTED; // probably
|
|
}
|
|
|
|
p->anything_changed = false;
|
|
}
|
|
|
|
struct mp_image wrap_img = {0};
|
|
mp_image_set_params(&wrap_img, &p->dst_params);
|
|
|
|
size_t bpp = wrap_img.fmt.bpp[0] / 8;
|
|
if (!bpp || bpp * wrap_img.w > *stride || *stride % bpp)
|
|
return MPV_ERROR_INVALID_PARAMETER;
|
|
|
|
wrap_img.planes[0] = ptr;
|
|
wrap_img.stride[0] = *stride;
|
|
|
|
struct mp_image *img = frame->current;
|
|
if (img) {
|
|
assert(p->src_params.imgfmt);
|
|
|
|
mp_image_clear_rc_inv(&wrap_img, p->dst_rc);
|
|
|
|
struct mp_image src = *img;
|
|
struct mp_rect src_rc = p->src_rc;
|
|
src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, src.fmt.align_x);
|
|
src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, src.fmt.align_y);
|
|
mp_image_crop_rc(&src, src_rc);
|
|
|
|
struct mp_image dst = wrap_img;
|
|
mp_image_crop_rc(&dst, p->dst_rc);
|
|
|
|
if (mp_sws_scale(p->sws, &dst, &src) < 0) {
|
|
mp_image_clear(&wrap_img, 0, 0, wrap_img.w, wrap_img.h);
|
|
return MPV_ERROR_GENERIC;
|
|
}
|
|
} else {
|
|
mp_image_clear(&wrap_img, 0, 0, wrap_img.w, wrap_img.h);
|
|
}
|
|
|
|
if (p->osd)
|
|
osd_draw_on_image(p->osd, p->osd_rc, img ? img->pts : 0, 0, &wrap_img);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void destroy(struct render_backend *ctx)
|
|
{
|
|
// nop
|
|
}
|
|
|
|
const struct render_backend_fns render_backend_sw = {
|
|
.init = init,
|
|
.check_format = check_format,
|
|
.set_parameter = set_parameter,
|
|
.reconfig = reconfig,
|
|
.reset = reset,
|
|
.update_external = update_external,
|
|
.resize = resize,
|
|
.get_target_size = get_target_size,
|
|
.render = render,
|
|
.destroy = destroy,
|
|
};
|