FFmpeg
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
ffplay_renderer.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 #define VK_NO_PROTOTYPES
20 #define VK_ENABLE_BETA_EXTENSIONS
21 
22 #include "config.h"
23 #include "ffplay_renderer.h"
24 
25 #if (SDL_VERSION_ATLEAST(2, 0, 6) && CONFIG_LIBPLACEBO)
26 /* Get PL_API_VER */
27 #include <libplacebo/config.h>
28 #define HAVE_VULKAN_RENDERER (PL_API_VER >= 278)
29 #else
30 #define HAVE_VULKAN_RENDERER 0
31 #endif
32 
33 #if HAVE_VULKAN_RENDERER
34 
35 #if defined(_WIN32) && !defined(VK_USE_PLATFORM_WIN32_KHR)
36 #define VK_USE_PLATFORM_WIN32_KHR
37 #endif
38 
39 #include <libplacebo/vulkan.h>
40 #include <libplacebo/utils/frame_queue.h>
41 #include <libplacebo/utils/libav.h>
42 #include <SDL_vulkan.h>
43 
44 #include "libavutil/bprint.h"
45 #include "libavutil/mem.h"
46 
47 #endif
48 
49 struct VkRenderer {
50  const AVClass *class;
51 
52  int (*create)(VkRenderer *renderer, SDL_Window *window, AVDictionary *dict);
53 
55 
57 
58  int (*resize)(VkRenderer *renderer, int width, int height);
59 
61 };
62 
63 #if HAVE_VULKAN_RENDERER
64 
65 typedef struct RendererContext {
66  VkRenderer api;
67 
68  // Can be NULL when vulkan instance is created by avutil
69  pl_vk_inst placebo_instance;
70  pl_vulkan placebo_vulkan;
71  pl_swapchain swapchain;
72  VkSurfaceKHR vk_surface;
73  pl_renderer renderer;
74  pl_tex tex[4];
75 
76  pl_log vk_log;
77 
78  AVBufferRef *hw_device_ref;
79  AVBufferRef *hw_frame_ref;
80  enum AVPixelFormat *transfer_formats;
81  AVHWFramesConstraints *constraints;
82 
83  PFN_vkGetInstanceProcAddr get_proc_addr;
84  // This field is a copy from pl_vk_inst->instance or hw_device_ref instance.
85  VkInstance inst;
86 
87  AVFrame *vk_frame;
88 } RendererContext;
89 
90 static void vk_log_cb(void *log_priv, enum pl_log_level level,
91  const char *msg)
92 {
93  static const int level_map[] = {
100  AV_LOG_TRACE,
101  };
102 
103  if (level > 0 && level < FF_ARRAY_ELEMS(level_map))
104  av_log(log_priv, level_map[level], "%s\n", msg);
105 }
106 
107 // Should keep sync with optional_device_exts inside hwcontext_vulkan.c
108 static const char *optional_device_exts[] = {
109  /* Misc or required by other extensions */
110  VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
111  VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
112  VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
113  VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,
114  VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME,
115  VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME,
116  VK_KHR_COOPERATIVE_MATRIX_EXTENSION_NAME,
117 
118  /* Imports/exports */
119  VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
120  VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
121  VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
122  VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
123  VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
124 #ifdef _WIN32
125  VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
126  VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,
127 #endif
128 
129  /* Video encoding/decoding */
130  VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,
131  VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME,
132  VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME,
133  VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME,
134  "VK_MESA_video_decode_av1",
135 };
136 
137 static inline int enable_debug(const AVDictionary *opt)
138 {
139  AVDictionaryEntry *entry = av_dict_get(opt, "debug", NULL, 0);
140  int debug = entry && strtol(entry->value, NULL, 10);
141  return debug;
142 }
143 
144 static void hwctx_lock_queue(void *priv, uint32_t qf, uint32_t qidx)
145 {
146  AVHWDeviceContext *avhwctx = priv;
147  const AVVulkanDeviceContext *hwctx = avhwctx->hwctx;
148  hwctx->lock_queue(avhwctx, qf, qidx);
149 }
150 
151 static void hwctx_unlock_queue(void *priv, uint32_t qf, uint32_t qidx)
152 {
153  AVHWDeviceContext *avhwctx = priv;
154  const AVVulkanDeviceContext *hwctx = avhwctx->hwctx;
155  hwctx->unlock_queue(avhwctx, qf, qidx);
156 }
157 
158 static int add_instance_extension(const char **ext, unsigned num_ext,
159  const AVDictionary *opt,
160  AVDictionary **dict)
161 {
162  const char *inst_ext_key = "instance_extensions";
164  AVBPrint buf;
165  char *ext_list = NULL;
166  int ret;
167 
169  for (int i = 0; i < num_ext; i++) {
170  if (i)
171  av_bprintf(&buf, "+%s", ext[i]);
172  else
173  av_bprintf(&buf, "%s", ext[i]);
174  }
175 
176  entry = av_dict_get(opt, inst_ext_key, NULL, 0);
177  if (entry && entry->value && entry->value[0]) {
178  if (num_ext)
179  av_bprintf(&buf, "+");
180  av_bprintf(&buf, "%s", entry->value);
181  }
182 
183  ret = av_bprint_finalize(&buf, &ext_list);
184  if (ret < 0)
185  return ret;
186  return av_dict_set(dict, inst_ext_key, ext_list, AV_DICT_DONT_STRDUP_VAL);
187 }
188 
189 static int add_device_extension(const AVDictionary *opt,
190  AVDictionary **dict)
191 {
192  const char *dev_ext_key = "device_extensions";
194  AVBPrint buf;
195  char *ext_list = NULL;
196  int ret;
197 
199  av_bprintf(&buf, "%s", VK_KHR_SWAPCHAIN_EXTENSION_NAME);
200  for (int i = 0; i < pl_vulkan_num_recommended_extensions; i++)
201  av_bprintf(&buf, "+%s", pl_vulkan_recommended_extensions[i]);
202 
203  entry = av_dict_get(opt, dev_ext_key, NULL, 0);
204  if (entry && entry->value && entry->value[0])
205  av_bprintf(&buf, "+%s", entry->value);
206 
207  ret = av_bprint_finalize(&buf, &ext_list);
208  if (ret < 0)
209  return ret;
210  return av_dict_set(dict, dev_ext_key, ext_list, AV_DICT_DONT_STRDUP_VAL);
211 }
212 
213 static const char *select_device(const AVDictionary *opt)
214 {
215  const AVDictionaryEntry *entry;
216 
217  entry = av_dict_get(opt, "device", NULL, 0);
218  if (entry)
219  return entry->value;
220  return NULL;
221 }
222 
223 static int create_vk_by_hwcontext(VkRenderer *renderer,
224  const char **ext, unsigned num_ext,
225  const AVDictionary *opt)
226 {
227  RendererContext *ctx = (RendererContext *) renderer;
228  AVHWDeviceContext *dev;
229  AVVulkanDeviceContext *hwctx;
230  AVDictionary *dict = NULL;
231  int ret;
232 
233  ret = add_instance_extension(ext, num_ext, opt, &dict);
234  if (ret < 0)
235  return ret;
236  ret = add_device_extension(opt, &dict);
237  if (ret) {
238  av_dict_free(&dict);
239  return ret;
240  }
241 
243  select_device(opt), dict, 0);
244  av_dict_free(&dict);
245  if (ret < 0)
246  return ret;
247 
248  dev = (AVHWDeviceContext *) ctx->hw_device_ref->data;
249  hwctx = dev->hwctx;
250 
251  // There is no way to pass SDL GetInstanceProcAddr to hwdevice.
252  // Check the result and return error if they don't match.
253  if (hwctx->get_proc_addr != SDL_Vulkan_GetVkGetInstanceProcAddr()) {
255  "hwdevice and SDL use different get_proc_addr. "
256  "Try -vulkan_params create_by_placebo=1\n");
257  return AVERROR_PATCHWELCOME;
258  }
259 
260  ctx->get_proc_addr = hwctx->get_proc_addr;
261  ctx->inst = hwctx->inst;
262 
263  struct pl_vulkan_import_params import_params = {
264  .instance = hwctx->inst,
265  .get_proc_addr = hwctx->get_proc_addr,
266  .phys_device = hwctx->phys_dev,
267  .device = hwctx->act_dev,
268  .extensions = hwctx->enabled_dev_extensions,
269  .num_extensions = hwctx->nb_enabled_dev_extensions,
270  .features = &hwctx->device_features,
271  .lock_queue = hwctx_lock_queue,
272  .unlock_queue = hwctx_unlock_queue,
273  .queue_ctx = dev,
274  .queue_graphics = {
275  .index = VK_QUEUE_FAMILY_IGNORED,
276  .count = 0,
277  },
278  .queue_compute = {
279  .index = VK_QUEUE_FAMILY_IGNORED,
280  .count = 0,
281  },
282  .queue_transfer = {
283  .index = VK_QUEUE_FAMILY_IGNORED,
284  .count = 0,
285  },
286  };
287  for (int i = 0; i < hwctx->nb_qf; i++) {
288  const AVVulkanDeviceQueueFamily *qf = &hwctx->qf[i];
289 
290  if (qf->flags & VK_QUEUE_GRAPHICS_BIT) {
291  import_params.queue_graphics.index = qf->idx;
292  import_params.queue_graphics.count = qf->num;
293  }
294  if (qf->flags & VK_QUEUE_COMPUTE_BIT) {
295  import_params.queue_compute.index = qf->idx;
296  import_params.queue_compute.count = qf->num;
297  }
298  if (qf->flags & VK_QUEUE_TRANSFER_BIT) {
299  import_params.queue_transfer.index = qf->idx;
300  import_params.queue_transfer.count = qf->num;
301  }
302  }
303 
304  ctx->placebo_vulkan = pl_vulkan_import(ctx->vk_log, &import_params);
305  if (!ctx->placebo_vulkan)
306  return AVERROR_EXTERNAL;
307 
308  return 0;
309 }
310 
311 static void placebo_lock_queue(struct AVHWDeviceContext *dev_ctx,
312  uint32_t queue_family, uint32_t index)
313 {
314  RendererContext *ctx = dev_ctx->user_opaque;
315  pl_vulkan vk = ctx->placebo_vulkan;
316  vk->lock_queue(vk, queue_family, index);
317 }
318 
319 static void placebo_unlock_queue(struct AVHWDeviceContext *dev_ctx,
320  uint32_t queue_family,
321  uint32_t index)
322 {
323  RendererContext *ctx = dev_ctx->user_opaque;
324  pl_vulkan vk = ctx->placebo_vulkan;
325  vk->unlock_queue(vk, queue_family, index);
326 }
327 
328 static int get_decode_queue(VkRenderer *renderer, int *index, int *count)
329 {
330  RendererContext *ctx = (RendererContext *) renderer;
331  VkQueueFamilyProperties *queue_family_prop = NULL;
332  uint32_t num_queue_family_prop = 0;
333  PFN_vkGetPhysicalDeviceQueueFamilyProperties get_queue_family_prop;
334  PFN_vkGetInstanceProcAddr get_proc_addr = ctx->get_proc_addr;
335 
336  *index = -1;
337  *count = 0;
338  get_queue_family_prop = (PFN_vkGetPhysicalDeviceQueueFamilyProperties)
339  get_proc_addr(ctx->placebo_instance->instance,
340  "vkGetPhysicalDeviceQueueFamilyProperties");
341  get_queue_family_prop(ctx->placebo_vulkan->phys_device,
342  &num_queue_family_prop, NULL);
343  if (!num_queue_family_prop)
344  return AVERROR_EXTERNAL;
345 
346  queue_family_prop = av_calloc(num_queue_family_prop,
347  sizeof(*queue_family_prop));
348  if (!queue_family_prop)
349  return AVERROR(ENOMEM);
350 
351  get_queue_family_prop(ctx->placebo_vulkan->phys_device,
352  &num_queue_family_prop,
353  queue_family_prop);
354 
355  for (int i = 0; i < num_queue_family_prop; i++) {
356  if (queue_family_prop[i].queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) {
357  *index = i;
358  *count = queue_family_prop[i].queueCount;
359  break;
360  }
361  }
362  av_free(queue_family_prop);
363 
364  return 0;
365 }
366 
367 static int create_vk_by_placebo(VkRenderer *renderer,
368  const char **ext, unsigned num_ext,
369  const AVDictionary *opt)
370 {
371  RendererContext *ctx = (RendererContext *) renderer;
372  AVHWDeviceContext *device_ctx;
373  AVVulkanDeviceContext *vk_dev_ctx;
374  int decode_index;
375  int decode_count;
376  int ret;
377 
378  ctx->get_proc_addr = SDL_Vulkan_GetVkGetInstanceProcAddr();
379 
380  ctx->placebo_instance = pl_vk_inst_create(ctx->vk_log, pl_vk_inst_params(
381  .get_proc_addr = ctx->get_proc_addr,
382  .debug = enable_debug(opt),
383  .extensions = ext,
384  .num_extensions = num_ext
385  ));
386  if (!ctx->placebo_instance) {
387  return AVERROR_EXTERNAL;
388  }
389  ctx->inst = ctx->placebo_instance->instance;
390 
391  ctx->placebo_vulkan = pl_vulkan_create(ctx->vk_log, pl_vulkan_params(
392  .instance = ctx->placebo_instance->instance,
393  .get_proc_addr = ctx->placebo_instance->get_proc_addr,
394  .surface = ctx->vk_surface,
395  .allow_software = false,
396  .opt_extensions = optional_device_exts,
397  .num_opt_extensions = FF_ARRAY_ELEMS(optional_device_exts),
398  .extra_queues = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
399  .device_name = select_device(opt),
400  ));
401  if (!ctx->placebo_vulkan)
402  return AVERROR_EXTERNAL;
404  if (!ctx->hw_device_ref) {
405  return AVERROR(ENOMEM);
406  }
407 
408  device_ctx = (AVHWDeviceContext *) ctx->hw_device_ref->data;
409  device_ctx->user_opaque = ctx;
410 
411  vk_dev_ctx = device_ctx->hwctx;
412  vk_dev_ctx->lock_queue = placebo_lock_queue;
413  vk_dev_ctx->unlock_queue = placebo_unlock_queue;
414 
415  vk_dev_ctx->get_proc_addr = ctx->placebo_instance->get_proc_addr;
416 
417  vk_dev_ctx->inst = ctx->placebo_instance->instance;
418  vk_dev_ctx->phys_dev = ctx->placebo_vulkan->phys_device;
419  vk_dev_ctx->act_dev = ctx->placebo_vulkan->device;
420 
421  vk_dev_ctx->device_features = *ctx->placebo_vulkan->features;
422 
423  vk_dev_ctx->enabled_inst_extensions = ctx->placebo_instance->extensions;
424  vk_dev_ctx->nb_enabled_inst_extensions = ctx->placebo_instance->num_extensions;
425 
426  vk_dev_ctx->enabled_dev_extensions = ctx->placebo_vulkan->extensions;
427  vk_dev_ctx->nb_enabled_dev_extensions = ctx->placebo_vulkan->num_extensions;
428 
429  int nb_qf = 0;
430  vk_dev_ctx->qf[nb_qf] = (AVVulkanDeviceQueueFamily) {
431  .idx = ctx->placebo_vulkan->queue_graphics.index,
432  .num = ctx->placebo_vulkan->queue_graphics.count,
433  .flags = VK_QUEUE_GRAPHICS_BIT,
434  };
435  nb_qf++;
436  vk_dev_ctx->qf[nb_qf] = (AVVulkanDeviceQueueFamily) {
437  .idx = ctx->placebo_vulkan->queue_transfer.index,
438  .num = ctx->placebo_vulkan->queue_transfer.count,
439  .flags = VK_QUEUE_TRANSFER_BIT,
440  };
441  nb_qf++;
442  vk_dev_ctx->qf[nb_qf] = (AVVulkanDeviceQueueFamily) {
443  .idx = ctx->placebo_vulkan->queue_compute.index,
444  .num = ctx->placebo_vulkan->queue_compute.count,
445  .flags = VK_QUEUE_COMPUTE_BIT,
446  };
447  nb_qf++;
448 
449  ret = get_decode_queue(renderer, &decode_index, &decode_count);
450  if (ret < 0)
451  return ret;
452 
453  vk_dev_ctx->qf[nb_qf] = (AVVulkanDeviceQueueFamily) {
454  .idx = decode_index,
455  .num = decode_count,
456  .flags = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
457  };
458  nb_qf++;
459 
460  vk_dev_ctx->nb_qf = nb_qf;
461 
462  ret = av_hwdevice_ctx_init(ctx->hw_device_ref);
463  if (ret < 0)
464  return ret;
465 
466  return 0;
467 }
468 
469 static int create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
470 {
471  int ret = 0;
472  unsigned num_ext = 0;
473  const char **ext = NULL;
474  int w, h;
475  struct pl_log_params vk_log_params = {
476  .log_cb = vk_log_cb,
477  .log_level = PL_LOG_DEBUG,
478  .log_priv = renderer,
479  };
480  RendererContext *ctx = (RendererContext *) renderer;
482 
483  ctx->vk_log = pl_log_create(PL_API_VER, &vk_log_params);
484 
485  if (!SDL_Vulkan_GetInstanceExtensions(window, &num_ext, NULL)) {
486  av_log(NULL, AV_LOG_FATAL, "Failed to get vulkan extensions: %s\n",
487  SDL_GetError());
488  return AVERROR_EXTERNAL;
489  }
490 
491  ext = av_calloc(num_ext, sizeof(*ext));
492  if (!ext) {
493  ret = AVERROR(ENOMEM);
494  goto out;
495  }
496 
497  SDL_Vulkan_GetInstanceExtensions(window, &num_ext, ext);
498 
499  entry = av_dict_get(opt, "create_by_placebo", NULL, 0);
500  if (entry && strtol(entry->value, NULL, 10))
501  ret = create_vk_by_placebo(renderer, ext, num_ext, opt);
502  else
503  ret = create_vk_by_hwcontext(renderer, ext, num_ext, opt);
504  if (ret < 0)
505  goto out;
506 
507  if (!SDL_Vulkan_CreateSurface(window, ctx->inst, &ctx->vk_surface)) {
509  goto out;
510  }
511 
512  ctx->swapchain = pl_vulkan_create_swapchain(
513  ctx->placebo_vulkan,
514  pl_vulkan_swapchain_params(
515  .surface = ctx->vk_surface,
516  .present_mode = VK_PRESENT_MODE_FIFO_KHR));
517  if (!ctx->swapchain) {
519  goto out;
520  }
521 
522  SDL_Vulkan_GetDrawableSize(window, &w, &h);
523  pl_swapchain_resize(ctx->swapchain, &w, &h);
524 
525  ctx->renderer = pl_renderer_create(ctx->vk_log, ctx->placebo_vulkan->gpu);
526  if (!ctx->renderer) {
528  goto out;
529  }
530 
531  ctx->vk_frame = av_frame_alloc();
532  if (!ctx->vk_frame) {
533  ret = AVERROR(ENOMEM);
534  goto out;
535  }
536 
537  ret = 0;
538 
539 out:
540  av_free(ext);
541  return ret;
542 }
543 
544 static int get_hw_dev(VkRenderer *renderer, AVBufferRef **dev)
545 {
546  RendererContext *ctx = (RendererContext *) renderer;
547 
548  *dev = ctx->hw_device_ref;
549  return 0;
550 }
551 
552 static int create_hw_frame(VkRenderer *renderer, AVFrame *frame)
553 {
554  RendererContext *ctx = (RendererContext *) renderer;
555  AVHWFramesContext *src_hw_frame = (AVHWFramesContext *)
556  frame->hw_frames_ctx->data;
557  AVHWFramesContext *hw_frame;
558  AVVulkanFramesContext *vk_frame_ctx;
559  int ret;
560 
561  if (ctx->hw_frame_ref) {
562  hw_frame = (AVHWFramesContext *) ctx->hw_frame_ref->data;
563 
564  if (hw_frame->width == frame->width &&
565  hw_frame->height == frame->height &&
566  hw_frame->sw_format == src_hw_frame->sw_format)
567  return 0;
568 
569  av_buffer_unref(&ctx->hw_frame_ref);
570  }
571 
572  if (!ctx->constraints) {
574  ctx->hw_device_ref, NULL);
575  if (!ctx->constraints)
576  return AVERROR(ENOMEM);
577  }
578 
579  // Check constraints and skip create hwframe. Don't take it as error since
580  // we can fallback to memory copy from GPU to CPU.
581  if ((ctx->constraints->max_width &&
582  ctx->constraints->max_width < frame->width) ||
583  (ctx->constraints->max_height &&
584  ctx->constraints->max_height < frame->height) ||
585  (ctx->constraints->min_width &&
586  ctx->constraints->min_width > frame->width) ||
587  (ctx->constraints->min_height &&
588  ctx->constraints->min_height > frame->height))
589  return 0;
590 
591  if (ctx->constraints->valid_sw_formats) {
592  enum AVPixelFormat *sw_formats = ctx->constraints->valid_sw_formats;
593  while (*sw_formats != AV_PIX_FMT_NONE) {
594  if (*sw_formats == src_hw_frame->sw_format)
595  break;
596  sw_formats++;
597  }
598  if (*sw_formats == AV_PIX_FMT_NONE)
599  return 0;
600  }
601 
602  ctx->hw_frame_ref = av_hwframe_ctx_alloc(ctx->hw_device_ref);
603  if (!ctx->hw_frame_ref)
604  return AVERROR(ENOMEM);
605 
606  hw_frame = (AVHWFramesContext *) ctx->hw_frame_ref->data;
607  hw_frame->format = AV_PIX_FMT_VULKAN;
608  hw_frame->sw_format = src_hw_frame->sw_format;
609  hw_frame->width = frame->width;
610  hw_frame->height = frame->height;
611 
612  if (frame->format == AV_PIX_FMT_CUDA) {
613  vk_frame_ctx = hw_frame->hwctx;
614  vk_frame_ctx->flags = AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE;
615  }
616 
617  ret = av_hwframe_ctx_init(ctx->hw_frame_ref);
618  if (ret < 0) {
619  av_log(renderer, AV_LOG_ERROR, "Create hwframe context failed, %s\n",
620  av_err2str(ret));
621  return ret;
622  }
623 
624  av_hwframe_transfer_get_formats(ctx->hw_frame_ref,
626  &ctx->transfer_formats, 0);
627 
628  return 0;
629 }
630 
631 static inline int check_hw_transfer(RendererContext *ctx, AVFrame *frame)
632 {
633  if (!ctx->hw_frame_ref || !ctx->transfer_formats)
634  return 0;
635 
636  for (int i = 0; ctx->transfer_formats[i] != AV_PIX_FMT_NONE; i++)
637  if (ctx->transfer_formats[i] == frame->format)
638  return 1;
639 
640  return 0;
641 }
642 
643 static inline int move_to_output_frame(RendererContext *ctx, AVFrame *frame)
644 {
645  int ret = av_frame_copy_props(ctx->vk_frame, frame);
646  if (ret < 0)
647  return ret;
649  av_frame_move_ref(frame, ctx->vk_frame);
650  return 0;
651 }
652 
653 static int map_frame(VkRenderer *renderer, AVFrame *frame, int use_hw_frame)
654 {
655  RendererContext *ctx = (RendererContext *) renderer;
656  int ret;
657 
658  if (use_hw_frame && !ctx->hw_frame_ref)
659  return AVERROR(ENOSYS);
660 
661  // Try map data first
662  av_frame_unref(ctx->vk_frame);
663  if (use_hw_frame) {
664  ctx->vk_frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frame_ref);
665  ctx->vk_frame->format = AV_PIX_FMT_VULKAN;
666  }
667  ret = av_hwframe_map(ctx->vk_frame, frame, 0);
668  if (!ret)
669  return move_to_output_frame(ctx, frame);
670 
671  if (ret != AVERROR(ENOSYS))
672  av_log(NULL, AV_LOG_FATAL, "Map frame failed: %s\n", av_err2str(ret));
673  return ret;
674 }
675 
676 static int transfer_frame(VkRenderer *renderer, AVFrame *frame, int use_hw_frame)
677 {
678  RendererContext *ctx = (RendererContext *) renderer;
679  int ret;
680 
681  if (use_hw_frame && !check_hw_transfer(ctx, frame))
682  return AVERROR(ENOSYS);
683 
684  av_frame_unref(ctx->vk_frame);
685  if (use_hw_frame)
686  av_hwframe_get_buffer(ctx->hw_frame_ref, ctx->vk_frame, 0);
687  ret = av_hwframe_transfer_data(ctx->vk_frame, frame, 1);
688  if (!ret)
689  return move_to_output_frame(ctx, frame);
690 
691  if (ret != AVERROR(ENOSYS))
692  av_log(NULL, AV_LOG_FATAL, "Transfer frame failed: %s\n",
693  av_err2str(ret));
694  return ret;
695 }
696 
698 {
699  int ret;
700 
701  if (!frame->hw_frames_ctx)
702  return 0;
703 
704  if (frame->format == AV_PIX_FMT_VULKAN)
705  return 0;
706 
707  ret = create_hw_frame(renderer, frame);
708  if (ret < 0)
709  return ret;
710 
711  for (int use_hw = 1; use_hw >=0; use_hw--) {
712  ret = map_frame(renderer, frame, use_hw);
713  if (!ret)
714  return 0;
715  if (ret != AVERROR(ENOSYS))
716  return ret;
717 
718  ret = transfer_frame(renderer, frame, use_hw);
719  if (!ret)
720  return 0;
721  if (ret != AVERROR(ENOSYS))
722  return ret;
723  }
724 
725  return ret;
726 }
727 
728 static int display(VkRenderer *renderer, AVFrame *frame)
729 {
730  struct pl_swapchain_frame swap_frame = {0};
731  struct pl_frame pl_frame = {0};
732  struct pl_frame target = {0};
733  RendererContext *ctx = (RendererContext *) renderer;
734  int ret = 0;
735  struct pl_color_space hint = {0};
736 
738  if (ret < 0)
739  return ret;
740 
741  if (!pl_map_avframe_ex(ctx->placebo_vulkan->gpu, &pl_frame, pl_avframe_params(
742  .frame = frame,
743  .tex = ctx->tex))) {
744  av_log(NULL, AV_LOG_ERROR, "pl_map_avframe_ex failed\n");
745  return AVERROR_EXTERNAL;
746  }
747 
748  pl_color_space_from_avframe(&hint, frame);
749  pl_swapchain_colorspace_hint(ctx->swapchain, &hint);
750  if (!pl_swapchain_start_frame(ctx->swapchain, &swap_frame)) {
751  av_log(NULL, AV_LOG_ERROR, "start frame failed\n");
753  goto out;
754  }
755 
756  pl_frame_from_swapchain(&target, &swap_frame);
757  if (!pl_render_image(ctx->renderer, &pl_frame, &target,
758  &pl_render_default_params)) {
759  av_log(NULL, AV_LOG_ERROR, "pl_render_image failed\n");
761  goto out;
762  }
763 
764  if (!pl_swapchain_submit_frame(ctx->swapchain)) {
765  av_log(NULL, AV_LOG_ERROR, "pl_swapchain_submit_frame failed\n");
767  goto out;
768  }
769  pl_swapchain_swap_buffers(ctx->swapchain);
770 
771 out:
772  pl_unmap_avframe(ctx->placebo_vulkan->gpu, &pl_frame);
773  return ret;
774 }
775 
776 static int resize(VkRenderer *renderer, int width, int height)
777 {
778  RendererContext *ctx = (RendererContext *) renderer;
779 
780  if (!pl_swapchain_resize(ctx->swapchain, &width, &height))
781  return AVERROR_EXTERNAL;
782  return 0;
783 }
784 
785 static void destroy(VkRenderer *renderer)
786 {
787  RendererContext *ctx = (RendererContext *) renderer;
788  PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
789 
790  av_frame_free(&ctx->vk_frame);
791  av_freep(&ctx->transfer_formats);
792  av_hwframe_constraints_free(&ctx->constraints);
793  av_buffer_unref(&ctx->hw_frame_ref);
794 
795  if (ctx->placebo_vulkan) {
796  for (int i = 0; i < FF_ARRAY_ELEMS(ctx->tex); i++)
797  pl_tex_destroy(ctx->placebo_vulkan->gpu, &ctx->tex[i]);
798  pl_renderer_destroy(&ctx->renderer);
799  pl_swapchain_destroy(&ctx->swapchain);
800  pl_vulkan_destroy(&ctx->placebo_vulkan);
801  }
802 
803  if (ctx->vk_surface) {
804  vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)
805  ctx->get_proc_addr(ctx->inst, "vkDestroySurfaceKHR");
806  vkDestroySurfaceKHR(ctx->inst, ctx->vk_surface, NULL);
807  ctx->vk_surface = VK_NULL_HANDLE;
808  }
809 
810  av_buffer_unref(&ctx->hw_device_ref);
811  pl_vk_inst_destroy(&ctx->placebo_instance);
812 
813  pl_log_destroy(&ctx->vk_log);
814 }
815 
816 static const AVClass vulkan_renderer_class = {
817  .class_name = "Vulkan Renderer",
818  .item_name = av_default_item_name,
819  .version = LIBAVUTIL_VERSION_INT,
820 };
821 
823 {
824  RendererContext *ctx = av_mallocz(sizeof(*ctx));
826 
827  if (!ctx)
828  return NULL;
829 
830  renderer = &ctx->api;
831  renderer->class = &vulkan_renderer_class;
832  renderer->get_hw_dev = get_hw_dev;
833  renderer->create = create;
834  renderer->display = display;
835  renderer->resize = resize;
836  renderer->destroy = destroy;
837 
838  return renderer;
839 }
840 
841 #else
842 
844 {
845  return NULL;
846 }
847 
848 #endif
849 
851  AVDictionary *opt)
852 {
853  return renderer->create(renderer, window, opt);
854 }
855 
857 {
858  return renderer->get_hw_dev(renderer, dev);
859 }
860 
862 {
863  return renderer->display(renderer, frame);
864 }
865 
867 {
868  return renderer->resize(renderer, width, height);
869 }
870 
872 {
873  renderer->destroy(renderer);
874 }
AVHWDeviceContext::hwctx
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition: hwcontext.h:86
VkRenderer::create
int(* create)(VkRenderer *renderer, SDL_Window *window, AVDictionary *dict)
Definition: ffplay_renderer.c:52
AVVulkanDeviceContext::phys_dev
VkPhysicalDevice phys_dev
Physical device.
Definition: hwcontext_vulkan.h:79
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
AV_PIX_FMT_CUDA
@ AV_PIX_FMT_CUDA
HW acceleration through CUDA.
Definition: pixfmt.h:260
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
convert_frame
static int convert_frame(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Convert a frame from linear RGB to logspace LAB, and accumulate channel totals for each row Convert f...
Definition: vf_grayworld.c:122
entry
#define entry
Definition: aom_film_grain_template.c:66
level
uint8_t level
Definition: svq3.c:208
AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE
@ AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE
Definition: hwcontext_vulkan.h:202
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
out
FILE * out
Definition: movenc.c:55
destroy
static void destroy(struct ResampleContext **c)
Definition: soxr_resample.c:64
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
AV_LOG_QUIET
#define AV_LOG_QUIET
Print no output.
Definition: log.h:192
ffplay_renderer.h
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:63
av_hwframe_ctx_init
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
Definition: hwcontext.c:333
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:410
AVVulkanDeviceContext::get_proc_addr
PFN_vkGetInstanceProcAddr get_proc_addr
Pointer to a vkGetInstanceProcAddr loading function.
Definition: hwcontext_vulkan.h:69
optional_device_exts
static const VulkanOptExtension optional_device_exts[]
Definition: hwcontext_vulkan.c:606
w
uint8_t w
Definition: llviddspenc.c:38
av_hwframe_ctx_alloc
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
Definition: hwcontext.c:259
AVHWDeviceContext::user_opaque
void * user_opaque
Arbitrary user data, to be used e.g.
Definition: hwcontext.h:103
av_hwframe_map
int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags)
Map a hardware frame.
Definition: hwcontext.c:789
vk_renderer_create
int vk_renderer_create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
Definition: ffplay_renderer.c:850
AVVulkanDeviceContext::inst
VkInstance inst
Vulkan instance.
Definition: hwcontext_vulkan.h:74
AVDictionary
Definition: dict.c:32
av_buffer_ref
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition: buffer.c:103
map_frame
static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src, struct pl_frame *out)
Definition: vf_libplacebo.c:997
vk_renderer_destroy
void vk_renderer_destroy(VkRenderer *renderer)
Definition: ffplay_renderer.c:871
av_hwdevice_get_hwframe_constraints
AVHWFramesConstraints * av_hwdevice_get_hwframe_constraints(AVBufferRef *ref, const void *hwconfig)
Get the constraints on HW frames given a device and the HW-specific configuration to be used with tha...
Definition: hwcontext.c:577
av_hwdevice_ctx_init
int av_hwdevice_ctx_init(AVBufferRef *ref)
Finalize the device context before use.
Definition: hwcontext.c:219
AV_PIX_FMT_VULKAN
@ AV_PIX_FMT_VULKAN
Vulkan hardware images.
Definition: pixfmt.h:379
AV_HWDEVICE_TYPE_VULKAN
@ AV_HWDEVICE_TYPE_VULKAN
Definition: hwcontext.h:39
AVHWFramesConstraints
This struct describes the constraints on hardware frames attached to a given device with a hardware-s...
Definition: hwcontext.h:442
window
static SDL_Window * window
Definition: ffplay.c:361
AVVulkanFramesContext
Allocated as AVHWFramesContext.hwctx, used to set pool-specific options.
Definition: hwcontext_vulkan.h:208
AV_BPRINT_SIZE_AUTOMATIC
#define AV_BPRINT_SIZE_AUTOMATIC
AV_DICT_DONT_STRDUP_VAL
#define AV_DICT_DONT_STRDUP_VAL
Take ownership of a value that's been allocated with av_malloc() or another memory allocation functio...
Definition: dict.h:79
AVHWDeviceContext
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
Definition: hwcontext.h:61
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:51
AVVulkanDeviceQueueFamily::num
int num
Definition: hwcontext_vulkan.h:37
AV_LOG_TRACE
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:236
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
vk_get_renderer
VkRenderer * vk_get_renderer(void)
Definition: ffplay_renderer.c:843
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
av_hwdevice_ctx_alloc
AVBufferRef * av_hwdevice_ctx_alloc(enum AVHWDeviceType type)
Allocate an AVHWDeviceContext for a given hardware type.
Definition: hwcontext.c:172
av_dict_get
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
Definition: dict.c:60
av_hwframe_constraints_free
void av_hwframe_constraints_free(AVHWFramesConstraints **constraints)
Free an AVHWFrameConstraints structure.
Definition: hwcontext.c:602
AVFormatContext::flags
int flags
Flags modifying the (de)muxer behaviour.
Definition: avformat.h:1415
decode_index
static int decode_index(SGAVideoContext *s, AVFrame *frame)
Definition: sga.c:181
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:231
ctx
AVFormatContext * ctx
Definition: movenc.c:49
vk_renderer_get_hw_dev
int vk_renderer_get_hw_dev(VkRenderer *renderer, AVBufferRef **dev)
Definition: ffplay_renderer.c:856
vk_renderer_display
int vk_renderer_display(VkRenderer *renderer, AVFrame *frame)
Definition: ffplay_renderer.c:861
renderer
static SDL_Renderer * renderer
Definition: ffplay.c:362
if
if(ret)
Definition: filter_design.txt:179
AVVulkanDeviceContext
Main Vulkan context, allocated as AVHWDeviceContext.hwctx.
Definition: hwcontext_vulkan.h:59
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
VkRenderer
Definition: ffplay_renderer.c:49
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
AVHWFramesContext::sw_format
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
Definition: hwcontext.h:211
AVERROR_PATCHWELCOME
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition: error.h:64
av_frame_copy_props
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:597
av_buffer_unref
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition: buffer.c:139
AVVulkanDeviceContext::nb_enabled_dev_extensions
int nb_enabled_dev_extensions
Definition: hwcontext_vulkan.h:113
create
static struct ResampleContext * create(struct ResampleContext *c, int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff, enum AVSampleFormat format, enum SwrFilterType filter_type, double kaiser_beta, double precision, int cheby, int exact_rational)
Definition: soxr_resample.c:32
AVVulkanDeviceContext::unlock_queue
void(* unlock_queue)(struct AVHWDeviceContext *ctx, uint32_t queue_family, uint32_t index)
Similar to lock_queue(), unlocks a queue.
Definition: hwcontext_vulkan.h:178
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:240
AVVulkanDeviceContext::nb_qf
int nb_qf
Definition: hwcontext_vulkan.h:189
AVVulkanDeviceContext::enabled_inst_extensions
const char *const * enabled_inst_extensions
Enabled instance extensions.
Definition: hwcontext_vulkan.h:101
AVVulkanDeviceContext::qf
AVVulkanDeviceQueueFamily qf[64]
Queue families used.
Definition: hwcontext_vulkan.h:188
index
int index
Definition: gxfenc.c:90
height
#define height
Definition: dsp.h:89
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:240
av_err2str
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:122
VkRenderer::get_hw_dev
int(* get_hw_dev)(VkRenderer *renderer, AVBufferRef **dev)
Definition: ffplay_renderer.c:54
AVVulkanDeviceQueueFamily::idx
int idx
Definition: hwcontext_vulkan.h:35
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
av_dict_free
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values.
Definition: dict.c:233
AVVulkanDeviceContext::lock_queue
void(* lock_queue)(struct AVHWDeviceContext *ctx, uint32_t queue_family, uint32_t index)
Locks a queue, preventing other threads from submitting any command buffers to this queue.
Definition: hwcontext_vulkan.h:173
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:221
bprint.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
av_frame_move_ref
void av_frame_move_ref(AVFrame *dst, AVFrame *src)
Move everything contained in src to dst and reset src.
Definition: frame.c:521
vk_renderer_resize
int vk_renderer_resize(VkRenderer *renderer, int width, int height)
Definition: ffplay_renderer.c:866
av_frame_unref
void av_frame_unref(AVFrame *frame)
Unreference all the buffers referenced by frame and reset the frame fields.
Definition: frame.c:494
av_mallocz
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:256
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
AVHWFramesContext
This struct describes a set or pool of "hardware" frames (i.e.
Definition: hwcontext.h:116
ret
ret
Definition: filter_design.txt:187
AV_LOG_FATAL
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
Definition: log.h:204
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:81
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:264
VkRenderer::destroy
void(* destroy)(VkRenderer *renderer)
Definition: ffplay_renderer.c:60
av_hwdevice_ctx_create
int av_hwdevice_ctx_create(AVBufferRef **pdevice_ref, enum AVHWDeviceType type, const char *device, AVDictionary *opts, int flags)
Open a device of the specified type and create an AVHWDeviceContext for it.
Definition: hwcontext.c:611
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:99
av_hwframe_transfer_data
int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags)
Copy data to or from a hw surface.
Definition: hwcontext.c:444
AVFormatContext::debug
int debug
Flags to enable debugging.
Definition: avformat.h:1539
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
av_hwframe_transfer_get_formats
int av_hwframe_transfer_get_formats(AVBufferRef *hwframe_ref, enum AVHWFrameTransferDirection dir, enum AVPixelFormat **formats, int flags)
Get a list of possible source or target formats usable in av_hwframe_transfer_data().
Definition: hwcontext.c:382
AVVulkanDeviceContext::enabled_dev_extensions
const char *const * enabled_dev_extensions
Enabled device extensions.
Definition: hwcontext_vulkan.h:112
mem.h
AVBufferRef
A reference to a data buffer.
Definition: buffer.h:82
AVVulkanDeviceContext::act_dev
VkDevice act_dev
Active device.
Definition: hwcontext_vulkan.h:84
AVVulkanDeviceContext::nb_enabled_inst_extensions
int nb_enabled_inst_extensions
Definition: hwcontext_vulkan.h:102
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
AVDictionaryEntry
Definition: dict.h:90
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
av_dict_set
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition: dict.c:86
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVVulkanDeviceQueueFamily
Definition: hwcontext_vulkan.h:33
h
h
Definition: vp9dsp_template.c:2070
AVVulkanDeviceContext::device_features
VkPhysicalDeviceFeatures2 device_features
This structure should be set to the set of features that present and enabled during device creation.
Definition: hwcontext_vulkan.h:92
width
#define width
Definition: dsp.h:89
VkRenderer::display
int(* display)(VkRenderer *renderer, AVFrame *frame)
Definition: ffplay_renderer.c:56
av_hwframe_get_buffer
int av_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags)
Allocate a new frame attached to the given AVHWFramesContext.
Definition: hwcontext.c:502
AVVulkanDeviceQueueFamily::flags
VkQueueFlagBits flags
Definition: hwcontext_vulkan.h:41
VkRenderer::resize
int(* resize)(VkRenderer *renderer, int width, int height)
Definition: ffplay_renderer.c:58
AV_HWFRAME_TRANSFER_DIRECTION_TO
@ AV_HWFRAME_TRANSFER_DIRECTION_TO
Transfer the data to the queried hw frame.
Definition: hwcontext.h:413