69#define VK_USE_PLATFORM_WIN32_KHR
76#define SDL_DISABLE_MMINTRIN_H 1
77#define SDL_DISABLE_IMMINTRIN_H 1
81#include <SDL2/SDL_vulkan.h>
84#include <SDL2/SDL_syswm.h>
91#include <condition_variable>
100#include <type_traits>
101#include <unordered_map>
107namespace chrono = std::chrono;
108using namespace std::literals;
110constexpr int magic_vulkan_sdl2 = 0x3cc234a2;
111constexpr int initial_frame_count = 0;
112#define MOD_NAME "[VULKAN_SDL2] "
115void display_vulkan_new_message(
module*);
119 SDL_Window* window =
nullptr;
121 explicit WindowCallback(SDL_Window* window):
128 SDL_Vulkan_GetDrawableSize(window, &width, &height);
129 if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) {
133 return {
static_cast<uint32_t
>(width),
static_cast<uint32_t
>(height) };
143 std::vector<Mapping> mappings;
146 for(
auto& pair : mappings){
147 if(pair.image ==
nullptr){
153 mappings.emplace_back(Mapping{new_frame, image});
158 for(
auto& pair : mappings){
159 if(pair.frame ==
frame){
168 for(
auto& pair : mappings){
174struct state_vulkan_sdl2 {
177 Uint32 sdl_user_new_message_event;
179 chrono::steady_clock::time_point time{};
182 bool deinterlace =
false;
183 bool fullscreen =
false;
184 bool keep_aspect =
false;
189 SDL_Window* window =
nullptr;
193 WindowCallback* window_callback =
nullptr;
194 FrameMappings* frame_mappings =
new FrameMappings();
196 std::atomic<bool> should_exit =
false;
201 mod.priv_magic = magic_vulkan_sdl2;
202 mod.new_message = display_vulkan_new_message;
206 sdl_user_new_message_event = SDL_RegisterEvents(1);
207 assert(sdl_user_new_message_event !=
static_cast<Uint32
>(-1));
210 state_vulkan_sdl2(
const state_vulkan_sdl2& other) =
delete;
211 state_vulkan_sdl2& operator=(
const state_vulkan_sdl2& other) =
delete;
212 state_vulkan_sdl2(state_vulkan_sdl2&& other) =
delete;
213 state_vulkan_sdl2& operator=(state_vulkan_sdl2&& other) =
delete;
215 ~state_vulkan_sdl2() {
216 delete frame_mappings;
222static_assert(std::is_standard_layout_v<state_vulkan_sdl2>);
225constexpr std::array<std::pair<char, std::string_view>, 3> display_vulkan_keybindings{{
226 {
'd',
"toggle deinterlace"},
227 {
'f',
"toggle fullscreen"},
243 sym.mod &= ~(KMOD_NUM | KMOD_CAPS);
246 if (sym.sym == SDLK_LCTRL || sym.sym == SDLK_RCTRL) {
252 if (sym.mod & KMOD_CTRL) {
255 sym.mod &= ~KMOD_CTRL;
257 if (sym.mod & KMOD_SHIFT) {
260 sym.mod &= ~KMOD_SHIFT;
266 if ((sym.sym & SDLK_SCANCODE_MASK) == 0) {
268 sym.sym = toupper(sym.sym);
270 return ctrl ?
K_CTRL(sym.sym) : sym.sym;
273 case SDLK_RIGHT:
return K_RIGHT;
274 case SDLK_LEFT:
return K_LEFT;
275 case SDLK_DOWN:
return K_DOWN;
276 case SDLK_UP:
return K_UP;
277 case SDLK_PAGEDOWN:
return K_PGDOWN;
278 case SDLK_PAGEUP:
return K_PGUP;
283constexpr bool display_vulkan_process_key(state_vulkan_sdl2& s, int64_t
key) {
286 s.deinterlace = !s.deinterlace;
288 s.deinterlace ?
"ON" :
"OFF");
291 s.fullscreen = !s.fullscreen;
292 int mouse_x = 0, mouse_y = 0;
293 SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
294 SDL_SetWindowFullscreen(s.window, s.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
295 SDL_WarpMouseGlobal(mouse_x, mouse_y);
306void log_and_exit_uv(std::exception& e) {
311void process_user_messages(state_vulkan_sdl2& s) {
317 if (strstr(
msg->text,
"win-title ") ==
msg->text) {
318 SDL_SetWindowTitle(s.window,
msg->text + strlen(
"win-title "));
320 }
else if (sscanf(
msg->text,
"%d", &
key) == 1) {
321 if (!display_vulkan_process_key(s,
key)) {
333void process_events(state_vulkan_sdl2& s) {
335 while (SDL_PollEvent(&sdl_event)) {
336 if (sdl_event.type == s.sdl_user_new_message_event) {
337 process_user_messages(s);
338 }
else if (sdl_event.type == SDL_KEYDOWN && sdl_event.key.repeat == 0) {
339 auto& keysym = sdl_event.key.keysym;
341 MOD_NAME "Pressed key %s (scancode: %d, sym: %d, mod: %d)!\n",
342 SDL_GetKeyName(keysym.sym),
348 if (!display_vulkan_process_key(s, sym)) {
352 }
else if (sym == -1) {
354 MOD_NAME "Cannot translate key %s (scancode: %d, sym: %d, mod: %d)!\n",
355 SDL_GetKeyName(keysym.sym),
361 }
else if (sdl_event.type == SDL_WINDOWEVENT) {
363 if (s.keep_aspect && sdl_event.window.event == SDL_WINDOWEVENT_RESIZED) {
364 int old_width = s.width, old_height = s.height;
365 auto width = sdl_event.window.data1;
366 auto height = sdl_event.window.data2;
367 if (old_width == width) {
368 width = old_width * height / old_height;
369 }
else if (old_height == height) {
370 height = old_height * width / old_width;
372 auto area = int64_t{ width } * height;
373 width = sqrt(area * old_width / old_height);
374 height = sqrt(area * old_height / old_width);
378 SDL_SetWindowSize(s.window, width, height);
380 }
else if (sdl_event.window.event == SDL_WINDOWEVENT_EXPOSED
381 || sdl_event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
386 }
else if (sdl_event.type == SDL_QUIT) {
392void display_vulkan_run(
void*
state) {
393 auto* s =
static_cast<state_vulkan_sdl2*
>(
state);
394 assert(s->mod.priv_magic == magic_vulkan_sdl2);
396 s->time = chrono::steady_clock::now();
397 while (!s->should_exit) {
405 catch (std::exception& e) { log_and_exit_uv(e);
break; }
406 auto now = chrono::steady_clock::now();
407 double seconds = chrono::duration<double>{ now - s->time }.count();
409 double fps = s->frames / seconds;
411 static_cast<long long unsigned>(s->frames), seconds, fps);
416 SDL_HideWindow(s->window);
421 for (
int i = 0; i < SDL_GetNumVideoDisplays(); ++i) {
425 const char* dname = SDL_GetDisplayName(i);
426 if (dname ==
nullptr) {
427 dname = SDL_GetError();
429 col() <<
SBOLD(i) <<
" - " << dname;
436 std::vector<const char*>required_extensions{};
437 std::vector<std::pair<std::string, bool>> gpus{};
439 instance.init(required_extensions,
false);
442 catch (std::exception& e) { log_and_exit_uv(e);
return; }
444 col() <<
"\n\tVulkan GPUs:\n";
445 uint32_t counter = 0;
446 for (
const auto& gpu : gpus) {
448 col() <<
"\t\t " << counter++ <<
"\t - " << gpu.first;
449 col() << (gpu.second ?
"" :
" - unsuitable") <<
TERM_RESET <<
'\n';
454 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
456 auto print_drivers = []() {
457 for (
int i = 0; i < SDL_GetNumVideoDrivers(); ++i) {
458 col() << (i == 0 ?
"" :
", ") <<
SBOLD(SDL_GetVideoDriver(i));
463 col() <<
"VULKAN_SDL2 options:\n";
465 <<
"[:d|:fs|:keep-aspect|:nocursor|:nodecorate|:novsync|:tearing|:validation|:display=<dis_id>|"
466 ":driver=<drv>|:gpu=<gpu_id>|:pos=<x>,<y>|:size=<W>x<H>|:window_flags=<f>|:help])") <<
"\n";
468 col() << (
"\twhere:\n");
470 col() <<
SBOLD(
"\t d") <<
" - deinterlace\n";
471 col() <<
SBOLD(
"\t fs") <<
" - fullscreen\n";
473 col() <<
SBOLD(
"\t keep-aspect") <<
" - keep window aspect ratio respecive to the video\n";
474 col() <<
SBOLD(
"\t nocursor") <<
" - hides cursor\n";
475 col() <<
SBOLD(
"\t nodecorate") <<
" - disable window border\n";
476 col() <<
SBOLD(
"\t novsync") <<
" - disable vsync\n";
477 col() <<
SBOLD(
"\t tearing") <<
" - permits screen tearing\n";
478 col() <<
SBOLD(
"\t validation") <<
" - enable vulkan validation layers\n";
480 col() <<
SBOLD(
"\tdisplay=<dis_id>") <<
" - display index, available indices: ";
482 col() <<
SBOLD(
"\t driver=<drv>") <<
" - available drivers: ";
484 col() <<
SBOLD(
"\t gpu=<gpu_id>") <<
" - gpu index selected from the following list or keywords " <<
SBOLD(
"integrated")
" or " <<
SBOLD(
"discrete")
"\n";
485 col() <<
SBOLD(
"\t pos=<x>,<y>") <<
" - set window position\n";
486 col() <<
SBOLD(
"\t size=<W>x<H>") <<
" - set window size\n";
487 col() <<
SBOLD(
"\twindow_flags=<f>") <<
" - flags to be passed to SDL_CreateWindow (use prefix 0x for hex)\n";
491 col() <<
"\n\tKeyboard shortcuts:\n";
492 for (
auto& binding : display_vulkan_keybindings) {
493 col() <<
SBOLD(
"\t\t'" << binding.first) <<
"'\t - " << binding.second <<
"\n";
499struct CodecToVulkanFormat{
506const std::vector<CodecToVulkanFormat>& get_ug_to_vkd_format_mapping(state_vulkan_sdl2& s){
508 constexpr std::array<CodecToVulkanFormat, 10> format_mapping {{
509 {
RGBA, vkd::Format::RGBA8},
510 {
RGB, vkd::Format::RGB8},
511 {
UYVY, vkd::Format::UYVY8_422},
512 {
UYVY, vkd::Format::UYVY8_422_conv},
513 {
YUYV, vkd::Format::YUYV8_422},
514 {
Y216, vkd::Format::YUYV16_422},
515 {
Y416, vkd::Format::UYVA16_422_conv},
516 {
R10k, vkd::Format::RGB10A2_conv},
517 {
RG48, vkd::Format::RGB16},
520 static std::vector<CodecToVulkanFormat> supported_formats_mapping{};
521 if(!supported_formats_mapping.empty()){
522 return supported_formats_mapping;
525 for(
auto& pair: format_mapping){
527 if (!supported_formats_mapping.empty() && supported_formats_mapping.back().ug_codec == pair.ug_codec){
531 if (format_supported) {
532 supported_formats_mapping.push_back(pair);
535 catch (std::exception& e) { log_and_exit_uv(e); }
537 return supported_formats_mapping;
542 auto&
mapping = get_ug_to_vkd_format_mapping(s);
545 [searched_codec](
auto pair) { return pair.ug_codec == searched_codec; });
547 return { ultragrid_desc.
width, ultragrid_desc.
height, iter->vulkan_format };
552 auto* s =
static_cast<state_vulkan_sdl2*
>(
state);
553 assert(s->mod.priv_magic == magic_vulkan_sdl2);
556 s->current_desc =
desc;
559 MOD_NAME "Video description is not supported - frames are too big or format isn't supported.");
570void draw_splashscreen(state_vulkan_sdl2& s) {
575 catch (std::exception& e) { log_and_exit_uv(e);
return; }
591 catch (std::exception& e) { log_and_exit_uv(e); }
595constexpr bool starts_with(std::string_view str, std::string_view match){
596 return str.rfind(match, 0) == 0;
599struct command_line_arguments {
603 bool tearing_permitted =
false;
604 bool validation =
false;
607 int x = SDL_WINDOWPOS_UNDEFINED;
608 int y = SDL_WINDOWPOS_UNDEFINED;
610 uint32_t window_flags = 0 ;
612 std::string driver{};
615bool parse_command_line_arguments(command_line_arguments& args, state_vulkan_sdl2& s, std::string_view arguments_sv) {
616 constexpr auto npos = std::string_view::npos;
617 constexpr std::string_view wrong_option_msg =
MOD_NAME "Wrong option: ";
620 auto next_token = [](std::string_view &
options) -> std::string_view {
621 auto colon_pos =
options.find(
':');
622 auto token =
options.substr(0, colon_pos);
623 options.remove_prefix(colon_pos == npos ?
options.size() : colon_pos + 1);
627 while (!arguments_sv.empty())
try {
628 const std::string_view token = next_token(arguments_sv);
634 auto svtoi = [token](std::string_view str) ->
int {
635 std::size_t endpos = 0;
636 int result = std::stoi(std::string(str), &endpos, 0);
637 if (endpos != str.size()) {
638 throw std::runtime_error{ std::string(token) };
644 s.deinterlace =
true;
645 }
else if (token ==
"fs") {
647 }
else if (token ==
"keep-aspect") {
648 s.keep_aspect =
true;
649 }
else if (token ==
"nocursor") {
651 }
else if (token ==
"nodecorate") {
652 args.window_flags |= SDL_WINDOW_BORDERLESS;
653 }
else if (token ==
"novsync") {
655 }
else if (token ==
"tearing") {
656 args.tearing_permitted =
true;
657 }
else if (token ==
"validation") {
658 args.validation =
true;
659 }
else if (starts_with(token,
"display=")) {
660 constexpr auto pos =
"display="sv.size();
661 args.display_idx = svtoi(token.substr(pos));
662 }
else if (starts_with(token,
"driver=")) {
663 constexpr auto pos =
"driver="sv.size();
664 args.driver = std::string{ token.substr(pos) };
665 }
else if (starts_with(token,
"gpu=")) {
666 if (token ==
"integrated"sv) {
668 }
else if (token ==
"discrete"sv) {
671 constexpr auto pos =
"gpu="sv.size();
672 args.gpu_idx = svtoi(token.substr(pos));
674 }
else if (starts_with(token,
"pos=")) {
676 tok.remove_prefix(
"pos="sv.size());
677 auto comma = tok.find(
',');
683 args.x = svtoi(tok.substr(0, comma));
684 args.y = svtoi(tok.substr(comma + 1));
685 }
else if (starts_with(token,
"size=")) {
687 tok.remove_prefix(
"size="sv.size());
688 auto x = tok.find(
'x');
694 s.width = svtoi(tok.substr(0, x));
695 s.height = svtoi(tok.substr(x + 1));
696 }
else if (starts_with(token,
"window_flags=")) {
697 constexpr auto pos =
"window_flags="sv.size();
698 int flags = svtoi(token.substr(pos));
699 args.window_flags |=
flags;
700 }
else if (token ==
"help") {
708 catch (std::exception& e) {
715void vulkan_display_log(
vkd::LogLevel vkd_log_level, std::string_view sv){
719 switch(vkd_log_level){
737 auto s = std::make_unique<state_vulkan_sdl2>(
parent);
739 command_line_arguments args{};
741 if (!parse_command_line_arguments(args, *s,
fmt)) {
749 int ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
755 ret = SDL_VideoInit(args.driver.empty() ?
nullptr : args.driver.c_str());
762 SDL_ShowCursor(args.cursor);
763 SDL_DisableScreenSaver();
765 for (
auto& binding : display_vulkan_keybindings) {
766 std::string
msg = std::to_string(
static_cast<int>(binding.first));
772 const char* window_title =
"UltraGrid - Vulkan Display";
777 int x = (args.x == SDL_WINDOWPOS_UNDEFINED ? SDL_WINDOWPOS_CENTERED_DISPLAY(args.display_idx) : args.x);
778 int y = (args.y == SDL_WINDOWPOS_UNDEFINED ? SDL_WINDOWPOS_CENTERED_DISPLAY(args.display_idx) : args.y);
779 if(s->width == -1 && s->height == -1){
780 SDL_DisplayMode mode;
781 int display_index = 0;
782 if(SDL_GetDesktopDisplayMode(display_index, &mode)){
783 log_msg(LOG_LEVEL_ERROR, MOD_NAME
"Failed to get display size\n");
792 if (s->width == 0) s->width = 960;
793 if (s->height == 0) s->height = 540;
795 int window_flags = args.window_flags | SDL_WINDOW_RESIZABLE | SDL_WINDOW_VULKAN | SDL_WINDOW_ALLOW_HIGHDPI;
797 window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
800 s->window = SDL_CreateWindow(window_title, x, y, s->width, s->height, window_flags);
802 log_msg(LOG_LEVEL_ERROR, MOD_NAME
"Unable to create window : %s\n", SDL_GetError());
805 s->window_callback =
new WindowCallback(s->window);
807 uint32_t extension_count = 0;
808 SDL_Vulkan_GetInstanceExtensions(s->window, &extension_count,
nullptr);
809 std::vector<const char*> required_extensions(extension_count);
810 SDL_Vulkan_GetInstanceExtensions(s->window, &extension_count, required_extensions.data());
811 assert(extension_count > 0);
814 std::string path_to_shaders{ path ? path :
".." };
815 path_to_shaders = path_to_shaders +
"/share/ultragrid/vulkan_shaders";
819 instance.init(required_extensions, args.validation, vulkan_display_log);
822 SDL_SysWMinfo wmInfo{};
823 SDL_VERSION(&wmInfo.version);
824 SDL_GetWindowWMInfo(s->window, &wmInfo);
825 HWND hwnd = wmInfo.info.win.window;
826 HINSTANCE hinst = wmInfo.info.win.hinstance;
827 vk::Win32SurfaceCreateInfoKHR create_info{};
831 vk::SurfaceKHR surface =
nullptr;
832 if (
instance.get_instance().createWin32SurfaceKHR(&create_info,
nullptr, &surface) != vk::Result::eSuccess) {
833 throw std::runtime_error(
"Surface cannot be created.");
836 VkSurfaceKHR surface{};
837 if (!SDL_Vulkan_CreateSurface(s->window, (VkInstance)
instance.get_instance(), &surface)) {
838 std::cout << SDL_GetError() << std::endl;
839 throw std::runtime_error(
"SDL cannot create surface.");
843 s->vulkan->
init(std::move(
instance), vk::SurfaceKHR(surface),
844 initial_frame_count, *s->window_callback,
845 args.gpu_idx, std::move(path_to_shaders),
846 args.vsync, args.tearing_permitted);
849 catch (std::exception& e) { log_and_exit_uv(e);
return nullptr; }
851 draw_splashscreen(*s);
852 return static_cast<void*
>(s.release());
855void display_vulkan_done(
void*
state) {
856 auto* s =
static_cast<state_vulkan_sdl2*
>(
state);
857 assert(s->mod.priv_magic == magic_vulkan_sdl2);
859 SDL_ShowCursor(SDL_ENABLE);
864 catch (std::exception& e) { log_and_exit_uv(e); }
868 delete s->window_callback;
869 s->window_callback =
nullptr;
872 SDL_DestroyWindow(s->window);
882 auto* s =
static_cast<state_vulkan_sdl2*
>(
state);
883 assert(s->mod.priv_magic == magic_vulkan_sdl2);
885 const auto&
desc = s->current_desc;
890 catch (std::exception& e) { log_and_exit_uv(e);
return nullptr; }
900 auto* s =
static_cast<state_vulkan_sdl2*
>(
state);
901 assert(s->mod.priv_magic == magic_vulkan_sdl2);
904 s->should_exit =
true;
914 catch (std::exception& e) { log_and_exit_uv(e);
return false; }
918 if (s->deinterlace) {
929 catch (std::exception& e) { log_and_exit_uv(e);
return 1; }
934 auto* s =
static_cast<state_vulkan_sdl2*
>(
state);
935 assert(s->mod.priv_magic == magic_vulkan_sdl2);
939 auto&
mapping = get_ug_to_vkd_format_mapping(*s);
940 std::vector<codec_t>
codecs{};
949 if (codecs_len > *
len) {
957 if (
sizeof(
int) > *
len) {
963 const auto&
desc = s->current_desc;
964 assert(s->current_desc.
width != 0);
968 memcpy(
val, &value,
sizeof(value));
971 catch (std::exception& e) { log_and_exit_uv(e);
return false; }
980void display_vulkan_new_message(
module* mod) {
981 auto s =
reinterpret_cast<state_vulkan_sdl2*
>(mod);
982 assert(s->mod.priv_magic == magic_vulkan_sdl2);
985 event.type = s->sdl_user_new_message_event;
986 SDL_PushEvent(&event);
990static void display_vulkan_put_audio_frame([[maybe_unused]]
void*
state, [[maybe_unused]]
const struct audio_frame*
frame)
995bool display_vulkan_reconfigure_audio([[maybe_unused]]
void*
state, [[maybe_unused]]
int quant_samples,
1005 auto&
info = **available_cards;
1006 strcpy(
info.dev,
"");
1007 strcpy(
info.name,
"VULKAN_SDL2 SW display");
1008 info.repeatable =
true;
1010 display_vulkan_init,
1012 display_vulkan_done,
1013 display_vulkan_getf,
1014 display_vulkan_putf,
1015 display_vulkan_reconfigure,
1016 display_vulkan_get_property,
1017 display_vulkan_put_audio_frame,
1018 display_vulkan_reconfigure_audio,
struct video_frame * frame
const char unsigned int flags
int void(** deleter)(void *))
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
void vf_free(struct video_frame *buf)
Frees video_frame structure.
const char * get_codec_name(codec_t codec)
static const struct codec_param mapping[AC_COUNT]
vk::Extent2D get_size() const
void set_process_function(std::function< void(TransferImage &image)> function)
unsigned char * get_memory_ptr()
vk::DeviceSize get_row_pitch() const
void discard_image(TransferImage image)
bool queue_image(TransferImage img, bool discardable)
bool is_image_description_supported(ImageDescription description)
void init(VulkanInstance &&instance, vk::SurfaceKHR surface, uint32_t transfer_image_count, WindowChangedCallback &window, uint32_t gpu_index=no_gpu_selected, std::string path_to_shaders="./shaders", bool vsync=true, bool tearing_permitted=false)
bool display_queued_image()
TransferImage acquire_image(ImageDescription description)
void window_parameters_changed(WindowParameters new_parameters)
Hint to vulkan display that some window parameters spicified in struct WindowParameters changed....
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_VERBOSE
#define debug_msg(...)
#define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, "[pid/%d +%d %s] ", getpid(), LINE,...
#define LOG_LEVEL_FATAL
errors that prevent UG run
#define LOG_LEVEL_WARNING
less severe errors
const char * get_install_root(void)
#define VIDEO_DISPLAY_ABI_VERSION
#define DISPLAY_FLAG_AUDIO_ANY
#define DISPLAY_NO_GENERIC_FPS_INDICATOR
@ DISPLAY_PROPERTY_BUF_PITCH
requested framebuffer pitch - int (bytes), may be PITCH_DEFAULT
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
void module_init_default(struct module *module_data)
void module_register(struct module *module_data, struct module *parent)
void module_done(struct module *module_data)
struct module * get_root_module(struct module *node)
const char * get_commandline_param(const char *key)
bool keycontrol_register_key(struct module *receiver_mod, int64_t key, const char *message, const char *description)
Registers callback message for given key.
void keycontrol_send_key(struct module *root, int64_t key)
@ LIBRARY_CLASS_VIDEO_DISPLAY
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
void free_message(struct message *msg, struct response *r)
struct response * new_response(int status, const char *text)
struct message * check_message(struct module *mod)
#define RESPONSE_BAD_REQUEST
constexpr uint32_t gpu_integrated
constexpr uint32_t gpu_discrete
constexpr uint32_t no_gpu_selected
static void sdl2_print_displays()
static int64_t translate_sdl_key_to_ug(SDL_Keysym sym)
#define HEADER_PIXEL(data, pixel)
static const char *const splash_data
static const unsigned int splash_width
static const unsigned int splash_height
struct used to comunicate with audio capture/playback device
char * data
Raw tile data. Pointer must be at least 4B aligned.
unsigned int data_len
length of the data
enum interlacing_t interlacing
unsigned int width
width of every tile (!)
unsigned int height
height of every tile (!)
Struct video_frame represents a video frame and contains video description.
enum interlacing_t interlacing
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
@ YUYV
YCbCr 422 8-bit - Y0 Cb Y1 Cr.
@ R10k
RGB 10-bit packed - RGBX, big-endian (2 bit padding)
@ RGB
RGB 8-bit (packed into 24-bit word)
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
@ RG48
16-bit RGB little-endian (RlRhGl...)
@ Y416
interleaved little-endian YCbCr 4444 16-bit - UYVA
@ Y216
YCbCr 422 16-bit little-endian - Y0 Cb Y1 Cr.
@ RGBA
RGBA 8-bit, big-endian.
This is an umbrella header for video functions.
void vc_deinterlace(unsigned char *src, long src_linesize, int lines)
Deinterlaces framebuffer.
static class worker_pool instance