2012-12-28 07:07:14 +00:00
|
|
|
/*
|
2012-12-28 10:41:30 +00:00
|
|
|
* video output driver for SDL 2.0+
|
2015-04-13 07:36:54 +00:00
|
|
|
*
|
2012-12-28 10:41:30 +00:00
|
|
|
* Copyright (C) 2012 Rudolf Polzer <divVerent@xonotic.org>
|
2012-12-28 07:07:14 +00:00
|
|
|
*
|
2012-12-28 10:41:30 +00:00
|
|
|
* This file is part of mpv.
|
2012-12-28 07:07:14 +00:00
|
|
|
*
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2012-12-28 07:07:14 +00:00
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is distributed in the hope that it will be useful,
|
2012-12-28 07:07:14 +00:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2012-12-28 07:07:14 +00:00
|
|
|
*
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2012-12-28 07:07:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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>
|
|
|
|
|
2013-12-17 00:23:09 +00:00
|
|
|
#include "input/input.h"
|
|
|
|
#include "input/keycodes.h"
|
|
|
|
#include "input/input.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/msg.h"
|
2013-12-17 01:02:25 +00:00
|
|
|
#include "options/options.h"
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
#include "osdep/timer.h"
|
|
|
|
|
2013-11-24 11:58:06 +00:00
|
|
|
#include "sub/osd.h"
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
#include "video/mp_image.h"
|
|
|
|
|
2014-05-06 18:35:19 +00:00
|
|
|
#include "win_state.h"
|
2012-12-28 07:07:14 +00:00
|
|
|
#include "config.h"
|
|
|
|
#include "vo.h"
|
|
|
|
|
|
|
|
struct formatmap_entry {
|
|
|
|
Uint32 sdl;
|
|
|
|
unsigned int mpv;
|
|
|
|
int is_rgba;
|
|
|
|
};
|
|
|
|
const struct formatmap_entry formats[] = {
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
{SDL_PIXELFORMAT_YV12, IMGFMT_420P, 0},
|
|
|
|
{SDL_PIXELFORMAT_IYUV, IMGFMT_420P, 0},
|
|
|
|
{SDL_PIXELFORMAT_YUY2, IMGFMT_YUYV, 0},
|
2012-12-28 07:07:14 +00:00
|
|
|
{SDL_PIXELFORMAT_UYVY, IMGFMT_UYVY, 0},
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
//{SDL_PIXELFORMAT_YVYU, IMGFMT_YVYU, 0},
|
2012-12-28 07:07:14 +00:00
|
|
|
#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
|
2012-12-28 13:27:03 +00:00
|
|
|
{SDL_PIXELFORMAT_ARGB8888, IMGFMT_ARGB, 1}, // matches SUBBITMAP_RGBA
|
2012-12-28 07:07:14 +00:00
|
|
|
{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},
|
2014-06-14 08:03:04 +00:00
|
|
|
{SDL_PIXELFORMAT_BGR565, IMGFMT_RGB565, 0},
|
2012-12-28 07:07:14 +00:00
|
|
|
#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
|
2012-12-28 13:27:03 +00:00
|
|
|
{SDL_PIXELFORMAT_ARGB8888, IMGFMT_BGRA, 1}, // matches SUBBITMAP_RGBA
|
2012-12-28 07:07:14 +00:00
|
|
|
{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},
|
2014-06-14 08:03:04 +00:00
|
|
|
{SDL_PIXELFORMAT_RGB565, IMGFMT_RGB565, 0},
|
2012-12-28 07:07:14 +00:00
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
struct keymap_entry {
|
|
|
|
SDL_Keycode sdl;
|
|
|
|
int mpv;
|
|
|
|
};
|
|
|
|
const struct keymap_entry keys[] = {
|
2013-02-12 00:34:04 +00:00
|
|
|
{SDLK_RETURN, MP_KEY_ENTER},
|
|
|
|
{SDLK_ESCAPE, MP_KEY_ESC},
|
|
|
|
{SDLK_BACKSPACE, MP_KEY_BACKSPACE},
|
|
|
|
{SDLK_TAB, MP_KEY_TAB},
|
|
|
|
{SDLK_PRINTSCREEN, MP_KEY_PRINT},
|
|
|
|
{SDLK_PAUSE, MP_KEY_PAUSE},
|
|
|
|
{SDLK_INSERT, MP_KEY_INSERT},
|
|
|
|
{SDLK_HOME, MP_KEY_HOME},
|
|
|
|
{SDLK_PAGEUP, MP_KEY_PAGE_UP},
|
|
|
|
{SDLK_DELETE, MP_KEY_DELETE},
|
|
|
|
{SDLK_END, MP_KEY_END},
|
|
|
|
{SDLK_PAGEDOWN, MP_KEY_PAGE_DOWN},
|
|
|
|
{SDLK_RIGHT, MP_KEY_RIGHT},
|
|
|
|
{SDLK_LEFT, MP_KEY_LEFT},
|
|
|
|
{SDLK_DOWN, MP_KEY_DOWN},
|
|
|
|
{SDLK_UP, MP_KEY_UP},
|
|
|
|
{SDLK_KP_ENTER, MP_KEY_KPENTER},
|
|
|
|
{SDLK_KP_1, MP_KEY_KP1},
|
|
|
|
{SDLK_KP_2, MP_KEY_KP2},
|
|
|
|
{SDLK_KP_3, MP_KEY_KP3},
|
|
|
|
{SDLK_KP_4, MP_KEY_KP4},
|
|
|
|
{SDLK_KP_5, MP_KEY_KP5},
|
|
|
|
{SDLK_KP_6, MP_KEY_KP6},
|
|
|
|
{SDLK_KP_7, MP_KEY_KP7},
|
|
|
|
{SDLK_KP_8, MP_KEY_KP8},
|
|
|
|
{SDLK_KP_9, MP_KEY_KP9},
|
|
|
|
{SDLK_KP_0, MP_KEY_KP0},
|
|
|
|
{SDLK_KP_PERIOD, MP_KEY_KPDEC},
|
|
|
|
{SDLK_POWER, MP_KEY_POWER},
|
|
|
|
{SDLK_MENU, MP_KEY_MENU},
|
|
|
|
{SDLK_STOP, MP_KEY_STOP},
|
|
|
|
{SDLK_MUTE, MP_KEY_MUTE},
|
|
|
|
{SDLK_VOLUMEUP, MP_KEY_VOLUME_UP},
|
|
|
|
{SDLK_VOLUMEDOWN, MP_KEY_VOLUME_DOWN},
|
|
|
|
{SDLK_KP_COMMA, MP_KEY_KPDEC},
|
|
|
|
{SDLK_AUDIONEXT, MP_KEY_NEXT},
|
|
|
|
{SDLK_AUDIOPREV, MP_KEY_PREV},
|
|
|
|
{SDLK_AUDIOSTOP, MP_KEY_STOP},
|
|
|
|
{SDLK_AUDIOPLAY, MP_KEY_PLAY},
|
|
|
|
{SDLK_AUDIOMUTE, MP_KEY_MUTE},
|
|
|
|
{SDLK_F1, MP_KEY_F + 1},
|
|
|
|
{SDLK_F2, MP_KEY_F + 2},
|
|
|
|
{SDLK_F3, MP_KEY_F + 3},
|
|
|
|
{SDLK_F4, MP_KEY_F + 4},
|
|
|
|
{SDLK_F5, MP_KEY_F + 5},
|
|
|
|
{SDLK_F6, MP_KEY_F + 6},
|
|
|
|
{SDLK_F7, MP_KEY_F + 7},
|
|
|
|
{SDLK_F8, MP_KEY_F + 8},
|
|
|
|
{SDLK_F9, MP_KEY_F + 9},
|
|
|
|
{SDLK_F10, MP_KEY_F + 10},
|
|
|
|
{SDLK_F11, MP_KEY_F + 11},
|
|
|
|
{SDLK_F12, MP_KEY_F + 12},
|
|
|
|
{SDLK_F13, MP_KEY_F + 13},
|
|
|
|
{SDLK_F14, MP_KEY_F + 14},
|
|
|
|
{SDLK_F15, MP_KEY_F + 15},
|
|
|
|
{SDLK_F16, MP_KEY_F + 16},
|
|
|
|
{SDLK_F17, MP_KEY_F + 17},
|
|
|
|
{SDLK_F18, MP_KEY_F + 18},
|
|
|
|
{SDLK_F19, MP_KEY_F + 19},
|
|
|
|
{SDLK_F20, MP_KEY_F + 20},
|
|
|
|
{SDLK_F21, MP_KEY_F + 21},
|
|
|
|
{SDLK_F22, MP_KEY_F + 22},
|
|
|
|
{SDLK_F23, MP_KEY_F + 23},
|
|
|
|
{SDLK_F24, MP_KEY_F + 24}
|
2012-12-28 07:07:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct priv {
|
|
|
|
bool reinit_renderer;
|
|
|
|
SDL_Window *window;
|
|
|
|
SDL_Renderer *renderer;
|
2013-01-04 16:43:37 +00:00
|
|
|
int renderer_index;
|
2012-12-28 07:07:14 +00:00
|
|
|
SDL_RendererInfo renderer_info;
|
|
|
|
SDL_Texture *tex;
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
int tex_swapped;
|
2014-06-18 18:18:22 +00:00
|
|
|
struct mp_image_params params;
|
2012-12-28 07:07:14 +00:00
|
|
|
struct mp_rect src_rect;
|
|
|
|
struct mp_rect dst_rect;
|
|
|
|
struct mp_osd_res osd_res;
|
|
|
|
struct formatmap_entry osd_format;
|
|
|
|
struct osd_bitmap_surface {
|
2015-03-18 11:33:14 +00:00
|
|
|
int change_id;
|
2012-12-28 07:07:14 +00:00
|
|
|
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];
|
2014-06-15 18:46:57 +00:00
|
|
|
double osd_pts;
|
2012-12-28 07:07:14 +00:00
|
|
|
int mouse_hidden;
|
|
|
|
int brightness, contrast;
|
2015-12-06 17:39:09 +00:00
|
|
|
char *window_title;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
Uint32 wakeup_event;
|
2013-01-04 16:43:37 +00:00
|
|
|
|
|
|
|
// options
|
|
|
|
int allow_sw;
|
2013-02-24 22:36:40 +00:00
|
|
|
int switch_mode;
|
2013-03-05 23:24:34 +00:00
|
|
|
int vsync;
|
2012-12-28 07:07:14 +00:00
|
|
|
};
|
|
|
|
|
2014-06-18 18:18:22 +00:00
|
|
|
static bool lock_texture(struct vo *vo, struct mp_image *texmpi)
|
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
*texmpi = (struct mp_image){0};
|
|
|
|
mp_image_set_size(texmpi, vc->params.w, vc->params.h);
|
|
|
|
mp_image_setfmt(texmpi, vc->params.imgfmt);
|
|
|
|
switch (texmpi->num_planes) {
|
|
|
|
case 1:
|
|
|
|
case 3:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
MP_ERR(vo, "Invalid plane count\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void *pixels;
|
|
|
|
int pitch;
|
|
|
|
if (SDL_LockTexture(vc->tex, NULL, &pixels, &pitch)) {
|
|
|
|
MP_ERR(vo, "SDL_LockTexture failed\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
texmpi->planes[0] = pixels;
|
|
|
|
texmpi->stride[0] = pitch;
|
|
|
|
if (texmpi->num_planes == 3) {
|
|
|
|
if (vc->tex_swapped) {
|
|
|
|
texmpi->planes[2] =
|
|
|
|
((Uint8 *) texmpi->planes[0] + texmpi->h * pitch);
|
|
|
|
texmpi->stride[2] = pitch / 2;
|
|
|
|
texmpi->planes[1] =
|
|
|
|
((Uint8 *) texmpi->planes[2] + (texmpi->h * pitch) / 4);
|
|
|
|
texmpi->stride[1] = pitch / 2;
|
|
|
|
} else {
|
|
|
|
texmpi->planes[1] =
|
|
|
|
((Uint8 *) texmpi->planes[0] + texmpi->h * pitch);
|
|
|
|
texmpi->stride[1] = pitch / 2;
|
|
|
|
texmpi->planes[2] =
|
|
|
|
((Uint8 *) texmpi->planes[1] + (texmpi->h * pitch) / 4);
|
|
|
|
texmpi->stride[2] = pitch / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-01-04 16:43:37 +00:00
|
|
|
static bool is_good_renderer(SDL_RendererInfo *ri,
|
|
|
|
const char *driver_name_wanted, int allow_sw,
|
|
|
|
struct formatmap_entry *osd_format)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
if (driver_name_wanted && driver_name_wanted[0])
|
2013-01-04 16:43:37 +00:00
|
|
|
if (strcmp(driver_name_wanted, ri->name))
|
2012-12-28 07:07:14 +00:00
|
|
|
return false;
|
|
|
|
|
2013-01-04 16:43:37 +00:00
|
|
|
if (!allow_sw &&
|
|
|
|
!(ri->flags & SDL_RENDERER_ACCELERATED))
|
|
|
|
return false;
|
|
|
|
|
2012-12-28 07:07:14 +00:00
|
|
|
int i, j;
|
2013-01-04 16:43:37 +00:00
|
|
|
for (i = 0; i < ri->num_texture_formats; ++i)
|
2012-12-28 07:07:14 +00:00
|
|
|
for (j = 0; j < sizeof(formats) / sizeof(formats[0]); ++j)
|
2013-01-04 16:43:37 +00:00
|
|
|
if (ri->texture_formats[i] == formats[j].sdl)
|
|
|
|
if (formats[j].is_rgba) {
|
|
|
|
if (osd_format)
|
|
|
|
*osd_format = formats[j];
|
2012-12-28 07:07:14 +00:00
|
|
|
return true;
|
2013-01-04 16:43:37 +00:00
|
|
|
}
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-04 16:43:37 +00:00
|
|
|
static bool try_create_renderer(struct vo *vo, int i, const char *driver,
|
2014-05-06 18:35:19 +00:00
|
|
|
struct mp_rect *rc, int flags)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
2013-01-04 16:43:37 +00:00
|
|
|
// first probe
|
|
|
|
SDL_RendererInfo ri;
|
|
|
|
if (SDL_GetRenderDriverInfo(i, &ri))
|
|
|
|
return false;
|
|
|
|
if (!is_good_renderer(&ri, driver, vc->allow_sw, NULL))
|
|
|
|
return false;
|
|
|
|
|
2014-05-06 18:35:19 +00:00
|
|
|
bool xy_valid = flags & VO_WIN_FORCE_POS;
|
2013-01-23 09:56:11 +00:00
|
|
|
|
2013-01-04 16:43:37 +00:00
|
|
|
// then actually try
|
2012-12-28 07:07:14 +00:00
|
|
|
vc->window = SDL_CreateWindow("MPV",
|
2014-05-06 18:35:19 +00:00
|
|
|
xy_valid ? rc->x0 : SDL_WINDOWPOS_UNDEFINED,
|
|
|
|
xy_valid ? rc->y0 : SDL_WINDOWPOS_UNDEFINED,
|
|
|
|
rc->x1 - rc->x0, rc->y1 - rc->y0,
|
2012-12-28 07:07:14 +00:00
|
|
|
SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN);
|
|
|
|
if (!vc->window) {
|
2014-09-05 20:10:20 +00:00
|
|
|
MP_ERR(vo, "SDL_CreateWindow failed\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
destroy_renderer(vo);
|
2013-01-04 16:43:37 +00:00
|
|
|
return false;
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vc->renderer = SDL_CreateRenderer(vc->window, i, 0);
|
|
|
|
if (!vc->renderer) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "SDL_CreateRenderer failed\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
destroy_renderer(vo);
|
2013-01-04 16:43:37 +00:00
|
|
|
return false;
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_GetRendererInfo(vc->renderer, &vc->renderer_info)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "SDL_GetRendererInfo failed\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
destroy_renderer(vo);
|
2013-01-04 16:43:37 +00:00
|
|
|
return false;
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
2013-01-04 16:43:37 +00:00
|
|
|
if (!is_good_renderer(&vc->renderer_info, NULL, vc->allow_sw,
|
|
|
|
&vc->osd_format)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "Renderer '%s' does not fulfill "
|
2013-01-04 16:43:37 +00:00
|
|
|
"requirements on this system\n",
|
|
|
|
vc->renderer_info.name);
|
|
|
|
destroy_renderer(vo);
|
|
|
|
return false;
|
|
|
|
}
|
2012-12-28 07:07:14 +00:00
|
|
|
|
2013-01-04 16:43:37 +00:00
|
|
|
if (vc->renderer_index != i) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_INFO(vo, "Using %s\n", vc->renderer_info.name);
|
2013-01-04 16:43:37 +00:00
|
|
|
vc->renderer_index = i;
|
|
|
|
}
|
2012-12-28 07:07:14 +00:00
|
|
|
|
2015-12-06 17:39:09 +00:00
|
|
|
if (vc->window_title)
|
|
|
|
SDL_SetWindowTitle(vc->window, vc->window_title);
|
|
|
|
|
2013-01-04 16:43:37 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-05-06 18:35:19 +00:00
|
|
|
// flags: VO_WIN_* bits
|
|
|
|
static int init_renderer(struct vo *vo, struct mp_rect *rc, int flags)
|
2013-01-04 16:43:37 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
|
|
|
int n = SDL_GetNumRenderDrivers();
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (vc->renderer_index >= 0)
|
2014-05-06 18:35:19 +00:00
|
|
|
if (try_create_renderer(vo, vc->renderer_index, NULL, rc, flags))
|
2013-01-04 16:43:37 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < n; ++i)
|
|
|
|
if (try_create_renderer(vo, i, SDL_GetHint(SDL_HINT_RENDER_DRIVER),
|
2014-05-06 18:35:19 +00:00
|
|
|
rc, flags))
|
2013-01-04 16:43:37 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < n; ++i)
|
2014-05-06 18:35:19 +00:00
|
|
|
if (try_create_renderer(vo, i, NULL, rc, flags))
|
2013-01-04 16:43:37 +00:00
|
|
|
return 0;
|
|
|
|
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "No supported renderer\n");
|
2013-01-04 16:43:37 +00:00
|
|
|
return -1;
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
vo_wakeup(vo);
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-18 12:05:39 +00:00
|
|
|
static void set_fullscreen(struct vo *vo)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
2013-07-18 12:05:39 +00:00
|
|
|
int fs = vo->opts->fullscreen;
|
2012-12-28 07:07:14 +00:00
|
|
|
|
2013-07-18 12:05:39 +00:00
|
|
|
Uint32 fs_flag;
|
|
|
|
if (vc->switch_mode)
|
|
|
|
fs_flag = SDL_WINDOW_FULLSCREEN;
|
|
|
|
else
|
|
|
|
fs_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
|
|
|
|
Uint32 old_flags = SDL_GetWindowFlags(vc->window);
|
|
|
|
int prev_fs = !!(old_flags & fs_flag);
|
|
|
|
if (fs == prev_fs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Uint32 flags = 0;
|
|
|
|
if (fs)
|
|
|
|
flags |= fs_flag;
|
2012-12-28 07:07:14 +00:00
|
|
|
|
2013-07-18 12:05:39 +00:00
|
|
|
if (SDL_SetWindowFullscreen(vc->window, flags)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "SDL_SetWindowFullscreen failed\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// toggling fullscreen might recreate the window, so better guard for this
|
|
|
|
SDL_DisableScreenSaver();
|
|
|
|
|
|
|
|
force_resize(vo);
|
|
|
|
}
|
|
|
|
|
2014-05-06 18:35:19 +00:00
|
|
|
static void update_screeninfo(struct vo *vo, struct mp_rect *screenrc)
|
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
SDL_DisplayMode mode;
|
|
|
|
if (SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(vc->window),
|
|
|
|
&mode)) {
|
|
|
|
MP_ERR(vo, "SDL_GetCurrentDisplayMode failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
*screenrc = (struct mp_rect){0, 0, mode.w, mode.h};
|
|
|
|
}
|
|
|
|
|
2015-10-03 16:20:16 +00:00
|
|
|
static int reconfig(struct vo *vo, struct mp_image_params *params)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
2014-05-06 18:35:19 +00:00
|
|
|
|
|
|
|
struct vo_win_geometry geo;
|
|
|
|
struct mp_rect screenrc;
|
|
|
|
|
|
|
|
update_screeninfo(vo, &screenrc);
|
|
|
|
vo_calc_window_geometry(vo, &screenrc, &geo);
|
|
|
|
vo_apply_window_geometry(vo, &geo);
|
|
|
|
|
2014-01-24 20:22:25 +00:00
|
|
|
int win_w = vo->dwidth;
|
|
|
|
int win_h = vo->dheight;
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
if (vc->reinit_renderer) {
|
|
|
|
destroy_renderer(vo);
|
|
|
|
vc->reinit_renderer = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vc->window)
|
2014-01-24 20:22:25 +00:00
|
|
|
SDL_SetWindowSize(vc->window, win_w, win_h);
|
2012-12-28 07:07:14 +00:00
|
|
|
else {
|
2014-05-06 18:35:19 +00:00
|
|
|
if (init_renderer(vo, &geo.win, geo.flags) != 0)
|
2012-12-28 07:07:14 +00:00
|
|
|
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)
|
2014-01-24 23:20:26 +00:00
|
|
|
if (params->imgfmt == formats[j].mpv)
|
2012-12-28 07:07:14 +00:00
|
|
|
texfmt = formats[j].sdl;
|
|
|
|
if (texfmt == SDL_PIXELFORMAT_UNKNOWN) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "Invalid pixel format\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
vc->tex_swapped = texfmt == SDL_PIXELFORMAT_YV12;
|
2012-12-28 07:07:14 +00:00
|
|
|
vc->tex = SDL_CreateTexture(vc->renderer, texfmt,
|
2014-01-24 20:22:25 +00:00
|
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
|
|
params->w, params->h);
|
2012-12-28 07:07:14 +00:00
|
|
|
if (!vc->tex) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "Could not create a texture\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-06-18 18:18:22 +00:00
|
|
|
vc->params = *params;
|
|
|
|
|
|
|
|
struct mp_image tmp;
|
|
|
|
if (!lock_texture(vo, &tmp)) {
|
2012-12-28 07:07:14 +00:00
|
|
|
SDL_DestroyTexture(vc->tex);
|
|
|
|
vc->tex = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
2014-06-18 18:18:22 +00:00
|
|
|
mp_image_clear(&tmp, 0, 0, tmp.w, tmp.h);
|
|
|
|
SDL_UnlockTexture(vc->tex);
|
2012-12-28 07:07:14 +00:00
|
|
|
|
2014-01-24 20:22:25 +00:00
|
|
|
resize(vo, win_w, win_h);
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
SDL_DisableScreenSaver();
|
|
|
|
|
2013-07-18 12:05:39 +00:00
|
|
|
set_fullscreen(vo);
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
static void wakeup(struct vo *vo)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
SDL_Event event = {.type = vc->wakeup_event};
|
|
|
|
// Note that there is no context - SDL is a singleton.
|
|
|
|
SDL_PushEvent(&event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wait_events(struct vo *vo, int64_t until_time_us)
|
|
|
|
{
|
|
|
|
int64_t wait_us = until_time_us - mp_time_us();
|
|
|
|
int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000);
|
2012-12-28 07:07:14 +00:00
|
|
|
SDL_Event ev;
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
while (SDL_WaitEventTimeout(&ev, timeout_ms)) {
|
|
|
|
timeout_ms = 0;
|
2012-12-28 07:07:14 +00:00
|
|
|
switch (ev.type) {
|
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
switch (ev.window.event) {
|
|
|
|
case SDL_WINDOWEVENT_EXPOSED:
|
|
|
|
vo->want_redraw = true;
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
|
|
|
check_resize(vo);
|
2014-11-02 19:26:51 +00:00
|
|
|
vo_event(vo, VO_EVENT_RESIZE);
|
2012-12-28 07:07:14 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_QUIT:
|
2013-07-02 12:04:36 +00:00
|
|
|
mp_input_put_key(vo->input_ctx, MP_KEY_CLOSE_WIN);
|
2012-12-28 07:07:14 +00:00
|
|
|
break;
|
|
|
|
case SDL_TEXTINPUT: {
|
|
|
|
int sdl_mod = SDL_GetModState();
|
|
|
|
int mpv_mod = 0;
|
2013-12-01 05:23:39 +00:00
|
|
|
// we ignore KMOD_LSHIFT, KMOD_RSHIFT and KMOD_RALT (if
|
|
|
|
// mp_input_use_alt_gr() is true) because these are already
|
|
|
|
// factored into ev.text.text
|
2012-12-28 07:07:14 +00:00
|
|
|
if (sdl_mod & (KMOD_LCTRL | KMOD_RCTRL))
|
2013-02-12 00:34:04 +00:00
|
|
|
mpv_mod |= MP_KEY_MODIFIER_CTRL;
|
2013-12-01 05:23:39 +00:00
|
|
|
if ((sdl_mod & KMOD_LALT) ||
|
2015-03-02 18:09:25 +00:00
|
|
|
((sdl_mod & KMOD_RALT) && !mp_input_use_alt_gr(vo->input_ctx)))
|
2013-02-12 00:34:04 +00:00
|
|
|
mpv_mod |= MP_KEY_MODIFIER_ALT;
|
2012-12-28 07:07:14 +00:00
|
|
|
if (sdl_mod & (KMOD_LGUI | KMOD_RGUI))
|
2013-02-12 00:34:04 +00:00
|
|
|
mpv_mod |= MP_KEY_MODIFIER_META;
|
2012-12-28 07:07:14 +00:00
|
|
|
struct bstr t = {
|
|
|
|
ev.text.text, strlen(ev.text.text)
|
|
|
|
};
|
2013-07-02 12:04:36 +00:00
|
|
|
mp_input_put_key_utf8(vo->input_ctx, mpv_mod, t);
|
2012-12-28 07:07:14 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SDL_KEYDOWN: {
|
|
|
|
// Issue: we don't know in advance whether this keydown event
|
|
|
|
// will ALSO cause a SDL_TEXTINPUT event
|
|
|
|
// So we're conservative, and only map non printable keycodes
|
|
|
|
// (e.g. function keys, arrow keys, etc.)
|
|
|
|
// However, this does lose some keypresses at least on X11
|
|
|
|
// (e.g. Ctrl-A generates SDL_KEYDOWN only, but the key is
|
|
|
|
// 'a'... and 'a' is normally also handled by SDL_TEXTINPUT).
|
|
|
|
// The default config does not use Ctrl, so this is fine...
|
|
|
|
int keycode = 0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i)
|
|
|
|
if (keys[i].sdl == ev.key.keysym.sym) {
|
|
|
|
keycode = keys[i].mpv;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (keycode) {
|
|
|
|
if (ev.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT))
|
2013-02-12 00:34:04 +00:00
|
|
|
keycode |= MP_KEY_MODIFIER_SHIFT;
|
2012-12-28 07:07:14 +00:00
|
|
|
if (ev.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL))
|
2013-02-12 00:34:04 +00:00
|
|
|
keycode |= MP_KEY_MODIFIER_CTRL;
|
2012-12-28 07:07:14 +00:00
|
|
|
if (ev.key.keysym.mod & (KMOD_LALT | KMOD_RALT))
|
2013-02-12 00:34:04 +00:00
|
|
|
keycode |= MP_KEY_MODIFIER_ALT;
|
2012-12-28 07:07:14 +00:00
|
|
|
if (ev.key.keysym.mod & (KMOD_LGUI | KMOD_RGUI))
|
2013-02-12 00:34:04 +00:00
|
|
|
keycode |= MP_KEY_MODIFIER_META;
|
2013-07-02 12:04:36 +00:00
|
|
|
mp_input_put_key(vo->input_ctx, keycode);
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SDL_MOUSEMOTION:
|
2014-07-27 19:53:29 +00:00
|
|
|
mp_input_set_mouse_pos(vo->input_ctx, ev.motion.x, ev.motion.y);
|
2012-12-28 07:07:14 +00:00
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
2013-07-02 12:04:36 +00:00
|
|
|
mp_input_put_key(vo->input_ctx,
|
2013-02-12 00:34:04 +00:00
|
|
|
(MP_MOUSE_BTN0 + ev.button.button - 1) | MP_KEY_STATE_DOWN);
|
2012-12-28 07:07:14 +00:00
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
2013-07-02 12:04:36 +00:00
|
|
|
mp_input_put_key(vo->input_ctx,
|
2013-07-01 21:54:59 +00:00
|
|
|
(MP_MOUSE_BTN0 + ev.button.button - 1) | MP_KEY_STATE_UP);
|
2012-12-28 07:07:14 +00:00
|
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
|
|
|
return 0;
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void uninit(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
destroy_renderer(vo);
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
|
|
talloc_free(vc);
|
|
|
|
}
|
|
|
|
|
2012-12-28 13:27:03 +00:00
|
|
|
static inline void upload_to_texture(struct vo *vo, SDL_Texture *tex,
|
|
|
|
int w, int h, void *bitmap, int stride)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
2012-12-28 13:27:03 +00:00
|
|
|
if (vc->osd_format.sdl == SDL_PIXELFORMAT_ARGB8888) {
|
|
|
|
// NOTE: this optimization is questionable, because SDL docs say
|
|
|
|
// that this way is slow.
|
|
|
|
// It did measure up faster, though...
|
|
|
|
SDL_UpdateTexture(tex, NULL, bitmap, stride);
|
|
|
|
return;
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void *pixels;
|
|
|
|
int pitch;
|
|
|
|
if (SDL_LockTexture(tex, NULL, &pixels, &pitch)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "Could not lock texture\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
} else {
|
2012-12-28 13:27:03 +00:00
|
|
|
SDL_ConvertPixels(w, h, SDL_PIXELFORMAT_ARGB8888,
|
|
|
|
bitmap, stride,
|
2012-12-28 07:07:14 +00:00
|
|
|
vc->osd_format.sdl,
|
|
|
|
pixels, pitch);
|
|
|
|
SDL_UnlockTexture(tex);
|
|
|
|
}
|
2012-12-28 13:27:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void subbitmap_to_texture(struct vo *vo, SDL_Texture *tex,
|
|
|
|
struct sub_bitmap *bmp,
|
|
|
|
uint32_t ormask)
|
|
|
|
{
|
|
|
|
if (ormask == 0) {
|
|
|
|
upload_to_texture(vo, tex, bmp->w, bmp->h,
|
|
|
|
bmp->bitmap, bmp->stride);
|
|
|
|
} else {
|
|
|
|
uint32_t *temppixels;
|
|
|
|
temppixels = talloc_array(vo, uint32_t, bmp->w * bmp->h);
|
|
|
|
|
|
|
|
int x, y;
|
|
|
|
for (y = 0; y < bmp->h; ++y) {
|
|
|
|
const uint32_t *src =
|
|
|
|
(const uint32_t *) ((const char *) bmp->bitmap + y * bmp->stride);
|
|
|
|
uint32_t *dst = temppixels + y * bmp->w;
|
|
|
|
for (x = 0; x < bmp->w; ++x)
|
|
|
|
dst[x] = src[x] | ormask;
|
|
|
|
}
|
2012-12-28 07:07:14 +00:00
|
|
|
|
2012-12-28 13:27:03 +00:00
|
|
|
upload_to_texture(vo, tex, bmp->w, bmp->h,
|
|
|
|
temppixels, sizeof(uint32_t) * bmp->w);
|
|
|
|
|
|
|
|
talloc_free(temppixels);
|
|
|
|
}
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
|
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
struct osd_bitmap_surface *sfc = &vc->osd_surfaces[imgs->render_index];
|
|
|
|
|
|
|
|
if (imgs->format == SUBBITMAP_EMPTY || imgs->num_parts == 0)
|
|
|
|
return;
|
|
|
|
|
2015-03-18 11:33:14 +00:00
|
|
|
if (imgs->change_id == sfc->change_id)
|
2012-12-28 07:07:14 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (imgs->num_parts > sfc->targets_size) {
|
|
|
|
sfc->targets = talloc_realloc(vc, sfc->targets,
|
|
|
|
struct osd_target, imgs->num_parts);
|
|
|
|
memset(&sfc->targets[sfc->targets_size], 0, sizeof(struct osd_target) *
|
|
|
|
(imgs->num_parts - sfc->targets_size));
|
|
|
|
sfc->targets_size = imgs->num_parts;
|
|
|
|
}
|
|
|
|
sfc->num_targets = imgs->num_parts;
|
|
|
|
|
|
|
|
for (int i = 0; i < imgs->num_parts; i++) {
|
|
|
|
struct osd_target *target = sfc->targets + i;
|
|
|
|
struct sub_bitmap *bmp = imgs->parts + i;
|
|
|
|
|
|
|
|
target->source = (SDL_Rect){
|
|
|
|
0, 0, bmp->w, bmp->h
|
|
|
|
};
|
|
|
|
target->dest = (SDL_Rect){
|
|
|
|
bmp->x, bmp->y, bmp->dw, bmp->dh
|
|
|
|
};
|
|
|
|
|
2015-03-18 11:33:14 +00:00
|
|
|
// tex: alpha blended texture
|
|
|
|
if (target->tex) {
|
|
|
|
SDL_DestroyTexture(target->tex);
|
|
|
|
target->tex = NULL;
|
|
|
|
}
|
|
|
|
if (!target->tex)
|
|
|
|
target->tex = SDL_CreateTexture(vc->renderer,
|
|
|
|
vc->osd_format.sdl, SDL_TEXTUREACCESS_STREAMING,
|
|
|
|
bmp->w, bmp->h);
|
|
|
|
if (!target->tex) {
|
|
|
|
MP_ERR(vo, "Could not create texture\n");
|
|
|
|
}
|
|
|
|
if (target->tex) {
|
|
|
|
SDL_SetTextureBlendMode(target->tex,
|
|
|
|
SDL_BLENDMODE_BLEND);
|
|
|
|
SDL_SetTextureColorMod(target->tex, 0, 0, 0);
|
|
|
|
subbitmap_to_texture(vo, target->tex, bmp, 0); // RGBA -> 000A
|
|
|
|
}
|
2012-12-28 07:07:14 +00:00
|
|
|
|
2015-03-18 11:33:14 +00:00
|
|
|
// tex2: added texture
|
|
|
|
if (target->tex2) {
|
|
|
|
SDL_DestroyTexture(target->tex2);
|
|
|
|
target->tex2 = NULL;
|
|
|
|
}
|
|
|
|
if (!target->tex2)
|
|
|
|
target->tex2 = SDL_CreateTexture(vc->renderer,
|
|
|
|
vc->osd_format.sdl, SDL_TEXTUREACCESS_STREAMING,
|
|
|
|
bmp->w, bmp->h);
|
|
|
|
if (!target->tex2) {
|
|
|
|
MP_ERR(vo, "Could not create texture\n");
|
|
|
|
}
|
|
|
|
if (target->tex2) {
|
|
|
|
SDL_SetTextureBlendMode(target->tex2,
|
|
|
|
SDL_BLENDMODE_ADD);
|
|
|
|
subbitmap_to_texture(vo, target->tex2, bmp,
|
|
|
|
0xFF000000); // RGBA -> RGB1
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-18 11:33:14 +00:00
|
|
|
sfc->change_id = imgs->change_id;
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_osd_part(struct vo *vo, int index)
|
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
struct osd_bitmap_surface *sfc = &vc->osd_surfaces[index];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sfc->num_targets; i++) {
|
|
|
|
struct osd_target *target = sfc->targets + i;
|
|
|
|
if (target->tex)
|
|
|
|
SDL_RenderCopy(vc->renderer, target->tex,
|
|
|
|
&target->source, &target->dest);
|
|
|
|
if (target->tex2)
|
|
|
|
SDL_RenderCopy(vc->renderer, target->tex2,
|
|
|
|
&target->source, &target->dest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
|
|
|
|
{
|
|
|
|
struct vo *vo = ctx;
|
|
|
|
generate_osd_part(vo, imgs);
|
|
|
|
draw_osd_part(vo, imgs->render_index);
|
|
|
|
}
|
|
|
|
|
2014-06-15 18:46:57 +00:00
|
|
|
static void draw_osd(struct vo *vo)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
2013-07-22 22:45:23 +00:00
|
|
|
static const bool osdformats[SUBBITMAP_COUNT] = {
|
2012-12-28 07:07:14 +00:00
|
|
|
[SUBBITMAP_RGBA] = true,
|
|
|
|
};
|
|
|
|
|
2014-06-15 18:46:57 +00:00
|
|
|
osd_draw(vo->osd, vc->osd_res, vc->osd_pts, 0, osdformats, draw_osd_cb, vo);
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
2013-07-22 20:52:42 +00:00
|
|
|
static int preinit(struct vo *vo)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
|
|
|
if (SDL_WasInit(SDL_INIT_VIDEO)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "already initialized\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// predefine SDL defaults (SDL env vars shall override)
|
|
|
|
SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, "1",
|
|
|
|
SDL_HINT_DEFAULT);
|
2013-03-06 17:09:35 +00:00
|
|
|
SDL_SetHintWithPriority(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0",
|
|
|
|
SDL_HINT_DEFAULT);
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
// predefine MPV options (SDL env vars shall be overridden)
|
2013-03-05 23:24:34 +00:00
|
|
|
SDL_SetHintWithPriority(SDL_HINT_RENDER_VSYNC, vc->vsync ? "1" : "0",
|
|
|
|
SDL_HINT_OVERRIDE);
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "SDL_Init failed\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// try creating a renderer (this also gets the renderer_info data
|
|
|
|
// for query_format to use!)
|
2014-05-06 18:35:19 +00:00
|
|
|
if (init_renderer(vo, &(struct mp_rect){.x1 = 640, .y1 = 480}, 0) != 0)
|
2012-12-28 07:07:14 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
// please reinitialize the renderer to proper size on config()
|
|
|
|
vc->reinit_renderer = true;
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
vc->wakeup_event = SDL_RegisterEvents(1);
|
|
|
|
if (vc->wakeup_event == (Uint32)-1)
|
|
|
|
MP_ERR(vo, "SDL_RegisterEvents() failed.\n");
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-21 21:08:24 +00:00
|
|
|
static int query_format(struct vo *vo, int format)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
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)
|
2015-01-21 21:08:24 +00:00
|
|
|
return 1;
|
2012-12-28 07:07:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-04 14:56:04 +00:00
|
|
|
static void draw_image(struct vo *vo, mp_image_t *mpi)
|
2012-12-28 07:07:14 +00:00
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
|
|
|
// decode brightness/contrast
|
|
|
|
int color_add = 0;
|
|
|
|
int color_mod = 255;
|
|
|
|
int brightness = vc->brightness;
|
|
|
|
int contrast = vc->contrast;
|
|
|
|
|
|
|
|
// only in this range it is possible to do brightness/contrast control
|
|
|
|
// properly, using just additive render operations and color modding
|
|
|
|
// (SDL2 provides no subtractive rendering, sorry)
|
|
|
|
if (2 * brightness < contrast) {
|
|
|
|
//brightness = (brightness + 2 * contrast) / 5; // closest point
|
|
|
|
brightness = (brightness + contrast) / 3; // equal adjustment
|
|
|
|
contrast = 2 * brightness;
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert to values SDL2 likes
|
|
|
|
color_mod = ((contrast + 100) * 255 + 50) / 100;
|
|
|
|
color_add = ((2 * brightness - contrast) * 255 + 100) / 200;
|
|
|
|
|
|
|
|
// clamp
|
|
|
|
if (color_mod < 0)
|
|
|
|
color_mod = 0;
|
|
|
|
if (color_mod > 255)
|
|
|
|
color_mod = 255;
|
|
|
|
// color_add can't be < 0
|
|
|
|
if (color_add > 255)
|
|
|
|
color_add = 255;
|
|
|
|
|
2012-12-22 20:46:22 +00:00
|
|
|
// typically this runs in parallel with the following mp_image_copy call
|
2012-12-28 07:07:14 +00:00
|
|
|
SDL_SetRenderDrawColor(vc->renderer, color_add, color_add, color_add, 255);
|
|
|
|
SDL_RenderClear(vc->renderer);
|
|
|
|
|
|
|
|
// use additive blending for the video texture only if the clear color is
|
|
|
|
// not black (faster especially for the software renderer)
|
|
|
|
if (color_add)
|
|
|
|
SDL_SetTextureBlendMode(vc->tex, SDL_BLENDMODE_ADD);
|
|
|
|
else
|
|
|
|
SDL_SetTextureBlendMode(vc->tex, SDL_BLENDMODE_NONE);
|
|
|
|
|
|
|
|
if (mpi) {
|
2014-06-15 18:46:57 +00:00
|
|
|
vc->osd_pts = mpi->pts;
|
2014-06-18 18:18:22 +00:00
|
|
|
|
|
|
|
mp_image_t texmpi;
|
|
|
|
if (!lock_texture(vo, &texmpi)) {
|
2014-06-17 21:05:50 +00:00
|
|
|
talloc_free(mpi);
|
2012-12-28 07:07:14 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-18 18:18:22 +00:00
|
|
|
mp_image_copy(&texmpi, mpi);
|
2012-12-28 07:07:14 +00:00
|
|
|
|
|
|
|
SDL_UnlockTexture(vc->tex);
|
2015-06-25 07:55:24 +00:00
|
|
|
|
|
|
|
talloc_free(mpi);
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SDL_Rect src, dst;
|
|
|
|
src.x = vc->src_rect.x0;
|
|
|
|
src.y = vc->src_rect.y0;
|
|
|
|
src.w = vc->src_rect.x1 - vc->src_rect.x0;
|
|
|
|
src.h = vc->src_rect.y1 - vc->src_rect.y0;
|
|
|
|
dst.x = vc->dst_rect.x0;
|
|
|
|
dst.y = vc->dst_rect.y0;
|
|
|
|
dst.w = vc->dst_rect.x1 - vc->dst_rect.x0;
|
|
|
|
dst.h = vc->dst_rect.y1 - vc->dst_rect.y0;
|
|
|
|
|
2012-12-22 20:46:22 +00:00
|
|
|
// typically this runs in parallel with the following mp_image_copy call
|
2012-12-28 07:07:14 +00:00
|
|
|
if (color_mod > 255) {
|
|
|
|
SDL_SetTextureColorMod(vc->tex, color_mod / 2, color_mod / 2, color_mod / 2);
|
|
|
|
SDL_RenderCopy(vc->renderer, vc->tex, &src, &dst);
|
|
|
|
SDL_RenderCopy(vc->renderer, vc->tex, &src, &dst);
|
|
|
|
} else {
|
|
|
|
SDL_SetTextureColorMod(vc->tex, color_mod, color_mod, color_mod);
|
|
|
|
SDL_RenderCopy(vc->renderer, vc->tex, &src, &dst);
|
|
|
|
}
|
2014-06-15 18:46:57 +00:00
|
|
|
|
|
|
|
draw_osd(vo);
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct mp_image *get_window_screenshot(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
2012-12-22 20:46:22 +00:00
|
|
|
struct mp_image *image = mp_image_alloc(vc->osd_format.mpv, vo->dwidth,
|
|
|
|
vo->dheight);
|
video: introduce failure path for image allocations
Until now, failure to allocate image data resulted in a crash (i.e.
abort() was called). This was intentional, because it's pretty silly to
degrade playback, and in almost all situations, the OOM will probably
kill you anyway. (And then there's the standard Linux overcommit
behavior, which also will kill you at some point.)
But I changed my opinion, so here we go. This change does not affect
_all_ memory allocations, just image data. Now in most failure cases,
the output will just be skipped. For video filters, this coincidentally
means that failure is treated as EOF (because the playback core assumes
EOF if nothing comes out of the video filter chain). In other
situations, output might be in some way degraded, like skipping frames,
not scaling OSD, and such.
Functions whose return values changed semantics:
mp_image_alloc
mp_image_new_copy
mp_image_new_ref
mp_image_make_writeable
mp_image_setrefp
mp_image_to_av_frame_and_unref
mp_image_from_av_frame
mp_image_new_external_ref
mp_image_new_custom_ref
mp_image_pool_make_writeable
mp_image_pool_get
mp_image_pool_new_copy
mp_vdpau_mixed_frame_create
vf_alloc_out_image
vf_make_out_image_writeable
glGetWindowScreenshot
2014-06-17 20:43:43 +00:00
|
|
|
if (!image)
|
|
|
|
return NULL;
|
2012-12-28 07:07:14 +00:00
|
|
|
if (SDL_RenderReadPixels(vc->renderer, NULL, vc->osd_format.sdl,
|
|
|
|
image->planes[0], image->stride[0])) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "SDL_RenderReadPixels failed\n");
|
2012-12-22 20:46:22 +00:00
|
|
|
talloc_free(image);
|
2012-12-28 07:07:14 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_eq(struct vo *vo, const char *name, int value)
|
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
2014-07-10 06:26:44 +00:00
|
|
|
if (!strcmp(name, "brightness"))
|
2012-12-28 07:07:14 +00:00
|
|
|
vc->brightness = value;
|
2014-07-10 06:26:44 +00:00
|
|
|
else if (!strcmp(name, "contrast"))
|
2012-12-28 07:07:14 +00:00
|
|
|
vc->contrast = value;
|
|
|
|
else
|
|
|
|
return VO_NOTIMPL;
|
|
|
|
|
|
|
|
vo->want_redraw = true;
|
|
|
|
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_eq(struct vo *vo, const char *name, int *value)
|
|
|
|
{
|
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
2014-07-10 06:26:44 +00:00
|
|
|
if (!strcmp(name, "brightness"))
|
2012-12-28 07:07:14 +00:00
|
|
|
*value = vc->brightness;
|
2014-07-10 06:26:44 +00:00
|
|
|
else if (!strcmp(name, "contrast"))
|
2012-12-28 07:07:14 +00:00
|
|
|
*value = vc->contrast;
|
|
|
|
else
|
|
|
|
return VO_NOTIMPL;
|
|
|
|
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int control(struct vo *vo, uint32_t request, void *data)
|
|
|
|
{
|
2013-06-15 17:04:20 +00:00
|
|
|
struct priv *vc = vo->priv;
|
|
|
|
|
2012-12-28 07:07:14 +00:00
|
|
|
switch (request) {
|
|
|
|
case VOCTRL_FULLSCREEN:
|
2015-01-16 22:07:13 +00:00
|
|
|
vo->opts->fullscreen = !vo->opts->fullscreen;
|
2013-07-18 12:05:39 +00:00
|
|
|
set_fullscreen(vo);
|
2012-12-28 07:07:14 +00:00
|
|
|
return 1;
|
|
|
|
case VOCTRL_REDRAW_FRAME:
|
2012-11-04 14:56:04 +00:00
|
|
|
draw_image(vo, NULL);
|
2012-12-28 07:07:14 +00:00
|
|
|
return 1;
|
|
|
|
case VOCTRL_GET_PANSCAN:
|
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_SET_PANSCAN:
|
|
|
|
force_resize(vo);
|
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_SET_EQUALIZER: {
|
|
|
|
struct voctrl_set_equalizer_args *args = data;
|
|
|
|
return set_eq(vo, args->name, args->value);
|
|
|
|
}
|
|
|
|
case VOCTRL_GET_EQUALIZER: {
|
|
|
|
struct voctrl_get_equalizer_args *args = data;
|
|
|
|
return get_eq(vo, args->name, args->valueptr);
|
|
|
|
}
|
2015-01-23 21:06:12 +00:00
|
|
|
case VOCTRL_SCREENSHOT_WIN:
|
|
|
|
*(struct mp_image **)data = get_window_screenshot(vo);
|
2012-12-28 07:07:14 +00:00
|
|
|
return true;
|
2013-05-16 21:37:17 +00:00
|
|
|
case VOCTRL_SET_CURSOR_VISIBILITY:
|
|
|
|
SDL_ShowCursor(*(bool *)data);
|
|
|
|
return true;
|
2013-06-15 17:04:20 +00:00
|
|
|
case VOCTRL_UPDATE_WINDOW_TITLE:
|
2015-12-06 17:39:09 +00:00
|
|
|
talloc_free(vc->window_title);
|
|
|
|
vc->window_title = talloc_strdup(vc, (char *)data);
|
|
|
|
if (vc->window && vc->window_title)
|
|
|
|
SDL_SetWindowTitle(vc->window, vc->window_title);
|
2013-06-15 17:04:20 +00:00
|
|
|
return true;
|
2012-12-28 07:07:14 +00:00
|
|
|
}
|
|
|
|
return VO_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define OPT_BASE_STRUCT struct priv
|
|
|
|
|
|
|
|
const struct vo_driver video_out_sdl = {
|
2013-10-23 17:06:14 +00:00
|
|
|
.description = "SDL 2.0 Renderer",
|
|
|
|
.name = "sdl",
|
2012-12-28 07:07:14 +00:00
|
|
|
.priv_size = sizeof(struct priv),
|
2013-01-04 16:43:37 +00:00
|
|
|
.priv_defaults = &(const struct priv) {
|
2013-03-05 23:24:34 +00:00
|
|
|
.renderer_index = -1,
|
|
|
|
.vsync = 1,
|
2013-01-04 16:43:37 +00:00
|
|
|
},
|
|
|
|
.options = (const struct m_option []){
|
2013-02-08 20:09:18 +00:00
|
|
|
OPT_FLAG("sw", allow_sw, 0),
|
2013-02-24 22:36:40 +00:00
|
|
|
OPT_FLAG("switch-mode", switch_mode, 0),
|
2013-03-05 23:24:34 +00:00
|
|
|
OPT_FLAG("vsync", vsync, 0),
|
2013-01-04 16:43:37 +00:00
|
|
|
{NULL}
|
|
|
|
},
|
2012-12-28 07:07:14 +00:00
|
|
|
.preinit = preinit,
|
2012-11-04 15:24:18 +00:00
|
|
|
.query_format = query_format,
|
2014-01-24 20:22:25 +00:00
|
|
|
.reconfig = reconfig,
|
2012-12-28 07:07:14 +00:00
|
|
|
.control = control,
|
2012-11-04 14:56:04 +00:00
|
|
|
.draw_image = draw_image,
|
2012-12-28 07:07:14 +00:00
|
|
|
.uninit = uninit,
|
|
|
|
.flip_page = flip_page,
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
.wait_events = wait_events,
|
|
|
|
.wakeup = wakeup,
|
2012-12-28 07:07:14 +00:00
|
|
|
};
|