Coverage Report

Created: 2025-12-27 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/gpu/hwdec.c
Line
Count
Source
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include <stddef.h>
19
#include <string.h>
20
21
#include "config.h"
22
23
#include "common/common.h"
24
#include "common/msg.h"
25
#include "options/m_config.h"
26
#include "hwdec.h"
27
28
extern const struct ra_hwdec_driver ra_hwdec_vaapi;
29
extern const struct ra_hwdec_driver ra_hwdec_videotoolbox;
30
extern const struct ra_hwdec_driver ra_hwdec_vdpau;
31
extern const struct ra_hwdec_driver ra_hwdec_dxva2egl;
32
extern const struct ra_hwdec_driver ra_hwdec_d3d11egl;
33
extern const struct ra_hwdec_driver ra_hwdec_dxva2gldx;
34
extern const struct ra_hwdec_driver ra_hwdec_d3d11va;
35
extern const struct ra_hwdec_driver ra_hwdec_dxva2dxgi;
36
extern const struct ra_hwdec_driver ra_hwdec_cuda;
37
extern const struct ra_hwdec_driver ra_hwdec_drmprime;
38
extern const struct ra_hwdec_driver ra_hwdec_drmprime_overlay;
39
extern const struct ra_hwdec_driver ra_hwdec_aimagereader;
40
extern const struct ra_hwdec_driver ra_hwdec_vulkan;
41
42
const struct ra_hwdec_driver *const ra_hwdec_drivers[] = {
43
#if HAVE_D3D_HWACCEL
44
 #if HAVE_D3D11
45
    &ra_hwdec_d3d11va,
46
  #if HAVE_D3D9_HWACCEL
47
    &ra_hwdec_dxva2dxgi,
48
  #endif
49
 #endif
50
 #if HAVE_EGL_ANGLE
51
    &ra_hwdec_d3d11egl,
52
  #if HAVE_D3D9_HWACCEL
53
    &ra_hwdec_dxva2egl,
54
  #endif
55
 #endif
56
#endif
57
#if HAVE_VAAPI
58
    &ra_hwdec_vaapi,
59
#endif
60
#if HAVE_VIDEOTOOLBOX_GL || HAVE_IOS_GL || HAVE_VIDEOTOOLBOX_PL
61
    &ra_hwdec_videotoolbox,
62
#endif
63
#if HAVE_GL_DXINTEROP_D3D9
64
    &ra_hwdec_dxva2gldx,
65
#endif
66
#if HAVE_CUDA_INTEROP
67
    &ra_hwdec_cuda,
68
#endif
69
#if HAVE_VDPAU_GL_X11
70
    &ra_hwdec_vdpau,
71
#endif
72
#if HAVE_DRM
73
    &ra_hwdec_drmprime,
74
    &ra_hwdec_drmprime_overlay,
75
#endif
76
#if HAVE_ANDROID_MEDIA_NDK
77
    &ra_hwdec_aimagereader,
78
#endif
79
#if HAVE_VULKAN
80
    &ra_hwdec_vulkan,
81
#endif
82
83
    NULL
84
};
85
86
struct ra_hwdec *ra_hwdec_load_driver(struct ra_ctx *ra_ctx,
87
                                      struct mp_log *log,
88
                                      struct mpv_global *global,
89
                                      struct mp_hwdec_devices *devs,
90
                                      const struct ra_hwdec_driver *drv,
91
                                      bool is_auto)
92
0
{
93
0
    struct ra_hwdec *hwdec = talloc(NULL, struct ra_hwdec);
94
0
    *hwdec = (struct ra_hwdec) {
95
0
        .driver = drv,
96
0
        .log = mp_log_new(hwdec, log, drv->name),
97
0
        .global = global,
98
0
        .ra_ctx = ra_ctx,
99
0
        .devs = devs,
100
0
        .probing = is_auto,
101
0
        .priv = talloc_zero_size(hwdec, drv->priv_size),
102
0
    };
103
0
    mp_verbose(log, "Loading hwdec driver '%s'\n", drv->name);
104
0
    if (hwdec->driver->init(hwdec) < 0) {
105
0
        ra_hwdec_uninit(hwdec);
106
0
        mp_verbose(log, "Loading failed.\n");
107
0
        return NULL;
108
0
    }
109
0
    return hwdec;
110
0
}
111
112
void ra_hwdec_uninit(struct ra_hwdec *hwdec)
113
0
{
114
0
    if (hwdec)
115
0
        hwdec->driver->uninit(hwdec);
116
0
    talloc_free(hwdec);
117
0
}
118
119
bool ra_hwdec_test_format(struct ra_hwdec *hwdec, int imgfmt)
120
0
{
121
0
    for (int n = 0; hwdec->driver->imgfmts[n]; n++) {
122
0
        if (hwdec->driver->imgfmts[n] == imgfmt)
123
0
            return true;
124
0
    }
125
0
    return false;
126
0
}
127
128
struct ra_hwdec_mapper *ra_hwdec_mapper_create(struct ra_hwdec *hwdec,
129
                                               const struct mp_image_params *params)
