summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/en/ao.rst15
-rw-r--r--DOCS/man/en/vo.rst5
-rw-r--r--Makefile2
-rw-r--r--audio/out/ao.c4
-rw-r--r--audio/out/ao_sdl.c379
-rwxr-xr-xconfigure45
-rw-r--r--video/out/vo.c4
-rw-r--r--video/out/vo_sdl.c1004
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.
diff --git a/Makefile b/Makefile
index 9999145c83..738d6bd20e 100644
--- a/Makefile
+++ b/Makefile
@@ -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,
+};
diff --git a/configure b/configure
index 02d8f070be..f106876cd4 100755
--- a/configure
+++ b/configure
@@ -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;
+ }
+ }
+
+ while (SDL_PollEvent(&ev)) {