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
27
31
35
38
42
49
50 // PTS when the fade should start (in IN_A timebase)
52
53 // PTS offset between IN_A and IN_B
55
56 // Duration of the transition
58
59 // Current PTS of the first input (IN_A)
61
62 // If frames are currently just passed through
63 // unmodified, like before and after the actual
64 // transition.
66
69
89 };
90
92 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
94 C(1, vec4
a = texture(a_images[idx],
pos); )
95 C(1, vec4
b = texture(b_images[idx],
pos); )
96 C(1, imageStore(output_images[idx],
pos,
mix(
a,
b, progress)); )
98 };
99
101 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
103 C(1, ivec2
size = imageSize(output_images[idx]); )
104 C(1,
int s =
int(
size.x * (1.0 - progress)); )
105 C(1, vec4
a = texture(a_images[idx],
pos); )
106 C(1, vec4
b = texture(b_images[idx],
pos); )
107 C(1, imageStore(output_images[idx],
pos,
pos.x >
s ?
b :
a); )
109 };
110
112 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
114 C(1, ivec2
size = imageSize(output_images[idx]); )
115 C(1,
int s =
int(
size.x * progress); )
116 C(1, vec4
a = texture(a_images[idx],
pos); )
117 C(1, vec4
b = texture(b_images[idx],
pos); )
118 C(1, imageStore(output_images[idx],
pos,
pos.x >
s ?
a :
b); )
120 };
121
123 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
125 C(1, ivec2
size = imageSize(output_images[idx]); )
126 C(1,
int s =
int(
size.y * (1.0 - progress)); )
127 C(1, vec4
a = texture(a_images[idx],
pos); )
128 C(1, vec4
b = texture(b_images[idx],
pos); )
129 C(1, imageStore(output_images[idx],
pos,
pos.y >
s ?
b :
a); )
131 };
132
134 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
136 C(1, ivec2
size = imageSize(output_images[idx]); )
137 C(1,
int s =
int(
size.y * progress); )
138 C(1, vec4
a = texture(a_images[idx],
pos); )
139 C(1, vec4
b = texture(b_images[idx],
pos); )
140 C(1, imageStore(output_images[idx],
pos,
pos.y >
s ?
a :
b); )
142 };
143
144 #define SHADER_SLIDE_COMMON \
145 C(0, void slide(int idx, ivec2 pos, float progress, ivec2 direction) ) \
146 C(0, { ) \
147 C(1, ivec2 size = imageSize(output_images[idx]); ) \
148 C(1, ivec2 pi = ivec2(progress * size); ) \
149 C(1, ivec2 p = pos + pi * direction; ) \
150 C(1, ivec2 f = p % size; ) \
151 C(1, f = f + size * ivec2(f.x < 0, f.y < 0); ) \
152 C(1, vec4 a = texture(a_images[idx], f); ) \
153 C(1, vec4 b = texture(b_images[idx], f); ) \
154 C(1, vec4 r = (p.y >= 0 && p.x >= 0 && size.y > p.y && size.x > p.x) ? a : b; ) \
155 C(1, imageStore(output_images[idx], pos, r); ) \
156 C(0, } )
157
160 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
162 C(1, slide(idx,
pos, progress, ivec2(0, -1)); )
164 };
165
168 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
170 C(1, slide(idx,
pos, progress, ivec2(0, +1)); )
172 };
173
176 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
178 C(1, slide(idx,
pos, progress, ivec2(+1, 0)); )
180 };
181
184 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
186 C(1, slide(idx,
pos, progress, ivec2(-1, 0)); )
188 };
189
190 #define SHADER_CIRCLE_COMMON \
191 C(0, void circle(int idx, ivec2 pos, float progress, bool open) ) \
192 C(0, { ) \
193 C(1, const ivec2 half_size = imageSize(output_images[idx]) / 2; ) \
194 C(1, const float z = dot(half_size, half_size); ) \
195 C(1, float p = ((open ? (1.0 - progress) : progress) - 0.5) * 3.0; ) \
196 C(1, ivec2 dsize = pos - half_size; ) \
197 C(1, float sm = dot(dsize, dsize) / z + p; ) \
198 C(1, vec4 a = texture(a_images[idx], pos); ) \
199 C(1, vec4 b = texture(b_images[idx], pos); ) \
200 C(1, imageStore(output_images[idx], pos, \
201 mix(open ? b : a, open ? a : b, \
202 smoothstep(0.f, 1.f, sm))); ) \
203 C(0, } )
204
207 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
209 C(1, circle(idx,
pos, progress,
true); )
211 };
212
215 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
217 C(1, circle(idx,
pos, progress,
false); )
219 };
220
221 #define SHADER_FRAND_FUNC \
222 C(0, float frand(vec2 v) ) \
223 C(0, { ) \
224 C(1, return fract(sin(dot(v, vec2(12.9898, 78.233))) * 43758.545); ) \
225 C(0, } )
226
229 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
231 C(1,
float sm =
frand(
pos) * 2.0 + (1.0 - progress) * 2.0 - 1.5; )
232 C(1, vec4
a = texture(a_images[idx],
pos); )
233 C(1, vec4
b = texture(b_images[idx],
pos); )
234 C(1, imageStore(output_images[idx],
pos, sm >= 0.5 ?
a :
b); )
236 };
237
239 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
241 C(1, ivec2
size = imageSize(output_images[idx]); )
242 C(1,
float d =
min(progress, 1.0 - progress); )
243 C(1,
float dist =
ceil(
d * 50.0) / 50.0; )
244 C(1,
float sq = 2.0 * dist *
min(
size.x,
size.y) / 20.0; )
247 C(1, vec4
a = texture(a_images[idx], vec2(sx, sy)); )
248 C(1, vec4
b = texture(b_images[idx], vec2(sx, sy)); )
249 C(1, imageStore(output_images[idx],
pos,
mix(
a,
b, progress)); )
251 };
252
254 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
256 C(1, ivec2
size = imageSize(output_images[idx]); )
257 C(1,
float zw =
size.x * (1.0 - progress); )
258 C(1,
float zh =
size.y * (1.0 - progress); )
259 C(1, vec4
a = texture(a_images[idx],
pos); )
260 C(1, vec4
b = texture(b_images[idx],
pos); )
261 C(1, imageStore(output_images[idx],
pos, (
pos.y <= zh &&
pos.x <= zw) ?
a :
b); )
263 };
264
266 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
268 C(1, ivec2
size = imageSize(output_images[idx]); )
269 C(1,
float zw =
size.x * (progress); )
270 C(1,
float zh =
size.y * (1.0 - progress); )
271 C(1, vec4
a = texture(a_images[idx],
pos); )
272 C(1, vec4
b = texture(b_images[idx],
pos); )
273 C(1, imageStore(output_images[idx],
pos, (
pos.y <= zh &&
pos.x > zw) ?
a :
b); )
275 };
276
278 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
280 C(1, ivec2
size = imageSize(output_images[idx]); )
281 C(1,
float zw =
size.x * (1.0 - progress); )
282 C(1,
float zh =
size.y * (progress); )
283 C(1, vec4
a = texture(a_images[idx],
pos); )
284 C(1, vec4
b = texture(b_images[idx],
pos); )
285 C(1, imageStore(output_images[idx],
pos, (
pos.y > zh &&
pos.x <= zw) ?
a :
b); )
287 };
288
290 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
292 C(1, ivec2
size = imageSize(output_images[idx]); )
293 C(1,
float zw =
size.x * (progress); )
294 C(1,
float zh =
size.y * (progress); )
295 C(1, vec4
a = texture(a_images[idx],
pos); )
296 C(1, vec4
b = texture(b_images[idx],
pos); )
297 C(1, imageStore(output_images[idx],
pos, (
pos.y > zh &&
pos.x > zw) ?
a :
b); )
299 };
300
319 };
320
322 {
323 int err = 0;
324 uint8_t *spv_data;
325 size_t spv_len;
326 void *spv_opaque =
NULL;
333
334 spv = ff_vk_spirv_init();
335 if (!spv) {
338 }
339
344 VK_SHADER_STAGE_COMPUTE_BIT, 0));
345
347
349 {
350 .name = "a_images",
351 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
352 .dimensions = 2,
354 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
356 },
357 {
358 .name = "b_images",
359 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
360 .dimensions = 2,
362 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
364 },
365 {
366 .name = "output_images",
367 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
369 .mem_quali = "writeonly",
370 .dimensions = 2,
372 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
373 },
374 };
375
377
378 GLSLC(0,
layout(push_constant, std430) uniform pushConstants { );
379 GLSLC(1,
float progress; );
381
383 VK_SHADER_STAGE_COMPUTE_BIT);
384
385 // Add the right transition type function to the shader
387
390 GLSLC(1, ivec2
pos = ivec2(gl_GlobalInvocationID.xy); );
393 GLSLC(2, transition(
i,
pos, progress); );
396
398 &spv_opaque));
400
403
405
407 if (spv_opaque)
409 if (spv)
411
412 return err;
413 }
414
416 {
417 int err;
420 float progress;
421
426 }
427
428 if (!
s->initialized) {
431 if (a_fc->
sw_format != b_fc->sw_format) {
433 "Currently the sw format of the first input needs to match the second!\n");
435 }
437 }
438
441
442 progress =
av_clipf((
float)(
s->pts -
s->start_pts) /
s->duration_pts,
443 0.f, 1.f);
444
446 (
AVFrame *[]){ frame_a, frame_b }, 2,
s->sampler,
448
450
453 return err;
454 }
455
457 {
458 int err;
463
464 if (inlink_a->
w != inlink_b->
w || inlink_a->
h != inlink_b->
h) {
466 "(size %dx%d) do not match the corresponding "
467 "second input link %s parameters (size %dx%d)\n",
471 }
472
476 "(%d/%d) does not match the corresponding "
477 "second input link %s timebase (%d/%d)\n",
481 }
482
484
488
492
494 return err;
495 }
496
499 {
503
507
509 // If we do not have an offset yet, it's because we
510 // never got a first input. Just offset to 0
513
514 // We got a frame, nothing to do other than adjusting the timestamp
517 }
518
519 // Forward status with our timestamp
522 s->inputs_offset_pts = -status_pts;
523
525 return 0;
526 }
527
528 // No frame available, request one if needed
531
532 return 0;
533 }
534
536 {
542
544
545 // Check if we already transitioned or IN_A ended prematurely,
546 // in which case just forward the frames from IN_B with adjusted
547 // timestamps until EOF.
550
551 // We did not finish transitioning yet and the first stream
552 // did not end either, so check if there are more frames to consume.
555 s->pts = peeked_frame->
pts;
556
560
561 // Check if we are not yet transitioning, in which case
562 // just request and forward the input frame.
563 if (
s->start_pts >
s->pts) {
568 }
570
571 // We are transitioning, so we need a frame from IN_B
577
578 // Calculate PTS offset to first input
580 s->inputs_offset_pts =
s->pts - frame_b->pts;
581
582 // Check if we finished transitioning, in which case we
583 // report back EOF to IN_A as it is no longer needed.
584 if (
s->pts -
s->start_pts >
s->duration_pts) {
588 }
593 }
594
595 // We did not get a frame from IN_B, check its status.
597 // We should transition, but IN_B is EOF so just report EOF output now.
599 return 0;
600 }
601
602 // We did not get a frame for IN_B but no EOF either, so just request more.
605 return 0;
606 }
607 }
608
609 // We did not get a frame from IN_A, check its status.
611 // No more frames from IN_A, do not report EOF though, we will just
612 // forward the IN_B frames in the next activate calls.
615 return 0;
616 }
617
618 // We have no frames yet from IN_A and no EOF, so request some.
621 return 0;
622 }
623
625 }
626
628 {
632
636
640
642
644 }
645
647 {
649
650 return s->passthrough ?
653 }
654
655 #define OFFSET(x) offsetof(XFadeVulkanContext, x)
656 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
657
680 };
681
683
685 {
690 },
691 {
692 .name = "xfade",
696 },
697 };
698
700 {
704 },
705 };
706
708 .
name =
"xfade_vulkan",
717 .priv_class = &xfade_vulkan_class,
720 };