130
0
{
131
0
    mp_assert(ra_hwdec_test_format(hwdec, params->imgfmt));
132
133
0
    struct ra_hwdec_mapper *mapper = talloc_ptrtype(NULL, mapper);
134
0
    *mapper = (struct ra_hwdec_mapper){
135
0
        .owner = hwdec,
136
0
        .driver = hwdec->driver->mapper,
137
0
        .log = hwdec->log,
138
0
        .ra = hwdec->ra_ctx->ra,
139
0
        .priv = talloc_zero_size(mapper, hwdec->driver->mapper->priv_size),
140
0
        .src_params = *params,
141
0
        .dst_params = *params,
142
0
    };
143
0
    if (mapper->driver->init(mapper) < 0)
144
0
        ra_hwdec_mapper_free(&mapper);
145
0
    return mapper;
146
0
}
147
148
void ra_hwdec_mapper_free(struct ra_hwdec_mapper **mapper)
149
0
{
150
0
    struct ra_hwdec_mapper *p = *mapper;
151
0
    if (p) {
152
0
        ra_hwdec_mapper_unmap(p);
153
0
        p->driver->uninit(p);
154
0
        talloc_free(p);
155
0
    }
156
0
    *mapper = NULL;
157
0
}
158
159
void ra_hwdec_mapper_unmap(struct ra_hwdec_mapper *mapper)
160
0
{
161
0
    if (mapper->driver->unmap)
162
0
        mapper->driver->unmap(mapper);
163
164
    // Clean up after the image if the mapper didn't already
165
0
    mp_image_unrefp(&mapper->src);
166
0
}
167
168
int ra_hwdec_mapper_map(struct ra_hwdec_mapper *mapper, struct mp_image *img)
169
0
{
170
0
    ra_hwdec_mapper_unmap(mapper);
171
0
    mp_image_setrefp(&mapper->src, img);
172
0
    if (mapper->driver->map(mapper) < 0) {
173
0
        ra_hwdec_mapper_unmap(mapper);
174
0
        return -1;
175
0
    }
176
0
    return 0;
177
0
}
178
179
static int ra_hwdec_validate_opt_full(struct mp_log *log, bool include_modes,
180
                                      const m_option_t *opt,
181
                                      struct bstr name, const char **value)
182
2.59k
{
183
2.59k
    struct bstr param = bstr0(*value);
184
2.59k
    bool help = bstr_equals0(param, "help");
185
2.59k
    if (help)
186
73
        mp_info(log, "Available hwdecs:\n");
187
2.59k
    for (int n = 0; ra_hwdec_drivers[n]; n++) {
188
0
        const struct ra_hwdec_driver *drv = ra_hwdec_drivers[n];
189
0
        if (help) {
190
0
            mp_info(log, "    %s\n", drv->name);
191
0
        } else if (bstr_equals0(param, drv->name)) {
192
0
            return 1;
193
0
        }
194
0
    }
195
2.59k
    if (help) {
196
73
        if (include_modes) {
197
36
            mp_info(log, "    auto (behavior depends on context)\n"
198
36
                        "    all (load all hwdecs)\n"
199
36
                        "    no (do not load any and block loading on demand)\n");
200
36
        }
201
73
        return M_OPT_EXIT;
202
73
    }
203
2.52k
    if (!param.len)
204
1.29k
        return 1; // "" is treated specially
205
1.23k
    if (include_modes &&
206
1.12k
       (bstr_equals0(param, "all") || bstr_equals0(param, "auto") ||
207
582
        bstr_equals0(param, "no")))
208
881
        return 1;
209
354
    mp_fatal(log, "No hwdec backend named '%.*s' found!\n", BSTR_P(param));
210
354
    return M_OPT_INVALID;
211
1.23k
}
212
213
int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
214
                          struct bstr name, const char **value)
215
2.03k
{
216
2.03k
    return ra_hwdec_validate_opt_full(log, true, opt, name, value);
217
2.03k
}
218
219
int ra_hwdec_validate_drivers_only_opt(struct mp_log *log,
220
                                       const m_option_t *opt,
221
                                       struct bstr name, const char **value)
222
569
{
223
569
    return ra_hwdec_validate_opt_full(log, false, opt, name, value);
224
569
}
225
226
static void load_add_hwdec(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs,
227
                           const struct ra_hwdec_driver *drv, bool is_auto)
228
0
{
229
    // Don't load duplicate hwdecs
230
0
    for (int j = 0; j < ctx->num_hwdecs; j++) {
231
0
        if (ctx->hwdecs[j]->driver == drv)
232
0
            return;
233
0
    }
234
235
0
    struct ra_hwdec *hwdec =
236
0
        ra_hwdec_load_driver(ctx->ra_ctx, ctx->log, ctx->global, devs, drv, is_auto);
237
0
    if (hwdec)
238
0
        MP_TARRAY_APPEND(NULL, ctx->hwdecs, ctx->num_hwdecs, hwdec);
239
0
}
240
241
static void load_hwdecs_all(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs)
242
0
{
243
0
    if (!ctx->loading_done) {
244
0
        for (int n = 0; ra_hwdec_drivers[n]; n++)
245
0
            load_add_hwdec(ctx, devs, ra_hwdec_drivers[n], true);
246
0
        ctx->loading_done = true;
247
0
    }
248
0
}
249
250
void ra_hwdec_ctx_init(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs,
251
                       const char *type, bool load_all_by_default)
