Carbon copy of vf_scdet.
Showing the changes to libavfilter (3 of 4 files). Show all
Unified · Split
| @@ -472,6 +472,7 @@ OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vf_scale_vulkan.o vulkan.o vulka · 471 unmodified lines | ||
| 472 | 472 | OBJS-$(CONFIG_SCALE2REF_FILTER) += vf_scale.o scale_eval.o framesync.o |
| 473 | 473 | OBJS-$(CONFIG_SCALE2REF_NPP_FILTER) += vf_scale_npp.o scale_eval.o |
| 474 | 474 | OBJS-$(CONFIG_SCDET_FILTER) += vf_scdet.o |
| 475 | +OBJS-$(CONFIG_SCDET_VULKAN_FILTER) += vf_scdet_vulkan.o | |
| 475 | 476 | OBJS-$(CONFIG_SCHARR_FILTER) += vf_convolution.o |
| 476 | 477 | OBJS-$(CONFIG_SCROLL_FILTER) += vf_scroll.o |
| 477 | 478 | OBJS-$(CONFIG_SEGMENT_FILTER) += f_segment.o |
| @@ -444,6 +444,7 @@ extern const FFFilter ff_vf_scale_vulkan; · 443 unmodified lines | ||
| 444 | 444 | extern const FFFilter ff_vf_scale2ref; |
| 445 | 445 | extern const FFFilter ff_vf_scale2ref_npp; |
| 446 | 446 | extern const FFFilter ff_vf_scdet; |
| 447 | +extern const FFFilter ff_vf_scdet_vulkan; | |
| 447 | 448 | extern const FFFilter ff_vf_scharr; |
| 448 | 449 | extern const FFFilter ff_vf_scroll; |
| 449 | 450 | extern const FFFilter ff_vf_segment; |
| @@ -0,0 +1,412 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2025 (c) Niklas Haas | |
| 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/avassert.h" | |
| 22 | +#include "libavutil/vulkan_spirv.h" | |
| 23 | +#include "libavutil/opt.h" | |
| 24 | +#include "libavutil/timestamp.h" | |
| 25 | +#include "vulkan_filter.h" | |
| 26 | + | |
| 27 | +#include "filters.h" | |
| 28 | + | |
| 29 | +typedef struct SceneDetectVulkanContext { | |
| 30 | + FFVulkanContext vkctx; | |
| 31 | + | |
| 32 | + int initialized; | |
| 33 | + FFVkExecPool e; | |
| 34 | + AVVulkanDeviceQueueFamily *qf; | |
| 35 | + FFVulkanShader shd; | |
| 36 | + AVBufferPool *det_buf_pool; | |
| 37 | + | |
| 38 | + double threshold; | |
| 39 | + int sc_pass; | |
| 40 | + | |
| 41 | + int nb_planes; | |
| 42 | + double prev_mafd; | |
| 43 | + AVFrame *prev; | |
| 44 | + AVFrame *cur; | |
| 45 | +} SceneDetectVulkanContext; | |
| 46 | + | |
| 47 | +typedef struct SceneDetectBuf { | |
| 48 | +#define SLICES 16 | |
| 49 | + uint32_t frame_sad[SLICES]; | |
| 50 | +} SceneDetectBuf; | |
| 51 | + | |
| 52 | +static av_cold int init_filter(AVFilterContext *ctx) | |
| 53 | +{ | |
| 54 | + int err; | |
| 55 | + uint8_t *spv_data; | |
| 56 | + size_t spv_len; | |
| 57 | + void *spv_opaque = NULL; | |
| 58 | + SceneDetectVulkanContext *s = ctx->priv; | |
| 59 | + FFVulkanContext *vkctx = &s->vkctx; | |
| 60 | + FFVulkanShader *shd; | |
| 61 | + FFVkSPIRVCompiler *spv; | |
| 62 | + FFVulkanDescriptorSetBinding *desc; | |
| 63 | + | |
| 64 | + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(s->vkctx.input_format); | |
| 65 | + const int lumaonly = !(pixdesc->flags & AV_PIX_FMT_FLAG_RGB) && | |
| 66 | + (pixdesc->flags & AV_PIX_FMT_FLAG_PLANAR); | |
| 67 | + s->nb_planes = lumaonly ? 1 : av_pix_fmt_count_planes(s->vkctx.input_format); | |
| 68 | + | |
| 69 | + spv = ff_vk_spirv_init(); | |
| 70 | + if (!spv) { | |
| 71 | + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); | |
| 72 | + return AVERROR_EXTERNAL; | |
| 73 | + } | |
| 74 | + | |
| 75 | + s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0); | |
| 76 | + 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 | + RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, s->qf->num*4, 0, 0, 0, NULL)); | |
| 83 | + RET(ff_vk_shader_init(vkctx, &s->shd, "scdet", | |
| 84 | + VK_SHADER_STAGE_COMPUTE_BIT, | |
| 85 | + (const char *[]) { "GL_KHR_shader_subgroup_arithmetic" }, 1, | |
| 86 | + 32, 32, 1, | |
| 87 | + 0)); | |
| 88 | + shd = &s->shd; | |
| 89 | + | |
| 90 | + desc = (FFVulkanDescriptorSetBinding []) { | |
| 91 | + { | |
| 92 | + .name = "prev_img", | |
| 93 | + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, | |
| 94 | + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.input_format, FF_VK_REP_UINT), | |
| 95 | + .mem_quali = "readonly", | |
| 96 | + .dimensions = 2, | |
| 97 | + .elems = av_pix_fmt_count_planes(s->vkctx.input_format), | |
| 98 | + .stages = VK_SHADER_STAGE_COMPUTE_BIT, | |
| 99 | + }, { | |
| 100 | + .name = "cur_img", | |
| 101 | + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, | |
| 102 | + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.input_format, FF_VK_REP_UINT), | |
| 103 | + .mem_quali = "readonly", | |
| 104 | + .dimensions = 2, | |
| 105 | + .elems = av_pix_fmt_count_planes(s->vkctx.input_format), | |
| 106 | + .stages = VK_SHADER_STAGE_COMPUTE_BIT, | |
| 107 | + }, { | |
| 108 | + .name = "sad_buffer", | |
| 109 | + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, | |
| 110 | + .stages = VK_SHADER_STAGE_COMPUTE_BIT, | |
| 111 | + .buf_content = "uint frame_sad[];", | |
| 112 | + } | |
| 113 | + }; | |
| 114 | + | |
| 115 | + RET(ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 3, 0, 0)); | |
| 116 | + | |
| 117 | + GLSLC(0, shared uint wg_sum; ); | |
| 118 | + GLSLC(0, void main() ); | |
| 119 | + GLSLC(0, { ); | |
| 120 | + GLSLF(1, const uint slice = gl_WorkGroupID.x %% %u; ,SLICES); | |
| 121 | + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); | |
| 122 | + GLSLC(1, wg_sum = 0; ); | |
| 123 | + GLSLC(1, barrier(); ); | |
| 124 | + for (int i = 0; i < s->nb_planes; i++) { | |
| 125 | + GLSLF(1, if (IS_WITHIN(pos, imageSize(cur_img[%d]))) { ,i); | |
| 126 | + GLSLF(2, uvec4 prev = imageLoad(prev_img[%d], pos); ,i); | |
| 127 | + GLSLF(2, uvec4 cur = imageLoad(cur_img[%d], pos); ,i); | |
| 128 | + GLSLC(2, uvec4 sad = abs(ivec4(cur) - ivec4(prev)); ); | |
| 129 | + GLSLC(2, uint sum = subgroupAdd(sad.x + sad.y + sad.z); ); | |
| 130 | + GLSLC(2, if (subgroupElect()) ); | |
| 131 | + GLSLC(3, atomicAdd(wg_sum, sum); ); | |
| 132 | + GLSLC(1, } ); | |
| 133 | + } | |
| 134 | + GLSLC(1, barrier(); ); | |
| 135 | + GLSLC(1, if (gl_LocalInvocationIndex == 0) ); | |
| 136 | + GLSLC(2, atomicAdd(frame_sad[slice], wg_sum); ); | |
| 137 | + GLSLC(0, } ); | |
| 138 | + | |
| 139 | + RET(spv->compile_shader(vkctx, spv, &s->shd, &spv_data, &spv_len, "main", | |
| 140 | + &spv_opaque)); | |
| 141 | + RET(ff_vk_shader_link(vkctx, &s->shd, spv_data, spv_len, "main")); | |
| 142 | + | |
| 143 | + RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->shd)); | |
| 144 | + | |
| 145 | + s->initialized = 1; | |
| 146 | + | |
| 147 | +fail: | |
| 148 | + if (spv_opaque) | |
| 149 | + spv->free_shader(spv, &spv_opaque); | |
| 150 | + if (spv) | |
| 151 | + spv->uninit(&spv); | |
| 152 | + | |
| 153 | + return err; | |
| 154 | +} | |
| 155 | + | |
| 156 | +static double evaluate(AVFilterContext *ctx, const SceneDetectBuf *buf) | |
| 157 | +{ | |
| 158 | + SceneDetectVulkanContext *s = ctx->priv; | |
| 159 | + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->vkctx.input_format); | |
| 160 | + const AVFilterLink *inlink = ctx->inputs[0]; | |
| 161 | + uint64_t count; | |
| 162 | + double mafd, diff; | |
| 163 | + | |
| 164 | + uint64_t sad = 0; | |
| 165 | + for (int i = 0; i < SLICES; i++) | |
| 166 | + sad += buf->frame_sad[i]; | |
| 167 | + | |
| 168 | + av_assert2(s->nb_planes == 1 || !(desc->log2_chroma_w || desc->log2_chroma_h)); | |
| 169 | + count = s->nb_planes * inlink->w * inlink->h; | |
| 170 | + mafd = (double) sad * 100.0 / count / (1ULL << desc->comp[0].depth); | |
| 171 | + diff = fabs(mafd - s->prev_mafd); | |
| 172 | + s->prev_mafd = mafd; | |
| 173 | + | |
| 174 | + return av_clipf(FFMIN(mafd, diff), 0.0, 100.0); | |
| 175 | +} | |
| 176 | + | |
| 177 | +static int scdet_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) | |
| 178 | +{ | |
| 179 | + int err; | |
| 180 | + AVFilterContext *ctx = link->dst; | |
| 181 | + SceneDetectVulkanContext *s = ctx->priv; | |
| 182 | + AVFilterLink *outlink = ctx->outputs[0]; | |
| 183 | + | |
| 184 | + VkImageView prev_views[AV_NUM_DATA_POINTERS]; | |
| 185 | + VkImageView cur_views[AV_NUM_DATA_POINTERS]; | |
| 186 | + VkImageMemoryBarrier2 img_bar[8]; | |
| 187 | + int nb_img_bar = 0; | |
| 188 | + | |
| 189 | + FFVulkanContext *vkctx = &s->vkctx; | |
| 190 | + FFVulkanFunctions *vk = &vkctx->vkfn; | |
| 191 | + FFVkExecContext *exec = NULL; | |
| 192 | + AVBufferRef *buf = NULL; | |
| 193 | + FFVkBuffer *buf_vk; | |
| 194 | + | |
| 195 | + SceneDetectBuf *sad; | |
| 196 | + double score = 0.0; | |
| 197 | + char str[64]; | |
| 198 | + | |
| 199 | + if (!s->initialized) | |
| 200 | + RET(init_filter(ctx)); | |
| 201 | + | |
| 202 | + av_frame_free(&s->prev); | |
| 203 | + s->prev = s->cur; | |
| 204 | + s->cur = av_frame_clone(in); | |
| 205 | + if (!s->prev) | |
| 206 | + goto done; | |
| 207 | + | |
| 208 | + RET(ff_vk_get_pooled_buffer(vkctx, &s->det_buf_pool, &buf, | |
| 209 | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | | |
| 210 | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, | |
| 211 | + NULL, | |
| 212 | + sizeof(SceneDetectBuf), | |
| 213 | + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | | |
| 214 | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | | |
| 215 | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); | |
| 216 | + buf_vk = (FFVkBuffer *)buf->data; | |
| 217 | + sad = (SceneDetectBuf *) buf_vk->mapped_mem; | |
| 218 | + | |
| 219 | + exec = ff_vk_exec_get(vkctx, &s->e); | |
| 220 | + ff_vk_exec_start(vkctx, exec); | |
| 221 | + | |
| 222 | + RET(ff_vk_exec_add_dep_frame(vkctx, exec, s->prev, | |
| 223 | + VK_PIPELINE_STAGE_2_NONE, | |
| 224 | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); | |
| 225 | + RET(ff_vk_create_imageviews(vkctx, exec, prev_views, s->prev, FF_VK_REP_UINT)); | |
| 226 | + | |
| 227 | + ff_vk_shader_update_img_array(vkctx, exec, &s->shd, s->prev, prev_views, 0, 0, | |
| 228 | + VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE); | |
| 229 | + | |
| 230 | + ff_vk_frame_barrier(vkctx, exec, s->prev, img_bar, &nb_img_bar, | |
| 231 | + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, | |
| 232 | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, | |
| 233 | + VK_ACCESS_SHADER_READ_BIT, | |
| 234 | + VK_IMAGE_LAYOUT_GENERAL, | |
| 235 | + VK_QUEUE_FAMILY_IGNORED); | |
| 236 | + | |
| 237 | + RET(ff_vk_exec_add_dep_frame(vkctx, exec, s->cur, | |
| 238 | + VK_PIPELINE_STAGE_2_NONE, | |
| 239 | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); | |
| 240 | + RET(ff_vk_create_imageviews(vkctx, exec, cur_views, s->cur, FF_VK_REP_UINT)); | |
| 241 | + | |
| 242 | + ff_vk_shader_update_img_array(vkctx, exec, &s->shd, s->cur, cur_views, 0, 1, | |
| 243 | + VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE); | |
| 244 | + | |
| 245 | + ff_vk_frame_barrier(vkctx, exec, s->cur, img_bar, &nb_img_bar, | |
| 246 | + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, | |
| 247 | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, | |
| 248 | + VK_ACCESS_SHADER_READ_BIT, | |
| 249 | + VK_IMAGE_LAYOUT_GENERAL, | |
| 250 | + VK_QUEUE_FAMILY_IGNORED); | |
| 251 | + | |
| 252 | + /* zero buffer */ | |
| 253 | + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { | |
| 254 | + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, | |
| 255 | + .pBufferMemoryBarriers = &(VkBufferMemoryBarrier2) { | |
| 256 | + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, | |
| 257 | + .srcStageMask = VK_PIPELINE_STAGE_2_NONE, | |
| 258 | + .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, | |
| 259 | + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | |
| 260 | + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| 261 | + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| 262 | + .buffer = buf_vk->buf, | |
| 263 | + .size = buf_vk->size, | |
| 264 | + .offset = 0, | |
| 265 | + }, | |
| 266 | + .bufferMemoryBarrierCount = 1, | |
| 267 | + }); | |
| 268 | + | |
| 269 | + vk->CmdFillBuffer(exec->buf, buf_vk->buf, 0, buf_vk->size, 0x0); | |
| 270 | + | |
| 271 | + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { | |
| 272 | + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, | |
| 273 | + .pImageMemoryBarriers = img_bar, | |
| 274 | + .imageMemoryBarrierCount = nb_img_bar, | |
| 275 | + .pBufferMemoryBarriers = &(VkBufferMemoryBarrier2) { | |
| 276 | + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, | |
| 277 | + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, | |
| 278 | + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, | |
| 279 | + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | |
| 280 | + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT | | |
| 281 | + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, | |
| 282 | + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| 283 | + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| 284 | + .buffer = buf_vk->buf, | |
| 285 | + .size = buf_vk->size, | |
| 286 | + .offset = 0, | |
| 287 | + }, | |
| 288 | + .bufferMemoryBarrierCount = 1, | |
| 289 | + }); | |
| 290 | + | |
| 291 | + RET(ff_vk_shader_update_desc_buffer(&s->vkctx, exec, &s->shd, 0, 2, 0, | |
| 292 | + buf_vk, 0, buf_vk->size, | |
| 293 | + VK_FORMAT_UNDEFINED)); | |
| 294 | + | |
| 295 | + ff_vk_exec_bind_shader(vkctx, exec, &s->shd); | |
| 296 | + | |
| 297 | + vk->CmdDispatch(exec->buf, | |
| 298 | + FFALIGN(in->width, s->shd.lg_size[0]) / s->shd.lg_size[0], | |
| 299 | + FFALIGN(in->height, s->shd.lg_size[1]) / s->shd.lg_size[1], | |
| 300 | + s->shd.lg_size[2]); | |
| 301 | + | |
| 302 | + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { | |
| 303 | + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, | |
| 304 | + .pBufferMemoryBarriers = &(VkBufferMemoryBarrier2) { | |
| 305 | + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, | |
| 306 | + .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, | |
| 307 | + .dstStageMask = VK_PIPELINE_STAGE_2_HOST_BIT, | |
| 308 | + .srcAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT | | |
| 309 | + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, | |
| 310 | + .dstAccessMask = VK_ACCESS_HOST_READ_BIT, | |
| 311 | + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| 312 | + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| 313 | + .buffer = buf_vk->buf, | |
| 314 | + .size = buf_vk->size, | |
| 315 | + .offset = 0, | |
| 316 | + }, | |
| 317 | + .bufferMemoryBarrierCount = 1, | |
| 318 | + }); | |
| 319 | + | |
| 320 | + RET(ff_vk_exec_submit(vkctx, exec)); | |
| 321 | + ff_vk_exec_wait(vkctx, exec); | |
| 322 | + score = evaluate(ctx, sad); | |
| 323 | + | |
| 324 | +done: | |
| 325 | + snprintf(str, sizeof(str), "%0.3f", s->prev_mafd); | |
| 326 | + av_dict_set(&in->metadata, "lavfi.scd.mafd", str, 0); | |
| 327 | + snprintf(str, sizeof(str), "%0.3f", score); | |
| 328 | + av_dict_set(&in->metadata, "lavfi.scd.score", str, 0); | |
| 329 | + | |
| 330 | + if (score >= s->threshold) { | |
| 331 | + const char *pts = av_ts2timestr(in->pts, &link->time_base); | |
| 332 | + av_dict_set(&in->metadata, "lavfi.scd.time", pts, 0); | |
| 333 | + av_log(s, AV_LOG_INFO, "lavfi.scd.score: %.3f, lavfi.scd.time: %s\n", | |
| 334 | + score, pts); | |
| 335 | + } | |
| 336 | + | |
| 337 | + av_buffer_unref(&buf); | |
| 338 | + if (!s->sc_pass || score >= s->threshold) | |
| 339 | + return ff_filter_frame(outlink, in); | |
| 340 | + else { | |
| 341 | + av_frame_free(&in); | |
| 342 | + return 0; | |
| 343 | + } | |
| 344 | + | |
| 345 | +fail: | |
| 346 | + if (exec) | |
| 347 | + ff_vk_exec_discard_deps(&s->vkctx, exec); | |
| 348 | + av_frame_free(&in); | |
| 349 | + av_buffer_unref(&buf); | |
| 350 | + return err; | |
| 351 | +} | |
| 352 | + | |
| 353 | +static void scdet_vulkan_uninit(AVFilterContext *avctx) | |
| 354 | +{ | |
| 355 | + SceneDetectVulkanContext *s = avctx->priv; | |
| 356 | + FFVulkanContext *vkctx = &s->vkctx; | |
| 357 | + | |
| 358 | + av_frame_free(&s->prev); | |
| 359 | + av_frame_free(&s->cur); | |
| 360 | + | |
| 361 | + ff_vk_exec_pool_free(vkctx, &s->e); | |
| 362 | + ff_vk_shader_free(vkctx, &s->shd); | |
| 363 | + | |
| 364 | + av_buffer_pool_uninit(&s->det_buf_pool); | |
| 365 | + | |
| 366 | + ff_vk_uninit(&s->vkctx); | |
| 367 | + | |
| 368 | + s->initialized = 0; | |
| 369 | +} | |
| 370 | + | |
| 371 | +#define OFFSET(x) offsetof(SceneDetectVulkanContext, x) | |
| 372 | +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) | |
| 373 | +static const AVOption scdet_vulkan_options[] = { | |
| 374 | + { "threshold", "set scene change detect threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 10.}, 0, 100., FLAGS }, | |
| 375 | + { "t", "set scene change detect threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 10.}, 0, 100., FLAGS }, | |
| 376 | + { "sc_pass", "Set the flag to pass scene change frames", OFFSET(sc_pass), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, FLAGS }, | |
| 377 | + { "s", "Set the flag to pass scene change frames", OFFSET(sc_pass), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, FLAGS }, | |
| 378 | + { NULL } | |
| 379 | +}; | |
| 380 | + | |
| 381 | +AVFILTER_DEFINE_CLASS(scdet_vulkan); | |
| 382 | + | |
| 383 | +static const AVFilterPad scdet_vulkan_inputs[] = { | |
| 384 | + { | |
| 385 | + .name = "default", | |
| 386 | + .type = AVMEDIA_TYPE_VIDEO, | |
| 387 | + .filter_frame = &scdet_vulkan_filter_frame, | |
| 388 | + .config_props = &ff_vk_filter_config_input, | |
| 389 | + }, | |
| 390 | +}; | |
| 391 | + | |
| 392 | +static const AVFilterPad scdet_vulkan_outputs[] = { | |
| 393 | + { | |
| 394 | + .name = "default", | |
| 395 | + .type = AVMEDIA_TYPE_VIDEO, | |
| 396 | + .config_props = &ff_vk_filter_config_output, | |
| 397 | + }, | |
| 398 | +}; | |
| 399 | + | |
| 400 | +const FFFilter ff_vf_scdet_vulkan = { | |
| 401 | + .p.name = "scdet_vulkan", | |
| 402 | + .p.description = NULL_IF_CONFIG_SMALL("Detect video scene change"), | |
| 403 | + .p.priv_class = &scdet_vulkan_class, | |
| 404 | + .p.flags = AVFILTER_FLAG_HWDEVICE, | |
| 405 | + .priv_size = sizeof(SceneDetectVulkanContext), | |
| 406 | + .init = &ff_vk_filter_init, | |
| 407 | + .uninit = &scdet_vulkan_uninit, | |
| 408 | + FILTER_INPUTS(scdet_vulkan_inputs), | |
| 409 | + FILTER_OUTPUTS(scdet_vulkan_outputs), | |
| 410 | + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), | |
| 411 | + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, | |
| 412 | +}; | |