FFmpeg
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 static inline int enable_debug(const AVDictionary *opt)
108 {
109  AVDictionaryEntry *entry = av_dict_get(opt, "debug", NULL, 0);
110  int debug = entry && strtol(entry->value, NULL, 10);
111  return debug;
112 }
113 
114 static void hwctx_lock_queue(void *priv, uint32_t qf, uint32_t qidx)
115 {
116  AVHWDeviceContext *avhwctx = priv;
117  const AVVulkanDeviceContext *hwctx = avhwctx->hwctx;
118  hwctx->lock_queue(avhwctx, qf, qidx);
119 }
120 
121 static void hwctx_unlock_queue(void *priv, uint32_t qf, uint32_t qidx)
122 {
123  AVHWDeviceContext *avhwctx = priv;
124  const AVVulkanDeviceContext *hwctx = avhwctx->hwctx;
125  hwctx->unlock_queue(avhwctx, qf, qidx);
126 }
127 
128 static int add_instance_extension(const char **ext, unsigned num_ext,
129  const AVDictionary *opt,
130  AVDictionary **dict)
131 {
132  const char *inst_ext_key = "instance_extensions";
134  AVBPrint buf;
135  char *ext_list = NULL;
136  int ret;
137 
139  for (int i = 0; i < num_ext; i++) {
140  if (i)
141  av_bprintf(&buf, "+%s", ext[i]);
142  else
143  av_bprintf(&buf, "%s", ext[i]);
144  }
145 
146  entry = av_dict_get(opt, inst_ext_key, NULL, 0);
147  if (entry && entry->value && entry->value[0]) {
148  if (num_ext)
149  av_bprintf(&buf, "+");
150  av_bprintf(&buf, "%s", entry->value);
151  }
152 
153  ret = av_bprint_finalize(&buf, &ext_list);
154  if (ret < 0)
155  return ret;
156  return av_dict_set(dict, inst_ext_key, ext_list, AV_DICT_DONT_STRDUP_VAL);
157 }
158 
159 static int add_device_extension(const AVDictionary *opt,
160  AVDictionary **dict)
161 {
162  const char *dev_ext_key = "device_extensions";
164  AVBPrint buf;
165  char *ext_list = NULL;
166  int ret;
167 
169  av_bprintf(&buf, "%s", VK_KHR_SWAPCHAIN_EXTENSION_NAME);
170  for (int i = 0; i < pl_vulkan_num_recommended_extensions; i++)
171  av_bprintf(&buf, "+%s", pl_vulkan_recommended_extensions[i]);
172 
173  entry = av_dict_get(opt, dev_ext_key, NULL, 0);
174  if (entry && entry->value && entry->value[0])
175  av_bprintf(&buf, "+%s", entry->value);
176 
177  ret = av_bprint_finalize(&buf, &ext_list);
178  if (ret < 0)
179  return ret;
180  return av_dict_set(dict, dev_ext_key, ext_list, AV_DICT_DONT_STRDUP_VAL);
181 }
182 
183 static const char *select_device(const AVDictionary *opt)
184 {
185  const AVDictionaryEntry *entry;
186 
187  entry = av_dict_get(opt, "device", NULL, 0);
188  if (entry)
189  return entry->value;
190  return NULL;
191 }
192 
193 static int create_vk_by_hwcontext(VkRenderer *renderer,
194  const char **ext, unsigned num_ext,
195  const AVDictionary *opt)
196 {
197  RendererContext *ctx = (RendererContext *) renderer;
198  AVHWDeviceContext *dev;
199  AVVulkanDeviceContext *hwctx;
200  AVDictionary *dict = NULL;
201  int ret;
202 
203  ret = add_instance_extension(ext, num_ext, opt, &dict);
204  if (ret < 0)
205  return ret;
206  ret = add_device_extension(opt, &dict);
207  if (ret) {
208  av_dict_free(&dict);
209  return ret;
210  }
211 
213  select_device(opt), dict, 0);
214  av_dict_free(&dict);
215  if (ret < 0)
216  return ret;
217 
218  dev = (AVHWDeviceContext *) ctx->hw_device_ref->data;
219  hwctx = dev->hwctx;
220 
221  // There is no way to pass SDL GetInstanceProcAddr to hwdevice.
222  // Check the result and return error if they don't match.
223  if (hwctx->get_proc_addr != SDL_Vulkan_GetVkGetInstanceProcAddr()) {
225  "hwdevice and SDL use different get_proc_addr. "
226  "Try -vulkan_params create_by_placebo=1\n");
227  return AVERROR_PATCHWELCOME;
228  }
229 
230  ctx->get_proc_addr = hwctx->get_proc_addr;
231  ctx->inst = hwctx->inst;
232 
233  struct pl_vulkan_import_params import_params = {
234  .instance = hwctx->inst,
235  .get_proc_addr = hwctx->get_proc_addr,
236  .phys_device = hwctx->phys_dev,
237  .device = hwctx->act_dev,
238  .extensions = hwctx->enabled_dev_extensions,
239  .num_extensions = hwctx->nb_enabled_dev_extensions,
240  .features = &hwctx->device_features,
241  .lock_queue = hwctx_lock_queue,
242  .unlock_queue = hwctx_unlock_queue,
243  .queue_ctx = dev,
244  .queue_graphics = {
245  .index = VK_QUEUE_FAMILY_IGNORED,
246  .count = 0,
247  },
248  .queue_compute = {
249  .index = VK_QUEUE_FAMILY_IGNORED,
250  .count = 0,
251  },
252  .queue_transfer = {
253  .index = VK_QUEUE_FAMILY_IGNORED,
254  .count = 0,
255  },
256  };
257  for (int i = 0; i < hwctx->nb_qf; i++) {
258  const AVVulkanDeviceQueueFamily *qf = &hwctx->qf[i];
259 
260  if (qf->flags & VK_QUEUE_GRAPHICS_BIT) {
261  import_params.queue_graphics.index = qf->idx;
262  import_params.queue_graphics.count = qf->num;
263  }
264  if (qf->flags & VK_QUEUE_COMPUTE_BIT) {
265  import_params.queue_compute.index = qf->idx;
266  import_params.queue_compute.count = qf->num;
267  }
268  if (qf->flags & VK_QUEUE_TRANSFER_BIT) {
269  import_params.queue_transfer.index = qf->idx;
270  import_params.queue_transfer.count = qf->num;
271  }
272  }
273 
274  ctx->placebo_vulkan = pl_vulkan_import(ctx->vk_log, &import_params);
275  if (!ctx->placebo_vulkan)
276  return AVERROR_EXTERNAL;
277 
278  return 0;
279 }
280 
281 static void placebo_lock_queue(struct AVHWDeviceContext *dev_ctx,
282  uint32_t queue_family, uint32_t index)
283 {
284  RendererContext *ctx = dev_ctx->user_opaque;
285  pl_vulkan vk = ctx->placebo_vulkan;
286  vk->lock_queue(vk, queue_family, index);
287 }
288 
289 static void placebo_unlock_queue(struct AVHWDeviceContext *dev_ctx,
290  uint32_t queue_family,
291  uint32_t index)
292 {
293  RendererContext *ctx = dev_ctx->user_opaque;
294  pl_vulkan vk = ctx->placebo_vulkan;
295  vk->unlock_queue(vk, queue_family, index);
296 }
297 
298 static int get_decode_queue(VkRenderer *renderer, int *index, int *count)
299 {
300  RendererContext *ctx = (RendererContext *) renderer;
301  VkQueueFamilyProperties *queue_family_prop = NULL;
302  uint32_t num_queue_family_prop = 0;
303  PFN_vkGetPhysicalDeviceQueueFamilyProperties get_queue_family_prop;
304  PFN_vkGetInstanceProcAddr get_proc_addr = ctx->get_proc_addr;
305 
306  *index = -1;
307  *count = 0;
308  get_queue_family_prop = (PFN_vkGetPhysicalDeviceQueueFamilyProperties)
309  get_proc_addr(ctx->placebo_instance->instance,
310  "vkGetPhysicalDeviceQueueFamilyProperties");
311  get_queue_family_prop(ctx->placebo_vulkan->phys_device,
312  &num_queue_family_prop, NULL);
313  if (!num_queue_family_prop)
314  return AVERROR_EXTERNAL;
315 
316  queue_family_prop = av_calloc(num_queue_family_prop,
317  sizeof(*queue_family_prop));
318  if (!queue_family_prop)
319  return AVERROR(ENOMEM);
320 
321  get_queue_family_prop(ctx->placebo_vulkan->phys_device,
322  &num_queue_family_prop,
323  queue_family_prop);
324 
325  for (int i = 0; i < num_queue_family_prop; i++) {
326  if (queue_family_prop[i].queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) {
327  *index = i;
328  *count = queue_family_prop[i].queueCount;
329  break;
330  }
331  }
332  av_free(queue_family_prop);
333 
334  return 0;
335 }
336 
337 static int create_vk_by_placebo(VkRenderer *renderer,
338  const char **ext, unsigned num_ext,
339  const AVDictionary *opt)
340 {
341  RendererContext *ctx = (RendererContext *) renderer;
342  AVHWDeviceContext *device_ctx;
343  AVVulkanDeviceContext *vk_dev_ctx;
344  int decode_index;
345  int decode_count;
346  int ret;
347  const char **dev_exts;
348  int num_dev_exts;
349 
350  ctx->get_proc_addr = SDL_Vulkan_GetVkGetInstanceProcAddr();
351 
352  ctx->placebo_instance = pl_vk_inst_create(ctx->vk_log, pl_vk_inst_params(
353  .get_proc_addr = ctx->get_proc_addr,
354  .debug = enable_debug(opt),
355  .extensions = ext,
356  .num_extensions = num_ext
357  ));
358  if (!ctx->placebo_instance) {
359  return AVERROR_EXTERNAL;
360  }
361  ctx->inst = ctx->placebo_instance->instance;
362 
363  dev_exts = av_vk_get_optional_device_extensions(&num_dev_exts);
364  if (!dev_exts)
365  return AVERROR(ENOMEM);
366 
367  ctx->placebo_vulkan = pl_vulkan_create(ctx->vk_log, pl_vulkan_params(
368  .instance = ctx->placebo_instance->instance,
369  .get_proc_addr = ctx->placebo_instance->get_proc_addr,
370  .surface = ctx->vk_surface,
371  .allow_software = false,
372  .opt_extensions = dev_exts,
373  .num_opt_extensions = num_dev_exts,
374  .extra_queues = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
375  .device_name = select_device(opt),
376  ));
377  av_free(dev_exts);
378  if (!ctx->placebo_vulkan)
379  return AVERROR_EXTERNAL;
381  if (!ctx->hw_device_ref) {
382  return AVERROR(ENOMEM);
383  }
384 
385  device_ctx = (AVHWDeviceContext *) ctx->hw_device_ref->data;
386  device_ctx->user_opaque = ctx;
387 
388  vk_dev_ctx = device_ctx->hwctx;
389  vk_dev_ctx->lock_queue = placebo_lock_queue;
390  vk_dev_ctx->unlock_queue = placebo_unlock_queue;
391 
392  vk_dev_ctx->get_proc_addr = ctx->placebo_instance->get_proc_addr;
393 
394  vk_dev_ctx->inst = ctx->placebo_instance->instance;
395  vk_dev_ctx->phys_dev = ctx->placebo_vulkan->phys_device;
396  vk_dev_ctx->act_dev = ctx->placebo_vulkan->device;
397 
398  vk_dev_ctx->device_features = *ctx->placebo_vulkan->features;
399 
400  vk_dev_ctx->enabled_inst_extensions = ctx->placebo_instance->extensions;
401  vk_dev_ctx->nb_enabled_inst_extensions = ctx->placebo_instance->num_extensions;
402 
403  vk_dev_ctx->enabled_dev_extensions = ctx->placebo_vulkan->extensions;
404  vk_dev_ctx->nb_enabled_dev_extensions = ctx->placebo_vulkan->num_extensions;
405 
406  int nb_qf = 0;
407  vk_dev_ctx->qf[nb_qf] = (AVVulkanDeviceQueueFamily) {
408  .idx = ctx->placebo_vulkan->queue_graphics.index,
409  .num = ctx->placebo_vulkan->queue_graphics.count,
410  .flags = VK_QUEUE_GRAPHICS_BIT,
411  };
412  nb_qf++;
413  vk_dev_ctx->qf[nb_qf] = (AVVulkanDeviceQueueFamily) {
414  .idx = ctx->placebo_vulkan->queue_transfer.index,
415  .num = ctx->placebo_vulkan->queue_transfer.count,
416  .flags = VK_QUEUE_TRANSFER_BIT,
417  };
418  nb_qf++;
419  vk_dev_ctx->qf[nb_qf] = (AVVulkanDeviceQueueFamily) {
420  .idx = ctx->placebo_vulkan->queue_compute.index,
421  .num = ctx->placebo_vulkan->queue_compute.count,
422  .flags = VK_QUEUE_COMPUTE_BIT,
423  };
424  nb_qf++;
425 
426  ret = get_decode_queue(renderer, &decode_index, &decode_count);
427  if (ret < 0)
428  return ret;
429 
430  vk_dev_ctx->qf[nb_qf] = (AVVulkanDeviceQueueFamily) {
431  .idx = decode_index,
432  .num = decode_count,
433  .flags = VK_QUEUE_VIDEO_DECODE_BIT_KHR,
434  };
435  nb_qf++;
436 
437  vk_dev_ctx->nb_qf = nb_qf;
438 
439  ret = av_hwdevice_ctx_init(ctx->hw_device_ref);
440  if (ret < 0)
441  return ret;
442 
443  return 0;
444 }
445 
446 static int create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
447 {
448  int ret = 0;
449  unsigned num_ext = 0;
450  const char **ext = NULL;
451  int w, h;
452  struct pl_log_params vk_log_params = {
453  .log_cb = vk_log_cb,
454  .log_level = PL_LOG_DEBUG,
455  .log_priv = renderer,
456  };
457  RendererContext *ctx = (RendererContext *) renderer;
459 
460  ctx->vk_log = pl_log_create(PL_API_VER, &vk_log_params);
461 
462  if (!SDL_Vulkan_GetInstanceExtensions(window, &num_ext, NULL)) {
463  av_log(NULL, AV_LOG_FATAL, "Failed to get vulkan extensions: %s\n",
464  SDL_GetError());
465  return AVERROR_EXTERNAL;
466  }
467 
468  ext = av_calloc(num_ext, sizeof(*ext));
469  if (!ext) {
470  ret = AVERROR(ENOMEM);
471  goto out;
472  }
473 
474  SDL_Vulkan_GetInstanceExtensions(window, &num_ext, ext);
475 
476  entry = av_dict_get(opt, "create_by_placebo", NULL, 0);
477  if (entry && strtol(entry->value, NULL, 10))
478  ret = create_vk_by_placebo(renderer, ext, num_ext, opt);
479  else
480  ret = create_vk_by_hwcontext(renderer, ext, num_ext, opt);
481  if (ret < 0)
482  goto out;
483 
484  if (!SDL_Vulkan_CreateSurface(window, ctx->inst, &ctx->vk_surface)) {
486  goto out;
487  }
488 
489  ctx->swapchain = pl_vulkan_create_swapchain(
490  ctx->placebo_vulkan,
491  pl_vulkan_swapchain_params(
492  .surface = ctx->vk_surface,
493  .present_mode = VK_PRESENT_MODE_FIFO_KHR));
494  if (!ctx->swapchain) {
496  goto out;
497  }
498 
499  SDL_Vulkan_GetDrawableSize(window, &w, &h);
500  pl_swapchain_resize(ctx->swapchain, &w, &h);
501 
502  ctx->renderer = pl_renderer_create(ctx->vk_log, ctx->placebo_vulkan->gpu);
503  if (!ctx->renderer) {
505  goto out;
506  }
507 
508  ctx->vk_frame = av_frame_alloc();
509  if (!ctx->vk_frame) {
510  ret = AVERROR(ENOMEM);
511  goto out;
512  }
513 
514  ret = 0;
515 
516 out:
517  av_free(ext);
518  return ret;
519 }
520 
521 static int get_hw_dev(VkRenderer *renderer, AVBufferRef **dev)
522 {
523  RendererContext *ctx = (RendererContext *) renderer;
524 
525  *dev = ctx->hw_device_ref;
526  return 0;
527 }
528 
529 static int create_hw_frame(VkRenderer *renderer, AVFrame *frame)
530 {
531  RendererContext *ctx = (RendererContext *) renderer;
532  AVHWFramesContext *src_hw_frame = (AVHWFramesContext *)
533  frame->hw_frames_ctx->data;
534  AVHWFramesContext *hw_frame;
535  AVVulkanFramesContext *vk_frame_ctx;
536  int ret;
537 
538  if (ctx->hw_frame_ref) {
539  hw_frame = (AVHWFramesContext *) ctx->hw_frame_ref->data;
540 
541  if (hw_frame->width == frame->width &&
542  hw_frame->height == frame->height &&
543  hw_frame->sw_format == src_hw_frame->sw_format)
544  return 0;
545 
546  av_buffer_unref(&ctx->hw_frame_ref);
547  }
548 
549  if (!ctx->constraints) {
551  ctx->hw_device_ref, NULL);
552  if (!ctx->constraints)
553  return AVERROR(ENOMEM);
554  }
555 
556  // Check constraints and skip create hwframe. Don't take it as error since
557  // we can fallback to memory copy from GPU to CPU.
558  if ((ctx->constraints->max_width &&
559  ctx->constraints->max_width < frame->width) ||
560  (ctx->constraints->max_height &&
561  ctx->constraints->max_height < frame->height) ||
562  (ctx->constraints->min_width &&
563  ctx->constraints->min_width > frame->width) ||
564  (ctx->constraints->min_height &&
565  ctx->constraints->min_height > frame->height))
566  return 0;
567 
568  if (ctx->constraints->valid_sw_formats) {
569  enum AVPixelFormat *sw_formats = ctx->constraints->valid_sw_formats;
570  while (*sw_formats != AV_PIX_FMT_NONE) {
571  if (*sw_formats == src_hw_frame->sw_format)
572  break;
573  sw_formats++;
574  }
575  if (*sw_formats == AV_PIX_FMT_NONE)
576  return 0;
577  }
578 
579  ctx->hw_frame_ref = av_hwframe_ctx_alloc(ctx->hw_device_ref);
580  if (!ctx->hw_frame_ref)
581  return AVERROR(ENOMEM);
582 
583  hw_frame = (AVHWFramesContext *) ctx->hw_frame_ref->data;
584  hw_frame->format = AV_PIX_FMT_VULKAN;
585  hw_frame->sw_format = src_hw_frame->sw_format;
586  hw_frame->width = frame->width;
587  hw_frame->height = frame->height;
588 
589  if (frame->format == AV_PIX_FMT_CUDA) {
590  vk_frame_ctx = hw_frame->hwctx;
591  vk_frame_ctx->flags = AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE;
592  }
593 
594  ret = av_hwframe_ctx_init(ctx->hw_frame_ref);
595  if (ret < 0) {
596  av_log(renderer, AV_LOG_ERROR, "Create hwframe context failed, %s\n",
597  av_err2str(ret));
598  return ret;
599  }
600 
601  av_hwframe_transfer_get_formats(ctx->hw_frame_ref,
603  &ctx->transfer_formats, 0);
604 
605  return 0;
606 }
607 
608 static inline int check_hw_transfer(RendererContext *ctx, AVFrame *frame)
609 {
610  if (!ctx->hw_frame_ref || !ctx->transfer_formats)
611  return 0;
612 
613  for (int i = 0; ctx->transfer_formats[i] != AV_PIX_FMT_NONE; i++)
614  if (ctx->transfer_formats[i] == frame->format)
615  return 1;
616 
617  return 0;
618 }
619 
620 static inline int move_to_output_frame(RendererContext *ctx, AVFrame *frame)
621 {
622  int ret = av_frame_copy_props(ctx->vk_frame, frame);
623  if (ret < 0)
624  return ret;
626  av_frame_move_ref(frame, ctx->vk_frame);
627  return 0;
628 }
629 
630 static int map_frame(VkRenderer *renderer, AVFrame *frame, int use_hw_frame)
631 {
632  RendererContext *ctx = (RendererContext *) renderer;
633  int ret;
634 
635  if (use_hw_frame && !ctx->hw_frame_ref)
636  return AVERROR(ENOSYS);
637 
638  // Try map data first
639  av_frame_unref(ctx->vk_frame);
640  if (use_hw_frame) {
641  ctx->vk_frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frame_ref);
642  ctx->vk_frame->format = AV_PIX_FMT_VULKAN;
643  }
644  ret = av_hwframe_map(ctx->vk_frame, frame, 0);
645  if (!ret)
646  return move_to_output_frame(ctx, frame);
647 
648  if (ret != AVERROR(ENOSYS))
649  av_log(NULL, AV_LOG_FATAL, "Map frame failed: %s\n", av_err2str(ret));
650  return ret;
651 }
652 
653 static int transfer_frame(VkRenderer *renderer, AVFrame *frame, int use_hw_frame)
654 {
655  RendererContext *ctx = (RendererContext *) renderer;
656  int ret;
657 
658  if (use_hw_frame && !check_hw_transfer(ctx, frame))
659  return AVERROR(ENOSYS);
660 
661  av_frame_unref(ctx->vk_frame);
662  if (use_hw_frame)
663  av_hwframe_get_buffer(ctx->hw_frame_ref, ctx->vk_frame, 0);
664  ret = av_hwframe_transfer_data(ctx->vk_frame, frame, 1);
665  if (!ret)
666  return move_to_output_frame(ctx, frame);
667 
668  if (ret != AVERROR(ENOSYS))
669  av_log(NULL, AV_LOG_FATAL, "Transfer frame failed: %s\n",
670  av_err2str(ret));
671  return ret;
672 }
673 
675 {
676  int ret;
677 
678  if (!frame->hw_frames_ctx)
679  return 0;
680 
681  if (frame->format == AV_PIX_FMT_VULKAN)
682  return 0;
683 
684  ret = create_hw_frame(renderer, frame);
685  if (ret < 0)
686  return ret;
687 
688  for (int use_hw = 1; use_hw >=0; use_hw--) {
689  ret = map_frame(renderer, frame, use_hw);
690  if (!ret)
691  return 0;
692  if (ret != AVERROR(ENOSYS))
693  return ret;
694 
695  ret = transfer_frame(renderer, frame, use_hw);
696  if (!ret)
697  return 0;
698  if (ret != AVERROR(ENOSYS))
699  return ret;
700  }
701 
702  return ret;
703 }
704 
705 static int display(VkRenderer *renderer, AVFrame *frame)
706 {
707  struct pl_swapchain_frame swap_frame = {0};
708  struct pl_frame pl_frame = {0};
709  struct pl_frame target = {0};
710  RendererContext *ctx = (RendererContext *) renderer;
711  int ret = 0;
712  struct pl_color_space hint = {0};
713 
715  if (ret < 0)
716  return ret;
717 
718  if (!pl_map_avframe_ex(ctx->placebo_vulkan->gpu, &pl_frame, pl_avframe_params(
719  .frame = frame,
720  .tex = ctx->tex))) {
721  av_log(NULL, AV_LOG_ERROR, "pl_map_avframe_ex failed\n");
722  return AVERROR_EXTERNAL;
723  }
724 
725  pl_color_space_from_avframe(&hint, frame);
726  pl_swapchain_colorspace_hint(ctx->swapchain, &hint);
727  if (!pl_swapchain_start_frame(ctx->swapchain, &swap_frame)) {
728  av_log(NULL, AV_LOG_ERROR, "start frame failed\n");
730  goto out;
731  }
732 
733  pl_frame_from_swapchain(&target, &swap_frame);
734  if (!pl_render_image(ctx->renderer, &pl_frame, &target,
735  &pl_render_default_params)) {
736  av_log(NULL, AV_LOG_ERROR, "pl_render_image failed\n");
738  goto out;
739  }
740 
741  if (!pl_swapchain_submit_frame(ctx->swapchain)) {
742  av_log(NULL, AV_LOG_ERROR, "pl_swapchain_submit_frame failed\n");
744  goto out;
745  }
746  pl_swapchain_swap_buffers(ctx->swapchain);
747 
748 out:
749  pl_unmap_avframe(ctx->placebo_vulkan->gpu, &pl_frame);
750  return ret;
751 }
752 
753 static int resize(VkRenderer *renderer, int width, int height)
754 {
755  RendererContext *ctx = (RendererContext *) renderer;
756 
757  if (!pl_swapchain_resize(ctx->swapchain, &width, &height))
758  return AVERROR_EXTERNAL;
759  return 0;
760 }
761 
762 static void destroy(VkRenderer *renderer)
763 {
764  RendererContext *ctx = (RendererContext *) renderer;
765  PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
766 
767  av_frame_free(&ctx->vk_frame);
768  av_freep(&ctx->transfer_formats);
769  av_hwframe_constraints_free(&ctx->constraints);
770  av_buffer_unref(&ctx->hw_frame_ref);
771 
772  if (ctx->placebo_vulkan) {
773  for (int i = 0; i < FF_ARRAY_ELEMS(ctx->tex); i++)
774  pl_tex_destroy(ctx->placebo_vulkan->gpu, &ctx->tex[i]);
775  pl_renderer_destroy(&ctx->renderer);
776  pl_swapchain_destroy(&ctx->swapchain);
777  pl_vulkan_destroy(&ctx->placebo_vulkan);
778  }
779 
780  if (ctx->vk_surface) {
781  vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)
782  ctx->get_proc_addr(ctx->inst, "vkDestroySurfaceKHR");
783  vkDestroySurfaceKHR(ctx->inst, ctx->vk_surface, NULL);
784  ctx->vk_surface = VK_NULL_HANDLE;
785  }
786 
787  av_buffer_unref(&ctx->hw_device_ref);
788  pl_vk_inst_destroy(&ctx->placebo_instance);
789 
790  pl_log_destroy(&ctx->vk_log);
791 }
792 
793 static const AVClass vulkan_renderer_class = {
794  .class_name = "Vulkan Renderer",
795  .item_name = av_default_item_name,
796  .version = LIBAVUTIL_VERSION_INT,
797 };
798 
800 {
801  RendererContext *ctx = av_mallocz(sizeof(*ctx));
803 
804  if (!ctx)
805  return NULL;
806 
807  renderer = &ctx->api;
808  renderer->class = &vulkan_renderer_class;
809  renderer->get_hw_dev = get_hw_dev;
810  renderer->create = create;
811  renderer->display = display;
812  renderer->resize = resize;
813  renderer->destroy = destroy;
814 
815  return renderer;
816 }
817 
818 #else
819 
821 {
822  return NULL;
823 }
824 
825 #endif
826 
828  AVDictionary *opt)
829 {
830  return renderer->create(renderer, window, opt);
831 }
832 
834 {
835  return renderer->get_hw_dev(renderer, dev);
836 }
837 
839 {
840  return renderer->display(renderer, frame);
841 }
842 
844 {
845  return renderer->resize(renderer, width, height);
846 }
847 
849 {
850  renderer->destroy(renderer);
851 }
AVHWDeviceContext::hwctx
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition: hwcontext.h:88
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:206
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:64
av_hwframe_ctx_init
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
Definition: hwcontext.c:337
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:427
AVVulkanDeviceContext::get_proc_addr
PFN_vkGetInstanceProcAddr get_proc_addr
Pointer to a vkGetInstanceProcAddr loading function.
Definition: hwcontext_vulkan.h:69
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:263
AVHWDeviceContext::user_opaque
void * user_opaque
Arbitrary user data, to be used e.g.
Definition: hwcontext.h:105
av_hwframe_map
int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags)
Map a hardware frame.
Definition: hwcontext.c:793
vk_renderer_create
int vk_renderer_create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
Definition: ffplay_renderer.c:827
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:1149
vk_renderer_destroy
void vk_renderer_destroy(VkRenderer *renderer)
Definition: ffplay_renderer.c:848
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:581
av_hwdevice_ctx_init
int av_hwdevice_ctx_init(AVBufferRef *ref)
Finalize the device context before use.
Definition: hwcontext.c:223
AV_PIX_FMT_VULKAN
@ AV_PIX_FMT_VULKAN
Vulkan hardware images.
Definition: pixfmt.h:379
av_vk_get_optional_device_extensions
const char ** av_vk_get_optional_device_extensions(int *count)
Returns an array of optional Vulkan device extensions that FFmpeg may use if enabled.
Definition: hwcontext_vulkan.c:710
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:444
window
static SDL_Window * window
Definition: ffplay.c:361
AVVulkanFramesContext
Allocated as AVHWFramesContext.hwctx, used to set pool-specific options.
Definition: hwcontext_vulkan.h:212
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:63
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:52
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:820
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:176
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:606
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:833
vk_renderer_display
int vk_renderer_display(VkRenderer *renderer, AVFrame *frame)
Definition: ffplay_renderer.c:838
renderer
static SDL_Renderer * renderer
Definition: ffplay.c:362
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
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:213
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:599
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:117
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:182
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:241
AVVulkanDeviceContext::nb_qf
int nb_qf
Definition: hwcontext_vulkan.h:193
AVVulkanDeviceContext::enabled_inst_extensions
const char *const * enabled_inst_extensions
Enabled instance extensions.
Definition: hwcontext_vulkan.h:103
AVVulkanDeviceContext::qf
AVVulkanDeviceQueueFamily qf[64]
Queue families used.
Definition: hwcontext_vulkan.h:192
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:235
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:177
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:523
vk_renderer_resize
int vk_renderer_resize(VkRenderer *renderer, int width, int height)
Definition: ffplay_renderer.c:843
av_frame_unref
void av_frame_unref(AVFrame *frame)
Unreference all the buffers referenced by frame and reset the frame fields.
Definition: frame.c:496
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:118
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:265
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:615
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:122
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:448
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:386
AVVulkanDeviceContext::enabled_dev_extensions
const char *const * enabled_dev_extensions
Enabled device extensions.
Definition: hwcontext_vulkan.h:116
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:104
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:506
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:415