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
26
30
34
37
41
48
49 // PTS when the fade should start (in IN_A timebase)
51
52 // PTS offset between IN_A and IN_B
54
55 // Duration of the transition
57
58 // Current PTS of the first input (IN_A)
60
61 // If frames are currently just passed through
62 // unmodified, like before and after the actual
63 // transition.
65
68
88 };
89
91 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
93 C(1, vec4
a = texture(a_images[idx],
pos); )
94 C(1, vec4
b = texture(b_images[idx],
pos); )
95 C(1, imageStore(output_images[idx],
pos,
mix(
a,
b, progress)); )
97 };
98
100 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
102 C(1, ivec2
size = imageSize(output_images[idx]); )
103 C(1,
int s =
int(
size.x * (1.0 - progress)); )
104 C(1, vec4
a = texture(a_images[idx],
pos); )
105 C(1, vec4
b = texture(b_images[idx],
pos); )
106 C(1, imageStore(output_images[idx],
pos,
pos.x >
s ?
b :
a); )
108 };
109
111 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
113 C(1, ivec2
size = imageSize(output_images[idx]); )
114 C(1,
int s =
int(
size.x * progress); )
115 C(1, vec4
a = texture(a_images[idx],
pos); )
116 C(1, vec4
b = texture(b_images[idx],
pos); )
117 C(1, imageStore(output_images[idx],
pos,
pos.x >
s ?
a :
b); )
119 };
120
122 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
124 C(1, ivec2
size = imageSize(output_images[idx]); )
125 C(1,
int s =
int(
size.y * (1.0 - progress)); )
126 C(1, vec4
a = texture(a_images[idx],
pos); )
127 C(1, vec4
b = texture(b_images[idx],
pos); )
128 C(1, imageStore(output_images[idx],
pos,
pos.y >
s ?
b :
a); )
130 };
131
133 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
135 C(1, ivec2
size = imageSize(output_images[idx]); )
136 C(1,
int s =
int(
size.y * progress); )
137 C(1, vec4
a = texture(a_images[idx],
pos); )
138 C(1, vec4
b = texture(b_images[idx],
pos); )
139 C(1, imageStore(output_images[idx],
pos,
pos.y >
s ?
a :
b); )
141 };
142
143 #define SHADER_SLIDE_COMMON \
144 C(0, void slide(int idx, ivec2 pos, float progress, ivec2 direction) ) \
145 C(0, { ) \
146 C(1, ivec2 size = imageSize(output_images[idx]); ) \
147 C(1, ivec2 pi = ivec2(progress * size); ) \
148 C(1, ivec2 p = pos + pi * direction; ) \
149 C(1, ivec2 f = p % size; ) \
150 C(1, f = f + size * ivec2(f.x < 0, f.y < 0); ) \
151 C(1, vec4 a = texture(a_images[idx], f); ) \
152 C(1, vec4 b = texture(b_images[idx], f); ) \
153 C(1, vec4 r = (p.y >= 0 && p.x >= 0 && size.y > p.y && size.x > p.x) ? a : b; ) \
154 C(1, imageStore(output_images[idx], pos, r); ) \
155 C(0, } )
156
159 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
161 C(1, slide(idx,
pos, progress, ivec2(0, -1)); )
163 };
164
167 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
169 C(1, slide(idx,
pos, progress, ivec2(0, +1)); )
171 };
172
175 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
177 C(1, slide(idx,
pos, progress, ivec2(+1, 0)); )
179 };
180
183 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
185 C(1, slide(idx,
pos, progress, ivec2(-1, 0)); )
187 };
188
189 #define SHADER_CIRCLE_COMMON \
190 C(0, void circle(int idx, ivec2 pos, float progress, bool open) ) \
191 C(0, { ) \
192 C(1, const ivec2 half_size = imageSize(output_images[idx]) / 2; ) \
193 C(1, const float z = dot(half_size, half_size); ) \
194 C(1, float p = ((open ? (1.0 - progress) : progress) - 0.5) * 3.0; ) \
195 C(1, ivec2 dsize = pos - half_size; ) \
196 C(1, float sm = dot(dsize, dsize) / z + p; ) \
197 C(1, vec4 a = texture(a_images[idx], pos); ) \
198 C(1, vec4 b = texture(b_images[idx], pos); ) \
199 C(1, imageStore(output_images[idx], pos, \
200 mix(open ? b : a, open ? a : b, \
201 smoothstep(0.f, 1.f, sm))); ) \
202 C(0, } )
203
206 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
208 C(1, circle(idx,
pos, progress,
true); )
210 };
211
214 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
216 C(1, circle(idx,
pos, progress,
false); )
218 };
219
220 #define SHADER_FRAND_FUNC \
221 C(0, float frand(vec2 v) ) \
222 C(0, { ) \
223 C(1, return fract(sin(dot(v, vec2(12.9898, 78.233))) * 43758.545); ) \
224 C(0, } )
225
228 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
230 C(1,
float sm =
frand(
pos) * 2.0 + (1.0 - progress) * 2.0 - 1.5; )
231 C(1, vec4
a = texture(a_images[idx],
pos); )
232 C(1, vec4
b = texture(b_images[idx],
pos); )
233 C(1, imageStore(output_images[idx],
pos, sm >= 0.5 ?
a :
b); )
235 };
236
238 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
240 C(1, ivec2
size = imageSize(output_images[idx]); )
241 C(1,
float d =
min(progress, 1.0 - progress); )
242 C(1,
float dist =
ceil(d * 50.0) / 50.0; )
243 C(1,
float sq = 2.0 * dist *
min(
size.x,
size.y) / 20.0; )
246 C(1, vec4
a = texture(a_images[idx], vec2(sx, sy)); )
247 C(1, vec4
b = texture(b_images[idx], vec2(sx, sy)); )
248 C(1, imageStore(output_images[idx],
pos,
mix(
a,
b, progress)); )
250 };
251
253 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
255 C(1, ivec2
size = imageSize(output_images[idx]); )
256 C(1,
float zw =
size.x * (1.0 - progress); )
257 C(1,
float zh =
size.y * (1.0 - progress); )
258 C(1, vec4
a = texture(a_images[idx],
pos); )
259 C(1, vec4
b = texture(b_images[idx],
pos); )
260 C(1, imageStore(output_images[idx],
pos, (
pos.y <= zh &&
pos.x <= zw) ?
a :
b); )
262 };
263
265 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
267 C(1, ivec2
size = imageSize(output_images[idx]); )
268 C(1,
float zw =
size.x * (progress); )
269 C(1,
float zh =
size.y * (1.0 - progress); )
270 C(1, vec4
a = texture(a_images[idx],
pos); )
271 C(1, vec4
b = texture(b_images[idx],
pos); )
272 C(1, imageStore(output_images[idx],
pos, (
pos.y <= zh &&
pos.x > zw) ?
a :
b); )
274 };
275
277 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
279 C(1, ivec2
size = imageSize(output_images[idx]); )
280 C(1,
float zw =
size.x * (1.0 - progress); )
281 C(1,
float zh =
size.y * (progress); )
282 C(1, vec4
a = texture(a_images[idx],
pos); )
283 C(1, vec4
b = texture(b_images[idx],
pos); )
284 C(1, imageStore(output_images[idx],
pos, (
pos.y > zh &&
pos.x <= zw) ?
a :
b); )
286 };
287
289 C(0,
void transition(
int idx, ivec2
pos,
float progress) )
291 C(1, ivec2
size = imageSize(output_images[idx]); )
292 C(1,
float zw =
size.x * (progress); )
293 C(1,
float zh =
size.y * (progress); )
294 C(1, vec4
a = texture(a_images[idx],
pos); )
295 C(1, vec4
b = texture(b_images[idx],
pos); )
296 C(1, imageStore(output_images[idx],
pos, (
pos.y > zh &&
pos.x > zw) ?
a :
b); )
298 };
299
318 };
319
321 {
322 int err = 0;
323 uint8_t *spv_data;
324 size_t spv_len;
325 void *spv_opaque =
NULL;
332
333 spv = ff_vk_spirv_init();
334 if (!spv) {
337 }
338
343 VK_SHADER_STAGE_COMPUTE_BIT, 0));
344
346
348 {
349 .name = "a_images",
350 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
351 .dimensions = 2,
353 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
355 },
356 {
357 .name = "b_images",
358 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
359 .dimensions = 2,
361 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
363 },
364 {
365 .name = "output_images",
366 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
368 .mem_quali = "writeonly",
369 .dimensions = 2,
371 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
372 },
373 };
374
376
377 GLSLC(0,
layout(push_constant, std430) uniform pushConstants { );
378 GLSLC(1,
float progress; );
380
382 VK_SHADER_STAGE_COMPUTE_BIT);
383
384 // Add the right transition type function to the shader
386
389 GLSLC(1, ivec2
pos = ivec2(gl_GlobalInvocationID.xy); );
392 GLSLC(2, transition(
i,
pos, progress); );
395
397 &spv_opaque));
399
402
404
406 if (spv_opaque)
408 if (spv)
410
411 return err;
412 }
413
415 {
416 int err;
419 float progress;
420
425 }
426
427 if (!
s->initialized) {
430 if (a_fc->
sw_format != b_fc->sw_format) {
432 "Currently the sw format of the first input needs to match the second!\n");
434 }
436 }
437
440
441 progress =
av_clipf((
float)(
s->pts -
s->start_pts) /
s->duration_pts,
442 0.f, 1.f);
443
445 (
AVFrame *[]){ frame_a, frame_b }, 2,
s->sampler,
447
449
452 return err;
453 }
454
456 {
457 int err;
464
465 if (inlink_a->
w != inlink_b->
w || inlink_a->
h != inlink_b->
h) {
467 "(size %dx%d) do not match the corresponding "
468 "second input link %s parameters (size %dx%d)\n",
472 }
473
477 "(%d/%d) does not match the corresponding "
478 "second input link %s timebase (%d/%d)\n",
482 }
483
485
489
493
495 return err;
496 }
497
500 {
504
508
510 // If we do not have an offset yet, it's because we
511 // never got a first input. Just offset to 0
513 s->inputs_offset_pts = -
frame->pts;
514
515 // We got a frame, nothing to do other than adjusting the timestamp
516 frame->pts +=
s->inputs_offset_pts;
518 }
519
520 // Forward status with our timestamp
523 s->inputs_offset_pts = -status_pts;
524
526 return 0;
527 }
528
529 // No frame available, request one if needed
532
533 return 0;
534 }
535
537 {
543
545
546 // Check if we already transitioned or IN_A ended prematurely,
547 // in which case just forward the frames from IN_B with adjusted
548 // timestamps until EOF.
551
552 // We did not finish transitioning yet and the first stream
553 // did not end either, so check if there are more frames to consume.
556 s->pts = peeked_frame->
pts;
557
561
562 // Check if we are not yet transitioning, in which case
563 // just request and forward the input frame.
564 if (
s->start_pts >
s->pts) {
569 }
571
572 // We are transitioning, so we need a frame from IN_B
578
579 // Calculate PTS offset to first input
581 s->inputs_offset_pts =
s->pts - frame_b->pts;
582
583 // Check if we finished transitioning, in which case we
584 // report back EOF to IN_A as it is no longer needed.
585 if (
s->pts -
s->start_pts >
s->duration_pts) {
589 }
594 }
595
596 // We did not get a frame from IN_B, check its status.
598 // We should transition, but IN_B is EOF so just report EOF output now.
600 return 0;
601 }
602
603 // We did not get a frame for IN_B but no EOF either, so just request more.
606 return 0;
607 }
608 }
609
610 // We did not get a frame from IN_A, check its status.
612 // No more frames from IN_A, do not report EOF though, we will just
613 // forward the IN_B frames in the next activate calls.
616 return 0;
617 }
618
619 // We have no frames yet from IN_A and no EOF, so request some.
622 return 0;
623 }
624
626 }
627
629 {
633
637
641
643
645 }
646
648 {
650
651 return s->passthrough ?
654 }
655
656 #define OFFSET(x) offsetof(XFadeVulkanContext, x)
657 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
658
681 };
682
684
686 {
691 },
692 {
693 .name = "xfade",
697 },
698 };
699
701 {
705 },
706 };
707
709 .
name =
"xfade_vulkan",
718 .priv_class = &xfade_vulkan_class,
721 };