FFmpeg
Loading...
Searching...
No Matches
vf_v360_vulkan.c
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg 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 * FFmpeg 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 GNU
12 * 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 FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include "libavutil/opt.h"
20#include "vulkan_filter.h"
21
22#include "v360.h"
23#include "filters.h"
24#include "video.h"
25#include "framesync.h"
26
27extern const unsigned char ff_v360_comp_spv_data[];
28extern const unsigned int ff_v360_comp_spv_len;
29
30/* Push constants */
31struct PushData {
32 float rot_mat[4][4];
33 float iflat_range[2];
34 float flat_range[2];
35};
36
37typedef struct V360ulkanContext {
40
45 VkSampler sampler;
46 struct PushData pd;
47
48 /* Options */
49 int in, out;
51 float h_fov, v_fov;
52 float ih_fov, iv_fov;
53 float yaw, pitch, roll;
54 char *rorder;
58
59static int get_rorder(char c)
60{
61 switch (c) {
62 case 'Y':
63 case 'y':
64 return YAW;
65 case 'P':
66 case 'p':
67 return PITCH;
68 case 'R':
69 case 'r':
70 return ROLL;
71 default:
72 return -1;
73 }
74}
75
76static void multiply_matrix(float c[4][4], const float a[4][4], const float b[4][4])
77{
78 for (int i = 0; i < 3; i++) {
79 for (int j = 0; j < 3; j++) {
80 float sum = 0.0f;
81 for (int k = 0; k < 3; k++)
82 sum += a[i][k] * b[k][j];
83 c[i][j] = sum;
84 }
85 }
86}
87
88#define degree2radian(degree) ((degree) * M_PI / 180.f)
89#define radian2degree(radian) ((radian) * 180.f / M_PI)
90
91static inline void calculate_iflat_range(int in, float ih_fov, float iv_fov,
92 float *iflat_range)
93{
94 switch (in) {
95 case FLAT:
96 iflat_range[0] = tanf(0.5f * FFMIN(ih_fov, 179.f) * M_PI / 180.f);
97 iflat_range[1] = tanf(0.5f * FFMIN(iv_fov, 179.f) * M_PI / 180.f);
98 break;
99 case STEREOGRAPHIC:
100 iflat_range[0] = tanf(FFMIN(ih_fov, 359.f) * M_PI / 720.f);
101 iflat_range[1] = tanf(FFMIN(iv_fov, 359.f) * M_PI / 720.f);
102 break;
103 case DUAL_FISHEYE:
104 case FISHEYE:
105 iflat_range[0] = ih_fov / 180.f;
106 iflat_range[1] = iv_fov / 180.f;
107 break;
108 default:
109 break;
110 }
111}
112
113static inline void calculate_flat_range(int out, float h_fov, float v_fov,
114 float *flat_range)
115{
116 switch (out) {
117 case FLAT:
118 flat_range[0] = tanf(0.5f * FFMIN(h_fov, 179.f) * M_PI / 180.f);
119 flat_range[1] = tanf(0.5f * FFMIN(v_fov, 179.f) * M_PI / 180.f);
120 break;
121 case STEREOGRAPHIC:
122 flat_range[0] = tanf(FFMIN(h_fov, 359.f) * M_PI / 720.f);
123 flat_range[1] = tanf(FFMIN(v_fov, 359.f) * M_PI / 720.f);
124 break;
125 case DUAL_FISHEYE:
126 case FISHEYE:
127 flat_range[0] = h_fov / 180.f;
128 flat_range[1] = v_fov / 180.f;
129 break;
130 default:
131 break;
132 }
133}
134
135static inline void calculate_rotation_matrix(float yaw, float pitch, float roll,
136 float rot_mat[4][4],
137 const int rotation_order[3])
138{
139 const float yaw_rad = yaw * M_PI / 180.f;
140 const float pitch_rad = pitch * M_PI / 180.f;
141 const float roll_rad = roll * M_PI / 180.f;
142
143 const float sin_yaw = sinf(yaw_rad);
144 const float cos_yaw = cosf(yaw_rad);
145 const float sin_pitch = sinf(pitch_rad);
146 const float cos_pitch = cosf(pitch_rad);
147 const float sin_roll = sinf(roll_rad);
148 const float cos_roll = cosf(roll_rad);
149
150 float m[3][4][4];
151 float temp[4][4];
152
153 m[0][0][0] = cos_yaw; m[0][0][1] = 0; m[0][0][2] = sin_yaw;
154 m[0][1][0] = 0; m[0][1][1] = 1; m[0][1][2] = 0;
155 m[0][2][0] = -sin_yaw; m[0][2][1] = 0; m[0][2][2] = cos_yaw;
156
157 m[1][0][0] = 1; m[1][0][1] = 0; m[1][0][2] = 0;
158 m[1][1][0] = 0; m[1][1][1] = cos_pitch; m[1][1][2] = -sin_pitch;
159 m[1][2][0] = 0; m[1][2][1] = sin_pitch; m[1][2][2] = cos_pitch;
160
161 m[2][0][0] = cos_roll; m[2][0][1] = -sin_roll; m[2][0][2] = 0;
162 m[2][1][0] = sin_roll; m[2][1][1] = cos_roll; m[2][1][2] = 0;
163 m[2][2][0] = 0; m[2][2][1] = 0; m[2][2][2] = 1;
164
165 multiply_matrix(temp, m[rotation_order[0]], m[rotation_order[1]]);
166 multiply_matrix(rot_mat, temp, m[rotation_order[2]]);
167}
168
170{
171 V360VulkanContext *s = ctx->priv;
172
173 switch (s->in) {
174 case FLAT: {
175 float sar = inlink->sample_aspect_ratio.num ?
176 (float) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
177 if (s->ih_fov == 0.f && s->iv_fov == 0.f) {
178 s->ih_fov = 90.f;
179 s->iv_fov = radian2degree(2.f * atanf((float)inlink->h / sar / inlink->w));
180 }
181 else if (s->ih_fov == 0.f || s->ih_fov >= 180.f) {
182 if (s->iv_fov >= 180.f)
183 s->ih_fov = s->iv_fov = 180.f;
184 else
185 s->iv_fov = radian2degree(2.f * atanf((float)inlink->h / sar / inlink->w * tanf(degree2radian(s->ih_fov) / 2.f)));
186 }
187 else if (s->iv_fov == 0.f || s->iv_fov >= 180.f) {
188 if (s->ih_fov >= 180.f)
189 s->ih_fov = s->iv_fov = 180.f;
190 else
191 s->ih_fov = radian2degree(2.f * atanf((float)inlink->w * sar / inlink->h * tanf(degree2radian(s->iv_fov) / 2.f)));
192 }
193 break;
194 }
195 case STEREOGRAPHIC:
196 case DUAL_FISHEYE:
197 case FISHEYE:
198 if (s->ih_fov == 0.f)
199 s->ih_fov = 180.f;
200 if (s->iv_fov == 0.f)
201 s->iv_fov = 180.f;
202 break;
203 case EQUIRECTANGULAR: /* unchangeable */
204 case GOPROMAX:
205 s->ih_fov = 360.f;
206 s->iv_fov = 180.f;
207 break;
208 default:
209 if (s->ih_fov == 0.f)
210 s->ih_fov = 360.f;
211 if (s->iv_fov == 0.f)
212 s->iv_fov = 180.f;
213 break;
214 }
215
216 switch (s->out) {
217 case FLAT:
218 if (s->width > 0 && s->height > 0 &&
219 (s->h_fov == 0.f || s->h_fov >= 180.f || s->v_fov == 0.f || s->v_fov >= 180.f)) {
220 if (s->h_fov == 0.f && s->v_fov == 0.f) {
221 s->h_fov = 90.f;
222 s->v_fov = radian2degree(2.f * atanf((float)s->height / s->width));
223 }
224 else if (s->h_fov == 0.f || s->h_fov >= 180.f) {
225 if (s->v_fov >= 180.f)
226 s->h_fov = s->v_fov = 180.f;
227 else
228 s->v_fov = radian2degree(2.f * atanf((float)s->height / s->width * tanf(degree2radian(s->h_fov) / 2.f)));
229 }
230 else if (s->v_fov == 0.f || s->v_fov >= 180.f) {
231 if (s->h_fov >= 180.f)
232 s->h_fov = s->v_fov = 180.f;
233 else
234 s->h_fov = radian2degree(2.f * atanf((float)s->width / s->height * tanf(degree2radian(s->v_fov) / 2.f)));
235 }
236 }
237 else {
238 if (s->h_fov >= 180.f || s->v_fov >= 180.f) {
239 s->h_fov = 180.f;
240 s->v_fov = 180.f;
241 }
242 else {
243 if (s->h_fov == 0.f)
244 s->h_fov = 90.f;
245 if (s->v_fov == 0.f)
246 s->v_fov = 45.f;
247 }
248 }
249 break;
250 case STEREOGRAPHIC:
251 case DUAL_FISHEYE:
252 case FISHEYE:
253 if (s->h_fov == 0.f)
254 s->h_fov = 180.f;
255 if (s->v_fov == 0.f)
256 s->v_fov = 180.f;
257 break;
258 default:
259 if (s->h_fov == 0.f)
260 s->h_fov = 360.f;
261 if (s->v_fov == 0.f)
262 s->v_fov = 180.f;
263 break;
264 }
265
266 for (int order = 0; order < NB_RORDERS; order++) {
267 const char c = s->rorder[order];
268 int rorder;
269
270 if (c == '\0') {
272 "Incomplete rorder option. "
273 "Direction for all 3 rotation orders should be specified. "
274 "Switching to default rorder.\n");
275 s->rotation_order[0] = YAW;
276 s->rotation_order[1] = PITCH;
277 s->rotation_order[2] = ROLL;
278 break;
279 }
280
281 rorder = get_rorder(c);
282 if (rorder == -1) {
284 "Incorrect rotation order symbol '%c' in rorder option. "
285 "Switching to default rorder.\n", c);
286 s->rotation_order[0] = YAW;
287 s->rotation_order[1] = PITCH;
288 s->rotation_order[2] = ROLL;
289 break; }
290
291 s->rotation_order[order] = rorder;
292 }
293
294 calculate_iflat_range(s->in, s->ih_fov, s->iv_fov, s->pd.iflat_range);
295 calculate_flat_range(s->out, s->h_fov, s->v_fov, s->pd.flat_range);
296 calculate_rotation_matrix(s->yaw, s->pitch, s->roll,
297 s->pd.rot_mat, s->rotation_order);
298
299 return;
300}
301
303{
304 V360VulkanContext *s = ctx->priv;
305 FFVulkanContext *vkctx = &s->vkctx;
306 AVFilterLink *inlink = ctx->inputs[0];
307 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->vkctx.output_format);
308 float sar = inlink->sample_aspect_ratio.num ?
309 (float) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
310 float wf = (float) s->width;
311 float hf = (float) s->height;
312 int min_w, min_h, pw, ph;
313
314 if (s->width > 0 && s->height > 0) {
315 if (sar == 1) {
316 vkctx->output_width = s->width;
317 }
318 else {
319 vkctx->output_width = lrint(wf / sar);
320 if (vkctx->output_width % 2 != 0)
321 vkctx->output_width++;
322 }
323 vkctx->output_height = s->height;
324 }
325 else if (s->width > 0 && s->height == 0) {
326 if (sar == 1) {
327 vkctx->output_width = s->width;
328 }
329 else {
330 vkctx->output_width = lrint(wf / sar);
331 if (vkctx->output_width % 2 != 0)
332 vkctx->output_width++;
333 }
334 switch (s->out) {
335 case FLAT:
336 hf = wf * s->pd.iflat_range[1] * 2.f;
337 break;
338 case EQUIRECTANGULAR:
339 case DUAL_FISHEYE:
340 hf = wf * sar / 2.f;
341 break;
342 case EQUIANGULAR:
343 hf = wf * sar / 3.f * 2.f;
344 break;
345 case STEREOGRAPHIC:
346 case FISHEYE:
347 hf = wf * sar;
348 break;
349 default:
350 break;
351 }
352 vkctx->output_height = lrint(hf);
353 if (vkctx->output_height % 2 != 0)
354 vkctx->output_height++;
355 }
356 else { /* s->width == 0 */
357 vkctx->output_height = s->height;
358 if (s->height == 0) {
359 switch (s->in) {
360 case FLAT:
361 hf = (float)inlink->h / s->pd.iflat_range[1] / 2.f;
362 break;
363 case GOPROMAX:
364 hf = (float)inlink->h * 2.f;
365 break;
366 default:
367 hf = (float)inlink->h;
368 break;
369 }
370 switch (s->out) {
371 case FLAT:
372 hf = (float)inlink->h * s->pd.flat_range[1] * 2.f;
373 break;
374 default:
375 break;
376 }
377 vkctx->output_height = lrint(hf);
378 if (vkctx->output_height % 2 != 0)
379 vkctx->output_height++;
380 }
381 switch (s->out) {
382 case FLAT:
383 wf = hf * s->pd.flat_range[0] * 2.f;
384 break;
385 case EQUIRECTANGULAR:
386 case DUAL_FISHEYE:
387 wf = hf * 2.f;
388 break;
389 case EQUIANGULAR:
390 wf = hf * 3.f / 2.f;
391 break;
392 case STEREOGRAPHIC:
393 case FISHEYE:
394 wf = hf;
395 break;
396 default:
397 break;
398 }
399 vkctx->output_width = lrint(wf / sar);
400 if (vkctx->output_width % 2 != 0)
401 vkctx->output_width++;
402 }
403
404 if (vkctx->output_width < 1 || vkctx->output_width > INT16_MAX ||
405 vkctx->output_height < 1 || vkctx->output_height > INT16_MAX) {
407 "Output dimensions %dx%d are outside the allowed range [1, %d].\n",
408 vkctx->output_width, vkctx->output_height, INT16_MAX);
409 return AVERROR(EINVAL);
410 }
411
412 pw = AV_CEIL_RSHIFT(vkctx->output_width, desc->log2_chroma_w);
413 ph = AV_CEIL_RSHIFT(vkctx->output_height, desc->log2_chroma_h);
414 switch (s->out) {
415 case EQUIRECTANGULAR:
416 case DUAL_FISHEYE:
417 min_w = 2;
418 min_h = 1;
419 break;
420 case EQUIANGULAR:
421 min_w = 3;
422 min_h = 2;
423 break;
424 default:
425 min_w = 1;
426 min_h = 1;
427 break;
428 }
429 if (pw < min_w || ph < min_h) {
431 "Output %dx%d is too small for the output projection "
432 "(requires at least %dx%d per plane).\n", pw, ph, min_w, min_h);
433 return AVERROR(EINVAL);
434 }
435
436 return 0;
437}
438
440{
441 int err;
442 V360VulkanContext *s = ctx->priv;
443 FFVulkanContext *vkctx = &s->vkctx;
444 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->vkctx.output_format);
445 const int planes = av_pix_fmt_count_planes(s->vkctx.output_format);
446
447 RET(ff_vk_init_sampler(vkctx, &s->sampler, 0, VK_FILTER_LINEAR));
448
449 s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0);
450 if (!s->qf) {
451 av_log(ctx, AV_LOG_ERROR, "Device has no compute queues\n");
452 err = AVERROR(ENOTSUP);
453 goto fail;
454 }
455
456 RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, FF_VK_DEFAULT_EXEC_CONTEXTS, 0, 0, 0, NULL));
457
458 SPEC_LIST_CREATE(sl, 17, 14*sizeof(int) + 3*sizeof(float))
459 SPEC_LIST_ADD(sl, 0, 32, s->out);
460 SPEC_LIST_ADD(sl, 1, 32, s->in);
461
462 const float m_pi = M_PI, m_pi2 = M_PI_2, m_pi4 = M_PI_4;
463 SPEC_LIST_ADD(sl, 2, 32, av_float2int(m_pi));
464 SPEC_LIST_ADD(sl, 3, 32, av_float2int(m_pi2));
465 SPEC_LIST_ADD(sl, 4, 32, av_float2int(m_pi4));
466
467 SPEC_LIST_ADD(sl, 5, 32, planes);
468 SPEC_LIST_ADD(sl, 6, 32, in->width);
469 SPEC_LIST_ADD(sl, 7, 32, in->height);
470 SPEC_LIST_ADD(sl, 8, 32, FF_CEIL_RSHIFT(in->width, desc->log2_chroma_w));
471 SPEC_LIST_ADD(sl, 9, 32, FF_CEIL_RSHIFT(in->height, desc->log2_chroma_h));
472
473 if (s->in == GOPROMAX) {
474 int cube_size = in->height;
475 int gopro_cube_width = (in->width - cube_size) / 2;
476
477 SPEC_LIST_ADD(sl, 10, 32, cube_size);
478 SPEC_LIST_ADD(sl, 11, 32, gopro_cube_width);
479 SPEC_LIST_ADD(sl, 12, 32, s->overlap);
480 SPEC_LIST_ADD(sl, 13, 32, gopro_cube_width + cube_size);
481 SPEC_LIST_ADD(sl, 14, 32, gopro_cube_width / 2 - s->overlap);
482 SPEC_LIST_ADD(sl, 15, 32, gopro_cube_width / 2);
483 SPEC_LIST_ADD(sl, 16, 32, gopro_cube_width / 2 + s->overlap);
484 }
485
486 ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT,
487 sl, (uint32_t []) { 16, 16, 1 }, 0);
488
489 ff_vk_shader_add_push_const(&s->shd, 0, sizeof(struct PushData),
490 VK_SHADER_STAGE_COMPUTE_BIT);
491
492 const FFVulkanDescriptorSetBinding desc_set[] = {
493 { /* output_img */
494 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
495 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
496 .elems = planes,
497 },
498 { /* input_img */
499 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
500 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
501 .elems = planes,
502 .samplers = DUP_SAMPLER(s->sampler),
503 },
504 { /* input2_img */
505 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
506 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
507 .elems = planes,
508 .samplers = DUP_SAMPLER(s->sampler),
509 },
510 };
511 ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 3, 0);
512
513 RET(ff_vk_shader_link(vkctx, &s->shd,
515 ff_v360_comp_spv_len, "main"));
516
517 RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->shd));
518
519 s->initialized = 1;
520
521fail:
522 return err;
523}
524
526{
527 int err;
528 AVFilterContext *ctx = fs->parent;
529 V360VulkanContext *s = ctx->priv;
530 AVFilterLink *outlink = ctx->outputs[0];
531 AVFrame *in[2] = { NULL };
532 AVFrame *out = NULL;
533
534 for (int i = 0; i < ctx->nb_inputs; i++) {
535 RET(ff_framesync_get_frame(fs, i, &in[i], 0));
536 if (!in[i])
537 return 0;
538 }
539
540 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
541 if (!out) {
542 err = AVERROR(ENOMEM);
543 goto fail;
544 }
545
546 if (!s->initialized) {
547 AVHWFramesContext *fc0 = (AVHWFramesContext*)in[0]->hw_frames_ctx->data;
548 for (int i = 1; i < ctx->nb_inputs; i++) {
549 AVHWFramesContext *fci = (AVHWFramesContext*)in[i]->hw_frames_ctx->data;
550 if (fc0->sw_format != fci->sw_format) {
551 av_log(ctx, AV_LOG_ERROR, "Input %d has a different format "
552 "from input 0\n", i);
553 err = AVERROR(EINVAL);
554 goto fail;
555 }
556 }
557 RET(init_filter(ctx, in[0]));
558 }
559
560 RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->shd, out,
561 in, ctx->nb_inputs, s->sampler, 1,
562 &s->pd, sizeof(s->pd)));
563 RET(av_frame_copy_props(out, in[0]));
564
565 return ff_filter_frame(outlink, out);
566
567fail:
569 return err;
570}
571
573{
574 V360VulkanContext *s = ctx->priv;
575 return ff_framesync_activate(&s->fs);
576}
577
578static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
579 char *res, int res_len, int flags)
580{
581 int err;
582 AVFilterLink *inlink = ctx->inputs[0];
583
584 RET(ff_filter_process_command(ctx, cmd, args, res, res_len, flags));
585 config_params(ctx, inlink);
586
587fail:
588 return err;
589}
590
592{
593 int err;
594 AVFilterContext *ctx = outlink->src;
595 V360VulkanContext *s = ctx->priv;
596 AVFilterLink *inlink = ctx->inputs[0];
597
598 if (s->in == GOPROMAX) {
599 if (s->overlap == 0) {
600 if (inlink->h <= 960) {
601 s->overlap = 32;
602 }
603 else if (inlink->h < 1920) {
604 s->overlap = 64;
605 }
606 else
607 s->overlap = 96;
608 }
609 if (ctx->nb_inputs != 2) {
610 av_log(ctx, AV_LOG_ERROR, "GoPro Max. requires 2 input streams.\n");
611 return AVERROR(EINVAL);
612 }
613 if ((inlink->w != ctx->inputs[1]->w) ||
614 (inlink->h != ctx->inputs[1]->h) ||
615 (inlink->w < inlink->h * 3) ||
616 (inlink->w > inlink->h * 3 + s->overlap * 2)) {
617 av_log(ctx, AV_LOG_ERROR, "Incompatible inputs for GoPro Max. (%dx%d + %dx%d)\n",
618 inlink->w, inlink->h, ctx->inputs[1]->w, ctx->inputs[1]->h);
619 return AVERROR(EINVAL);
620 }
621 }
622 else if (ctx->nb_inputs != 1) {
623 av_log(ctx, AV_LOG_ERROR, "Too many inputs (%d)\n", ctx->nb_inputs);
624 return AVERROR(EINVAL);
625 }
626
627 config_params(ctx, inlink);
630
631 RET(ff_framesync_init(&s->fs, ctx, ctx->nb_inputs));
632 for (int i = 0; i < ctx->nb_inputs; i++) {
633 s->fs.in[i].time_base = ctx->inputs[i]->time_base;
634 s->fs.in[i].sync = 1;
635 s->fs.in[i].before = EXT_STOP;
636 s->fs.in[i].after = EXT_STOP;
637 }
638 s->fs.on_event = &v360_vulkan_filter_frame;
639
641 outlink->time_base = s->fs.time_base;
642
643fail:
644 return err;
645}
646
648{
649 int err;
650 V360VulkanContext *s = avctx->priv;
651 AVFilterPad pad = {
652 .type = AVMEDIA_TYPE_VIDEO,
653 .config_props = ff_vk_filter_config_input,
654 };
655
656 if (s->in == GOPROMAX) {
657 pad.name = "front";
658 RET(ff_append_inpad(avctx, &pad));
659 pad.name = "rear";
660 RET(ff_append_inpad(avctx, &pad));
661 }
662 else {
663 pad.name = "default";
664 RET(ff_append_inpad(avctx, &pad));
665 }
666
667 return ff_vk_filter_init(avctx);
668
669fail:
670 return err;
671}
672
674{
675 V360VulkanContext *s = avctx->priv;
676 FFVulkanContext *vkctx = &s->vkctx;
677 FFVulkanFunctions *vk = &vkctx->vkfn;
678
680 ff_vk_exec_pool_free(vkctx, &s->e);
681 ff_vk_shader_free(vkctx, &s->shd);
682
683 if (s->sampler)
684 vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler,
685 vkctx->hwctx->alloc);
686
687 ff_vk_uninit(&s->vkctx);
688
689 s->initialized = 0;
690}
691
692#define OFFSET(x) offsetof(V360VulkanContext, x)
693#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
694#define DYNAMIC (FLAGS | AV_OPT_FLAG_RUNTIME_PARAM)
696 { "input", "set input projection", OFFSET(in), AV_OPT_TYPE_INT, {.i64=EQUIRECTANGULAR}, 0, NB_PROJECTIONS-1, FLAGS, "in" },
697 { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
698 { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
699 { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
700 { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" },
701 { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" },
702 { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "in" },
703 { "gopromax", "GoPro Max (two inputs)", 0, AV_OPT_TYPE_CONST, {.i64=GOPROMAX}, 0, 0, FLAGS, "in" },
704
705 { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=FLAT}, 0, NB_PROJECTIONS-1, FLAGS, "out" },
706 { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
707 { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
708 { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" },
709 { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
710 { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" },
711 { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" },
712 { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "out" },
713
714 { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT16_MAX, FLAGS, "w" },
715 { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT16_MAX, FLAGS, "h" },
716 { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, -180.f, 180.f, DYNAMIC, "yaw" },
717 { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, -180.f, 180.f, DYNAMIC, "pitch" },
718 { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, -180.f, 180.f, DYNAMIC, "roll" },
719 { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str = "ypr"}, 0, 0, DYNAMIC, "rorder" },
720 { "h_fov", "set output horizontal FOV angle", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "h_fov" },
721 { "v_fov", "set output vertical FOV angle", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "v_fov" },
722 { "ih_fov", "set input horizontal FOV angle", OFFSET(ih_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "ih_fov" },
723 { "iv_fov", "set input vertical FOV angle", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "iv_fov" },
724 { "overlap", "overlapped pixels for GoPro Max", OFFSET(overlap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1024, FLAGS, "overlap" },
725
726 { NULL },
727};
728
730
732 {
733 .name = "default",
734 .type = AVMEDIA_TYPE_VIDEO,
735 .config_props = v360_vulkan_config_output,
736 },
737};
738
740 .p.name = "v360_vulkan",
741 .p.description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
742 .p.priv_class = &v360_vulkan_class,
744 .priv_size = sizeof(V360VulkanContext),
745 .preinit = v360_vulkan_framesync_preinit,
751 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
752 .process_command = process_command,
753};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int const int8_t * hf
Definition dsp.h:262
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, char *res, int res_len, int flags)
const FFFilter ff_vf_v360_vulkan
static FILE * out
static AVFormatContext * ctx
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1073
int ff_append_inpad(AVFilterContext *f, AVFilterPad *p)
Append a new input/output pad to the filter's list of such pads.
Definition avfilter.c:127
int ff_filter_process_command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
Generic processing of user supplied commands that are set in the same way as the filter options.
Definition avfilter.c:911
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
static int FUNC ph(CodedBitstreamContext *ctx, RWContext *rw, H266RawPH *current)
#define s(width, name)
Definition cbs_vp9.c:198
#define fs(width, name, subs,...)
Definition cbs_vp9.c:200
#define FLAGS
Definition cmdutils.c:596
#define FF_CEIL_RSHIFT
Definition common.h:63
#define AV_CEIL_RSHIFT(a, b)
Definition common.h:60
#define NULL
Definition coverity.c:32
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
int ff_framesync_configure(FFFrameSync *fs)
Configure a frame sync structure.
Definition framesync.c:137
int ff_framesync_activate(FFFrameSync *fs)
Examine the frames in the filter's input and try to produce output.
Definition framesync.c:352
int ff_framesync_get_frame(FFFrameSync *fs, unsigned in, AVFrame **rframe, unsigned get)
Get the current frame in an input.
Definition framesync.c:269
void ff_framesync_uninit(FFFrameSync *fs)
Free all memory currently allocated.
Definition framesync.c:301
int ff_framesync_init(FFFrameSync *fs, AVFilterContext *parent, unsigned nb_in)
Initialize a frame sync structure.
Definition framesync.c:86
#define FRAMESYNC_DEFINE_CLASS(name, context, field)
Definition framesync.h:352
#define fail
Definition test.h:479
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_FLOAT
Underlying C type is float.
Definition opt.h:270
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
#define AVFILTER_FLAG_HWDEVICE
The filter can create hardware frames using AVFilterContext.hw_device_ctx.
Definition avfilter.h:193
#define AVFILTER_FLAG_DYNAMIC_INPUTS
The number of the filter inputs is not determined just by AVFilter.inputs.
Definition avfilter.h:161
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
int a
#define b
Definition input.c:43
static av_always_inline uint32_t av_float2int(float f)
Reinterpret a float as a 32-bit integer.
Definition intfloat.h:50
static av_cold void uninit(AVBitStreamFilterContext *ctx)
static int activate(AVBitStreamFilterContext *ctx)
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FF_FILTER_FLAG_HWFRAME_AWARE
The filter is aware of hardware frames, and any hardware frame context should not be automatically pr...
Definition filters.h:208
#define FILTER_SINGLE_PIXFMT(pix_fmt_)
Definition filters.h:254
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:97
int ff_vk_shader_load(FFVulkanShader *shd, VkPipelineStageFlags stage, VkSpecializationInfo *spec, uint32_t wg_size[3], uint32_t required_subgroup_size)
Initialize a shader object.
Definition vulkan.c:2262
void ff_vk_shader_add_descriptor_set(FFVulkanContext *s, FFVulkanShader *shd, const FFVulkanDescriptorSetBinding *desc, int nb, int singular)
Add descriptor to a shader.
Definition vulkan.c:2521
void ff_vk_exec_pool_free(FFVulkanContext *s, FFVkExecPool *pool)
Definition vulkan.c:335
int ff_vk_exec_pool_init(FFVulkanContext *s, AVVulkanDeviceQueueFamily *qf, FFVkExecPool *pool, int nb_contexts, int nb_queries, VkQueryType query_type, int query_64bit, const void *query_create_pnext)
Allocates/frees an execution pool.
Definition vulkan.c:399
int ff_vk_shader_add_push_const(FFVulkanShader *shd, int offset, int size, VkShaderStageFlagBits stage)
Add/update push constants for execution.
Definition vulkan.c:1631
void ff_vk_uninit(FFVulkanContext *s)
Frees main context.
Definition vulkan.c:2779
void ff_vk_shader_free(FFVulkanContext *s, FFVulkanShader *shd)
Free a shader.
Definition vulkan.c:2757
int ff_vk_shader_register_exec(FFVulkanContext *s, FFVkExecPool *pool, FFVulkanShader *shd)
Register a shader with an exec pool.
Definition vulkan.c:2555
int ff_vk_init_sampler(FFVulkanContext *s, VkSampler *sampler, int unnorm_coords, VkFilter filt)
Create a sampler.
Definition vulkan.c:1642
AVVulkanDeviceQueueFamily * ff_vk_qf_find(FFVulkanContext *s, VkQueueFlagBits dev_family, VkVideoCodecOperationFlagBitsKHR vid_ops)
Chooses an appropriate QF.
Definition vulkan.c:320
int ff_vk_shader_link(FFVulkanContext *s, FFVulkanShader *shd, const char *spirv, size_t spirv_len, const char *entrypoint)
Link a shader into an executable.
Definition vulkan.c:2425
const char * desc
Definition libsvtav1.c:83
static const struct @257111027162314367033347246032313251342043035002 planes[]
#define FFMIN(a, b)
Definition macros.h:49
#define M_PI_2
Definition mathematics.h:73
#define M_PI
Definition mathematics.h:67
#define M_PI_4
Definition mathematics.h:79
AVOptions.
@ EXT_STOP
Completely stop all streams with this one.
Definition packetsync.h:62
int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3500
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
@ AV_PIX_FMT_VULKAN
Vulkan hardware images.
Definition pixfmt.h:379
static av_cold int preinit(AVBitStreamFilterContext *ctx)
Definition source.c:137
An instance of a filter.
Definition avfilter.h:279
void * priv
private data for use by the filter
Definition avfilter.h:294
A filter pad used for either input or output.
Definition filters.h:40
const char * name
Pad name.
Definition filters.h:46
This structure describes decoded (raw) audio or video data.
Definition frame.h:479
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:500
int width
Definition frame.h:551
int height
Definition frame.h:551
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
Definition hwcontext.h:213
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
const VkAllocationCallbacks * alloc
Custom memory allocator, else NULL.
VkDevice act_dev
Active device.
Frame sync structure.
Definition framesync.h:168
int output_height
Definition vulkan.h:352
AVVulkanDeviceContext * hwctx
Definition vulkan.h:340
FFVulkanFunctions vkfn
Definition vulkan.h:296
float rot_mat[4][4]
float flat_range[2]
float iflat_range[2]
struct PushData pd
AVVulkanDeviceQueueFamily * qf
FFVulkanContext vkctx
FFVulkanShader shd
#define lrint
Definition tablegen.h:53
#define av_log(a,...)
#define height
Definition dsp.h:89
#define width
Definition dsp.h:89
@ FLAT
Definition v360.h:37
@ FISHEYE
Definition v360.h:46
@ DUAL_FISHEYE
Definition v360.h:38
@ EQUIRECTANGULAR
Definition v360.h:33
@ STEREOGRAPHIC
Definition v360.h:41
@ GOPROMAX
Definition v360.h:58
@ EQUIANGULAR
Definition v360.h:36
@ NB_PROJECTIONS
Definition v360.h:59
@ ROLL
Definition v360.h:105
@ NB_RORDERS
Definition v360.h:106
@ YAW
Definition v360.h:103
@ PITCH
Definition v360.h:104
#define DYNAMIC
else temp
Definition vf_mcdeint.c:275
static int v360_vulkan_activate(AVFilterContext *ctx)
static const AVOption v360_vulkan_options[]
static const AVFilterPad v360_vulkan_outputs[]
static av_cold int v360_vulkan_config_output(AVFilterLink *outlink)
static void v360_vulkan_uninit(AVFilterContext *avctx)
#define radian2degree(radian)
const unsigned char ff_v360_comp_spv_data[]
static void calculate_iflat_range(int in, float ih_fov, float iv_fov, float *iflat_range)
static void calculate_rotation_matrix(float yaw, float pitch, float roll, float rot_mat[4][4], const int rotation_order[3])
static void config_params(AVFilterContext *ctx, AVFilterLink *inlink)
const unsigned int ff_v360_comp_spv_len
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, char *res, int res_len, int flags)
#define degree2radian(degree)
static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in)
static void calculate_flat_range(int out, float h_fov, float v_fov, float *flat_range)
static int v360_vulkan_filter_frame(FFFrameSync *fs)
static av_cold int v360_vulkan_init(AVFilterContext *avctx)
static void multiply_matrix(float c[4][4], const float a[4][4], const float b[4][4])
#define OFFSET(x)
static int get_rorder(char c)
static av_cold int calculate_output_size(AVFilterContext *ctx)
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition video.c:90
static double c[64]
#define FF_VK_DEFAULT_EXEC_CONTEXTS
Definition vulkan.h:121
#define RET(x)
Definition vulkan.h:37
#define SPEC_LIST_ADD(name, idx, val_bits, val)
Definition vulkan.h:55
#define SPEC_LIST_CREATE(name, max_length, max_size)
Definition vulkan.h:45
#define DUP_SAMPLER(x)
Definition vulkan.h:73
int ff_vk_filter_config_input(AVFilterLink *inlink)
int ff_vk_filter_config_output(AVFilterLink *outlink)
int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, FFVulkanShader *shd, AVFrame *out, AVFrame *in[], int nb_in, VkSampler sampler, uint32_t wgc_z, void *push_src, size_t push_size)
Up to 16 inputs, one output.
int ff_vk_filter_init(AVFilterContext *avctx)
General lavfi IO functions.