Coverage Report

Created: 2026-03-27 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/libmpv_sw.c
Line
Count
Source
1
#include "mpv/render_gl.h"
2
#include "libmpv.h"
3
#include "sub/osd.h"
4
#include "video/sws_utils.h"
5
6
struct priv {
7
    struct libmpv_gpu_context *context;
8
9
    struct mp_sws_context *sws;
10
    struct osd_state *osd;
11
12
    struct mp_image_params src_params, dst_params;
13
    struct mp_rect src_rc, dst_rc;
14
    struct mp_osd_res osd_rc;
15
    bool anything_changed;
16
};
17
18
static int init(struct render_backend *ctx, mpv_render_param *params)
19
0
{
20
0
    ctx->priv = talloc_zero(NULL, struct priv);
21
0
    struct priv *p = ctx->priv;
22
23
0
    char *api = get_mpv_render_param(params, MPV_RENDER_PARAM_API_TYPE, NULL);
24
0
    if (!api)
25
0
        return MPV_ERROR_INVALID_PARAMETER;
26
27
0
    if (strcmp(api, MPV_RENDER_API_TYPE_SW) != 0)
28
0
        return MPV_ERROR_NOT_IMPLEMENTED;
29
30
0
    p->sws = mp_sws_alloc(p);
31
0
    mp_sws_enable_cmdline_opts(p->sws, ctx->global);
32
33
0
    p->anything_changed = true;
34
35
0
    return 0;
36
0
}
37
38
static bool check_format(struct render_backend *ctx, int imgfmt)
39
0
{
40
0
    struct priv *p = ctx->priv;
41
42
    // Note: we don't know the output format yet. Using an arbitrary supported
43
    //       format is fine, because we know that any supported input format can
44
    //       be converted to any supported output format.
45
0
    return mp_sws_supports_formats(p->sws, IMGFMT_RGB0, imgfmt);
46
0
}
47
48
static int set_parameter(struct render_backend *ctx, mpv_render_param param)
49
0
{
50
0
    return MPV_ERROR_NOT_IMPLEMENTED;
51
0
}
52
53
static void reconfig(struct render_backend *ctx, struct mp_image_params *params)
54
0
{
55
0
    struct priv *p = ctx->priv;
56
57
0
    p->src_params = *params;
58
0
    p->anything_changed = true;
59
0
}
60
61
static void reset(struct render_backend *ctx)
62
0
{
63
    // stateless
64
0
}
65
66
static void update_external(struct render_backend *ctx, struct vo *vo)
67
0
{
68
0
    struct priv *p = ctx->priv;
69
70
0
    p->osd = vo ? vo->osd : NULL;
71
0
}
72
73
static void resize(struct render_backend *ctx, struct mp_rect *src,
74
                   struct mp_rect *dst, struct mp_osd_res *osd)
75
0
{
76
0
    struct priv *p = ctx->priv;
77
78
0
    p->src_rc = *src;
79
0
    p->dst_rc = *dst;
80
0
    p->osd_rc = *osd;
81
0
    p->anything_changed = true;
82
0
}
83
84
static int get_target_size(struct render_backend *ctx, mpv_render_param *params,
85
                           int *out_w, int *out_h)
86
0
{
87
0
    int *sz = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_SIZE, NULL);
88
0
    if (!sz)
89
0
        return MPV_ERROR_INVALID_PARAMETER;
90
91
0
    *out_w = sz[0];
92
0
    *out_h = sz[1];
93
0
    return 0;
94
0
}
95
96
static int render(struct render_backend *ctx, mpv_render_param *params,
97
                  struct vo_frame *frame)
98
0
{
99
0
    struct priv *p = ctx->priv;
100
101
0
    int *sz = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_SIZE, NULL);
102
0
    char *fmt = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_FORMAT, NULL);
103
0
    size_t *stride = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_STRIDE, NULL);
104
0
    void *ptr = get_mpv_render_param(params, MPV_RENDER_PARAM_SW_POINTER, NULL);
105
106
0
    if (!sz || !fmt || !stride || !ptr)
107
0
        return MPV_ERROR_INVALID_PARAMETER;
108
109
0
    char *prev_fmt = mp_imgfmt_to_name(p->dst_params.imgfmt);
110
0
    if (strcmp(prev_fmt, fmt) != 0)
