4 * This file is part of FFmpeg.
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.
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.
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
21 #include "libavutil/random_seed.h"
22 #include "libavutil/csp.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/vulkan_spirv.h"
25 #include "vulkan_filter.h"
27 #include "colorspace.h"
30 enum TestSrcVulkanMode
{
34 typedef struct TestSrcVulkanPushData
{
36 } TestSrcVulkanPushData
;
38 typedef struct TestSrcVulkanContext
{
39 FFVulkanContext vkctx
;
43 AVVulkanDeviceQueueFamily
*qf
;
46 /* Only used by color_vulkan */
47 uint8_t color_rgba
[4];
49 TestSrcVulkanPushData opts
;
53 char *out_format_string
;
54 enum AVColorRange out_range
;
55 unsigned int nb_frame
;
56 AVRational time_base
, frame_rate
;
58 int64_t duration
; ///< duration expressed in microseconds
59 AVRational sar
; ///< sample aspect ratio
60 int draw_once
; ///< draw only the first frame, always put out the same picture
61 int draw_once_reset
; ///< draw only the first frame or in case of reset
62 AVFrame
*picref
; ///< cached reference containing the painted picture
63 } TestSrcVulkanContext
;
65 static av_cold
int init_filter(AVFilterContext
*ctx
, enum TestSrcVulkanMode mode
)
70 void *spv_opaque
= NULL
;
71 TestSrcVulkanContext
*s
= ctx
->priv
;
72 FFVulkanContext
*vkctx
= &s
->vkctx
;
73 const int planes
= av_pix_fmt_count_planes(s
->vkctx
.output_format
);
74 FFVulkanShader
*shd
= &s
->shd
;
75 FFVkSPIRVCompiler
*spv
;
76 FFVulkanDescriptorSetBinding
*desc_set
;
77 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(s
->vkctx
.output_format
);
79 spv
= ff_vk_spirv_init();
81 av_log(ctx
, AV_LOG_ERROR
, "Unable to initialize SPIR-V compiler!\n");
82 return AVERROR_EXTERNAL
;
85 s
->qf
= ff_vk_qf_find(vkctx
, VK_QUEUE_COMPUTE_BIT
, 0);
87 av_log(ctx
, AV_LOG_ERROR
, "Device has no compute queues\n");
88 err
= AVERROR(ENOTSUP
);
92 RET(ff_vk_exec_pool_init(vkctx
, s
->qf
, &s
->e
, s
->qf
->num
*4, 0, 0, 0, NULL
));
93 RET(ff_vk_shader_init(vkctx
, &s
->shd
, "scale",
94 VK_SHADER_STAGE_COMPUTE_BIT
,
99 GLSLC(0, layout(push_constant
, std430
) uniform pushConstants
{ );
100 GLSLC(1, vec4 color_comp
; );
104 ff_vk_shader_add_push_const(&s
->shd
, 0, sizeof(s
->opts
),
105 VK_SHADER_STAGE_COMPUTE_BIT
);
107 desc_set
= (FFVulkanDescriptorSetBinding
[]) {
109 .name
= "output_img",
110 .type
= VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
,
111 .mem_layout
= ff_vk_shader_rep_fmt(s
->vkctx
.output_format
, FF_VK_REP_FLOAT
),
112 .mem_quali
= "writeonly",
115 .stages
= VK_SHADER_STAGE_COMPUTE_BIT
,
119 RET(ff_vk_shader_add_descriptor_set(vkctx
, &s
->shd
, desc_set
, 1, 0, 0));
121 GLSLC(0, void main() );
123 GLSLC(1, ivec2 pos
= ivec2(gl_GlobalInvocationID
.xy
); );
124 if (mode
== TESTSRC_COLOR
) {
125 double rgb2yuv
[3][3];
129 enum AVColorSpace csp
;
130 const AVLumaCoefficients
*luma
= NULL
;
134 if (desc
->flags
& AV_PIX_FMT_FLAG_RGB
)
137 csp
= AVCOL_SPC_SMPTE170M
;
139 if (!(desc
->flags
& AV_PIX_FMT_FLAG_RGB
) && !(luma
= av_csp_luma_coeffs_from_avcsp(csp
)))
140 return AVERROR(EINVAL
);
141 else if (!(desc
->flags
& AV_PIX_FMT_FLAG_RGB
))
142 ff_fill_rgb2yuv_table(luma
, rgb2yuv
);
144 for (int i
= 0; i
< 4; i
++)
145 rgbad
[i
] = s
->color_rgba
[i
] / 255.0;
147 if (!(desc
->flags
& AV_PIX_FMT_FLAG_RGB
))
148 ff_matrix_mul_3x3_vec(yuvad
, rgbad
, rgb2yuv
);
150 memcpy(yuvad
, rgbad
, sizeof(rgbad
));
154 if (!(desc
->flags
& AV_PIX_FMT_FLAG_RGB
)) {
155 for (int i
= 0; i
< 3; i
++) {
156 int chroma
= (!(desc
->flags
& AV_PIX_FMT_FLAG_RGB
) && i
> 0);
157 if (s
->out_range
== AVCOL_RANGE_MPEG
) {
158 yuvad
[i
] *= (chroma
? 224.0 : 219.0) / 255.0;
159 yuvad
[i
] += (chroma
? 128.0 : 16.0) / 255.0;
166 /* Ensure we place the alpha appropriately for gray formats */
167 if (desc
->nb_components
<= 2)
170 for (int i
= 0; i
< 4; i
++)
171 s
->opts
.color_comp
[i
] = yuvad
[i
];
175 for (int i
= 0, c_off
= 0; i
< planes
; i
++) {
176 for (int c
= 0; c
< desc
->nb_components
; c
++) {
177 if (desc
->comp
[c
].plane
== i
) {
178 int off
= desc
->comp
[c
].offset
/ (FFALIGN(desc
->comp
[c
].depth
, 8)/8);
179 GLSLF(1, r
[%i
] = color_comp
[%i
]; ,off
, c_off
++);
182 GLSLF(1, imageStore(output_img
[%i
], pos
, r
); ,i
);
188 RET(spv
->compile_shader(vkctx
, spv
, shd
, &spv_data
, &spv_len
, "main",
190 RET(ff_vk_shader_link(vkctx
, shd
, spv_data
, spv_len
, "main"));
192 RET(ff_vk_shader_register_exec(vkctx
, &s
->e
, &s
->shd
));
198 spv
->free_shader(spv
, &spv_opaque
);
205 static int testsrc_vulkan_activate(AVFilterContext
*ctx
)
208 AVFilterLink
*outlink
= ctx
->outputs
[0];
209 TestSrcVulkanContext
*s
= ctx
->priv
;
212 if (!s
->initialized
) {
213 enum TestSrcVulkanMode mode
= TESTSRC_COLOR
;
214 err
= init_filter(ctx
, mode
);
219 if (!ff_outlink_frame_wanted(outlink
))
220 return FFERROR_NOT_READY
;
221 if (s
->duration
>= 0 &&
222 av_rescale_q(s
->pts
, s
->time_base
, AV_TIME_BASE_Q
) >= s
->duration
) {
223 ff_outlink_set_status(outlink
, AVERROR_EOF
, s
->pts
);
228 if (s
->draw_once_reset
) {
229 av_frame_free(&s
->picref
);
230 s
->draw_once_reset
= 0;
233 s
->picref
= ff_get_video_buffer(outlink
, s
->w
, s
->h
);
235 return AVERROR(ENOMEM
);
237 err
= ff_vk_filter_process_simple(&s
->vkctx
, &s
->e
, &s
->shd
, s
->picref
, NULL
,
238 VK_NULL_HANDLE
, &s
->opts
, sizeof(s
->opts
));
242 frame
= av_frame_clone(s
->picref
);
244 frame
= ff_get_video_buffer(outlink
, s
->w
, s
->h
);
248 return AVERROR(ENOMEM
);
252 frame
->flags
= AV_FRAME_FLAG_KEY
;
253 frame
->pict_type
= AV_PICTURE_TYPE_I
;
254 frame
->sample_aspect_ratio
= s
->sar
;
256 err
= ff_vk_filter_process_simple(&s
->vkctx
, &s
->e
, &s
->shd
, frame
, NULL
,
257 VK_NULL_HANDLE
, &s
->opts
, sizeof(s
->opts
));
259 av_frame_free(&frame
);
267 return ff_filter_frame(outlink
, frame
);
270 static int testsrc_vulkan_config_props(AVFilterLink
*outlink
)
273 FilterLink
*l
= ff_filter_link(outlink
);
274 TestSrcVulkanContext
*s
= outlink
->src
->priv
;
275 FFVulkanContext
*vkctx
= &s
->vkctx
;
277 if (!s
->out_format_string
) {
278 vkctx
->output_format
= AV_PIX_FMT_YUV444P
;
280 vkctx
->output_format
= av_get_pix_fmt(s
->out_format_string
);
281 if (vkctx
->output_format
== AV_PIX_FMT_NONE
) {
282 av_log(vkctx
, AV_LOG_ERROR
, "Invalid output format.\n");
283 return AVERROR(EINVAL
);
287 err
= ff_vk_filter_init_context(outlink
->src
, vkctx
, NULL
,
288 s
->w
, s
->h
, vkctx
->output_format
);
292 l
->hw_frames_ctx
= av_buffer_ref(vkctx
->frames_ref
);
293 if (!l
->hw_frames_ctx
)
294 return AVERROR(ENOMEM
);
296 s
->time_base
= av_inv_q(s
->frame_rate
);
300 s
->vkctx
.output_width
= s
->w
;
301 s
->vkctx
.output_height
= s
->h
;
304 outlink
->sample_aspect_ratio
= s
->sar
;
305 l
->frame_rate
= s
->frame_rate
;
306 outlink
->time_base
= s
->time_base
;
311 static void testsrc_vulkan_uninit(AVFilterContext
*avctx
)
313 TestSrcVulkanContext
*s
= avctx
->priv
;
314 FFVulkanContext
*vkctx
= &s
->vkctx
;
316 av_frame_free(&s
->picref
);
318 ff_vk_exec_pool_free(vkctx
, &s
->e
);
319 ff_vk_shader_free(vkctx
, &s
->shd
);
321 ff_vk_uninit(&s
->vkctx
);
326 #define OFFSET(x) offsetof(TestSrcVulkanContext, x)
327 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
329 #define COMMON_OPTS \
330 { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS }, \
331 { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS }, \
333 { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS }, \
334 { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS }, \
336 { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, \
337 { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, \
339 { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, { .dbl = 1 }, 0, INT_MAX, FLAGS }, \
341 { "format", "Output video format (software format of hardware frames)", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS },
343 static const AVOption color_vulkan_options
[] = {
344 { "color", "set color", OFFSET(color_rgba
), AV_OPT_TYPE_COLOR
, {.str
= "black"}, 0, 0, FLAGS
},
345 { "c", "set color", OFFSET(color_rgba
), AV_OPT_TYPE_COLOR
, {.str
= "black"}, 0, 0, FLAGS
},
347 { "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" },
348 { "full", "Full range", 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_JPEG
}, 0, 0, FLAGS
, .unit
= "range" },
349 { "limited", "Limited range", 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_MPEG
}, 0, 0, FLAGS
, .unit
= "range" },
350 { "jpeg", "Full range", 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_JPEG
}, 0, 0, FLAGS
, .unit
= "range" },
351 { "mpeg", "Limited range", 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_MPEG
}, 0, 0, FLAGS
, .unit
= "range" },
352 { "tv", "Limited range", 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_MPEG
}, 0, 0, FLAGS
, .unit
= "range" },
353 { "pc", "Full range", 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_JPEG
}, 0, 0, FLAGS
, .unit
= "range" },
357 AVFILTER_DEFINE_CLASS(color_vulkan
);
359 static const AVFilterPad testsrc_vulkan_outputs
[] = {
362 .type
= AVMEDIA_TYPE_VIDEO
,
363 .config_props
= testsrc_vulkan_config_props
,
367 const FFFilter ff_vsrc_color_vulkan
= {
368 .p
.name
= "color_vulkan",
369 .p
.description
= NULL_IF_CONFIG_SMALL("Generate a constant color (Vulkan)"),
371 .p
.flags
= AVFILTER_FLAG_HWDEVICE
,
372 .p
.priv_class
= &color_vulkan_class
,
373 .priv_size
= sizeof(TestSrcVulkanContext
),
374 .init
= &ff_vk_filter_init
,
375 .uninit
= &testsrc_vulkan_uninit
,
376 .activate
= testsrc_vulkan_activate
,
377 FILTER_OUTPUTS(testsrc_vulkan_outputs
),
378 FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN
),
379 .flags_internal
= FF_FILTER_FLAG_HWFRAME_AWARE
,