252
0
{
253
0
    mp_assert(ctx->ra_ctx);
254
255
    /*
256
     * By default, or if the option value is "auto", we will not pre-emptively
257
     * load any interops, and instead allow them to be loaded on-demand.
258
     *
259
     * If the option value is "no", then no interops will be loaded now, and
260
     * no interops will be loaded, even if requested later.
261
     *
262
     * If the option value is "all", then all interops will be loaded now, and
263
     * obviously no interops will need to be loaded later.
264
     *
265
     * Finally, if a specific interop is requested, it will be loaded now, and
266
     * other interops can be loaded, if requested later.
267
     */
268
0
    if (!type || !type[0] || strcmp(type, "auto") == 0) {
269
0
        if (!load_all_by_default)
270
0
            return;
271
0
        type = "all";
272
0
    }
273
0
    if (strcmp(type, "no") == 0) {
274
        // do nothing, just block further loading
275
0
    } else if (strcmp(type, "all") == 0) {
276
0
        load_hwdecs_all(ctx, devs);
277
0
    } else {
278
0
        for (int n = 0; ra_hwdec_drivers[n]; n++) {
279
0
            const struct ra_hwdec_driver *drv = ra_hwdec_drivers[n];
280
0
            if (strcmp(type, drv->name) == 0) {
281
0
                load_add_hwdec(ctx, devs, drv, false);
282
0
                break;
283
0
            }
284
0
        }
285
0
    }
286
0
    ctx->loading_done = true;
287
0
}
288
289
void ra_hwdec_ctx_uninit(struct ra_hwdec_ctx *ctx)
290
0
{
291
0
    for (int n = 0; n < ctx->num_hwdecs; n++)
292
0
        ra_hwdec_uninit(ctx->hwdecs[n]);
293
294
0
    talloc_free(ctx->hwdecs);
295
0
    memset(ctx, 0, sizeof(*ctx));
296
0
}
297
298
void ra_hwdec_ctx_load_fmt(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs,
299
                           struct hwdec_imgfmt_request *params)
300
0
{
301
0
    int imgfmt = params->imgfmt;
302
0
    if (ctx->loading_done) {
303
        /*
304
         * If we previously marked interop loading as done (for reasons
305
         * discussed above), then do not load any other interops regardless
306
         * of imgfmt.
307
         */
308
0
        return;
309
0
    }
310
311
0
    if (imgfmt == IMGFMT_NONE) {
312
0
        MP_VERBOSE(ctx, "Loading hwdec drivers for all formats\n");
313
0
        load_hwdecs_all(ctx, devs);
314
0
        return;
315
0
    }
316
317
0
    MP_VERBOSE(ctx, "Loading hwdec drivers for format: '%s'\n",
318
0
               mp_imgfmt_to_name(imgfmt));
319
0
    for (int i = 0; ra_hwdec_drivers[i]; i++) {
320
0
        bool matched_fmt = false;
321
0
        const struct ra_hwdec_driver *drv = ra_hwdec_drivers[i];
322
0
        for (int j = 0; drv->imgfmts[j]; j++) {
323
0
            if (imgfmt == drv->imgfmts[j]) {
324
0
                matched_fmt = true;
325
0
                break;
326
0
            }
327
0
        }
328
0
        if (!matched_fmt) {
329
0
            continue;
330
0
        }
331
332
0
        load_add_hwdec(ctx, devs, drv, params->probing);
333
0
    }
334
0
}
335
336
struct ra_hwdec *ra_hwdec_get(struct ra_hwdec_ctx *ctx, int imgfmt)
337
0
{
338
0
    for (int n = 0; n < ctx->num_hwdecs; n++) {
339
0
        if (ra_hwdec_test_format(ctx->hwdecs[n], imgfmt))
340
0
            return ctx->hwdecs[n];
341
0
    }
342
343
0
    return NULL;
344
0
}
345
346
int ra_hwdec_driver_get_imgfmt_for_name(const char *name)
347
0
{
348
0
    for (int i = 0; ra_hwdec_drivers[i]; i++) {
349
0
        if (!strcmp(ra_hwdec_drivers[i]->name, name)) {
350
0
            return ra_hwdec_drivers[i]->imgfmts[0];
351
0
        }
352
0
    }
353
0
    return IMGFMT_NONE;
354
0
}
355
356
enum AVHWDeviceType ra_hwdec_driver_get_device_type_for_name(const char *name)
357
0
{
358
0
    for (int i = 0; ra_hwdec_drivers[i]; i++) {
359
0
        if (!strcmp(ra_hwdec_drivers[i]->name, name)) {
360
0
            return ra_hwdec_drivers[i]->device_type;
361
0
        }
362
0
    }
363
0
    return AV_HWDEVICE_TYPE_NONE;
364
0
}