111
0
        p->anything_changed = true;
112
113
0
    if (sz[0] != p->dst_params.w || sz[1] != p->dst_params.h)
114
0
        p->anything_changed = true;
115
116
0
    if (p->anything_changed) {
117
0
        p->dst_params = (struct mp_image_params){
118
0
            .imgfmt = mp_imgfmt_from_name(bstr0(fmt)),
119
0
            .w = sz[0],
120
0
            .h = sz[1],
121
0
        };
122
123
        // Exclude "problematic" formats. In particular, reject multi-plane and
124
        // hw formats. Exclude non-byte-aligned formats for easier stride
125
        // checking.
126
0
        struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(p->dst_params.imgfmt);
127
0
        if (!(desc.flags & MP_IMGFLAG_COLOR_RGB) ||
128
0
            !(desc.flags & (MP_IMGFLAG_TYPE_UINT | MP_IMGFLAG_TYPE_FLOAT)) ||
129
0
            (desc.flags & MP_IMGFLAG_TYPE_PAL8) ||
130
0
            !(desc.flags & MP_IMGFLAG_BYTE_ALIGNED) ||
131
0
            desc.num_planes != 1)
132
0
            return MPV_ERROR_UNSUPPORTED;
133
134
0
        mp_image_params_guess_csp(&p->dst_params);
135
136
        // Can be unset if rendering before any video was loaded.
137
0
        if (p->src_params.imgfmt) {
138
0
            p->sws->src = p->src_params;
139
0
            p->sws->src.w = mp_rect_w(p->src_rc);
140
0
            p->sws->src.h = mp_rect_h(p->src_rc);
141
142
0
            p->sws->dst = p->dst_params;
143
0
            p->sws->dst.w = mp_rect_w(p->dst_rc);
144
0
            p->sws->dst.h = mp_rect_h(p->dst_rc);
145
146
0
            if (mp_sws_reinit(p->sws) < 0)
147
0
                return MPV_ERROR_UNSUPPORTED; // probably
148
0
        }
149
150
0
        p->anything_changed = false;
151
0
    }
152
153
0
    struct mp_image wrap_img = {0};
154
0
    mp_image_set_params(&wrap_img, &p->dst_params);
155
156
0
    size_t bpp = wrap_img.fmt.bpp[0] / 8;
157
0
    if (!bpp || bpp * wrap_img.w > *stride || *stride % bpp)
158
0
        return MPV_ERROR_INVALID_PARAMETER;
159
160
0
    wrap_img.planes[0] = ptr;
161
0
    wrap_img.stride[0] = *stride;
162
163
0
    struct mp_image *img = frame->current;
164
0
    if (img) {
165
0
        mp_assert(p->src_params.imgfmt);
166
167
0
        mp_image_clear_rc_inv(&wrap_img, p->dst_rc);
168
169
0
        struct mp_image src = *img;
170
0
        struct mp_rect src_rc = p->src_rc;
171
0
        src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, src.fmt.align_x);
172
0
        src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, src.fmt.align_y);
173
0
        mp_image_crop_rc(&src, src_rc);
174
175
0
        struct mp_image dst = wrap_img;
176
0
        mp_image_crop_rc(&dst, p->dst_rc);
177
178
0
        if (mp_sws_scale(p->sws, &dst, &src) < 0) {
179
0
            mp_image_clear(&wrap_img, 0, 0, wrap_img.w, wrap_img.h);
180
0
            return MPV_ERROR_GENERIC;
181
0
        }
182
0
    } else {
183
0
        mp_image_clear(&wrap_img, 0, 0, wrap_img.w, wrap_img.h);
184
0
    }
185
186
0
    if (p->osd)
187
0
        osd_draw_on_image(p->osd, p->osd_rc, img ? img->pts : 0, 0, &wrap_img);
188
189
0
    return 0;
190
0
}
191
192
static void destroy(struct render_backend *ctx)
193
0
{
194
    // nop
195
0
}
196
197
const struct render_backend_fns render_backend_sw = {
198
    .init = init,
199
    .check_format = check_format,
200
    .set_parameter = set_parameter,
201
    .reconfig = reconfig,
202
    .reset = reset,
203
    .update_external = update_external,
204
    .resize = resize,
205
    .get_target_size = get_target_size,
206
    .render = render,
207
    .destroy = destroy,
208
};