diff options
-rw-r--r-- | DOCS/man/en/ao.rst | 15 | ||||
-rw-r--r-- | DOCS/man/en/vo.rst | 5 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | audio/out/ao.c | 4 | ||||
-rw-r--r-- | audio/out/ao_sdl.c | 379 | ||||
-rwxr-xr-x | configure | 45 | ||||
-rw-r--r-- | video/out/vo.c | 4 | ||||
-rw-r--r-- | video/out/vo_sdl.c | 1004 |
8 files changed, 1458 insertions, 0 deletions
diff --git a/DOCS/man/en/ao.rst b/DOCS/man/en/ao.rst index ae0432784f..dc7b7fccb1 100644 --- a/DOCS/man/en/ao.rst +++ b/DOCS/man/en/ao.rst @@ -92,6 +92,21 @@ dsound (Windows only) Sets the device number to use. Playing a file with ``-v`` will show a list of available devices. +sdl + SDL 1.2+ audio output driver. Should work everywhere where SDL 1.2 builds, + but may require the SDL_AUDIODRIVER environment variable to be set + appropriately for your system. + + buflen=<length> + Sets the audio buffer length in seconds. Is used only approximately, + or even disaregarded entirely by the sound system. Playing a file with + ``-v`` will show the requested and obtained exact buffer size. A value + of 0 selects the sound system default. + + bufcnt=<count> + Sets the number of extra audio buffers in mpv. Usually needs not be + changed. + null Produces no audio output but maintains video playback speed. Use ``--no-audio`` for benchmarking. diff --git a/DOCS/man/en/vo.rst b/DOCS/man/en/vo.rst index 1bfe5fbf1f..cf92d97efa 100644 --- a/DOCS/man/en/vo.rst +++ b/DOCS/man/en/vo.rst @@ -660,6 +660,11 @@ opengl-old x11 X11/GLX +sdl + SDL 2.0+ Render video output driver, depending on system with or without + hardware acceleration. Should work everywhere where SDL 2.0 builds. For + tuning, refer to your copy of the file SDL_hints.h. + null Produces no video output. Useful for benchmarking. @@ -89,6 +89,8 @@ SOURCES-$(ALSA) += audio/out/ao_alsa.c SOURCES-$(APPLE_IR) += core/input/appleir.c SOURCES-$(APPLE_REMOTE) += core/input/ar.c SOURCES-$(CACA) += video/out/vo_caca.c +SOURCES-$(SDL) += audio/out/ao_sdl.c +SOURCES-$(SDL2) += video/out/vo_sdl.c SOURCES-$(COREAUDIO) += audio/out/ao_coreaudio.c SOURCES-$(COREVIDEO) += video/out/vo_corevideo.m SOURCES-$(DIRECT3D) += video/out/vo_direct3d.c \ diff --git a/audio/out/ao.c b/audio/out/ao.c index 915af93793..85e9548454 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -45,6 +45,7 @@ extern const struct ao_driver audio_out_pcm; extern const struct ao_driver audio_out_pss; extern const struct ao_driver audio_out_lavc; extern const struct ao_driver audio_out_portaudio; +extern const struct ao_driver audio_out_sdl; static const struct ao_driver * const audio_out_drivers[] = { // native: @@ -73,6 +74,9 @@ static const struct ao_driver * const audio_out_drivers[] = { #ifdef CONFIG_OPENAL &audio_out_openal, #endif +#ifdef CONFIG_SDL + &audio_out_sdl, +#endif &audio_out_null, // should not be auto-selected: &audio_out_pcm, diff --git a/audio/out/ao_sdl.c b/audio/out/ao_sdl.c new file mode 100644 index 0000000000..26d8717602 --- /dev/null +++ b/audio/out/ao_sdl.c @@ -0,0 +1,379 @@ +/* + * SDL2 audio output + * Copyright (C) 2012 Rudolf Polzer <divVerent@xonotic.org> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "audio/format.h" +#include "talloc.h" +#include "ao.h" +#include "core/mp_msg.h" +#include "core/subopt-helper.h" +#include "osdep/timer.h" + +#include <libavutil/fifo.h> +#include <SDL.h> + +// hack because SDL can't be asked about the current delay +#define ESTIMATE_DELAY + +struct priv +{ + AVFifoBuffer *buffer; + SDL_mutex *buffer_mutex; + SDL_cond *underrun_cond; + bool unpause; + bool paused; +#ifdef ESTIMATE_DELAY + unsigned int callback_time0; + unsigned int callback_time1; +#endif +}; + +static void audio_callback(void *userdata, Uint8 *stream, int len) +{ + struct ao *ao = userdata; + struct priv *priv = ao->priv; + + SDL_LockMutex(priv->buffer_mutex); + +#ifdef ESTIMATE_DELAY + priv->callback_time1 = priv->callback_time0; + priv->callback_time0 = GetTimer(); +#endif + + while (len > 0 && !priv->paused) { + int got = av_fifo_size(priv->buffer); + if (got > len) + got = len; + if (got > 0) { + av_fifo_generic_read(priv->buffer, stream, got, NULL); + len -= got; + stream += got; + } + if (len > 0) + SDL_CondWait(priv->underrun_cond, priv->buffer_mutex); + } + + SDL_UnlockMutex(priv->buffer_mutex); +} + +static void uninit(struct ao *ao, bool cut_audio) +{ + struct priv *priv = ao->priv; + if (!priv) + return; + + // abort the callback + priv->paused = 1; + + if (SDL_WasInit(SDL_INIT_AUDIO)) { + if (priv->buffer_mutex) + SDL_LockMutex(priv->buffer_mutex); + if (priv->underrun_cond) + SDL_CondSignal(priv->underrun_cond); + if (priv->buffer_mutex) + SDL_UnlockMutex(priv->buffer_mutex); + + // make sure the callback exits + SDL_LockAudio(); + + // close audio device + SDL_QuitSubSystem(SDL_INIT_AUDIO); + } + + // get rid of the mutex + if (priv->underrun_cond) + SDL_DestroyCond(priv->underrun_cond); + if (priv->buffer_mutex) + SDL_DestroyMutex(priv->buffer_mutex); + if (priv->buffer) + av_fifo_free(priv->buffer); + + talloc_free(ao->priv); + ao->priv = NULL; +} + +static unsigned int ceil_power_of_two(unsigned int x) +{ + int y = 1; + while (y < x) + y *= 2; + return y; +} + +static void print_help(void) { + mp_msg(MSGT_AO, MSGL_FATAL, + "\n-ao sdl commandline help:\n" + "Example: mpv -ao sdl:buflen=len\n" + "\nOptions:\n" + " buflen=len\n" + " Length of audio buffer in seconds\n" + " bufcnt=cnt\n" + " Count of extra audio buffers\n" + ); +} + +static int init(struct ao *ao, char *params) +{ + if (SDL_WasInit(SDL_INIT_AUDIO)) { + mp_msg(MSGT_AO, MSGL_ERR, "[sdl] already initialized\n"); + return -1; + } + + float buflen = 0; // use SDL default + float bufcnt = 2; + const opt_t subopts[] = { + {"buflen", OPT_ARG_FLOAT, &buflen, NULL}, + {"bufcnt", OPT_ARG_FLOAT, &bufcnt, NULL}, + {NULL} + }; + if (subopt_parse(params, subopts) != 0) { + print_help(); + return -1; + } + + struct priv *priv = talloc_zero(ao, struct priv); + ao->priv = priv; + + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { + if (!ao->probing) + mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_Init failed\n"); + uninit(ao, true); + return -1; + } + + SDL_AudioSpec desired, obtained; + + int bytes = 0; + switch (ao->format) { + case AF_FORMAT_U8: desired.format = AUDIO_U8; bytes = 1; break; + case AF_FORMAT_S8: desired.format = AUDIO_S8; bytes = 1; break; + case AF_FORMAT_U16_LE: desired.format = AUDIO_U16LSB; bytes = 2; break; + case AF_FORMAT_U16_BE: desired.format = AUDIO_U16MSB; bytes = 2; break; + default: + case AF_FORMAT_S16_LE: desired.format = AUDIO_S16LSB; bytes = 2; break; + case AF_FORMAT_S16_BE: desired.format = AUDIO_S16MSB; bytes = 2; break; +#ifdef AUDIO_S32LSB + case AF_FORMAT_S32_LE: desired.format = AUDIO_S32LSB; bytes = 4; break; +#endif +#ifdef AUDIO_S32MSB + case AF_FORMAT_S32_BE: desired.format = AUDIO_S32MSB; bytes = 4; break; +#endif +#ifdef AUDIO_F32LSB + case AF_FORMAT_FLOAT_LE: desired.format = AUDIO_F32LSB; bytes = 4; break; +#endif +#ifdef AUDIO_F32MSB + case AF_FORMAT_FLOAT_BE: desired.format = AUDIO_F32MSB; bytes = 4; break; +#endif + } + desired.freq = ao->samplerate; + desired.channels = ao->channels; + desired.samples = FFMIN(32768, ceil_power_of_two(ao->samplerate * buflen)); + desired.callback = audio_callback; + desired.userdata = ao; + + mp_msg(MSGT_AO, MSGL_V, "[sdl] requested format: %d Hz, %d channels, %x, " + "buffer size: %d samples\n", + (int) desired.freq, (int) desired.channels, + (int) desired.format, (int) desired.samples); + + obtained = desired; + if (SDL_OpenAudio(&desired, &obtained)) { + if (!ao->probing) + mp_msg(MSGT_AO, MSGL_ERR, "[sdl] could not open audio: %s\n", + SDL_GetError()); + uninit(ao, true); + return -1; + } + + mp_msg(MSGT_AO, MSGL_V, "[sdl] obtained format: %d Hz, %d channels, %x, " + "buffer size: %d samples\n", + (int) obtained.freq, (int) obtained.channels, + (int) obtained.format, (int) obtained.samples); + + switch (obtained.format) { + case AUDIO_U8: ao->format = AF_FORMAT_U8; bytes = 1; break; + case AUDIO_S8: ao->format = AF_FORMAT_S8; bytes = 1; break; + case AUDIO_S16LSB: ao->format = AF_FORMAT_S16_LE; bytes = 2; break; + case AUDIO_S16MSB: ao->format = AF_FORMAT_S16_BE; bytes = 2; break; + case AUDIO_U16LSB: ao->format = AF_FORMAT_U16_LE; bytes = 2; break; + case AUDIO_U16MSB: ao->format = AF_FORMAT_U16_BE; bytes = 2; break; +#ifdef AUDIO_S32LSB + case AUDIO_S32LSB: ao->format = AF_FORMAT_S32_LE; bytes = 4; break; +#endif +#ifdef AUDIO_S32MSB + case AUDIO_S32MSB: ao->format = AF_FORMAT_S32_BE; bytes = 4; break; +#endif +#ifdef AUDIO_F32LSB + case AUDIO_F32LSB: ao->format = AF_FORMAT_FLOAT_LE; bytes = 4; break; +#endif +#ifdef AUDIO_F32MSB + case AUDIO_F32MSB: ao->format = AF_FORMAT_FLOAT_BE; bytes = 4; break; +#endif + default: + if (!ao->probing) + mp_msg(MSGT_AO, MSGL_ERR, + "[sdl] could not find matching format\n"); + uninit(ao, true); + return -1; + } + + ao->samplerate = obtained.freq; + ao->channels = obtained.channels; + ao->bps = ao->channels * ao->samplerate * bytes; + ao->buffersize = obtained.size * bufcnt; + ao->outburst = obtained.size; + priv->buffer = av_fifo_alloc(ao->buffersize); + priv->buffer_mutex = SDL_CreateMutex(); + if (!priv->buffer_mutex) { + mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_CreateMutex failed\n"); + uninit(ao, true); + return -1; + } + priv->underrun_cond = SDL_CreateCond(); + if (!priv->underrun_cond) { + mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_CreateCond failed\n"); + uninit(ao, true); + return -1; + } + + priv->unpause = 1; + priv->paused = 1; + priv->callback_time0 = priv->callback_time1 = GetTimer(); + + return 1; +} + +static void reset(struct ao *ao) +{ + struct priv *priv = ao->priv; + SDL_LockMutex(priv->buffer_mutex); + av_fifo_reset(priv->buffer); + SDL_UnlockMutex(priv->buffer_mutex); +} + +static int get_space(struct ao *ao) +{ + struct priv *priv = ao->priv; + SDL_LockMutex(priv->buffer_mutex); + int space = av_fifo_space(priv->buffer); + SDL_UnlockMutex(priv->buffer_mutex); + return space; +} + +static void pause(struct ao *ao) +{ + struct priv *priv = ao->priv; + SDL_PauseAudio(SDL_TRUE); + priv->unpause = 0; + priv->paused = 1; + SDL_CondSignal(priv->underrun_cond); +} + +static void do_resume(struct ao *ao) +{ + struct priv *priv = ao->priv; + priv->paused = 0; + SDL_PauseAudio(SDL_FALSE); +} + +static void resume(struct ao *ao) +{ + struct priv *priv = ao->priv; + SDL_LockMutex(priv->buffer_mutex); + int free = av_fifo_space(priv->buffer); + SDL_UnlockMutex(priv->buffer_mutex); + if (free) + priv->unpause = 1; + else + do_resume(ao); +} + +static int play(struct ao *ao, void *data, int len, int flags) +{ + struct priv *priv = ao->priv; + SDL_LockMutex(priv->buffer_mutex); + int free = av_fifo_space(priv->buffer); + if (len > free) len = free; + av_fifo_generic_write(priv->buffer, data, len, NULL); + SDL_CondSignal(priv->underrun_cond); + SDL_UnlockMutex(priv->buffer_mutex); + if (priv->unpause) { + priv->unpause = 0; + do_resume(ao); + } + return len; +} + +static float get_delay(struct ao *ao) +{ + struct priv *priv = ao->priv; + SDL_LockMutex(priv->buffer_mutex); + int sz = av_fifo_size(priv->buffer); +#ifdef ESTIMATE_DELAY + unsigned int callback_time0 = priv->callback_time0; + unsigned int callback_time1 = priv->callback_time1; +#endif + SDL_UnlockMutex(priv->buffer_mutex); + + // delay component: our FIFO's length + float delay = sz / (float) ao->bps; + +#ifdef ESTIMATE_DELAY + // delay component: outstanding audio living in SDL + + unsigned int current_time = GetTimer(); + + // interval between callbacks + unsigned int callback_interval = callback_time0 - callback_time1; + unsigned int elapsed_interval = current_time - callback_time0; + if (elapsed_interval > callback_interval) + elapsed_interval = callback_interval; + + // delay subcomponent: remaining audio from the currently played buffer + unsigned int buffer_interval = callback_interval - elapsed_interval; + + // delay subcomponent: remaining audio from the next played buffer, as + // provided by the callback + buffer_interval += callback_interval; + + delay += buffer_interval / 1000000.0; +#endif + + return delay; +} + +const struct ao_driver audio_out_sdl = { + .is_new = true, + .info = &(const struct ao_info) { + "SDL Audio", + "sdl", + "Rudolf Polzer <divVerent@xonotic.org>", + "" + }, + .init = init, + .uninit = uninit, + .get_space = get_space, + .play = play, + .get_delay = get_delay, + .pause = pause, + .resume = resume, + .reset = reset, +}; @@ -348,6 +348,8 @@ Video output: --enable-gl enable OpenGL video output [autodetect] --enable-caca enable CACA video output [autodetect] --enable-direct3d enable Direct3D video output [autodetect] + --enable-sdl enable SDL audio output [autodetect] + --enable-sdl2 enable SDL 2.0+ audio and video output [autodetect] --enable-xv enable Xv video output [autodetect] --enable-vdpau enable VDPAU acceleration [autodetect] --enable-vm enable XF86VidMode support [autodetect] @@ -422,6 +424,8 @@ _xss=auto _xv=auto _vdpau=auto _direct3d=auto +_sdl=auto +_sdl2=auto _dsound=auto _nas=auto _mng=auto @@ -595,6 +599,10 @@ for ac_option do --disable-vdpau) _vdpau=no ;; --enable-direct3d) _direct3d=yes ;; --disable-direct3d) _direct3d=no ;; + --enable-sdl) _sdl=yes ;; + --disable-sdl) _sdl=no ;; + --enable-sdl2) _sdl2=yes ;; + --disable-sdl2) _sdl2=no ;; --enable-dsound) _dsound=yes ;; --disable-dsound) _dsound=no ;; --enable-mng) _mng=yes ;; @@ -2319,6 +2327,39 @@ echores "$_dsound" fi #if win32; then +echocheck "SDL 2.0" +if test "$_sdl2" = auto ; then + pkg_config_add 'sdl2' && _sdl2=yes +fi +if test "$_sdl2" = yes ; then + _sdl=yes # sdl2 implies sdl + def_sdl='#define CONFIG_SDL 1' + def_sdl2='#define CONFIG_SDL2 1' + vomodules="sdl $vomodules" + aomodules="sdl $aomodules" + echores "$_sdl2" +else + def_sdl2='#undef CONFIG_SDL2' + echores "$_sdl2" + echocheck "SDL" + if test "$_sdl" = auto ; then + pkg_config_add 'sdl' && _sdl=yes + fi + if test "$_sdl" = yes ; then + def_sdl='#define CONFIG_SDL 1' + novomodules="sdl $novomodules" + aomodules="sdl $aomodules" + else + def_sdl='#undef CONFIG_SDL' + novomodules="sdl $novomodules" + noaomodules="sdl $noaomodules" + fi + echores "$_sdl" +fi + + + + ######### # AUDIO # ######### @@ -3159,6 +3200,8 @@ COCOA = $_cocoa COREAUDIO = $_coreaudio COREVIDEO = $_corevideo DIRECT3D = $_direct3d +SDL = $_sdl +SDL2 = $_sdl2 DSOUND = $_dsound DVBIN = $_dvbin DVDREAD = $_dvdread @@ -3405,6 +3448,8 @@ $def_caca $def_corevideo $def_cocoa $def_direct3d +$def_sdl +$def_sdl2 $def_dsound $def_dvb $def_dvbin diff --git a/video/out/vo.c b/video/out/vo.c index 04bd9cbb1b..7ec9ccdaf7 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -83,6 +83,7 @@ extern struct vo_driver video_out_lavc; extern struct vo_driver video_out_caca; extern struct vo_driver video_out_direct3d; extern struct vo_driver video_out_direct3d_shaders; +extern struct vo_driver video_out_sdl; extern struct vo_driver video_out_corevideo; const struct vo_driver *video_out_drivers[] = @@ -110,6 +111,9 @@ const struct vo_driver *video_out_drivers[] = &video_out_opengl_old, #endif #endif +#ifdef CONFIG_SDL2 + &video_out_sdl, +#endif #ifdef CONFIG_X11 &video_out_x11, #endif diff --git a/video/out/vo_sdl.c b/video/out/vo_sdl.c new file mode 100644 index 0000000000..cef54af4cb --- /dev/null +++ b/video/out/vo_sdl.c @@ -0,0 +1,1004 @@ +/* + * video output driver for SDL2 + * + * by divVerent <divVerent@xonotic.org> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <assert.h> + +#include <SDL.h> + +#include "core/input/input.h" +#include "core/input/keycodes.h" +#include "core/mp_fifo.h" +#include "core/mp_msg.h" +#include "core/options.h" + +#include "osdep/timer.h" + +#include "sub/sub.h" + +#include "video/mp_image.h" +#include "video/vfcap.h" + +#include "aspect.h" +#include "config.h" +#include "vo.h" + +struct formatmap_entry { + Uint32 sdl; + unsigned int mpv; + int is_rgba; +}; +const struct formatmap_entry formats[] = { + {SDL_PIXELFORMAT_YV12, IMGFMT_YV12, 0}, + {SDL_PIXELFORMAT_IYUV, IMGFMT_IYUV, 0}, + {SDL_PIXELFORMAT_YUY2, IMGFMT_YUY2, 0}, + {SDL_PIXELFORMAT_UYVY, IMGFMT_UYVY, 0}, + {SDL_PIXELFORMAT_YVYU, IMGFMT_YVYU, 0}, +#if BYTE_ORDER == BIG_ENDIAN + {SDL_PIXELFORMAT_RGBX8888, IMGFMT_RGBA, 0}, // has no alpha -> bad for OSD + {SDL_PIXELFORMAT_BGRX8888, IMGFMT_BGRA, 0}, // has no alpha -> bad for OSD + {SDL_PIXELFORMAT_ARGB8888, IMGFMT_ARGB, 1}, + {SDL_PIXELFORMAT_RGBA8888, IMGFMT_RGBA, 1}, + {SDL_PIXELFORMAT_ABGR8888, IMGFMT_ABGR, 1}, + {SDL_PIXELFORMAT_BGRA8888, IMGFMT_BGRA, 1}, + {SDL_PIXELFORMAT_RGB24, IMGFMT_RGB24, 0}, + {SDL_PIXELFORMAT_BGR24, IMGFMT_BGR24, 0}, + {SDL_PIXELFORMAT_RGB888, IMGFMT_RGB24, 0}, + {SDL_PIXELFORMAT_BGR888, IMGFMT_BGR24, 0}, + {SDL_PIXELFORMAT_RGB565, IMGFMT_RGB16, 0}, + {SDL_PIXELFORMAT_BGR565, IMGFMT_BGR16, 0}, + {SDL_PIXELFORMAT_RGB555, IMGFMT_RGB15, 0}, + {SDL_PIXELFORMAT_BGR555, IMGFMT_BGR15, 0}, + {SDL_PIXELFORMAT_RGB444, IMGFMT_RGB12, 0} +#else + {SDL_PIXELFORMAT_RGBX8888, IMGFMT_ABGR, 0}, // has no alpha -> bad for OSD + {SDL_PIXELFORMAT_BGRX8888, IMGFMT_ARGB, 0}, // has no alpha -> bad for OSD + {SDL_PIXELFORMAT_ARGB8888, IMGFMT_BGRA, 1}, + {SDL_PIXELFORMAT_RGBA8888, IMGFMT_ABGR, 1}, + {SDL_PIXELFORMAT_ABGR8888, IMGFMT_RGBA, 1}, + {SDL_PIXELFORMAT_BGRA8888, IMGFMT_ARGB, 1}, + {SDL_PIXELFORMAT_RGB24, IMGFMT_RGB24, 0}, + {SDL_PIXELFORMAT_BGR24, IMGFMT_BGR24, 0}, + {SDL_PIXELFORMAT_RGB888, IMGFMT_BGR24, 0}, + {SDL_PIXELFORMAT_BGR888, IMGFMT_RGB24, 0}, + {SDL_PIXELFORMAT_RGB565, IMGFMT_BGR16, 0}, + {SDL_PIXELFORMAT_BGR565, IMGFMT_RGB16, 0}, + {SDL_PIXELFORMAT_RGB555, IMGFMT_BGR15, 0}, + {SDL_PIXELFORMAT_BGR555, IMGFMT_RGB15, 0}, + {SDL_PIXELFORMAT_RGB444, IMGFMT_BGR12, 0} +#endif +}; + +struct keymap_entry { + SDL_Keycode sdl; + int mpv; +}; +const struct keymap_entry keys[] = { + {SDLK_RETURN, KEY_ENTER}, + {SDLK_ESCAPE, KEY_ESC}, + {SDLK_BACKSPACE, KEY_BACKSPACE}, + {SDLK_TAB, KEY_TAB}, + {SDLK_PRINTSCREEN, KEY_PRINT}, + {SDLK_PAUSE, KEY_PAUSE}, + {SDLK_INSERT, KEY_INSERT}, + {SDLK_HOME, KEY_HOME}, + {SDLK_PAGEUP, KEY_PAGE_UP}, + {SDLK_DELETE, KEY_DELETE}, + {SDLK_END, KEY_END}, + {SDLK_PAGEDOWN, KEY_PAGE_DOWN}, + {SDLK_RIGHT, KEY_RIGHT}, + {SDLK_LEFT, KEY_LEFT}, + {SDLK_DOWN, KEY_DOWN}, + {SDLK_UP, KEY_UP}, + {SDLK_KP_ENTER, KEY_KPENTER}, + {SDLK_KP_1, KEY_KP1}, + {SDLK_KP_2, KEY_KP2}, + {SDLK_KP_3, KEY_KP3}, + {SDLK_KP_4, KEY_KP4}, + {SDLK_KP_5, KEY_KP5}, + {SDLK_KP_6, KEY_KP6}, + {SDLK_KP_7, KEY_KP7}, + {SDLK_KP_8, KEY_KP8}, + {SDLK_KP_9, KEY_KP9}, + {SDLK_KP_0, KEY_KP0}, + {SDLK_KP_PERIOD, KEY_KPDEC}, + {SDLK_POWER, KEY_POWER}, + {SDLK_MENU, KEY_MENU}, + {SDLK_STOP, KEY_STOP}, + {SDLK_MUTE, KEY_MUTE}, + {SDLK_VOLUMEUP, KEY_VOLUME_UP}, + {SDLK_VOLUMEDOWN, KEY_VOLUME_DOWN}, + {SDLK_KP_COMMA, KEY_KPDEC}, + {SDLK_AUDIONEXT, KEY_NEXT}, + {SDLK_AUDIOPREV, KEY_PREV}, + {SDLK_AUDIOSTOP, KEY_STOP}, + {SDLK_AUDIOPLAY, KEY_PLAY}, + {SDLK_AUDIOMUTE, KEY_MUTE}, + {SDLK_F1, KEY_F + 1}, + {SDLK_F2, KEY_F + 2}, + {SDLK_F3, KEY_F + 3}, + {SDLK_F4, KEY_F + 4}, + {SDLK_F5, KEY_F + 5}, + {SDLK_F6, KEY_F + 6}, + {SDLK_F7, KEY_F + 7}, + {SDLK_F8, KEY_F + 8}, + {SDLK_F9, KEY_F + 9}, + {SDLK_F10, KEY_F + 10}, + {SDLK_F11, KEY_F + 11}, + {SDLK_F12, KEY_F + 12}, + {SDLK_F13, KEY_F + 13}, + {SDLK_F14, KEY_F + 14}, + {SDLK_F15, KEY_F + 15}, + {SDLK_F16, KEY_F + 16}, + {SDLK_F17, KEY_F + 17}, + {SDLK_F18, KEY_F + 18}, + {SDLK_F19, KEY_F + 19}, + {SDLK_F20, KEY_F + 20}, + {SDLK_F21, KEY_F + 21}, + {SDLK_F22, KEY_F + 22}, + {SDLK_F23, KEY_F + 23}, + {SDLK_F24, KEY_F + 24} +}; + +struct priv { + bool reinit_renderer; + SDL_Window *window; + SDL_Renderer *renderer; + SDL_RendererInfo renderer_info; + SDL_Texture *tex; + mp_image_t texmpi; + mp_image_t *ssmpi; + struct mp_rect src_rect; + struct mp_rect dst_rect; + struct mp_osd_res osd_res; + int int_pause; + struct formatmap_entry osd_format; + struct osd_bitmap_surface { + int bitmap_id; + int bitmap_pos_id; + struct osd_target { + SDL_Rect source; + SDL_Rect dest; + SDL_Texture *tex; + SDL_Texture *tex2; + } *targets; + int num_targets; + int targets_size; + } osd_surfaces[MAX_OSD_PARTS]; + unsigned int mouse_timer; + int mouse_hidden; + int brightness, contrast; +}; + +static bool is_good_renderer(int n, const char *driver_name_wanted) +{ + SDL_RendererInfo ri; + if (SDL_GetRenderDriverInfo(n, &ri)) + return false; + + if (driver_name_wanted && driver_name_wanted[0]) + if (strcmp(driver_name_wanted, ri.name)) + return false; + + int i, j; + for (i = 0; i < ri.num_texture_formats; ++i) + for (j = 0; j < sizeof(formats) / sizeof(formats[0]); ++j) + if (ri.texture_formats[i] == formats[j].sdl) + if (formats[j].is_rgba) + return true; + + return false; +} + +static void destroy_renderer(struct vo *vo) +{ + struct priv *vc = vo->priv; + + // free ALL the textures + if (vc->tex) { + SDL_DestroyTexture(vc->tex); + vc->tex = NULL; + } + + int i, j; + for (i = 0; i < MAX_OSD_PARTS; ++i) { + for (j = 0; j < vc->osd_surfaces[i].targets_size; ++j) { + if (vc->osd_surfaces[i].targets[j].tex) { + SDL_DestroyTexture(vc->osd_surfaces[i].targets[j].tex); + vc->osd_surfaces[i].targets[j].tex = NULL; + } + if (vc->osd_surfaces[i].targets[j].tex2) { + SDL_DestroyTexture(vc->osd_surfaces[i].targets[j].tex2); + vc->osd_surfaces[i].targets[j].tex2 = NULL; + } + } + } + + if (vc->renderer) { + SDL_DestroyRenderer(vc->renderer); + vc->renderer = NULL; + } + + if (vc->window) { + SDL_DestroyWindow(vc->window); + vc->window = NULL; + } +} + +static int init_renderer(struct vo *vo, int w, int h) +{ + struct priv *vc = vo->priv; + + vc->window = SDL_CreateWindow("MPV", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + w, h, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN); + if (!vc->window) { + mp_msg(MSGT_VO, MSGL_ERR, "[sdl] SDL_CreateWindow failedd\n"); + destroy_renderer(vo); + return -1; + } + + int n = SDL_GetNumRenderDrivers(); + int i; + for (i = 0; i < n; ++i) + if (is_good_renderer(i, SDL_GetHint(SDL_HINT_RENDER_DRIVER))) + break; + if (i >= n) + for (i = 0; i < n; ++i) + if (is_good_renderer(i, NULL)) + break; + if (i >= n) { + mp_msg(MSGT_VO, MSGL_ERR, "[sdl] No supported renderer\n"); + destroy_renderer(vo); + return -1; + } + + vc->renderer = SDL_CreateRenderer(vc->window, i, 0); + if (!vc->renderer) { + mp_msg(MSGT_VO, MSGL_ERR, "[sdl] SDL_CreateRenderer failed\n"); + destroy_renderer(vo); + return -1; + } + + if (SDL_GetRendererInfo(vc->renderer, &vc->renderer_info)) { + mp_msg(MSGT_VO, MSGL_ERR, "[sdl] SDL_GetRendererInfo failed\n"); + destroy_renderer(vo); + return -1; + } + + mp_msg(MSGT_VO, MSGL_INFO, "[sdl] Using %s\n", vc->renderer_info.name); + + int j; + for (i = 0; i < vc->renderer_info.num_texture_formats; ++i) + for (j = 0; j < sizeof(formats) / sizeof(formats[0]); ++j) + if (vc->renderer_info.texture_formats[i] == formats[j].sdl) + if (formats[j].is_rgba) + vc->osd_format = formats[j]; + + return 0; +} + +static void resize(struct vo *vo, int w, int h) +{ + struct priv *vc = vo->priv; + vo->dwidth = w; + vo->dheight = h; + vo_get_src_dst_rects(vo, &vc->src_rect, &vc->dst_rect, + &vc->osd_res); + SDL_RenderSetLogicalSize(vc->renderer, w, h); + vo->want_redraw = true; +} + +static void force_resize(struct vo *vo) +{ + struct priv *vc = vo->priv; + int w, h; + SDL_GetWindowSize(vc->window, &w, &h); + resize(vo, w, h); +} + +static void check_resize(struct vo *vo) +{ + struct priv *vc = vo->priv; + int w, h; + SDL_GetWindowSize(vc->window, &w, &h); + if (vo->dwidth != w || vo->dheight != h) + resize(vo, w, h); +} + +static void set_fullscreen(struct vo *vo, int fs) +{ + struct priv *vc = vo->priv; + struct MPOpts *opts = vo->opts; + + if (opts->vidmode) + SDL_SetWindowDisplayMode(vc->window, NULL); + else { + SDL_DisplayMode mode; + if (!SDL_GetCurrentDisplayMode(SDL_GetWindowDisplay(vc->window), &mode)) + SDL_SetWindowDisplayMode(vc->window, &mode); + } + + if (SDL_SetWindowFullscreen(vc->window, fs)) { + mp_msg(MSGT_VO, MSGL_ERR, "[sdl] SDL_SetWindowFullscreen failed\n"); + return; + } + + // toggling fullscreen might recreate the window, so better guard for this + SDL_DisableScreenSaver(); + + vo_fs = fs; + force_resize(vo); +} + +static int config(struct vo *vo, uint32_t width, uint32_t height, + uint32_t d_width, uint32_t d_height, uint32_t flags, + uint32_t format) +{ + struct priv *vc = vo->priv; + + if (vc->reinit_renderer) { + destroy_renderer(vo); + vc->reinit_renderer = false; + } + + if (vc->window) + SDL_SetWindowSize(vc->window, d_width, d_height); + else { + if (init_renderer(vo, d_width, d_height) != 0) + return -1; + } + + if (vc->tex) + SDL_DestroyTexture(vc->tex); + Uint32 texfmt = SDL_PIXELFORMAT_UNKNOWN; + int i, j; + for (i = 0; i < vc->renderer_info.num_texture_formats; ++i) + for (j = 0; j < sizeof(formats) / sizeof(formats[0]); ++j) + if (vc->renderer_info.texture_formats[i] == formats[j].sdl) + if (format == formats[j].mpv) + texfmt = formats[j].sdl; + if (texfmt == SDL_PIXELFORMAT_UNKNOWN) { + mp_msg(MSGT_VO, MSGL_ERR, "[sdl] Invalid pixel format\n"); + return -1; + } + + vc->tex = SDL_CreateTexture(vc->renderer, texfmt, + SDL_TEXTUREACCESS_STREAMING, width, height); + if (!vc->tex) { + mp_msg(MSGT_VO, MSGL_ERR, "[sdl] Could not create a texture\n"); + return -1; + } + + mp_image_t *texmpi = &vc->texmpi; + texmpi->width = texmpi->w = width; + texmpi->height = texmpi->h = height; + mp_image_setfmt(texmpi, format); + switch (texmpi->num_planes) { + case 1: + case 3: + break; + default: + mp_msg(MSGT_VO, MSGL_ERR, "[sdl] Invalid plane count\n"); + SDL_DestroyTexture(vc->tex); + vc->tex = NULL; + return -1; + } + + vc->ssmpi = alloc_mpi(width, height, format); + + resize(vo, d_width, d_height); + + SDL_DisableScreenSaver(); + + if (flags & VOFLAG_FULLSCREEN) + set_fullscreen(vo, 1); + + SDL_SetWindowTitle(vc->window, vo_get_window_title(vo)); + + SDL_ShowWindow(vc->window); + + check_resize(vo); + + return 0; +} + +static void flip_page(struct vo *vo) +{ + struct priv *vc = vo->priv; + SDL_RenderPresent(vc->renderer); +} + +static void check_events(struct vo *vo) +{ + struct priv *vc = vo->priv; + struct MPOpts *opts = vo->opts; + SDL_Event ev; + + if (opts->cursor_autohide_delay >= 0) { + if (!vc->mouse_hidden && + (GetTimerMS() - vc->mouse_timer >= opts->cursor_autohide_delay)) { + SDL_ShowCursor(0); + vc->mouse_hidden = 1; + } + } else if (opts->cursor_autohide_delay == -1) { + if (vc->mouse_hidden) { + SDL_ShowCursor(1); + vc->mouse_hidden = 0; + } + } else if (opts->cursor_autohide_delay == -2) { + if (!vc->mouse_hidden) { + SDL_ShowCursor(0); + vc->mouse_hidden = 1; + } + } + |