/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 | | }; |