summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-07-08 21:55:24 +0200
committerwm4 <wm4@nowhere>2020-07-08 22:42:05 +0200
commitb93f142011381a9ad753a246ea112115f262e7a1 (patch)
tree0b5ed52a08599240f0d4f4ffad815dc7be33ee22 /video
parent688251bc662c50bc7fe56bb1ec8fef4e1407cdf0 (diff)
downloadmpv-b93f142011381a9ad753a246ea112115f262e7a1.tar.bz2
mpv-b93f142011381a9ad753a246ea112115f262e7a1.tar.xz
client API: add software rendering API
This can be used to make vo_libmpv render video to a memory buffer. It only adds a new backend API that takes memory surfaces. All the render API (such as frame rendering control and so on) is reused. I'm not quite convinced of the usefulness of this, and until now I always resisted providing something like this. It only seems to facilitate inefficient implementation. But whatever. Unfortunately, this duplicates the software rendering glue code yet again (like it exists in vo_x11, vo_wlshm, vo_drm, and probably more). But in theory, these could reuse this backend in the future, just like vo_gpu could reuse the render_gl API. Fixes: #7852
Diffstat (limited to 'video')
-rw-r--r--video/out/gpu/libmpv_gpu.c2
-rw-r--r--video/out/libmpv.h1
-rw-r--r--video/out/libmpv_sw.c209
-rw-r--r--video/out/vo_libmpv.c1
4 files changed, 212 insertions, 1 deletions
diff --git a/video/out/gpu/libmpv_gpu.c b/video/out/gpu/libmpv_gpu.c
index b8edc694db..8b93dcadfd 100644
--- a/video/out/gpu/libmpv_gpu.c
+++ b/video/out/gpu/libmpv_gpu.c
@@ -65,7 +65,7 @@ static int init(struct render_backend *ctx, mpv_render_param *params)
}
if (!p->context)
- return MPV_ERROR_INVALID_PARAMETER;
+ return MPV_ERROR_NOT_IMPLEMENTED;
int err = p->context->fns->init(p->context, params);
if (err < 0)
diff --git a/video/out/libmpv.h b/video/out/libmpv.h
index 329b2dffec..485ba8a76c 100644
--- a/video/out/libmpv.h
+++ b/video/out/libmpv.h
@@ -80,3 +80,4 @@ struct render_backend_fns {
};
extern const struct render_backend_fns render_backend_gpu;
+extern const struct render_backend_fns render_backend_sw;
diff --git a/video/out/libmpv_sw.c b/video/out/libmpv_sw.c
new file mode 100644
index 0000000000..901bd4e663
--- /dev/null
+++ b/video/out/libmpv_sw.c
@@ -0,0 +1,209 @@
+#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,
+};
diff --git a/video/out/vo_libmpv.c b/video/out/vo_libmpv.c
index fffb65b29a..d07ab4c4eb 100644
--- a/video/out/vo_libmpv.c
+++ b/video/out/vo_libmpv.c
@@ -111,6 +111,7 @@ struct mpv_render_context {
const struct render_backend_fns *render_backends[] = {
&render_backend_gpu,
+ &render_backend_sw,
NULL
};