#include "config.h" #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, };