../../../ffmpeg/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 / 136
0.0%
Branches:
0 / 82
0.0%
Functions:
0 / 4
0.0%

libavfilter/vsrc_testsrc_vulkan.c
Line Branch Exec Source
1 /*
2 * Copyright (c) Lynne
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "libavutil/csp.h"
22 #include "libavutil/opt.h"
23 #include "vulkan_filter.h"
24 #include "filters.h"
25 #include "colorspace.h"
26 #include "video.h"
27
28 enum TestSrcVulkanMode {
29 TESTSRC_COLOR,
30 };
31
32 extern const unsigned char ff_color_comp_spv_data[];
33 extern const unsigned int ff_color_comp_spv_len;
34
35 typedef struct TestSrcVulkanPushData {
36 float color_data[4][4];
37 } TestSrcVulkanPushData;
38
39 typedef struct TestSrcVulkanContext {
40 FFVulkanContext vkctx;
41
42 int initialized;
43 FFVkExecPool e;
44 AVVulkanDeviceQueueFamily *qf;
45 FFVulkanShader shd;
46
47 /* Only used by color_vulkan */
48 uint8_t color_rgba[4];
49
50 TestSrcVulkanPushData opts;
51
52 int w, h;
53 int pw, ph;
54 char *out_format_string;
55 /* enum AVColorRange */
56 int out_range;
57 unsigned int nb_frame;
58 AVRational time_base, frame_rate;
59 int64_t pts;
60 int64_t duration; ///< duration expressed in microseconds
61 AVRational sar; ///< sample aspect ratio
62 int draw_once; ///< draw only the first frame, always put out the same picture
63 int draw_once_reset; ///< draw only the first frame or in case of reset
64 AVFrame *picref; ///< cached reference containing the painted picture
65 } TestSrcVulkanContext;
66
67 ✗ static av_cold int init_filter(AVFilterContext *ctx, enum TestSrcVulkanMode mode)
68 {
69 int err;
70 ✗ TestSrcVulkanContext *s = ctx->priv;
71 ✗ FFVulkanContext *vkctx = &s->vkctx;
72 ✗ const int planes = av_pix_fmt_count_planes(s->vkctx.output_format);
73 ✗ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->vkctx.output_format);
74
75 ✗ s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0);
76
0/2
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 8 not taken.
✗ if (!s->qf) {
77 ✗ av_log(ctx, AV_LOG_ERROR, "Device has no compute queues\n");
78 ✗ err = AVERROR(ENOTSUP);
79 ✗ goto fail;
80 }
81
82
0/2
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 11 not taken.
✗ RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, FF_VK_DEFAULT_EXEC_CONTEXTS, 0, 0, 0, NULL));
83
84 ✗ ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
85 ✗ (uint32_t []) { 32, 32, 1 }, 0);
86
87 ✗ ff_vk_shader_add_push_const(&s->shd, 0, sizeof(s->opts),
88 VK_SHADER_STAGE_COMPUTE_BIT);
89
90 ✗ const FFVulkanDescriptorSetBinding desc_set[] = {
91 { /* output_img */
92 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
93 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
94 .elems = planes,
95 },
96 };
97 ✗ ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 1, 0);
98
99
0/2
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 67 not taken.
✗ if (mode == TESTSRC_COLOR) {
100 double rgb2yuv[3][3];
101 double rgbad[4];
102 double yuvad[4];
103
104 enum AVColorSpace csp;
105 ✗ const AVLumaCoefficients *luma = NULL;
106
107 ✗ s->draw_once = 1;
108
109
0/2
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
✗ if (desc->flags & AV_PIX_FMT_FLAG_RGB)
110 ✗ csp = AVCOL_SPC_RGB;
111 else
112 ✗ csp = AVCOL_SPC_SMPTE170M;
113
114
0/4
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 22 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
✗ if (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && !(luma = av_csp_luma_coeffs_from_avcsp(csp)))
115 ✗ return AVERROR(EINVAL);
116
0/2
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
✗ else if (!(desc->flags & AV_PIX_FMT_FLAG_RGB))
117 ✗ ff_fill_rgb2yuv_table(luma, rgb2yuv);
118
119
0/2
✗ Branch 26 → 25 not taken.
✗ Branch 26 → 27 not taken.
✗ for (int i = 0; i < 4; i++)
120 ✗ rgbad[i] = s->color_rgba[i] / 255.0;
121
122
0/2
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
✗ if (!(desc->flags & AV_PIX_FMT_FLAG_RGB))
123 ✗ ff_matrix_mul_3x3_vec(yuvad, rgbad, rgb2yuv);
124 else
125 ✗ memcpy(yuvad, rgbad, sizeof(rgbad));
126
127 ✗ yuvad[3] = rgbad[3];
128
129
0/2
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 48 not taken.
✗ if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) {
130
0/2
✗ Branch 47 → 32 not taken.
✗ Branch 47 → 48 not taken.
✗ for (int i = 0; i < 3; i++) {
131
0/4
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 35 not taken.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
✗ int chroma = (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && i > 0);
132
0/2
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 44 not taken.
✗ if (s->out_range == AVCOL_RANGE_MPEG) {
133
0/2
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
✗ yuvad[i] *= (chroma ? 224.0 : 219.0) / 255.0;
134
0/2
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
✗ yuvad[i] += (chroma ? 128.0 : 16.0) / 255.0;
135
0/2
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 46 not taken.
✗ } else if (chroma) {
136 ✗ yuvad[i] += 0.5;
137 }
138 }
139 }
140
141 /* Ensure we place the alpha appropriately for gray formats */
142
0/2
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 50 not taken.
✗ if (desc->nb_components <= 2)
143 ✗ yuvad[1] = yuvad[3];
144
145 float color_comp[4];
146
0/2
✗ Branch 52 → 51 not taken.
✗ Branch 52 → 53 not taken.
✗ for (int i = 0; i < 4; i++)
147 ✗ color_comp[i] = yuvad[i];
148
149 int fmt_lut[4];
150 ✗ ff_vk_set_perm(s->vkctx.output_format, fmt_lut, 0);
151
0/2
✗ Branch 61 → 55 not taken.
✗ Branch 61 → 62 not taken.
✗ for (int i = 0, c_off = 0; i < planes; i++) {
152
0/2
✗ Branch 59 → 56 not taken.
✗ Branch 59 → 60 not taken.
✗ for (int c = 0; c < desc->nb_components; c++) {
153
0/2
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
✗ if (desc->comp[c].plane == i) {
154 ✗ int off = desc->comp[c].offset / (FFALIGN(desc->comp[c].depth, 8)/8);
155 ✗ s->opts.color_data[i][off] = color_comp[fmt_lut[c_off++]];
156 }
157 }
158 }
159
160
0/2
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 65 not taken.
✗ RET(ff_vk_shader_link(vkctx, &s->shd, ff_color_comp_spv_data,
161 ff_color_comp_spv_len, "main"));
162 }
163
164
0/2
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
✗ RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->shd));
165
166 ✗ s->initialized = 1;
167
168 ✗ fail:
169 ✗ return err;
170 }
171
172 ✗ static int testsrc_vulkan_activate(AVFilterContext *ctx)
173 {
174 int err;
175 ✗ AVFilterLink *outlink = ctx->outputs[0];
176 ✗ TestSrcVulkanContext *s = ctx->priv;
177 ✗ const int planes = av_pix_fmt_count_planes(s->vkctx.output_format);
178 AVFrame *frame;
179
180
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 7 not taken.
✗ if (!s->initialized) {
181 ✗ enum TestSrcVulkanMode mode = TESTSRC_COLOR;
182 ✗ err = init_filter(ctx, mode);
183
0/2
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
✗ if (err < 0)
184 ✗ return err;
185 }
186
187
0/2
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 10 not taken.
✗ if (!ff_outlink_frame_wanted(outlink))
188 ✗ return FFERROR_NOT_READY;
189
0/2
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 14 not taken.
✗ if (s->duration >= 0 &&
190
0/2
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 14 not taken.
✗ av_rescale_q(s->pts, s->time_base, AV_TIME_BASE_Q) >= s->duration) {
191 ✗ ff_outlink_set_status(outlink, AVERROR_EOF, s->pts);
192 ✗ return 0;
193 }
194
195
0/2
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 27 not taken.
✗ if (s->draw_once) {
196
0/2
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 18 not taken.
✗ if (s->draw_once_reset) {
197 ✗ av_frame_free(&s->picref);
198 ✗ s->draw_once_reset = 0;
199 }
200
0/2
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 25 not taken.
✗ if (!s->picref) {
201 ✗ s->picref = ff_get_video_buffer(outlink, s->w, s->h);
202
0/2
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
✗ if (!s->picref)
203 ✗ return AVERROR(ENOMEM);
204
205 ✗ err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, s->picref, NULL,
206 VK_NULL_HANDLE, planes,
207 ✗ &s->opts, sizeof(s->opts));
208
0/2
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 25 not taken.
✗ if (err < 0)
209 ✗ return err;
210 }
211 ✗ frame = av_frame_clone(s->picref);
212 } else {
213 ✗ frame = ff_get_video_buffer(outlink, s->w, s->h);
214 }
215
216
0/2
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
✗ if (!frame)
217 ✗ return AVERROR(ENOMEM);
218
219 ✗ frame->pts = s->pts;
220 ✗ frame->duration = 1;
221 ✗ frame->flags = AV_FRAME_FLAG_KEY;
222 ✗ frame->pict_type = AV_PICTURE_TYPE_I;
223 ✗ frame->sample_aspect_ratio = s->sar;
224
0/2
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 36 not taken.
✗ if (!s->draw_once) {
225 ✗ err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, frame, NULL,
226 VK_NULL_HANDLE, planes,
227 ✗ &s->opts, sizeof(s->opts));
228
0/2
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 36 not taken.
✗ if (err < 0) {
229 ✗ av_frame_free(&frame);
230 ✗ return err;
231 }
232 }
233
234 ✗ s->pts++;
235 ✗ s->nb_frame++;
236
237 ✗ return ff_filter_frame(outlink, frame);
238 }
239
240 ✗ static int testsrc_vulkan_config_props(AVFilterLink *outlink)
241 {
242 int err;
243 ✗ FilterLink *l = ff_filter_link(outlink);
244 ✗ TestSrcVulkanContext *s = outlink->src->priv;
245 ✗ FFVulkanContext *vkctx = &s->vkctx;
246
247
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
✗ if (!s->out_format_string) {
248 ✗ vkctx->output_format = AV_PIX_FMT_YUV444P;
249 } else {
250 ✗ vkctx->output_format = av_get_pix_fmt(s->out_format_string);
251
0/2
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 9 not taken.
✗ if (vkctx->output_format == AV_PIX_FMT_NONE) {
252 ✗ av_log(vkctx, AV_LOG_ERROR, "Invalid output format.\n");
253 ✗ return AVERROR(EINVAL);
254 }
255 }
256
257 ✗ err = ff_vk_filter_init_context(outlink->src, vkctx, NULL,
258 s->w, s->h, vkctx->output_format);
259
0/2
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
✗ if (err < 0)
260 ✗ return err;
261
262 ✗ l->hw_frames_ctx = av_buffer_ref(vkctx->frames_ref);
263
0/2
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
✗ if (!l->hw_frames_ctx)
264 ✗ return AVERROR(ENOMEM);
265
266 ✗ s->time_base = av_inv_q(s->frame_rate);
267 ✗ s->nb_frame = 0;
268 ✗ s->pts = 0;
269
270 ✗ s->vkctx.output_width = s->w;
271 ✗ s->vkctx.output_height = s->h;
272 ✗ outlink->w = s->w;
273 ✗ outlink->h = s->h;
274 ✗ outlink->sample_aspect_ratio = s->sar;
275 ✗ l->frame_rate = s->frame_rate;
276 ✗ outlink->time_base = s->time_base;
277
278 ✗ return 0;
279 }
280
281 ✗ static void testsrc_vulkan_uninit(AVFilterContext *avctx)
282 {
283 ✗ TestSrcVulkanContext *s = avctx->priv;
284 ✗ FFVulkanContext *vkctx = &s->vkctx;
285
286 ✗ av_frame_free(&s->picref);
287
288 ✗ ff_vk_exec_pool_free(vkctx, &s->e);
289 ✗ ff_vk_shader_free(vkctx, &s->shd);
290
291 ✗ ff_vk_uninit(&s->vkctx);
292
293 ✗ s->initialized = 0;
294 ✗ }
295
296 #define OFFSET(x) offsetof(TestSrcVulkanContext, x)
297 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
298
299 #define COMMON_OPTS \
300 { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS }, \
301 { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS }, \
302 \
303 { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS }, \
304 { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS }, \
305 \
306 { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, \
307 { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, \
308 \
309 { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, { .dbl = 1 }, 0, INT_MAX, FLAGS }, \
310 \
311 { "format", "Output video format (software format of hardware frames)", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS },
312
313 static const AVOption color_vulkan_options[] = {
314 { "color", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS },
315 { "c", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS },
316 COMMON_OPTS
317 { "out_range", "Output colour range (from 0 to 2) (default 0)", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED}, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, .flags = FLAGS, .unit = "range" },
318 { "full", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" },
319 { "limited", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" },
320 { "jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" },
321 { "mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" },
322 { "tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" },
323 { "pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" },
324 { NULL },
325 };
326
327 AVFILTER_DEFINE_CLASS(color_vulkan);
328
329 static const AVFilterPad testsrc_vulkan_outputs[] = {
330 {
331 .name = "default",
332 .type = AVMEDIA_TYPE_VIDEO,
333 .config_props = testsrc_vulkan_config_props,
334 },
335 };
336
337 const FFFilter ff_vsrc_color_vulkan = {
338 .p.name = "color_vulkan",
339 .p.description = NULL_IF_CONFIG_SMALL("Generate a constant color (Vulkan)"),
340 .p.inputs = NULL,
341 .p.flags = AVFILTER_FLAG_HWDEVICE,
342 .p.priv_class = &color_vulkan_class,
343 .priv_size = sizeof(TestSrcVulkanContext),
344 .init = &ff_vk_filter_init,
345 .uninit = &testsrc_vulkan_uninit,
346 .activate = testsrc_vulkan_activate,
347 FILTER_OUTPUTS(testsrc_vulkan_outputs),
348 FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN),
349 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
350 };
351