49#define SDL_DISABLE_MMINTRIN_H 1
50#define SDL_DISABLE_IMMINTRIN_H 1
52#if __has_include(<SDL2/SDL.h>)
85#define SDL2_DEINTERLACE_IMPOSSIBLE_MSG_ID 0x327058e5
86#define MAGIC_SDL2 0x3cc234a1
88#define MOD_NAME "[SDL] "
151 {
'd',
"toggle deinterlace"},
152 {
'f',
"toggle fullscreen"},
156#define SDL_CHECK(cmd) do { int ret = cmd; if (ret < 0) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Error (%s): %s\n", #cmd, SDL_GetError());} } while(0)
173 SDL_UnlockTexture(texture);
185 pthread_mutex_lock(&s->
lock);
187 pthread_mutex_unlock(&s->
lock);
193 sym.mod &= ~(KMOD_NUM | KMOD_CAPS);
196 if (sym.sym == SDLK_LCTRL || sym.sym == SDLK_RCTRL) {
202 if (sym.mod & KMOD_CTRL) {
205 sym.mod &= ~KMOD_CTRL;
207 if (sym.mod & KMOD_SHIFT) {
210 sym.mod &= ~KMOD_SHIFT;
216 if ((sym.sym & SDLK_SCANCODE_MASK) == 0) {
218 sym.sym = toupper(sym.sym);
220 return ctrl ?
K_CTRL(sym.sym) : sym.sym;
223 case SDLK_RIGHT:
return K_RIGHT;
224 case SDLK_LEFT:
return K_LEFT;
225 case SDLK_DOWN:
return K_DOWN;
226 case SDLK_UP:
return K_UP;
227 case SDLK_PAGEDOWN:
return K_PGDOWN;
228 case SDLK_PAGEUP:
return K_PGUP;
242 SDL_SetWindowFullscreen(s->
window, s->
fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
260 if (!SDL_WaitEvent(&sdl_event)) {
265 pthread_mutex_lock(&s->
lock);
268 pthread_mutex_unlock(&s->
lock);
272 if (sdl_event.user.data1 == NULL) {
282 if (strstr(
msg->text,
"win-title ") ==
msg->text) {
283 SDL_SetWindowTitle(s->
window,
msg->text + strlen(
"win-title "));
285 }
else if (sscanf(
msg->text,
"%d", &
key) == 1) {
297 }
else if (sdl_event.type == SDL_KEYDOWN) {
298 log_msg(
LOG_LEVEL_VERBOSE,
MOD_NAME "Pressed key %s (scancode: %d, sym: %d, mod: %d)!\n", SDL_GetKeyName(sdl_event.key.keysym.sym), sdl_event.key.keysym.scancode, sdl_event.key.keysym.sym, sdl_event.key.keysym.mod);
304 }
else if (sym == -1) {
305 log_msg(
LOG_LEVEL_WARNING,
MOD_NAME "Cannot translate key %s (scancode: %d, sym: %d, mod: %d)!\n", SDL_GetKeyName(sdl_event.key.keysym.sym), sdl_event.key.keysym.scancode, sdl_event.key.keysym.sym, sdl_event.key.keysym.mod);
307 }
else if (sdl_event.type == SDL_WINDOWEVENT) {
309 if (s->
keep_aspect && sdl_event.window.event == SDL_WINDOWEVENT_RESIZED) {
310 double area = sdl_event.window.data1 * sdl_event.window.data2;
313 SDL_SetWindowSize(s->
window, width, height);
314 debug_msg(
"[SDL] resizing to %d x %d\n", width, height);
316 if (sdl_event.window.event == SDL_WINDOWEVENT_EXPOSED
317 || sdl_event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
324 }
else if (sdl_event.type == SDL_QUIT) {
331 for (
int i = 0; i < SDL_GetNumVideoDisplays(); ++i) {
335 const char *dname = SDL_GetDisplayName(i);
337 dname = SDL_GetError();
348 printf(
"SDL options:\n");
350 TRED(
"\t-d sdl")
"[[:fs|:d|:display=<didx>|:driver=<drv>|:novsync|:"
351 "renderer=<ridx>|:nodecorate|:size[=WxH]|:window_"
352 "flags=<f>|:keep-aspect]*|:help]")
"\n");
354 color_printf(
TBOLD(
"\td[force]")
" - deinterlace (force even for progresive video)\n");
359 for (
int i = 0; i < SDL_GetNumVideoDrivers(); ++i) {
363 color_printf(
TBOLD(
" keep-aspect")
" - keep window aspect ratio respecive to the video\n");
367 TBOLD(
" size")
" - window size in pixels with optional "
371 "[<W>x<H>][{+-}<X>[{+-}<Y>]]")
")\n");
373 for (
int i = 0; i < SDL_GetNumRenderDrivers(); ++i) {
374 SDL_RendererInfo renderer_info;
375 if (SDL_GetRenderDriverInfo(i, &renderer_info) == 0) {
381 printf(
"\nKeyboard shortcuts:\n");
386 SDL_GetVersion(&ver);
387 printf(
"\nSDL version (linked): %" PRIu8
".%" PRIu8
".%" PRIu8
"\n",
388 ver.major, ver.minor, ver.patch);
398 log_msg(
LOG_LEVEL_WARNING,
MOD_NAME "Receiving interlaced video but deinterlacing is off - suggesting toggling it on (press 'd' or pass cmdline option)\n");
402 "Displaying 10-bit RGB, which is experimental. In case of "
403 "problems use '--param decoder-use-codec='!R10k'` and please report.\n");
406 pthread_mutex_lock(&s->
lock);
410 event.user.data1 = &
desc;
417 pthread_mutex_unlock(&s->
lock);
426 {
I420, SDL_PIXELFORMAT_IYUV },
427 {
UYVY, SDL_PIXELFORMAT_UYVY },
428 {
YUYV, SDL_PIXELFORMAT_YUY2 },
429 {
RGB, SDL_PIXELFORMAT_RGB24 },
430 {
BGR, SDL_PIXELFORMAT_BGR24 },
431#if SDL_COMPILEDVERSION >= SDL_VERSIONNUM(2, 0, 5)
432 {
RGBA, SDL_PIXELFORMAT_RGBA32 },
434 {
RGBA, SDL_PIXELFORMAT_ABGR8888 },
436 {
R10k, SDL_PIXELFORMAT_ARGB2101010 },
446 return SDL_PIXELFORMAT_UNKNOWN;
452 for (
int i = 0; i <
count; ++i) {
468 SDL_DestroyTexture(texture);
505 SDL_DestroyWindow(s->
window);
509 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
511 const char *window_title =
"UltraGrid - SDL2 Display";
517 int x = s->
x == SDL_WINDOWPOS_UNDEFINED ? (int) SDL_WINDOWPOS_CENTERED_DISPLAY(s->
display_idx) : s->
x;
518 int y = s->
y == SDL_WINDOWPOS_UNDEFINED ? (int) SDL_WINDOWPOS_CENTERED_DISPLAY(s->
display_idx) : s->
y;
519 s->
window = SDL_CreateWindow(window_title,
x,
y, width, height,
flags);
533 SDL_RendererInfo renderer_info;
534 if (SDL_GetRendererInfo(s->
renderer, &renderer_info) == 0) {
538 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,
"linear");
553 MSG(WARNING,
"Cannot render splashscreeen!\n");
565 if (strstr(tok,
"fixed_size=") == tok) {
567 MOD_NAME "fixed_size with dimensions is "
568 " deprecated, use size"
571 tok = strchr(tok,
'=') + 1;
572 if (strpbrk(tok,
"x+-") == NULL) {
576 if (strchr(tok,
'x') != NULL) {
579 s->
fixed_h = atoi(strchr(tok,
'x') + 1);
581 tok = strpbrk(tok,
"+-");
584 tok = strpbrk(tok + 1,
"+-");
600 if (renderer == NULL) {
603 const int renderer_cnt = SDL_GetNumRenderDrivers();
606 const long number = strtol(renderer, &endptr, 0);
607 if (*endptr ==
'\0') {
608 if (number < 0 || number >= renderer_cnt) {
610 "Invalid renderer index - valid range [0,%d], got "
612 renderer_cnt, number);
618 for (
int i = 0; i < renderer_cnt; ++i) {
619 SDL_RendererInfo renderer_info;
620 if (SDL_GetRenderDriverInfo(i, &renderer_info) == 0) {
621 if (strcmp(renderer, renderer_info.name) == 0) {
627 MSG(ERROR,
"Unknown renderer name: %s\n", renderer);
637 const char *driver = NULL;
638 const char *renderer = NULL;
641 s->
x = s->
y = SDL_WINDOWPOS_UNDEFINED;
648 snprintf(buf,
sizeof buf,
"%s",
fmt);
650 char *tok, *save_ptr;
651 while((tok = strtok_r(tmp,
":", &save_ptr)))
653 if (strcmp(tok,
"d") == 0 || strcmp(tok,
"dforce") == 0) {
658 driver = strchr(tok,
'=') + 1;;
667 }
else if (
IS_PREFIX(tok,
"nodecorate")) {
669 }
else if (
IS_PREFIX(tok,
"keep-aspect")) {
677 }
else if (strcmp(tok,
"fixed_size") == 0) {
679 MOD_NAME "fixed_size deprecated, use size with "
684 if (sscanf(strchr(tok,
'=') + 1,
"%i", &f) != 1) {
691 tok = strchr(tok,
'=') + 1;
692 if (strchr(tok,
',') == NULL) {
698 s->
y = atoi(strchr(tok,
',') + 1);
701 "\"size=%+d%+d\" instead.\n",
719 if (driver == NULL && getenv(
"DISPLAY") == NULL &&
720 getenv(
"WAYLAND_DISPLAY") == NULL) {
721 MSG(NOTICE,
"X11/Wayland doesn't seem to be running (according "
722 "to env vars). Setting driver=KMSDRM.\n");
727 if (SDL_VideoInit(driver) < 0) {
728 MSG(ERROR,
"Unable to initialize SDL2 video: %s\n",
733 if (SDL_Init(SDL_INIT_EVENTS) < 0) {
734 MSG(ERROR,
"Unable to initialize SDL2 events: %s\n",
741 SDL_ShowCursor(SDL_DISABLE);
742 SDL_DisableScreenSaver();
750 pthread_mutex_init(&s->
lock, NULL);
788 SDL_DestroyWindow(s->
window);
791 SDL_ShowCursor(SDL_ENABLE);
794 SDL_QuitSubSystem(SDL_INIT_EVENTS);
799 pthread_mutex_destroy(&s->
lock);
813 pthread_mutex_lock(&s->
lock);
818 pthread_mutex_unlock(&s->
lock);
830 for (; i <
count / LOOP_ITEMS; ++i) {
831 for (
int j = 0; j < LOOP_ITEMS; ++j) {
832 uint32_t
val = htonl(*buf);
837 for (; i <
count; ++i) {
838 uint32_t
val = htonl(*buf);
852 event.user.data1 = NULL;
863 pthread_mutex_lock(&s->
lock);
865 assert(
frame != NULL);
867 pthread_mutex_unlock(&s->
lock);
871 if (timeout_ns > 0) {
887 pthread_mutex_unlock(&s->
lock);
890 pthread_mutex_unlock(&s->
lock);
893 event.user.data1 =
frame;
907 if (codecs_len <= *
len) {
937 strcpy((*available_cards)[0].
dev,
"");
938 strcpy((*available_cards)[0].
name,
"SDL2 SW display");
939 dev_add_option(&(*available_cards)[0],
"Deinterlace",
"Deinterlace",
"deinterlace",
":d",
true);
940 dev_add_option(&(*available_cards)[0],
"Fullscreen",
"Launch as fullscreen",
"fullscreen",
":fs",
true);
941 dev_add_option(&(*available_cards)[0],
"No decorate",
"Disable window decorations",
"nodecorate",
":nodecorate",
true);
942 dev_add_option(&(*available_cards)[0],
"Disable vsync",
"Disable vsync",
"novsync",
":novsync",
true);
944 (*available_cards)[0].repeatable =
true;
struct video_frame * frame
const char unsigned int flags
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
const char * get_codec_name(codec_t codec)
int color_printf(const char *format,...)
void log_msg(int level, const char *format,...)
void log_msg_once(int level, uint32_t id, const char *msg,...)
#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_WARNING
less severe errors
#define PITCH_DEFAULT
default pitch, i. e. respective linesize
#define VIDEO_DISPLAY_ABI_VERSION
#define DISPLAY_FLAG_AUDIO_ANY
struct video_frame * get_splashscreen()
@ 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 simple_linked_list_append(struct simple_linked_list *l, void *data)
int simple_linked_list_size(struct simple_linked_list *l)
struct simple_linked_list * simple_linked_list_init(void)
void simple_linked_list_destroy(struct simple_linked_list *l)
void * simple_linked_list_pop(struct simple_linked_list *l)
#define IS_KEY_PREFIX(tok, key)
expands to true value if <k> from tok in format <k>=<v> is prefix of key
#define IS_PREFIX(tok, key)
similar as above, but also key without a value is accepted (value optional)
@ STR_LEN
"standard" string length placeholder
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
static struct video_frame * display_sdl2_getf(void *state)
static bool display_sdl2_process_key(struct state_sdl2 *s, int64_t key)
static void cleanup_frames(struct state_sdl2 *s)
static bool set_size(struct state_sdl2 *s, const char *tok)
static const char * deint_to_string(enum deint val)
static const struct @93 pf_mapping[]
static const struct video_display_info display_sdl2_info
static void vf_sdl_texture_data_deleter(struct video_frame *buf)
static void display_sdl2_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
static int get_renderer_idx(const char *renderer)
static bool display_sdl2_get_property(void *state, int property, void *val, size_t *len)
static void sdl2_print_displays()
static void display_sdl2_new_message(struct module *mod)
static bool display_sdl2_reconfigure_real(void *state, struct video_desc desc)
static void display_sdl2_done(void *state)
static bool display_sdl2_putf(void *state, struct video_frame *frame, long long timeout_ns)
static void display_frame(struct state_sdl2 *s, struct video_frame *frame)
static int64_t translate_sdl_key_to_ug(SDL_Keysym sym)
static void r10k_to_sdl2(size_t count, uint32_t *buf)
static uint32_t get_ug_to_sdl_format(codec_t ug_codec)
static int get_supported_pfs(codec_t *codecs)
static const struct @92 keybindings[]
static bool recreate_textures(struct state_sdl2 *s, struct video_desc desc)
#define SDL2_DEINTERLACE_IMPOSSIBLE_MSG_ID
static bool display_sdl2_reconfigure(void *state, struct video_desc desc)
static void * display_sdl2_init(struct module *parent, const char *fmt, unsigned int flags)
static void loadSplashscreen(struct state_sdl2 *s)
static void display_sdl2_run(void *arg)
uint32_t priv_magic
optional magic number that can a module set
notify_t new_message
if set, notifies module that new message is in queue, receiver lock is hold during the call
Uint32 sdl_user_reconfigure_event
struct video_frame * last_frame
struct video_desc current_display_desc
pthread_cond_t reconfigured_cv
Uint32 sdl_user_new_frame_event
int reconfiguration_status
uint32_t window_flags
user requested flags
struct simple_linked_list * free_frame_queue
Uint32 sdl_user_new_message_event
pthread_cond_t frame_consumed_cv
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 (!)
void(* data_deleter)(struct video_frame *)
Struct video_frame represents a video frame and contains video description.
enum interlacing_t interlacing
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
struct video_frame_callbacks callbacks
void ts_add_nsec(struct timespec *ts, long long offset)
int timespec_get(struct timespec *ts, int base)
void dev_add_option(struct device_info *dev, const char *name, const char *desc, const char *key, const char *opt_str, bool is_boolean)
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
@ 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.
@ VIDEO_CODEC_COUNT
count of known video codecs (including VIDEO_CODEC_NONE)
@ RGBA
RGBA 8-bit, big-endian.
bool vc_deinterlace_ex(codec_t codec, unsigned char *src, size_t src_linesize, unsigned char *dst, size_t dst_pitch, size_t lines)
bool codec_is_planar(codec_t codec)