aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--DOCS/man/en/mplayer.13
-rw-r--r--Makefile11
-rw-r--r--bin_to_header.py43
-rw-r--r--bstr.c38
-rw-r--r--bstr.h20
-rw-r--r--cfg-mplayer.h18
-rw-r--r--codec-cfg.c1
-rw-r--r--command.c27
-rwxr-xr-xconfigure21
-rw-r--r--defaultopts.c4
-rw-r--r--etc/input.conf2
-rw-r--r--fmt-conversion.c1
-rw-r--r--input/input.c68
-rw-r--r--input/input.h2
-rw-r--r--input/keycodes.h19
-rw-r--r--libao2/ao_dsound.c70
-rw-r--r--libao2/ao_openal.c30
-rw-r--r--libmpcodecs/img_format.c1
-rw-r--r--libmpcodecs/img_format.h2
-rw-r--r--libmpcodecs/mp_image.c7
-rw-r--r--libmpcodecs/mp_image.h2
-rw-r--r--libmpcodecs/vd.c6
-rw-r--r--libmpcodecs/vf.c2
-rw-r--r--libmpcodecs/vf_ass.c50
-rw-r--r--libmpcodecs/vf_gradfun.c56
-rw-r--r--libmpcodecs/vf_scale.c20
-rw-r--r--libmpcodecs/vf_scale.h1
-rw-r--r--libmpcodecs/vf_screenshot.c12
-rw-r--r--libmpcodecs/vf_vo.c44
-rw-r--r--libmpdemux/demux_cue.c65
-rw-r--r--libmpdemux/demux_lmlm4.c1
-rw-r--r--libmpdemux/demuxer.c29
-rw-r--r--libmpdemux/demuxer.h1
-rw-r--r--libvo/cocoa_common.h5
-rw-r--r--libvo/cocoa_common.m76
-rw-r--r--libvo/d3d_shader_yuv.h142
-rw-r--r--libvo/d3d_shader_yuv.hlsl44
-rw-r--r--libvo/d3d_shader_yuv_2ch.h170
-rw-r--r--libvo/eosd_packer.c254
-rw-r--r--libvo/eosd_packer.h71
-rw-r--r--libvo/filter_kernels.c279
-rw-r--r--libvo/filter_kernels.h45
-rw-r--r--libvo/gl_common.c860
-rw-r--r--libvo/gl_common.h342
-rw-r--r--libvo/gl_header_fixes.h231
-rw-r--r--libvo/mga_template.c2
-rw-r--r--libvo/video_out.c32
-rw-r--r--libvo/video_out.h1
-rw-r--r--libvo/vo_direct3d.c2157
-rw-r--r--libvo/vo_directfb2.c14
-rw-r--r--libvo/vo_directx.c1789
-rw-r--r--libvo/vo_gl.c348
-rw-r--r--libvo/vo_gl3.c2418
-rw-r--r--libvo/vo_gl3_shaders.glsl316
-rw-r--r--libvo/vo_png.c15
-rw-r--r--libvo/vo_svga.c4
-rw-r--r--libvo/vo_vdpau.c255
-rw-r--r--libvo/w32_common.c679
-rw-r--r--libvo/w32_common.h57
-rw-r--r--libvo/x11_common.c3
-rw-r--r--m_option.c24
-rw-r--r--m_option.h5
-rw-r--r--mp_core.h3
-rw-r--r--mp_msg.c2
-rw-r--r--mpcommon.c2
-rw-r--r--mplayer.c49
-rw-r--r--options.h6
-rw-r--r--osdep/getch2.c27
-rw-r--r--osdep/io.c25
-rw-r--r--osdep/io.h2
-rw-r--r--osdep/mplayer.rc6
-rw-r--r--playtree.c54
-rw-r--r--screenshot.c431
-rw-r--r--screenshot.h3
-rw-r--r--stream/stream_file.c9
-rw-r--r--sub/ass_mp.c3
-rw-r--r--sub/subreader.c10
-rw-r--r--timeline/tl_cue.c436
-rw-r--r--timeline/tl_matroska.c51
-rwxr-xr-xversion.sh2
81 files changed, 9551 insertions, 2886 deletions
diff --git a/.gitignore b/.gitignore
index fd301449c2..c68c0fe2c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@
/TAGS
/locale
/po
+/libvo/vo_gl3_shaders.h
/libmpdemux/ebml_defs.c
/libmpdemux/ebml_types.h
/libvo/vdpau_template.c
diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1
index 3575fb22a6..0e1ff45516 100644
--- a/DOCS/man/en/mplayer.1
+++ b/DOCS/man/en/mplayer.1
@@ -3894,6 +3894,9 @@ Select the scaling function to use for chrominance scaling.
For details see lscale.
.IPs filter-strength=<value>
Set the effect strength for the lscale/cscale filters that support it.
+.IPs noise-strength=<value>
+Set how much noise to add. 0 to disable (default), 1.0 for level suitable
+for dithering to 6 bit.
.IPs stereo=<value>
Select a method for stereo display.
You may have to use \-aspect to fix the aspect value.
diff --git a/Makefile b/Makefile
index d1ce8dd532..ffec492a44 100644
--- a/Makefile
+++ b/Makefile
@@ -369,6 +369,7 @@ SRCS_COMMON = asxparser.c \
libmpdemux/demux_avi.c \
libmpdemux/demux_demuxers.c \
libmpdemux/demux_edl.c \
+ libmpdemux/demux_cue.c \
libmpdemux/demux_film.c \
libmpdemux/demux_fli.c \
libmpdemux/demux_lavf.c \
@@ -403,6 +404,7 @@ SRCS_COMMON = asxparser.c \
libmpdemux/yuv4mpeg.c \
libmpdemux/yuv4mpeg_ratio.c \
libvo/osd.c \
+ libvo/eosd_packer.c \
osdep/numcores.c \
osdep/io.c \
osdep/$(GETCH) \
@@ -427,6 +429,7 @@ SRCS_COMMON = asxparser.c \
sub/vobsub.c \
timeline/tl_edl.c \
timeline/tl_matroska.c \
+ timeline/tl_cue.c \
$(SRCS_COMMON-yes)
@@ -448,7 +451,7 @@ SRCS_MPLAYER-$(DXR3) += libvo/vo_dxr3.c
SRCS_MPLAYER-$(FBDEV) += libvo/vo_fbdev.c libvo/vo_fbdev2.c
SRCS_MPLAYER-$(GGI) += libvo/vo_ggi.c
SRCS_MPLAYER-$(GIF) += libvo/vo_gif89a.c
-SRCS_MPLAYER-$(GL) += libvo/gl_common.c libvo/vo_gl.c \
+SRCS_MPLAYER-$(GL) += libvo/gl_common.c libvo/vo_gl.c libvo/vo_gl3.c \
pnm_loader.c
SRCS_MPLAYER-$(GL_SDL) += libvo/sdl_common.c
SRCS_MPLAYER-$(GL_WIN32) += libvo/w32_common.c
@@ -503,6 +506,7 @@ SRCS_MPLAYER = command.c \
libao2/audio_out.c \
libvo/aspect.c \
libvo/csputils.c \
+ libvo/filter_kernels.c \
libvo/geometry.c \
libvo/old_vo_wrapper.c \
libvo/spuenc.c \
@@ -604,6 +608,11 @@ libmpdemux/ebml_types.h: TOOLS/matroska.py
libmpdemux/ebml_defs.c: TOOLS/matroska.py
./$< --generate-definitions > $@
+libvo/vo_gl3_shaders.h: libvo/vo_gl3_shaders.glsl
+ python ./bin_to_header.py $^ $@
+
+libvo/vo_gl3.c: libvo/vo_gl3_shaders.h
+
# ./configure must be rerun if it changed
config.mak: configure
@echo "############################################################"
diff --git a/bin_to_header.py b/bin_to_header.py
new file mode 100644
index 0000000000..137a5b3728
--- /dev/null
+++ b/bin_to_header.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+# Script to embed arbitrary binary files in C header files.
+
+CHARS_PER_LINE = 19
+
+import sys
+import os
+
+if len(sys.argv) != 3:
+ print("Embed binary files in C headers.")
+ print("Usage: ")
+ print(" bin_to_header.py infile outfile.h")
+ print("outfile.h will be overwritten with the new contents.")
+ sys.exit(1)
+
+infile_name = sys.argv[1]
+outfile_name = sys.argv[2]
+
+varname = os.path.splitext(os.path.basename(outfile_name))[0]
+
+infile = open(infile_name, "rb")
+outfile = open(outfile_name, "w")
+
+outfile.write("// Generated with " + " ".join(sys.argv) + "\n")
+outfile.write("\nstatic const unsigned char " + varname + "[] = {\n")
+
+while True:
+ data = infile.read(CHARS_PER_LINE)
+ if len(data) == 0:
+ break
+ outfile.write(" ")
+ for c in data:
+ # make it work both in Python 2.x (c is str) and 3.x (c is int)
+ if type(c) != int:
+ c = ord(c)
+ outfile.write("{0:3},".format(c))
+ outfile.write("\n")
+
+outfile.write("};\n")
+
+infile.close()
+outfile.close()
diff --git a/bstr.c b/bstr.c
index bb407a10d8..036cf69d42 100644
--- a/bstr.c
+++ b/bstr.c
@@ -195,6 +195,16 @@ struct bstr bstr_getline(struct bstr str, struct bstr *rest)
return bstr_splice(str, 0, pos + 1);
}
+struct bstr bstr_strip_linebreaks(struct bstr str)
+{
+ if (bstr_endswith0(str, "\r\n")) {
+ str = bstr_splice(str, 0, str.len - 2);
+ } else if (bstr_endswith0(str, "\n")) {
+ str = bstr_splice(str, 0, str.len - 1);
+ }
+ return str;
+}
+
bool bstr_eatstart(struct bstr *s, struct bstr prefix)
{
if (!bstr_startswith(*s, prefix))
@@ -251,3 +261,31 @@ int bstr_decode_utf8(struct bstr s, struct bstr *out_next)
*out_next = s;
return codepoint;
}
+
+bool bstr_case_startswith(struct bstr s, struct bstr prefix)
+{
+ struct bstr start = bstr_splice(s, 0, prefix.len);
+ return start.len == prefix.len && bstrcasecmp(start, prefix) == 0;
+}
+
+bool bstr_case_endswith(struct bstr s, struct bstr suffix)
+{
+ struct bstr end = bstr_cut(s, -suffix.len);
+ return end.len == suffix.len && bstrcasecmp(end, suffix) == 0;
+}
+
+struct bstr bstr_strip_ext(struct bstr str)
+{
+ int dotpos = bstrrchr(str, '.');
+ if (dotpos < 0)
+ return str;
+ return (struct bstr){str.start, dotpos};
+}
+
+struct bstr bstr_get_ext(struct bstr s)
+{
+ int dotpos = bstrrchr(s, '.');
+ if (dotpos < 0)
+ return (struct bstr){NULL, 0};
+ return bstr_splice(s, dotpos + 1, s.len);
+}
diff --git a/bstr.h b/bstr.h
index 09b1fda489..d3434cfa13 100644
--- a/bstr.h
+++ b/bstr.h
@@ -64,7 +64,6 @@ int bstrcspn(struct bstr str, const char *reject);
int bstr_find(struct bstr haystack, struct bstr needle);
struct bstr *bstr_splitlines(void *talloc_ctx, struct bstr str);
-struct bstr bstr_getline(struct bstr str, struct bstr *rest);
struct bstr bstr_lstrip(struct bstr str);
struct bstr bstr_strip(struct bstr str);
struct bstr bstr_split(struct bstr str, const char *sep, struct bstr *rest);
@@ -87,11 +86,30 @@ int bstr_decode_utf8(struct bstr str, struct bstr *out_next);
// On error, -1 is returned. On success, it returns a value in the range [1, 4].
int bstr_parse_utf8_code_length(unsigned char b);
+// Return the text before the next line break, and return it. Change *rest to
+// point to the text following this line break. (rest can be NULL.)
+// Line break characters are not stripped.
+struct bstr bstr_getline(struct bstr str, struct bstr *rest);
+
+// Strip one trailing line break. This is intended for use with bstr_getline,
+// and will remove the trailing \n or \r\n sequence.
+struct bstr bstr_strip_linebreaks(struct bstr str);
+
// If s starts with prefix, return true and return the rest of the string in s.
bool bstr_eatstart(struct bstr *s, struct bstr prefix);
+bool bstr_case_startswith(struct bstr s, struct bstr prefix);
+bool bstr_case_endswith(struct bstr s, struct bstr suffix);
+struct bstr bstr_strip_ext(struct bstr str);
+struct bstr bstr_get_ext(struct bstr s);
+
static inline struct bstr bstr_cut(struct bstr str, int n)
{
+ if (n < 0) {
+ n += str.len;
+ if (n < 0)
+ n = 0;
+ }
if (n > str.len)
n = str.len;
return (struct bstr){str.start + n, str.len - n};
diff --git a/cfg-mplayer.h b/cfg-mplayer.h
index 5440de9946..6044b86645 100644
--- a/cfg-mplayer.h
+++ b/cfg-mplayer.h
@@ -486,6 +486,9 @@ const m_option_t common_opts[] = {
{"sb", &seek_to_byte, CONF_TYPE_POSITION, CONF_MIN, 0, 0, NULL},
OPT_TIME("ss", seek_to_sec, 0),
+ // start paused
+ OPT_FLAG_ON("pause", start_paused, 0),
+
// stop at given position
{"endpos", &end_at, CONF_TYPE_TIME_SIZE, 0, 0, 0, NULL},
@@ -911,7 +914,15 @@ const m_option_t mplayer_opts[]={
OPT_STRING("rtc-device", rtc_device, 0),
#endif
- OPT_MAKE_FLAGS("term-osd", term_osd, 0),
+ OPT_CHOICE("term-osd", term_osd, M_OPT_IMPLICIT_DEFAULT,
+ ({"force", 1},
+ {"auto", 2},
+ {"off", 0})),
+
+ // set term_osd to 0
+ // this is for compatibility
+ {"noterm-osd", NULL, &m_option_type_flag, 0, 1, 0, NULL, 1, offsetof(struct MPOpts, term_osd)},
+
OPT_STRING("term-osd-esc", term_osd_esc, 0, OPTDEF_STR("\x1b[A\r\x1b[K")),
OPT_STRING("playing-msg", playing_msg, 0),
@@ -928,6 +939,11 @@ const m_option_t mplayer_opts[]={
{"tvscan", "MPlayer was compiled without TV interface support.\n", CONF_TYPE_PRINT, 0, 0, 0, NULL},
#endif /* CONFIG_TV */
+ OPT_INTRANGE("screenshot-jpeg-quality", screenshot_jpeg_quality, 0, 0, 100),
+ OPT_INTRANGE("screenshot-png-compression", screenshot_png_compression, 0, 0, 9),
+ OPT_STRING("screenshot-filetype", screenshot_filetype, 0),
+ OPT_STRING("screenshot-template", screenshot_template, 0),
+
OPT_FLAG_ON("list-properties", list_properties, CONF_GLOBAL),
{"identify", &mp_msg_levels[MSGT_IDENTIFY], CONF_TYPE_FLAG, CONF_GLOBAL, 0, MSGL_V, NULL},
{"help", (void *) help_text, CONF_TYPE_PRINT, CONF_NOCFG|CONF_GLOBAL, 0, 0, NULL},
diff --git a/codec-cfg.c b/codec-cfg.c
index 0c25e73901..83d9f9e606 100644
--- a/codec-cfg.c
+++ b/codec-cfg.c
@@ -197,6 +197,7 @@ static const struct {
{"BGR32", IMGFMT_BGR32},
{"RGB1", IMGFMT_RGB1},
{"BGR1", IMGFMT_BGR1},
+ {"GBRP", IMGFMT_GBRP},
{"MPES", IMGFMT_MPEGPES},
diff --git a/command.c b/command.c
index d511e52c4a..a7446cb855 100644
--- a/command.c
+++ b/command.c
@@ -21,6 +21,7 @@
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
+#include <assert.h>
#include "config.h"
#include "talloc.h"
@@ -2783,6 +2784,28 @@ static void remove_subtitle_range(MPContext *mpctx, int start, int count)
}
}
+static void do_clear_pt(struct play_tree *node, struct play_tree *exclude)
+{
+ while (node) {
+ do_clear_pt(node->child, exclude);
+ struct play_tree *next = node->next;
+ // do not delete root node, or nodes that could lead to "exclude" node
+ if (node->parent && !node->child && node != exclude)
+ play_tree_remove(node, 1, 1);
+ node = next;
+ }
+}
+
+static void clear_play_tree(MPContext *mpctx)
+{
+ struct play_tree *exclude = NULL;
+ if (mpctx->playtree_iter) {
+ assert(mpctx->playtree == mpctx->playtree_iter->root);
+ exclude = mpctx->playtree_iter->tree;
+ }
+ do_clear_pt(mpctx->playtree, exclude);
+}
+
void run_command(MPContext *mpctx, mp_cmd_t *cmd)
{
struct MPOpts *opts = &mpctx->opts;
@@ -3112,6 +3135,10 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
break;
}
+ case MP_CMD_PLAY_TREE_CLEAR:
+ clear_play_tree(mpctx);
+ break;
+
case MP_CMD_STOP:
// Go back to the starting point.
while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1) !=
diff --git a/configure b/configure
index 5c1e288c42..4fb2de3d8e 100755
--- a/configure
+++ b/configure
@@ -336,6 +336,7 @@ Optional features:
--enable-smb enable Samba (SMB) input [autodetect]
--enable-live enable LIVE555 Streaming Media [disable]
--enable-nemesi enable Nemesi Streaming Media [autodetect]
+ --enable-lcms2 enable LCMS2 support [autodetect]
--disable-vcd disable VCD support [autodetect]
--disable-bluray disable Blu-ray support [autodetect]
--disable-dvdnav disable libdvdnav [autodetect]
@@ -624,6 +625,7 @@ _xanim=auto
_real=auto
_live=no
_nemesi=auto
+_lcms2=auto
_native_rtsp=yes
_xinerama=auto
_mga=auto
@@ -965,6 +967,8 @@ for ac_option do
--disable-live) _live=no ;;
--enable-nemesi) _nemesi=yes ;;
--disable-nemesi) _nemesi=no ;;
+ --enable-lcms2) _lcms2=yes ;;
+ --disable-lcms2) _lcms2=no ;;
--enable-xinerama) _xinerama=yes ;;
--disable-xinerama) _xinerama=no ;;
--enable-mga) _mga=yes ;;
@@ -5544,6 +5548,20 @@ else
fi
echores "$_qtx"
+echocheck "LCMS2 support"
+if test "$_lcms2" = auto ; then
+ _lcms2=no
+ if pkg_config_add lcms2 ; then
+ _lcms2=yes
+ fi
+fi
+if test "$_lcms2" = yes; then
+ def_lcms2="#define CONFIG_LCMS2 1"
+else
+ def_lcms2="#undef CONFIG_LCMS2"
+fi
+echores "$_lcms2"
+
echocheck "Nemesi Streaming Media libraries"
if test "$_nemesi" = auto && test "$networking" = yes ; then
_nemesi=no
@@ -6322,6 +6340,7 @@ LIBDV = $_libdv
LIBDVDCSS_INTERNAL = $_libdvdcss_internal
LIBMAD = $_mad
LIBNEMESI = $_nemesi
+LCMS2 = $_lcms2
LIBNUT = $_libnut
LIBPOSTPROC = $libpostproc
LIBSMBCLIENT = $_smb
@@ -6671,6 +6690,8 @@ $def_smb
$def_socklen_t
$def_vstream
+$def_lcms2
+
/* libvo options */
$def_3dfx
diff --git a/defaultopts.c b/defaultopts.c
index da19528838..d9485c24bf 100644
--- a/defaultopts.c
+++ b/defaultopts.c
@@ -31,13 +31,15 @@ void set_default_mplayer_options(struct MPOpts *opts)
.edition_id = -1,
.user_correct_pts = -1,
.initial_audio_sync = 1,
- .term_osd = 1,
+ .term_osd = 2,
.consolecontrols = 1,
.doubleclick_time = 300,
.audio_id = -1,
.video_id = -1,
.sub_id = -1,
.extension_parsing = 1,
+ .screenshot_jpeg_quality = 85,
+ .screenshot_png_compression = 7,
.audio_output_channels = 2,
.audio_output_format = -1, // AF_FORMAT_UNKNOWN
.playback_speed = 1.,
diff --git a/etc/input.conf b/etc/input.conf
index 7b61344f7f..46bc17d500 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -23,7 +23,7 @@
#
# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with
# modifiers Shift, Ctrl, Alt and Meta, but note that currently reading
-# key combinations is only supported through the video windows of X-based
+# key combinations is only supported through the video windows of certain
# output drivers (not in output windows of other drivers or in a terminal).
MOUSE_BTN0_DBL vo_fullscreen # toggle fullscreen on/off
diff --git a/fmt-conversion.c b/fmt-conversion.c
index 6ab6a4b82f..a35d99174f 100644
--- a/fmt-conversion.c
+++ b/fmt-conversion.c
@@ -55,6 +55,7 @@ static const struct {
{IMGFMT_RGB8, PIX_FMT_BGR8},
{IMGFMT_RGB4, PIX_FMT_BGR4},
{IMGFMT_BGR8, PIX_FMT_PAL8},
+ {IMGFMT_GBRP, PIX_FMT_GBRP},
{IMGFMT_YUY2, PIX_FMT_YUYV422},
{IMGFMT_UYVY, PIX_FMT_UYVY422},
{IMGFMT_NV12, PIX_FMT_NV12},
diff --git a/input/input.c b/input/input.c
index af78314153..91b55d2aeb 100644
--- a/input/input.c
+++ b/input/input.c
@@ -193,6 +193,7 @@ static const mp_cmd_t mp_cmds[] = {
{ MP_CMD_SWITCH_VSYNC, "switch_vsync", { OARG_INT(0) } },
{ MP_CMD_LOADFILE, "loadfile", { ARG_STRING, OARG_INT(0) } },
{ MP_CMD_LOADLIST, "loadlist", { ARG_STRING, OARG_INT(0) } },
+ { MP_CMD_PLAY_TREE_CLEAR, "pt_clear", },
{ MP_CMD_RUN, "run", { ARG_STRING } },
{ MP_CMD_CAPTURING, "capturing", },
{ MP_CMD_VF_CHANGE_RECTANGLE, "change_rectangle", { ARG_INT, ARG_INT } },
@@ -583,9 +584,6 @@ struct cmd_bind_section {
struct cmd_queue {
struct mp_cmd *first;
- struct mp_cmd *last;
- int num_cmds;
- int num_abort_cmds;
};
struct input_ctx {
@@ -719,32 +717,52 @@ static bool is_abort_cmd(int cmd_id)
return false;
}
+static int queue_count_cmds(struct cmd_queue *queue)
+{
+ int res = 0;
+ for (struct mp_cmd *cmd = queue->first; cmd; cmd = cmd->queue_next)
+ res++;
+ return res;
+}
+
+static bool queue_has_abort_cmds(struct cmd_queue *queue)
+{
+ for (struct mp_cmd *cmd = queue->first; cmd; cmd = cmd->queue_next) {
+ if (is_abort_cmd(cmd->id))
+ return true;
+ }
+ return false;
+}
+
+static void queue_remove(struct cmd_queue *queue, struct mp_cmd *cmd)
+{
+ struct mp_cmd **p_prev = &queue->first;
+ while (*p_prev != cmd) {
+ p_prev = &(*p_prev)->queue_next;
+ }
+ // if this fails, cmd was not in the queue
+ assert(*p_prev == cmd);
+ *p_prev = cmd->queue_next;
+}
+
static void queue_pop(struct cmd_queue *queue)
{
- assert(queue->num_cmds > 0);
- struct mp_cmd *cmd = queue->first;
- queue->first = cmd->queue_next;
- queue->num_cmds--;
- queue->num_abort_cmds -= is_abort_cmd(cmd->id);
+ queue_remove(queue, queue->first);
}
static void queue_add(struct cmd_queue *queue, struct mp_cmd *cmd,
bool at_head)
{
- if (!queue->num_cmds) {
- queue->first = cmd;
- queue->last = cmd;
- } else if (at_head) {
- queue->first->queue_prev = cmd;
+ if (at_head) {
cmd->queue_next = queue->first;
queue->first = cmd;
} else {
- queue->last->queue_next = cmd;
- cmd->queue_prev = queue->last;
- queue->last = cmd;
+ struct mp_cmd **p_prev = &queue->first;
+ while (*p_prev)
+ p_prev = &(*p_prev)->queue_next;
+ *p_prev = cmd;
+ cmd->queue_next = NULL;
}
- queue->num_cmds++;
- queue->num_abort_cmds += is_abort_cmd(cmd->id);
}
int mp_input_add_cmd_fd(struct input_ctx *ictx, int fd, int select,
@@ -1316,8 +1334,8 @@ void mp_input_feed_key(struct input_ctx *ictx, int code)
if (!cmd)
return;
struct cmd_queue *queue = &ictx->key_cmd_queue;
- if (queue->num_cmds >= ictx->key_fifo_size &&
- (!is_abort_cmd(cmd->id) || queue->num_abort_cmds))
+ if (queue_count_cmds(queue) >= ictx->key_fifo_size &&
+ (!is_abort_cmd(cmd->id) || queue_has_abort_cmds(queue)))
return;
queue_add(queue, cmd, false);
}
@@ -1477,14 +1495,14 @@ mp_cmd_t *mp_input_get_cmd(struct input_ctx *ictx, int time, int peek_only)
if (async_quit_request)
return mp_input_parse_cmd("quit 1");
- if (ictx->control_cmd_queue.num_cmds || ictx->key_cmd_queue.num_cmds)
+ if (ictx->control_cmd_queue.first || ictx->key_cmd_queue.first)
time = 0;
read_all_events(ictx, time);
struct mp_cmd *ret;
struct cmd_queue *queue = &ictx->control_cmd_queue;
- if (!queue->num_cmds)
+ if (!queue->first)
queue = &ictx->key_cmd_queue;
- if (!queue->num_cmds) {
+ if (!queue->first) {
ret = check_autorepeat(ictx);
if (!ret)
return NULL;
@@ -1950,8 +1968,8 @@ void mp_input_wakeup(struct input_ctx *ictx)
int mp_input_check_interrupt(struct input_ctx *ictx, int time)
{
for (int i = 0; ; i++) {
- if (async_quit_request || ictx->key_cmd_queue.num_abort_cmds ||
- ictx->control_cmd_queue.num_abort_cmds) {
+ if (async_quit_request || queue_has_abort_cmds(&ictx->key_cmd_queue) ||
+ queue_has_abort_cmds(&ictx->control_cmd_queue)) {
mp_tmsg(MSGT_INPUT, MSGL_WARN, "Received command to move to "
"another file. Aborting current processing.\n");
return true;
diff --git a/input/input.h b/input/input.h
index c5f6940990..87814dd56c 100644
--- a/input/input.h
+++ b/input/input.h
@@ -52,6 +52,7 @@ enum mp_command_type {
MP_CMD_MUTE,
MP_CMD_LOADFILE,
MP_CMD_LOADLIST,
+ MP_CMD_PLAY_TREE_CLEAR,
MP_CMD_VF_CHANGE_RECTANGLE,
MP_CMD_GAMMA,
MP_CMD_SUB_VISIBILITY,
@@ -193,7 +194,6 @@ typedef struct mp_cmd {
struct mp_cmd_arg args[MP_CMD_MAX_ARGS];
int nargs;
int pausing;
- struct mp_cmd *queue_prev;
struct mp_cmd *queue_next;
} mp_cmd_t;
diff --git a/input/keycodes.h b/input/keycodes.h
index 84b41a3e89..c86a4bc138 100644
--- a/input/keycodes.h
+++ b/input/keycodes.h
@@ -30,16 +30,15 @@
#define KEY_TAB 9
/* Control keys */
-#define KEY_CTRL (MP_KEY_BASE)
-#define KEY_BACKSPACE (KEY_CTRL+0)
-#define KEY_DELETE (KEY_CTRL+1)
-#define KEY_INSERT (KEY_CTRL+2)
-#define KEY_HOME (KEY_CTRL+3)
-#define KEY_END (KEY_CTRL+4)
-#define KEY_PAGE_UP (KEY_CTRL+5)
-#define KEY_PAGE_DOWN (KEY_CTRL+6)
-#define KEY_ESC (KEY_CTRL+7)
-#define KEY_PRINT (KEY_CTRL+8)
+#define KEY_BACKSPACE (MP_KEY_BASE+0)
+#define KEY_DELETE (MP_KEY_BASE+1)
+#define KEY_INSERT (MP_KEY_BASE+2)
+#define KEY_HOME (MP_KEY_BASE+3)
+#define KEY_END (MP_KEY_BASE+4)
+#define KEY_PAGE_UP (MP_KEY_BASE+5)
+#define KEY_PAGE_DOWN (MP_KEY_BASE+6)
+#define KEY_ESC (MP_KEY_BASE+7)
+#define KEY_PRINT (MP_KEY_BASE+8)
/* Control keys short name */
#define KEY_BS KEY_BACKSPACE
diff --git a/libao2/ao_dsound.c b/libao2/ao_dsound.c
index b33f2949fd..f1fc0dd00a 100644
--- a/libao2/ao_dsound.c
+++ b/libao2/ao_dsound.c
@@ -121,8 +121,10 @@ static int min_free_space = 0; ///if the free space is below this val
///there will always be at least this amout of free space to prevent
///get_space() from returning wrong values when buffer is 100% full.
///will be replaced with nBlockAlign in init()
+static int underrun_check = 0; ///0 or last reported free space (underrun detection)
static int device_num = 0; ///wanted device number
static GUID device; ///guid of the device
+static int audio_volume;
/***************************************************************************************/
@@ -314,6 +316,8 @@ static int write_buffer(unsigned char *data, int len)
LPVOID lpvPtr2;
DWORD dwBytes2;
+ underrun_check = 0;
+
// Lock the buffer
res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
// If the buffer was lost, restore and retry lock.
@@ -391,16 +395,16 @@ static int control(int cmd, void *arg)
switch (cmd) {
case AOCONTROL_GET_VOLUME: {
ao_control_vol_t* vol = (ao_control_vol_t*)arg;
- IDirectSoundBuffer_GetVolume(hdsbuf, &volume);
- vol->left = vol->right = pow(10.0, (float)(volume+10000) / 5000.0);
- //printf("ao_dsound: volume: %f\n",vol->left);
+ vol->left = vol->right = audio_volume;
return CONTROL_OK;
}
case AOCONTROL_SET_VOLUME: {
ao_control_vol_t* vol = (ao_control_vol_t*)arg;
- volume = (DWORD)(log10(vol->right) * 5000.0) - 10000;
+ volume = audio_volume = vol->right;
+ if (volume < 1)
+ volume = 1;
+ volume = (DWORD)(log10(volume) * 5000.0) - 10000;
IDirectSoundBuffer_SetVolume(hdsbuf, volume);
- //printf("ao_dsound: volume: %f\n",vol->left);
return CONTROL_OK;
}
}
@@ -421,6 +425,7 @@ static int init(int rate, int channels, int format, int flags)
if (!InitDirectSound()) return 0;
global_ao->no_persistent_volume = true;
+ audio_volume = 100;
// ok, now create the buffers
WAVEFORMATEXTENSIBLE wformat;
@@ -544,6 +549,7 @@ static void reset(void)
// reset directsound buffer
IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
write_offset=0;
+ underrun_check=0;
}
/**
@@ -568,22 +574,16 @@ static void audio_resume(void)
*/
static void uninit(int immed)
{
- if(immed)reset();
- else{
- DWORD status;
- IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0);
- while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING))
- usec_sleep(20000);
- }
+ if (!immed)
+ usec_sleep(get_delay() * 1000000);
+ reset();
+
DestroyBuffer();
UninitDirectSound();
}
-/**
-\brief find out how many bytes can be written into the audio buffer without
-\return free space in bytes, has to return 0 if the buffer is almost full
-*/
-static int get_space(void)
+// return exact number of free (safe to write) bytes
+static int check_free_buffer_size(void)
{
int space;
DWORD play_offset;
@@ -595,6 +595,28 @@ static int get_space(void)
// write_cursor is the position after which it is assumed to be save to write data
// write_offset is the postion where we actually write the data to
if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
+ // Check for buffer underruns. An underrun happens if DirectSound
+ // started to play old data beyond the current write_offset. Detect this
+ // by checking whether the free space shrinks, even though no data was
+ // written (i.e. no write_buffer). Doesn't always work, but the only
+ // reason we need this is to deal with the situation when playback ends,
+ // and the buffer is only half-filled.
+ if (space < underrun_check) {
+ // there's no useful data in the buffers
+ space = buffer_size;
+ reset();
+ }
+ underrun_check = space;
+ return space;
+}
+
+/**
+\brief find out how many bytes can be written into the audio buffer without
+\return free space in bytes, has to return 0 if the buffer is almost full
+*/
+static int get_space(void)
+{
+ int space = check_free_buffer_size();
if(space < min_free_space)return 0;
return space-min_free_space;
}
@@ -608,13 +630,7 @@ static int get_space(void)
*/
static int play(void* data, int len, int flags)
{
- DWORD play_offset;
- int space;
-
- // make sure we have enough space to write data
- IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
- space=buffer_size-(write_offset-play_offset);
- if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
+ int space = check_free_buffer_size();
if(space < len) len = space;
if (!(flags & AOPLAY_FINAL_CHUNK))
@@ -628,10 +644,6 @@ static int play(void* data, int len, int flags)
*/
static float get_delay(void)
{
- DWORD play_offset;
- int space;
- IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
- space=play_offset-write_offset;
- if(space <= 0)space += buffer_size;
+ int space = check_free_buffer_size();
return (float)(buffer_size - space) / (float)ao_data.bps;
}
diff --git a/libao2/ao_openal.c b/libao2/ao_openal.c
index 490aac0eb8..0e04f2cc81 100644
--- a/libao2/ao_openal.c
+++ b/libao2/ao_openal.c
@@ -28,9 +28,11 @@
#ifdef OPENAL_AL_H
#include <OpenAL/alc.h>
#include <OpenAL/al.h>
+#include <OpenAL/alext.h>
#else
#include <AL/alc.h>
#include <AL/al.h>
+#include <AL/alext.h>
#endif
#include "mp_msg.h"
@@ -86,11 +88,27 @@ static int control(int cmd, void *arg) {
static void print_help(void) {
mp_msg(MSGT_AO, MSGL_FATAL,
"\n-ao openal commandline help:\n"
- "Example: mplayer -ao openal\n"
+ "Example: mplayer -ao openal:device=subdevice\n"
"\nOptions:\n"
+ " device=subdevice\n"
+ " Audio device OpenAL should use. Devices can be listed\n"
+ " with -ao openal:device=help\n"
);
}
+static void list_devices(void) {
+ if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_TRUE) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "Device listing not supported.\n");
+ return;
+ }
+ const char *list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
+ mp_msg(MSGT_AO, MSGL_FATAL, "OpenAL devices:\n");
+ while (list && *list) {
+ mp_msg(MSGT_AO, MSGL_FATAL, " '%s'\n", list);
+ list = list + strlen(list) + 1;
+ }
+}
+
static int init(int rate, int channels, int format, int flags) {
float position[3] = {0, 0, 0};
float direction[6] = {0, 0, 1, 0, -1, 0};
@@ -105,7 +123,9 @@ static int init(int rate, int channels, int format, int flags) {
ALCint freq = 0;
ALCint attribs[] = {ALC_FREQUENCY, rate, 0, 0};
int i;
+ char *device = NULL;
const opt_t subopts[] = {
+ {"device", OPT_ARG_MSTRZ, &device, NULL},
{NULL}
};
global_ao->no_persistent_volume = true;
@@ -113,11 +133,15 @@ static int init(int rate, int channels, int format, int flags) {
print_help();
return 0;
}
+ if (device && strcmp(device, "help") == 0) {
+ list_devices();
+ goto err_out;
+ }
if (channels > MAX_CHANS) {
mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Invalid number of channels: %i\n", channels);
goto err_out;
}
- dev = alcOpenDevice(NULL);
+ dev = alcOpenDevice(device);
if (!dev) {
mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] could not open device\n");
goto err_out;
@@ -146,9 +170,11 @@ static int init(int rate, int channels, int format, int flags) {
ao_data.buffersize = CHUNK_SIZE * NUM_BUF;
ao_data.outburst = channels * CHUNK_SIZE;
tmpbuf = malloc(CHUNK_SIZE);
+ free(device);
return 1;
err_out:
+ free(device);
return 0;
}
diff --git a/libmpcodecs/img_format.c b/libmpcodecs/img_format.c
index c6d3e82149..033c0a4e3e 100644
--- a/libmpcodecs/img_format.c
+++ b/libmpcodecs/img_format.c
@@ -50,6 +50,7 @@ const char *vo_format_name(int format)
case IMGFMT_BGRA: return "BGRA";
case IMGFMT_ARGB: return "ARGB";
case IMGFMT_RGBA: return "RGBA";
+ case IMGFMT_GBRP: return "Planar GBR 24-bit";
case IMGFMT_YVU9: return "Planar YVU9";
case IMGFMT_IF09: return "Planar IF09";
case IMGFMT_YV12: return "Planar YV12";
diff --git a/libmpcodecs/img_format.h b/libmpcodecs/img_format.h
index d4443af2c9..4200282f98 100644
--- a/libmpcodecs/img_format.h
+++ b/libmpcodecs/img_format.h
@@ -49,6 +49,8 @@
#define IMGFMT_BGR24 (IMGFMT_BGR|24)
#define IMGFMT_BGR32 (IMGFMT_BGR|32)
+#define IMGFMT_GBRP (('G'<<24)|('B'<<16)|('R'<<8)|24)
+
#if HAVE_BIGENDIAN
#define IMGFMT_ABGR IMGFMT_RGB32
#define IMGFMT_BGRA (IMGFMT_RGB32|64)
diff --git a/libmpcodecs/mp_image.c b/libmpcodecs/mp_image.c
index 45426eb673..4e20dee119 100644
--- a/libmpcodecs/mp_image.c
+++ b/libmpcodecs/mp_image.c
@@ -119,8 +119,13 @@ void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt){
mpi->flags|=MP_IMGFLAG_SWAPPED;
return;
}
- mpi->flags|=MP_IMGFLAG_YUV;
mpi->num_planes=3;
+ if (out_fmt == IMGFMT_GBRP) {
+ mpi->bpp=24;
+ mpi->flags|=MP_IMGFLAG_PLANAR;
+ return;
+ }
+ mpi->flags|=MP_IMGFLAG_YUV;
if (mp_get_chroma_shift(out_fmt, NULL, NULL, NULL)) {
mpi->flags|=MP_IMGFLAG_PLANAR;
mpi->bpp = mp_get_chroma_shift(out_fmt, &mpi->chroma_x_shift, &mpi->chroma_y_shift, NULL);
diff --git a/libmpcodecs/mp_image.h b/libmpcodecs/mp_image.h
index dd69788f26..6dbe3bfe02 100644
--- a/libmpcodecs/mp_image.h
+++ b/libmpcodecs/mp_image.h
@@ -103,7 +103,7 @@ typedef struct mp_image {
unsigned char bpp; // bits/pixel. NOT depth! for RGB it will be n*8
unsigned int imgfmt;
int width,height; // stored dimensions
- int x,y,w,h; // visible dimensions
+ int w,h; // visible dimensions
unsigned char* planes[MP_MAX_PLANES];
int stride[MP_MAX_PLANES];
char * qscale;
diff --git a/libmpcodecs/vd.c b/libmpcodecs/vd.c
index a883f257b5..e756c2d53e 100644
--- a/libmpcodecs/vd.c
+++ b/libmpcodecs/vd.c
@@ -364,11 +364,7 @@ int mpcodecs_config_vo2(sh_video_t *sh, int w, int h,
mp_image_t *mpcodecs_get_image(sh_video_t *sh, int mp_imgtype, int mp_imgflag,
int w, int h)
{
- mp_image_t *mpi =
- vf_get_image(sh->vfilter, sh->outfmt, mp_imgtype, mp_imgflag, w, h);
- if (mpi)
- mpi->x = mpi->y = 0;
- return mpi;
+ return vf_get_image(sh->vfilter, sh->outfmt, mp_imgtype, mp_imgflag, w, h);
}
void mpcodecs_draw_slice(sh_video_t *sh, unsigned char **src, int *stride,
diff --git a/libmpcodecs/vf.c b/libmpcodecs/vf.c
index f4d6768531..83ae763fba 100644
--- a/libmpcodecs/vf.c
+++ b/libmpcodecs/vf.c
@@ -106,6 +106,7 @@ extern const vf_info_t vf_info_divtc;
extern const vf_info_t vf_info_harddup;
extern const vf_info_t vf_info_softskip;
extern const vf_info_t vf_info_screenshot;
+extern const vf_info_t vf_info_screenshot_force;
extern const vf_info_t vf_info_ass;
extern const vf_info_t vf_info_mcdeint;
extern const vf_info_t vf_info_yadif;
@@ -140,6 +141,7 @@ static const vf_info_t *const filter_list[] = {
&vf_info_lavc,
&vf_info_lavcdeint,
&vf_info_screenshot,
+ &vf_info_screenshot_force,
&vf_info_fspp,
&vf_info_uspp,
diff --git a/libmpcodecs/vf_ass.c b/libmpcodecs/vf_ass.c
index 82351500b3..4dcddb1d08 100644
--- a/libmpcodecs/vf_ass.c
+++ b/libmpcodecs/vf_ass.c
@@ -61,8 +61,9 @@ static const struct vf_priv_s {
int auto_insert;
struct osd_state *osd;
- ASS_Renderer *renderer_realaspect;
- ASS_Renderer *renderer_vsfilter;
+ ASS_Renderer *renderer;
+
+ double realaspect;
unsigned char *planes[3];
struct line_limits {
@@ -93,14 +94,10 @@ static int config(struct vf_instance *vf,
vf->priv->planes[2] = malloc(vf->priv->outw * vf->priv->outh);
vf->priv->line_limits = malloc((vf->priv->outh + 1) / 2 * sizeof(*vf->priv->line_limits));
- if (vf->priv->renderer_realaspect) {
- mp_ass_configure(vf->priv->renderer_realaspect, opts,
- vf->priv->outw, vf->priv->outh, 0);
- mp_ass_configure(vf->priv->renderer_vsfilter, opts,
+ if (vf->priv->renderer) {
+ mp_ass_configure(vf->priv->renderer, opts,
vf->priv->outw, vf->priv->outh, 0);
- ass_set_aspect_ratio(vf->priv->renderer_realaspect,
- (double)width / height * d_height / d_width, 1);
- ass_set_aspect_ratio(vf->priv->renderer_vsfilter, 1, 1);
+ vf->priv->realaspect = (double)width / height * d_height / d_width;
}
return vf_next_config(vf, vf->priv->outw, vf->priv->outh, d_width,
@@ -363,17 +360,16 @@ static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
{
struct osd_state *osd = vf->priv->osd;
ASS_Image *images = 0;
- ASS_Renderer *renderer = osd->vsfilter_aspect
+ double scale = osd->vsfilter_aspect
&& vf->opts->ass_vsfilter_aspect_compat
- ? vf->priv->renderer_vsfilter : vf->priv->renderer_realaspect;
- if (sub_visibility && renderer && osd->ass_track
+ ? 1 : vf->priv->realaspect;
+ if (sub_visibility && vf->priv->renderer && osd->ass_track
&& (pts != MP_NOPTS_VALUE)) {
- if (osd->ass_force_reload) {
- mp_ass_reload_options(vf->priv->renderer_realaspect, vf->opts);
- mp_ass_reload_options(vf->priv->renderer_vsfilter, vf->opts);
- }
+ ass_set_aspect_ratio(vf->priv->renderer, scale, 1);
+ if (osd->ass_force_reload)
+ mp_ass_reload_options(vf->priv->renderer, vf->opts);
osd->ass_force_reload = false;
- images = ass_render_frame(renderer, osd->ass_track,
+ images = ass_render_frame(vf->priv->renderer, osd->ass_track,
(pts - osd->sub_offset + sub_delay) * 1000 + .5, NULL);
}
@@ -402,19 +398,13 @@ static int control(vf_instance_t *vf, int request, void *data)
vf->priv->osd = data;
break;
case VFCTRL_INIT_EOSD:
- vf->priv->renderer_realaspect = ass_renderer_init((ASS_Library *)data);
- if (!vf->priv->renderer_realaspect)
+ vf->priv->renderer = ass_renderer_init((ASS_Library *)data);
+ if (!vf->priv->renderer)
return CONTROL_FALSE;
- vf->priv->renderer_vsfilter = ass_renderer_init((ASS_Library *)data);
- if (!vf->priv->renderer_vsfilter) {
- ass_renderer_done(vf->priv->renderer_realaspect);
- return CONTROL_FALSE;
- }
- mp_ass_configure_fonts(vf->priv->renderer_realaspect);
- mp_ass_configure_fonts(vf->priv->renderer_vsfilter);
+ mp_ass_configure_fonts(vf->priv->renderer);
return CONTROL_TRUE;
case VFCTRL_DRAW_EOSD:
- if (vf->priv->renderer_realaspect)
+ if (vf->priv->renderer)
return CONTROL_TRUE;
break;
}
@@ -423,10 +413,8 @@ static int control(vf_instance_t *vf, int request, void *data)
static void uninit(struct vf_instance *vf)
{
- if (vf->priv->renderer_realaspect) {
- ass_renderer_done(vf->priv->renderer_realaspect);
- ass_renderer_done(vf->priv->renderer_vsfilter);
- }
+ if (vf->priv->renderer)
+ ass_renderer_done(vf->priv->renderer);
free(vf->priv->planes[1]);
free(vf->priv->planes[2]);
free(vf->priv->line_limits);
diff --git a/libmpcodecs/vf_gradfun.c b/libmpcodecs/vf_gradfun.c
index 813b9ecb3f..c74fbfe5fb 100644
--- a/libmpcodecs/vf_gradfun.c
+++ b/libmpcodecs/vf_gradfun.c
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
+#include <math.h>
#include "config.h"
#include "cpudetect.h"
@@ -41,7 +42,13 @@
#include "libavutil/avutil.h"
#include "ffmpeg_files/x86_cpu.h"
+#include "m_option.h"
+#include "m_struct.h"
+
struct vf_priv_s {
+ float cfg_thresh;
+ int cfg_radius;
+ float cfg_size;
int thresh;
int radius;
uint16_t *buf;
@@ -49,6 +56,10 @@ struct vf_priv_s {
int width, int thresh, const uint16_t *dithers);
void (*blur_line)(uint16_t *dc, uint16_t *buf, uint16_t *buf1,
uint8_t *src, int sstride, int width);
+} const vf_priv_dflt = {
+ .cfg_thresh = 1.5,
+ .cfg_radius = -1,
+ .cfg_size = -1,
};
static const uint16_t __attribute__((aligned(16))) pw_7f[8] = {127,127,127,127,127,127,127,127};
@@ -354,6 +365,12 @@ static int config(struct vf_instance *vf,
unsigned int flags, unsigned int outfmt)
{
free(vf->priv->buf);
+ vf->priv->radius = vf->priv->cfg_radius;
+ if (vf->priv->cfg_size > -1) {
+ vf->priv->radius = (vf->priv->cfg_size / 100.0f)
+ * sqrtf(width * width + height * height);
+ }
+ vf->priv->radius = av_clip((vf->priv->radius+1)&~1, 4, 32);
vf->priv->buf = av_mallocz((((width+15)&~15)*(vf->priv->radius+1)/2+32)*sizeof(uint16_t));
return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
}
@@ -368,20 +385,25 @@ static void uninit(struct vf_instance *vf)
static int vf_open(vf_instance_t *vf, char *args)
{
- float thresh = 1.2;
- int radius = 16;
-
vf->get_image=get_image;
vf->put_image=put_image;
vf->query_format=query_format;
vf->config=config;
vf->uninit=uninit;
- vf->priv=malloc(sizeof(struct vf_priv_s));
- memset(vf->priv, 0, sizeof(struct vf_priv_s));
- if (args) sscanf(args, "%f:%d", &thresh, &radius);
- vf->priv->thresh = (1<<15)/av_clipf(thresh,0.51,255);
- vf->priv->radius = av_clip((radius+1)&~1,4,32);
+ bool have_radius = vf->priv->cfg_radius > -1;
+ bool have_size = vf->priv->cfg_size > -1;
+
+ if (have_radius && have_size) {
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "scale: gradfun: only one of "
+ "radius/size parameters allowed at the same time!\n");
+ return 0;
+ }
+
+ if (!have_radius && !have_size)
+ vf->priv->cfg_size = 1.0;
+
+ vf->priv->thresh = (1<<15)/av_clipf(vf->priv->cfg_thresh,0.51,255);
vf->priv->blur_line = blur_line_c;
vf->priv->filter_line = filter_line_c;
@@ -401,11 +423,27 @@ static int vf_open(vf_instance_t *vf, char *args)
return 1;
}
+#undef ST_OFF
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ {"strength", ST_OFF(cfg_thresh), CONF_TYPE_FLOAT, M_OPT_RANGE, 0.51, 255, NULL},
+ {"radius", ST_OFF(cfg_radius), CONF_TYPE_INT, M_OPT_RANGE, 4, 32, NULL},
+ {"size", ST_OFF(cfg_size), CONF_TYPE_FLOAT, M_OPT_RANGE, 0.1, 5.0, NULL},
+ { NULL, NULL, 0, 0, 0, 0, NULL }
+};
+
+static const m_struct_t vf_opts = {
+ "gradfun",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
const vf_info_t vf_info_gradfun = {
"gradient deband",
"gradfun",
"Loren Merritt",
"",
vf_open,
- NULL
+ &vf_opts
};
diff --git a/libmpcodecs/vf_scale.c b/libmpcodecs/vf_scale.c
index 585ef4d9a1..4ccef63054 100644
--- a/libmpcodecs/vf_scale.c
+++ b/libmpcodecs/vf_scale.c
@@ -107,6 +107,7 @@ static const unsigned int outfmt_list[]={
IMGFMT_RGB32,
IMGFMT_BGR24,
IMGFMT_RGB24,
+ IMGFMT_GBRP,
IMGFMT_RGB48LE,
IMGFMT_RGB48BE,
IMGFMT_BGR16,
@@ -141,6 +142,10 @@ static int preferred_conversions[][2] = {
{IMGFMT_UYVY, IMGFMT_422P},
{IMGFMT_422P, IMGFMT_YUY2},
{IMGFMT_422P, IMGFMT_UYVY},
+ {IMGFMT_GBRP, IMGFMT_BGR24},
+ {IMGFMT_GBRP, IMGFMT_RGB24},
+ {IMGFMT_GBRP, IMGFMT_BGR32},
+ {IMGFMT_GBRP, IMGFMT_RGB32},
{0, 0}
};
@@ -704,7 +709,7 @@ void sws_getFlagsAndFilterFromCmdLine(int *flags, SwsFilter **srcFilterParam, Sw
}
// will use sws_flags & src_filter (from cmd line)
-struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat)
+static struct SwsContext *sws_getContextFromCmdLine2(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat, int extraflags)
{
int flags;
SwsFilter *dstFilterParam, *srcFilterParam;
@@ -715,7 +720,18 @@ struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat,
if (srcFormat == IMGFMT_RGB8 || srcFormat == IMGFMT_BGR8) sfmt = PIX_FMT_PAL8;
sws_getFlagsAndFilterFromCmdLine(&flags, &srcFilterParam, &dstFilterParam);
- return sws_getContext(srcW, srcH, sfmt, dstW, dstH, dfmt, flags | get_sws_cpuflags(), srcFilterParam, dstFilterParam, NULL);
+ return sws_getContext(srcW, srcH, sfmt, dstW, dstH, dfmt, flags | extraflags | get_sws_cpuflags(), srcFilterParam, dstFilterParam, NULL);
+}
+
+struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat)
+{
+ return sws_getContextFromCmdLine2(srcW, srcH, srcFormat, dstW, dstH, dstFormat, 0);
+}
+
+struct SwsContext *sws_getContextFromCmdLine_hq(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat)
+{
+ return sws_getContextFromCmdLine2(srcW, srcH, srcFormat, dstW, dstH, dstFormat,
+ SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP | SWS_ACCURATE_RND | SWS_BITEXACT);
}
/// An example of presets usage
diff --git a/libmpcodecs/vf_scale.h b/libmpcodecs/vf_scale.h
index a9b3b506d5..08d651ce16 100644
--- a/libmpcodecs/vf_scale.h
+++ b/libmpcodecs/vf_scale.h
@@ -21,6 +21,7 @@
int get_sws_cpuflags(void);
struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat);
+struct SwsContext *sws_getContextFromCmdLine_hq(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat);
struct mp_csp_details;
int mp_sws_set_colorspace(struct SwsContext *sws, struct mp_csp_details *csp);
diff --git a/libmpcodecs/vf_screenshot.c b/libmpcodecs/vf_screenshot.c
index 7f9a45c879..eb961d8118 100644
--- a/libmpcodecs/vf_screenshot.c
+++ b/libmpcodecs/vf_screenshot.c
@@ -197,7 +197,6 @@ static int vf_open(vf_instance_t *vf, char *args)
return 1;
}
-
const vf_info_t vf_info_screenshot = {
"screenshot to file",
"screenshot",
@@ -207,4 +206,15 @@ const vf_info_t vf_info_screenshot = {
NULL
};
+// screenshot.c will look for a filter named "screenshot_force", and not use
+// the VO based screenshot code if it's in the filter chain.
+const vf_info_t vf_info_screenshot_force = {
+ "screenshot to file (override VO based screenshot code)",
+ "screenshot_force",
+ "A'rpi, Jindrich Makovicka",
+ "",
+ vf_open,
+ NULL
+};
+
//===========================================================================//
diff --git a/libmpcodecs/vf_vo.c b/libmpcodecs/vf_vo.c
index 0f89d7312f..a6bd165dee 100644
--- a/libmpcodecs/vf_vo.c
+++ b/libmpcodecs/vf_vo.c
@@ -38,8 +38,7 @@ extern float sub_delay;
struct vf_priv_s {
struct vo *vo;
#ifdef CONFIG_ASS
- ASS_Renderer *renderer_realaspect;
- ASS_Renderer *renderer_vsfilter;
+ ASS_Renderer *renderer;
bool prev_visibility;
double scale_ratio;
#endif
@@ -85,10 +84,8 @@ static int config(struct vf_instance *vf,
#ifdef CONFIG_ASS
vf->priv->scale_ratio = (double) d_width / d_height * height / width;
- if (vf->priv->renderer_realaspect) {
- mp_ass_configure(vf->priv->renderer_realaspect, vf->opts, width, height,
- vf->default_caps & VFCAP_EOSD_UNSCALED);
- mp_ass_configure(vf->priv->renderer_vsfilter, vf->opts, width, height,
+ if (vf->priv->renderer) {
+ mp_ass_configure(vf->priv->renderer, vf->opts, width, height,
vf->default_caps & VFCAP_EOSD_UNSCALED);
}
@@ -138,30 +135,23 @@ static int control(struct vf_instance *vf, int request, void *data)
return vo_control(video_out, VOCTRL_GET_EQUALIZER, &param) == VO_TRUE;
}
#ifdef CONFIG_ASS
- case VFCTRL_INIT_EOSD: {
- vf->priv->renderer_realaspect = ass_renderer_init(data);
- if (!vf->priv->renderer_realaspect)
- return CONTROL_FALSE;
- vf->priv->renderer_vsfilter = ass_renderer_init(data);
- if (!vf->priv->renderer_vsfilter) {
- ass_renderer_done(vf->priv->renderer_realaspect);
+ case VFCTRL_INIT_EOSD:
+ {
+ vf->priv->renderer = ass_renderer_init(data);
+ if (!vf->priv->renderer)
return CONTROL_FALSE;
- }
- mp_ass_configure_fonts(vf->priv->renderer_realaspect);
- mp_ass_configure_fonts(vf->priv->renderer_vsfilter);
+ mp_ass_configure_fonts(vf->priv->renderer);
vf->priv->prev_visibility = false;
return CONTROL_TRUE;
}
case VFCTRL_DRAW_EOSD: {
struct osd_state *osd = data;
- mp_eosd_images_t images = { NULL, 2 };
- ASS_Renderer *renderer;
+ mp_eosd_images_t images = {NULL, 2};
+ ASS_Renderer *renderer = vf->priv->renderer;
double scale;
if (osd->vsfilter_aspect && vf->opts->ass_vsfilter_aspect_compat) {
- renderer = vf->priv->renderer_vsfilter;
scale = vf->priv->scale_ratio;
} else {
- renderer = vf->priv->renderer_realaspect;
scale = 1;
}
if (!video_out->config_ok || !renderer)
@@ -177,10 +167,8 @@ static int control(struct vf_instance *vf, int request, void *data)
ass_set_aspect_ratio(renderer, scale, 1);
}
- if (osd->ass_force_reload) {
- mp_ass_reload_options(vf->priv->renderer_realaspect, vf->opts);
- mp_ass_reload_options(vf->priv->renderer_vsfilter, vf->opts);
- }
+ if (osd->ass_force_reload)
+ mp_ass_reload_options(vf->priv->renderer, vf->opts);
images.imgs = ass_render_frame(renderer, osd->ass_track,
(osd->pts + sub_delay) * 1000 + .5,
&images.changed);
@@ -229,7 +217,7 @@ static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
// blit frame:
if (vf->default_caps & VFCAP_ACCEPT_STRIDE)
vo_draw_slice(video_out, mpi->planes, mpi->stride, mpi->w, mpi->h,
- mpi->x, mpi->y);
+ 0, 0);
else
vo_draw_frame(video_out, mpi->planes);
}
@@ -258,10 +246,8 @@ static void uninit(struct vf_instance *vf)
* to get rid of numbered-mpi references that will now be invalid. */
vo_seek_reset(video_out);
#ifdef CONFIG_ASS
- if (vf->priv->renderer_realaspect) {
- ass_renderer_done(vf->priv->renderer_realaspect);
- ass_renderer_done(vf->priv->renderer_vsfilter);
- }
+ if (vf->priv->renderer)
+ ass_renderer_done(vf->priv->renderer);
#endif
free(vf->priv);
}
diff --git a/libmpdemux/demux_cue.c b/libmpdemux/demux_cue.c
new file mode 100644
index 0000000000..d2fd06ce71
--- /dev/null
+++ b/libmpdemux/demux_cue.c
@@ -0,0 +1,65 @@
+/*
+ * 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 <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "bstr.h"
+#include "demuxer.h"
+#include "stream/stream.h"
+
+// timeline/tl_cue.c
+bool mp_probe_cue(struct bstr s);
+
+#define PROBE_SIZE 512
+
+static int try_open_file(struct demuxer *demuxer)
+{
+ struct stream *s = demuxer->stream;
+ char buf[PROBE_SIZE];
+ int len = stream_read(s, buf, sizeof(buf));
+ if (len <= 0)
+ return 0;
+ if (!mp_probe_cue((struct bstr) { buf, len }))
+ return 0;
+ stream_seek(s, 0);
+ demuxer->file_contents = stream_read_complete(s, demuxer, 1000000, 0);
+ if (demuxer->file_contents.start == NULL)
+ return 0;
+ if (!mp_probe_cue((struct bstr) { buf, len }))
+ return 0;
+ return DEMUXER_TYPE_CUE;
+}
+
+static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds)
+{
+ return 0;
+}
+
+const struct demuxer_desc demuxer_desc_cue = {
+ .info = "CUE file demuxer",
+ .name = "cue",
+ .shortdesc = "CUE",
+ .author = "Uoti Urpala",
+ .comment = "",
+ .type = DEMUXER_TYPE_CUE,
+ .safe_check = true,
+ .check_file = try_open_file, // no separate .open
+ .fill_buffer = dummy_fill_buffer,
+};
diff --git a/libmpdemux/demux_lmlm4.c b/libmpdemux/demux_lmlm4.c
index 05cec05282..739343cdb4 100644
--- a/libmpdemux/demux_lmlm4.c
+++ b/libmpdemux/demux_lmlm4.c
@@ -181,7 +181,6 @@ static int lmlm4_check_file(demuxer_t* demuxer)
mp_msg(MSGT_DEMUX, MSGL_V, "Checking for LMLM4 Stream Format\n");
if(getFrame(demuxer, &frameInfo)!=1){
- stream_skip(demuxer->stream,-8);
mp_msg(MSGT_DEMUX, MSGL_V, "LMLM4 Stream Format not found\n");
return 0;
}
diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c
index f00b0551d5..ea8c4e01f1 100644
--- a/libmpdemux/demuxer.c
+++ b/libmpdemux/demuxer.c
@@ -52,6 +52,7 @@ static void clear_parser(sh_common_t *sh);
// Demuxer list
extern const struct demuxer_desc demuxer_desc_edl;
+extern const struct demuxer_desc demuxer_desc_cue;
extern const demuxer_desc_t demuxer_desc_rawaudio;
extern const demuxer_desc_t demuxer_desc_rawvideo;
extern const demuxer_desc_t demuxer_desc_tv;
@@ -101,6 +102,7 @@ extern const demuxer_desc_t demuxer_desc_mng;
const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_edl,
+ &demuxer_desc_cue,
&demuxer_desc_rawaudio,
&demuxer_desc_rawvideo,
#ifdef CONFIG_TV
@@ -1360,6 +1362,24 @@ int demuxer_add_attachment(demuxer_t *demuxer, struct bstr name,
return demuxer->num_attachments++;
}
+static int chapter_compare(const void *p1, const void *p2)
+{
+ struct demux_chapter *c1 = (void *)p1;
+ struct demux_chapter *c2 = (void *)p2;
+
+ if (c1->start > c2->start)
+ return 1;
+ else if (c1->start < c2->start)
+ return -1;
+ return 0;
+}
+
+static void demuxer_sort_chapters(demuxer_t *demuxer)
+{
+ qsort(demuxer->chapters, demuxer->num_chapters,
+ sizeof(struct demux_chapter), chapter_compare);
+}
+
int demuxer_add_chapter(demuxer_t *demuxer, struct bstr name,
uint64_t start, uint64_t end)
{
@@ -1374,7 +1394,14 @@ int demuxer_add_chapter(demuxer_t *demuxer, struct bstr name,
talloc_strndup(demuxer->chapters, name.start, name.len) :
talloc_strdup(demuxer->chapters, mp_gtext("unknown"));
- return demuxer->num_chapters++;
+ demuxer->num_chapters++;
+
+ if (demuxer->num_chapters > 1
+ && demuxer->chapters[demuxer->num_chapters - 2].start
+ < demuxer->chapters[demuxer->num_chapters - 1].start)
+ demuxer_sort_chapters(demuxer);
+
+ return 0;
}
/**
diff --git a/libmpdemux/demuxer.h b/libmpdemux/demuxer.h
index 24b4edac19..03b1fee830 100644
--- a/libmpdemux/demuxer.h
+++ b/libmpdemux/demuxer.h
@@ -89,6 +89,7 @@ enum demuxer_type {
DEMUXER_TYPE_RTP_NEMESI,
DEMUXER_TYPE_MNG,
DEMUXER_TYPE_EDL,
+ DEMUXER_TYPE_CUE,
/* Values after this are for internal use and can not be selected
* as demuxer type by the user (-demuxer option). */
diff --git a/libvo/cocoa_common.h b/libvo/cocoa_common.h
index e8cd7d9dc4..943a5fb8ba 100644
--- a/libvo/cocoa_common.h
+++ b/libvo/cocoa_common.h
@@ -32,7 +32,8 @@ void vo_cocoa_update_xinerama_info(struct vo *vo);
int vo_cocoa_change_attributes(struct vo *vo);
int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
- uint32_t d_height, uint32_t flags);
+ uint32_t d_height, uint32_t flags,
+ int gl3profile);
void vo_cocoa_swap_buffers(void);
int vo_cocoa_check_events(struct vo *vo);
@@ -45,4 +46,6 @@ int vo_cocoa_swap_interval(int enabled);
void *vo_cocoa_cgl_context(void);
void *vo_cocoa_cgl_pixel_format(void);
+int vo_cocoa_cgl_color_size(void);
+
#endif /* MPLAYER_COCOA_COMMON_H */
diff --git a/libvo/cocoa_common.m b/libvo/cocoa_common.m
index c3578d27c1..9a822cc4ea 100644
--- a/libvo/cocoa_common.m
+++ b/libvo/cocoa_common.m
@@ -20,7 +20,7 @@
#import <Cocoa/Cocoa.h>
#import <OpenGL/OpenGL.h>
#import <QuartzCore/QuartzCore.h>
-#import <CoreServices/CoreServices.h> // for CGDisplayHideCursor
+#import <CoreServices/CoreServices.h> // for CGDisplayHideCursor and Gestalt
#include <dlfcn.h>
#include "cocoa_common.h"
@@ -37,6 +37,18 @@
#include "osx_common.h"
#include "mp_msg.h"
+#ifndef NSOpenGLPFAOpenGLProfile
+#define NSOpenGLPFAOpenGLProfile 99
+#endif
+
+#ifndef NSOpenGLProfileVersionLegacy
+#define NSOpenGLProfileVersionLegacy 0x1000
+#endif
+
+#ifndef NSOpenGLProfileVersion3_2Core
+#define NSOpenGLProfileVersion3_2Core 0x3200
+#endif
+
#define NSLeftAlternateKeyMask (0x000020 | NSAlternateKeyMask)
#define NSRightAlternateKeyMask (0x000040 | NSAlternateKeyMask)
@@ -97,6 +109,8 @@ void resize_window(struct vo *vo);
void vo_cocoa_display_cursor(int requested_state);
void create_menu(void);
+bool is_lion_or_better(void);
+
struct vo_cocoa_state *vo_cocoa_init_state(void)
{
struct vo_cocoa_state *s = talloc_ptrtype(NULL, s);
@@ -216,7 +230,8 @@ void vo_cocoa_ontop(struct vo *vo)
}
int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
- uint32_t d_height, uint32_t flags)
+ uint32_t d_height, uint32_t flags,
+ int gl3profile)
{
struct MPOpts *opts = vo->opts;
if (s->current_video_size.width > 0 || s->current_video_size.height > 0)
@@ -230,13 +245,27 @@ int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
GLMPlayerOpenGLView *glView = [[GLMPlayerOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
- NSOpenGLPixelFormatAttribute attrs[] = {
- NSOpenGLPFADoubleBuffer, // double buffered
- NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16, // 16 bit depth buffer
- (NSOpenGLPixelFormatAttribute)0
- };
-
- s->pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
+ int i = 0;
+ NSOpenGLPixelFormatAttribute attr[32];
+ if (is_lion_or_better()) {
+ attr[i++] = NSOpenGLPFAOpenGLProfile;
+ attr[i++] = (gl3profile ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy);
+ } else if(gl3profile) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[cocoa] Invalid pixel format attribute "
+ "(GL3 is not supported on OSX versions prior to 10.7)\n");
+ return -1;
+ }
+ attr[i++] = NSOpenGLPFADoubleBuffer; // double buffered
+ attr[i] = (NSOpenGLPixelFormatAttribute)0;
+
+ s->pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attr] autorelease];
+ if (!s->pixelFormat) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[cocoa] Invalid pixel format attribute "
+ "(GL3 not supported?)\n");
+ return -1;
+ }
s->glContext = [[NSOpenGLContext alloc] initWithFormat:s->pixelFormat shareContext:nil];
create_menu();
@@ -376,7 +405,23 @@ void *vo_cocoa_cgl_context(void)
void *vo_cocoa_cgl_pixel_format(void)
{
- return [s->pixelFormat CGLPixelFormatObj];
+ return CGLGetPixelFormat(vo_cocoa_cgl_context());
+}
+
+int vo_cocoa_cgl_color_size(void)
+{
+ GLint value;
+ CGLDescribePixelFormat(vo_cocoa_cgl_pixel_format(), 0,
+ kCGLPFAColorSize, &value);
+ switch (value) {
+ case 32:
+ case 24:
+ return 8;
+ case 16:
+ return 5;
+ }
+
+ return 8;
}
void create_menu()
@@ -402,6 +447,17 @@ void create_menu()
[menuItem release];
}
+bool is_lion_or_better(void)
+{
+ SInt32 major, minor;
+ Gestalt(gestaltSystemVersionMajor, &major);
+ Gestalt(gestaltSystemVersionMinor, &minor);
+ if(major >= 10 && minor >= 7)
+ return YES;
+ else
+ return NO;
+}
+
@implementation GLMPlayerWindow
- (void) windowDidResize:(NSNotification *) notification
diff --git a/libvo/d3d_shader_yuv.h b/libvo/d3d_shader_yuv.h
new file mode 100644
index 0000000000..49ef753b4c
--- /dev/null
+++ b/libvo/d3d_shader_yuv.h
@@ -0,0 +1,142 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+//
+// fxc /Tps_2_0 /Fhz:\tmp\mplayer\libvo\d3d_shader_yuv.h
+// z:\tmp\mplayer\libvo\d3d_shader_yuv.hlsl /Vnd3d_shader_yuv
+//
+//
+// Parameters:
+//
+// float4x4 colormatrix;
+// sampler2D tex0;
+// sampler2D tex1;
+// sampler2D tex2;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// colormatrix c0 4
+// tex0 s0 1
+// tex1 s1 1
+// tex2 s2 1
+//
+
+ ps_2_0
+ def c4, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xy
+ dcl t2.xy
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ texld r0, t0, s0
+ texld r1, t1, s1
+ texld r2, t2, s2
+ mov r0.y, r1.x
+ mov r0.z, r2.x
+ mov r0.w, c4.x
+ dp4 r1.x, r0, c0
+ dp4 r1.y, r0, c1
+ dp4 r1.z, r0, c2
+ dp4 r1.w, r0, c3
+ mov oC0, r1
+
+// approximately 11 instruction slots used (3 texture, 8 arithmetic)
+#endif
+
+const BYTE d3d_shader_yuv[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 67, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 215, 0,
+ 0, 0, 0, 2, 255, 255,
+ 4, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 208, 0, 0, 0, 108, 0,
+ 0, 0, 2, 0, 0, 0,
+ 4, 0, 2, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 2, 0,
+ 144, 0, 0, 0, 0, 0,
+ 0, 0, 160, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 6, 0, 168, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 3, 0, 2, 0,
+ 1, 0, 10, 0, 192, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 111, 108, 111, 114, 109,
+ 97, 116, 114, 105, 120, 0,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 48, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 49, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 50, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 112, 115,
+ 95, 50, 95, 48, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 57, 46,
+ 50, 55, 46, 57, 53, 50,
+ 46, 51, 48, 50, 50, 0,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 2, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 2, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 176, 2, 8, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 2, 128, 1, 0, 0, 128,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 2, 0, 0, 128,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 4, 0, 0, 160,
+ 9, 0, 0, 3, 1, 0,
+ 1, 128, 0, 0, 228, 128,
+ 0, 0, 228, 160, 9, 0,
+ 0, 3, 1, 0, 2, 128,
+ 0, 0, 228, 128, 1, 0,
+ 228, 160, 9, 0, 0, 3,
+ 1, 0, 4, 128, 0, 0,
+ 228, 128, 2, 0, 228, 160,
+ 9, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 228, 128,
+ 3, 0, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 1, 0, 228, 128, 255, 255,
+ 0, 0
+};
diff --git a/libvo/d3d_shader_yuv.hlsl b/libvo/d3d_shader_yuv.hlsl
new file mode 100644
index 0000000000..b17e257210
--- /dev/null
+++ b/libvo/d3d_shader_yuv.hlsl
@@ -0,0 +1,44 @@
+// Compile with:
+// fxc.exe /Tps_2_0 /Fhd3d_shader_yuv.h d3d_shader_yuv.hlsl /Vnd3d_shader_yuv
+// fxc.exe /Tps_2_0 /Fhd3d_shader_yuv_2ch.h d3d_shader_yuv.hlsl /Vnd3d_shader_yuv_2ch /DUSE_2CH=1
+
+// Be careful with this shader. You can't use constant slots, since we don't
+// load the shader with D3DX. All uniform variables are mapped to hardcoded
+// constant slots.
+
+sampler2D tex0 : register(s0);
+sampler2D tex1 : register(s1);
+sampler2D tex2 : register(s2);
+
+uniform float4x4 colormatrix : register(c0);
+uniform float2 depth : register(c5);
+
+#ifdef USE_2CH
+
+float1 sample(sampler2D tex, float2 t)
+{
+ // Sample from A8L8 format as if we sampled a single value from L16.
+ // We compute the 2 channel values back into one.
+ return dot(tex2D(tex, t).xw, depth);
+}
+
+#else
+
+float1 sample(sampler2D tex, float2 t)
+{
+ return tex2D(tex, t).x;
+}
+
+#endif
+
+float4 main(float2 t0 : TEXCOORD0,
+ float2 t1 : TEXCOORD1,
+ float2 t2 : TEXCOORD2)
+ : COLOR
+{
+ float4 c = float4(sample(tex0, t0),
+ sample(tex1, t1),
+ sample(tex2, t2),
+ 1);
+ return mul(c, colormatrix);
+}
diff --git a/libvo/d3d_shader_yuv_2ch.h b/libvo/d3d_shader_yuv_2ch.h
new file mode 100644
index 0000000000..45dcc73992
--- /dev/null
+++ b/libvo/d3d_shader_yuv_2ch.h
@@ -0,0 +1,170 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+//
+// fxc /Tps_2_0 /Fhz:\tmp\mplayer\libvo\d3d_shader_yuv_2ch.h
+// z:\tmp\mplayer\libvo\d3d_shader_yuv.hlsl /Vnd3d_shader_yuv_2ch
+// /DUSE_2CH=1
+//
+//
+// Parameters:
+//
+// float4x4 colormatrix;
+// float2 depth;
+// sampler2D tex0;
+// sampler2D tex1;
+// sampler2D tex2;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// colormatrix c0 4
+// depth c5 1
+// tex0 s0 1
+// tex1 s1 1
+// tex2 s2 1
+//
+
+ ps_2_0
+ def c4, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xy
+ dcl t2.xy
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ texld r0, t0, s0
+ texld r1, t1, s1
+ texld r2, t2, s2
+ mul r0.x, r0.x, c5.x
+ mad r0.x, r0.w, c5.y, r0.x
+ mul r1.x, r1.x, c5.x
+ mad r0.y, r1.w, c5.y, r1.x
+ mul r1.x, r2.x, c5.x
+ mad r0.z, r2.w, c5.y, r1.x
+ mov r0.w, c4.x
+ dp4 r1.x, r0, c0
+ dp4 r1.y, r0, c1
+ dp4 r1.z, r0, c2
+ dp4 r1.w, r0, c3
+ mov oC0, r1
+
+// approximately 15 instruction slots used (3 texture, 12 arithmetic)
+#endif
+
+const BYTE d3d_shader_yuv_2ch[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 78, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 3, 1,
+ 0, 0, 0, 2, 255, 255,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 252, 0, 0, 0, 128, 0,
+ 0, 0, 2, 0, 0, 0,
+ 4, 0, 2, 0, 140, 0,
+ 0, 0, 0, 0, 0, 0,
+ 156, 0, 0, 0, 2, 0,
+ 5, 0, 1, 0, 22, 0,
+ 164, 0, 0, 0, 0, 0,
+ 0, 0, 180, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 2, 0, 188, 0, 0, 0,
+ 0, 0, 0, 0, 204, 0,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 6, 0, 212, 0,
+ 0, 0, 0, 0, 0, 0,
+ 228, 0, 0, 0, 3, 0,
+ 2, 0, 1, 0, 10, 0,
+ 236, 0, 0, 0, 0, 0,
+ 0, 0, 99, 111, 108, 111,
+ 114, 109, 97, 116, 114, 105,
+ 120, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 101, 112, 116, 104, 0,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 101, 120, 48, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 101, 120, 49, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 101, 120, 50, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 57, 46, 50, 55, 46, 57,
+ 53, 50, 46, 51, 48, 50,
+ 50, 0, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 2, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 66, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 2, 0, 228, 176, 2, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 5, 0, 0, 160,
+ 4, 0, 0, 4, 0, 0,
+ 1, 128, 0, 0, 255, 128,
+ 5, 0, 85, 160, 0, 0,
+ 0, 128, 5, 0, 0, 3,
+ 1, 0, 1, 128, 1, 0,
+ 0, 128, 5, 0, 0, 160,
+ 4, 0, 0, 4, 0, 0,
+ 2, 128, 1, 0, 255, 128,
+ 5, 0, 85, 160, 1, 0,
+ 0, 128, 5, 0, 0, 3,
+ 1, 0, 1, 128, 2, 0,
+ 0, 128, 5, 0, 0, 160,
+ 4, 0, 0, 4, 0, 0,
+ 4, 128, 2, 0, 255, 128,
+ 5, 0, 85, 160, 1, 0,
+ 0, 128, 1, 0, 0, 2,
+ 0, 0, 8, 128, 4, 0,
+ 0, 160, 9, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 9, 0, 0, 3, 1, 0,
+ 2, 128, 0, 0, 228, 128,
+ 1, 0, 228, 160, 9, 0,
+ 0, 3, 1, 0, 4, 128,
+ 0, 0, 228, 128, 2, 0,
+ 228, 160, 9, 0, 0, 3,
+ 1, 0, 8, 128, 0, 0,
+ 228, 128, 3, 0, 228, 160,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 1, 0, 228, 128,
+ 255, 255, 0, 0
+};
diff --git a/libvo/eosd_packer.c b/libvo/eosd_packer.c
new file mode 100644
index 0000000000..103648b7c4
--- /dev/null
+++ b/libvo/eosd_packer.c
@@ -0,0 +1,254 @@
+/*
+ * Common code for packing EOSD images into larger surfaces.
+ *
+ * This file is part of mplayer2.
+ *
+ * mplayer2 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.
+ *
+ * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <libavutil/common.h>
+#include "talloc.h"
+#include "mp_msg.h"
+#include "eosd_packer.h"
+
+// Initial size of EOSD surface in pixels (x*x)
+#define EOSD_SURFACE_INITIAL_SIZE 256
+
+// Allocate an eosd_packer, which can be used to layout and cache the list of
+// EOSD images contained in a mp_eosd_images_t into a flat surface.
+// It can be free'd with talloc_free().
+// Don't forget to call eosd_init() before using it.
+struct eosd_packer *eosd_packer_create(void *talloc_ctx) {
+ return talloc_zero(talloc_ctx, struct eosd_packer);
+}
+
+// Call this when you need to completely reinitialize the EOSD state, e.g. when
+// when your EOSD surface was deleted.
+// max_width and max_height are the maximum surface sizes that should be
+// allowed.
+void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width,
+ uint32_t max_height)
+{
+ state->max_surface_width = max_width;
+ state->max_surface_height = max_height;
+ state->surface.w = 0;
+ state->surface.h = 0;
+ state->targets_count = 0;
+}
+
+#define HEIGHT_SORT_BITS 4
+static int size_index(struct eosd_target *r)
+{
+ unsigned int h = r->source.y1;
+ int n = av_log2_16bit(h);
+ return (n << HEIGHT_SORT_BITS)
+ + (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
+}
+
+/* Pack the given rectangles into an area of size w * h.
+ * The size of each rectangle is read from .source.x1/.source.y1.
+ * The height of each rectangle must be at least 1 and less than 65536.
+ * The .source rectangle is then set corresponding to the packed position.
+ * 'scratch' must point to work memory for num_rects+16 ints.
+ * Return 0 on success, -1 if the rectangles did not fit in w*h.
+ *
+ * The rectangles are placed in rows in order approximately sorted by
+ * height (the approximate sorting is simpler than a full one would be,
+ * and allows the algorithm to work in linear time). Additionally, to
+ * reduce wasted space when there are a few tall rectangles, empty
+ * lower-right parts of rows are filled recursively when the size of
+ * rectangles in the row drops past a power-of-two threshold. So if a
+ * row starts with rectangles of size 3x50, 10x40 and 5x20 then the
+ * free rectangle with corners (13, 20)-(w, 50) is filled recursively.
+ */
+static int pack_rectangles(struct eosd_target *rects, int num_rects,
+ int w, int h, int *scratch)
+{
+ int bins[16 << HEIGHT_SORT_BITS];
+ int sizes[16 << HEIGHT_SORT_BITS] = {};
+ for (int i = 0; i < num_rects; i++)
+ sizes[size_index(rects + i)]++;
+ int idx = 0;
+ for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
+ for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
+ bins[i + j] = idx;
+ idx += sizes[i + j];
+ }
+ scratch[idx++] = -1;
+ }
+ for (int i = 0; i < num_rects; i++)
+ scratch[bins[size_index(rects + i)]++] = i;
+ for (int i = 0; i < 16; i++)
+ bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
+ struct {
+ int size, x, bottom;
+ } stack[16] = {{15, 0, h}}, s = {};
+ int stackpos = 1;
+ int y;
+ while (stackpos) {
+ y = s.bottom;
+ s = stack[--stackpos];
+ s.size++;
+ while (s.size--) {
+ int maxy = -1;
+ int obj;
+ while ((obj = scratch[bins[s.size]]) >= 0) {
+ int bottom = y + rects[obj].source.y1;
+ if (bottom > s.bottom)
+ break;
+ int right = s.x + rects[obj].source.x1;
+ if (right > w)
+ break;
+ bins[s.size]++;
+ rects[obj].source.x0 = s.x;
+ rects[obj].source.x1 += s.x;
+ rects[obj].source.y0 = y;
+ rects[obj].source.y1 += y;
+ num_rects--;
+ if (maxy <= 0)
+ stack[stackpos++] = s;
+ s.x = right;
+ maxy = FFMAX(maxy, bottom);
+ }
+ if (maxy > 0)
+ s.bottom = maxy;
+ }
+ }
+ return num_rects ? -1 : 0;
+}
+
+// padding to reduce interpolation artifacts when doing scaling & filtering
+#define EOSD_PADDING 0
+
+// Release all previous images, and packs the images in imgs into state. The
+// caller must check the change variables:
+// *out_need_reposition == true: sub-image positions changed
+// *out_need_upload == true: upload all sub-images again
+// *out_need_reallocate == true: resize the EOSD texture to state->surface.w/h
+// Logical implications: need_reallocate => need_upload => need_reposition
+void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs,
+ bool *out_need_reposition, bool *out_need_upload,
+ bool *out_need_reallocate)
+{
+ int i;
+ ASS_Image *img = imgs->imgs;
+ ASS_Image *p;
+ struct eosd_surface *sfc = &state->surface;
+
+ *out_need_reposition = false;
+ *out_need_upload = false;
+ *out_need_reallocate = false;
+
+ int change_state = imgs->changed;
+
+ // eosd_reinit() was probably called, force full reupload.
+ if (state->targets_count == 0 && img)
+ change_state = 2;
+
+ if (change_state == 0)
+ return; // Nothing changed, no need to redraw
+
+ state->targets_count = 0;
+
+ *out_need_reposition = true;
+
+ if (!img)
+ return; // There's nothing to render!
+
+ if (change_state == 1)
+ goto eosd_skip_upload;
+
+ *out_need_upload = true;
+ while (1) {
+ for (p = img, i = 0; p; p = p->next) {
+ if (p->w <= 0 || p->h <= 0)
+ continue;
+ // Allocate new space for surface/target arrays
+ if (i >= state->targets_size) {
+ state->targets_size = FFMAX(state->targets_size * 2, 512);
+ state->targets =
+ talloc_realloc_size(state, state->targets,
+ state->targets_size
+ * sizeof(*state->targets));
+ state->scratch =
+ talloc_realloc_size(state, state->scratch,
+ (state->targets_size + 16)
+ * sizeof(*state->scratch));
+ }
+ state->targets[i].source.x1 = p->w + EOSD_PADDING;
+ state->targets[i].source.y1 = p->h + EOSD_PADDING;
+ i++;
+ }
+ if (pack_rectangles(state->targets, i, sfc->w, sfc->h,
+ state->scratch) >= 0)
+ break;
+ int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE),
+ state->max_surface_width);
+ int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE),
+ state->max_surface_height);
+ if (w == sfc->w && h == sfc->h) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[eosd] EOSD bitmaps do not fit on "
+ "a surface with the maximum supported size\n");
+ return;
+ }
+ sfc->w = w;
+ sfc->h = h;
+ *out_need_reallocate = true;
+ }
+ if (*out_need_reallocate) {
+ mp_msg(MSGT_VO, MSGL_V, "[eosd] Allocate a %dx%d surface for "
+ "EOSD bitmaps.\n", sfc->w, sfc->h);
+ }
+
+eosd_skip_upload:
+ for (p = img; p; p = p->next) {
+ if (p->w <= 0 || p->h <= 0)
+ continue;
+ struct eosd_target *target = &state->targets[state->targets_count];
+ target->source.x1 -= EOSD_PADDING;
+ target->source.y1 -= EOSD_PADDING;
+ target->dest.x0 = p->dst_x;
+ target->dest.y0 = p->dst_y;
+ target->dest.x1 = p->w + p->dst_x;
+ target->dest.y1 = p->h + p->dst_y;
+ target->color = p->color;
+ target->ass_img = p;
+ state->targets_count++;
+ }
+}
+
+// Calculate the bounding box of all sub-rectangles in the EOSD surface that
+// will be used for EOSD rendering.
+// If the bounding box is empty, return false.
+bool eosd_packer_calculate_source_bb(struct eosd_packer *state,
+ struct eosd_rect *out_bb)
+{
+ struct eosd_rect bb = { state->surface.w, state->surface.h, 0, 0 };
+
+ for (int n = 0; n < state->targets_count; n++) {
+ struct eosd_rect s = state->targets[n].source;
+ bb.x0 = FFMIN(bb.x0, s.x0);
+ bb.y0 = FFMIN(bb.y0, s.y0);
+ bb.x1 = FFMAX(bb.x1, s.x1);
+ bb.y1 = FFMAX(bb.y1, s.y1);
+ }
+
+ // avoid degenerate bounding box if empty
+ bb.x0 = FFMIN(bb.x0, bb.x1);
+ bb.y0 = FFMIN(bb.y0, bb.y1);
+
+ *out_bb = bb;
+ return state->targets_count > 0;
+}
diff --git a/libvo/eosd_packer.h b/libvo/eosd_packer.h
new file mode 100644
index 0000000000..e207a4a2dd
--- /dev/null
+++ b/libvo/eosd_packer.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * mplayer2 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.
+ *
+ * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_EOSD_PACKER_H
+#define MPLAYER_EOSD_PACKER_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "sub/ass_mp.h"
+
+// Pool of surfaces
+struct eosd_surface {
+ //void *native_surface;
+ int w;
+ int h;
+};
+
+struct eosd_rect {
+ int x0, y0, x1, y1;
+};
+
+// List of surfaces to be rendered
+struct eosd_target {
+ struct eosd_rect source; // position in EOSD surface
+ struct eosd_rect dest; // position on screen
+ uint32_t color; // libass-style color of the image
+ // NOTE: This must not be accessed after you return from your VO's
+ // VOCTRL_DRAW_EOSD call - libass will free or reuse the associated
+ // memory. Feel free to set this to NULL to make erroneous accesses to
+ // this member fail early.
+ ASS_Image *ass_img;
+};
+
+struct eosd_packer {
+ struct eosd_surface surface;
+ struct eosd_target *targets;
+ int targets_count; // number of valid elements in targets
+ int targets_size; // number of allocated elements in targets
+
+ uint32_t max_surface_width;
+ uint32_t max_surface_height;
+
+ int *scratch;
+};
+
+struct eosd_packer *eosd_packer_create(void *talloc_ctx);
+void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width,
+ uint32_t max_height);
+void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs,
+ bool *out_need_reposition, bool *out_need_upload,
+ bool *out_need_reallocate);
+bool eosd_packer_calculate_source_bb(struct eosd_packer *state,
+ struct eosd_rect *out_bb);
+
+#endif /* MPLAYER_EOSD_PACKER_H */
diff --git a/libvo/filter_kernels.c b/libvo/filter_kernels.c
new file mode 100644
index 0000000000..2c2f56ee51
--- /dev/null
+++ b/libvo/filter_kernels.c
@@ -0,0 +1,279 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * Most code for computing the weights is taken from Anti-Grain Geometry (AGG)
+ * (licensed under GPL 2 or later), with modifications.
+ * Copyright (C) 2002-2006 Maxim Shemanarev
+ * http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h?view=markup
+ *
+ * Also see glumpy (BSD licensed), contains the same code in Python:
+ * http://code.google.com/p/glumpy/source/browse/glumpy/image/filter.py
+ *
+ * Also see: Paul Heckbert's "zoom"
+ *
+ * Also see XBMC: ConvolutionKernels.cpp etc.
+ *
+ * mplayer2 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.
+ *
+ * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#include "filter_kernels.h"
+
+// NOTE: all filters are separable, symmetric, and are intended for use with
+// a lookup table/texture.
+
+const struct filter_kernel *mp_find_filter_kernel(const char *name)
+{
+ for (const struct filter_kernel *k = mp_filter_kernels; k->name; k++) {
+ if (strcmp(k->name, name) == 0)
+ return k;
+ }
+ return NULL;
+}
+
+// sizes = sorted list of available filter sizes, terminated with size 0
+// inv_scale = source_size / dest_size
+bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
+ double inv_scale)
+{
+ // only downscaling requires widening the filter
+ filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0;
+ double support = filter->radius * filter->inv_scale;
+ int size = ceil(2.0 * support);
+ // round up to smallest available size that's still large enough
+ if (size < sizes[0])
+ size = sizes[0];
+ const int *cursize = sizes;
+ while (size > *cursize && *cursize)
+ cursize++;
+ if (*cursize) {
+ filter->size = *cursize;
+ return true;
+ } else {
+ // The filter doesn't fit - instead of failing completely, use the
+ // largest filter available. This is incorrect, but better than refusing
+ // to do anything.
+ filter->size = cursize[-1];
+ filter->inv_scale = filter->size / 2.0 / filter->radius;
+ return false;
+ }
+}
+
+// Calculate the 1D filtering kernel for N sample points.
+// N = number of samples, which is filter->size
+// The weights will be stored in out_w[0] to out_w[N - 1]
+// f = x0 - abs(x0), subpixel position in the range [0,1) or [0,1].
+void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w)
+{
+ assert(filter->size > 0);
+ double sum = 0;
+ for (int n = 0; n < filter->size; n++) {
+ double x = f - (n - filter->size / 2 + 1);
+ double w = filter->weight(filter, fabs(x) / filter->inv_scale);
+ out_w[n] = w;
+ sum += w;
+ }
+ //normalize
+ for (int n = 0; n < filter->size; n++)
+ out_w[n] /= sum;
+}
+
+// Fill the given array with weights for the range [0.0, 1.0]. The array is
+// interpreted as rectangular array of count * filter->size items.
+void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array)
+{
+ for (int n = 0; n < count; n++) {
+ mp_compute_weights(filter, n / (double)(count - 1),
+ out_array + filter->size * n);
+ }
+}
+
+typedef struct filter_kernel kernel;
+
+static double bilinear(kernel *k, double x)
+{
+ return 1.0 - x;
+}
+
+static double hanning(kernel *k, double x)
+{
+ return 0.5 + 0.5 * cos(M_PI * x);
+}
+
+static double hamming(kernel *k, double x)
+{
+ return 0.54 + 0.46 * cos(M_PI * x);
+}
+
+static double hermite(kernel *k, double x)
+{
+ return (2.0 * x - 3.0) * x * x + 1.0;
+}
+
+static double quadric(kernel *k, double x)
+{
+ // NOTE: glumpy uses 0.75, AGG uses 0.5
+ if (x < 0.5)
+ return 0.75 - x * x;
+ if (x < 1.5)
+ return 0.5 * (x - 1.5) * (x - 1.5);
+ return 0;
+}
+
+static double bc_pow3(double x)
+{
+ return (x <= 0) ? 0 : x * x * x;
+}
+
+static double bicubic(kernel *k, double x)
+{
+ return (1.0/6.0) * ( bc_pow3(x + 2)
+ - 4 * bc_pow3(x + 1)
+ + 6 * bc_pow3(x)
+ - 4 * bc_pow3(x - 1));
+}
+
+static double bessel_i0(double epsilon, double x)
+{
+ double sum = 1;
+ double y = x * x / 4;
+ double t = y;
+ for (int i = 2; t > epsilon; i++) {
+ sum += t;
+ t *= y / (i * i);
+ }
+ return sum;
+}
+
+static double kaiser(kernel *k, double x)
+{
+ double a = k->params[0];
+ double b = k->params[1];
+ double epsilon = 1e-12;
+ double i0a = 1 / bessel_i0(epsilon, b);
+ return bessel_i0(epsilon, a * sqrt(1 - x * x)) * i0a;
+}
+
+static double catmull_rom(kernel *k, double x)
+{
+ if (x < 1.0)
+ return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0));
+ if (x < 2.0)
+ return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x)));
+ return 0;
+}
+
+// Mitchell-Netravali
+static double mitchell(kernel *k, double x)
+{
+ double b = k->params[0];
+ double c = k->params[1];
+ double
+ p0 = (6.0 - 2.0 * b) / 6.0,
+ p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0,
+ p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0,
+ q0 = (8.0 * b + 24.0 * c) / 6.0,
+ q1 = (-12.0 * b - 48.0 * c) / 6.0,
+ q2 = (6.0 * b + 30.0 * c) / 6.0,
+ q3 = (-b - 6.0 * c) / 6.0;
+ if (x < 1.0)
+ return p0 + x * x * (p2 + x * p3);
+ if (x < 2.0)
+ return q0 + x * (q1 + x * (q2 + x * q3));
+ return 0;
+}
+
+static double spline16(kernel *k, double x)
+{
+ if (x < 1.0)
+ return ((x - 9.0/5.0 ) * x - 1.0/5.0 ) * x + 1.0;
+ return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0 ) * (x-1);
+}
+
+static double spline36(kernel *k, double x)
+{
+ if(x < 1.0)
+ return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0;
+ if(x < 2.0)
+ return ((-6.0/11.0 * (x - 1) + 270.0/209.0) * (x - 1) - 156.0/209.0)
+ * (x - 1);
+ return ((1.0/11.0 * (x - 2) - 45.0/209.0) * (x - 2) + 26.0/209.0)
+ * (x - 2);
+}
+
+static double gaussian(kernel *k, double x)
+{
+ return exp(-2.0 * x * x) * sqrt(2.0 / M_PI);
+}
+
+static double sinc(kernel *k, double x)
+{
+ if (x == 0.0)
+ return 1.0;
+ double pix = M_PI * x;
+ return sin(pix) / pix;
+}
+
+static double lanczos(kernel *k, double x)
+{
+ double radius = k->size / 2;
+ if (x < -radius || x > radius)
+ return 0;
+ if (x == 0)
+ return 1;
+ double pix = M_PI * x;
+ return radius * sin(pix) * sin(pix / radius) / (pix * pix);
+}
+
+static double blackman(kernel *k, double x)
+{
+ double radius = k->size / 2;
+ if (x == 0.0)
+ return 1.0;
+ if (x > radius)
+ return 0.0;
+ x *= M_PI;
+ double xr = x / radius;
+ return (sin(x) / x) * (0.42 + 0.5 * cos(xr) + 0.08 * cos(2 * xr));
+}
+
+const struct filter_kernel mp_filter_kernels[] = {
+ {"bilinear_slow", 1, bilinear},
+ {"hanning", 1, hanning},
+ {"hamming", 1, hamming},
+ {"hermite", 1, hermite},
+ {"quadric", 1.5, quadric},
+ {"bicubic", 2, bicubic},
+ {"kaiser", 1, kaiser, .params = {6.33, 6.33} },
+ {"catmull_rom", 2, catmull_rom},
+ {"mitchell", 2, mitchell, .params = {1.0/3.0, 1.0/3.0} },
+ {"spline16", 2, spline16},
+ {"spline36", 3, spline36},
+ {"gaussian", 2, gaussian},
+ {"sinc2", 2, sinc},
+ {"sinc3", 3, sinc},
+ {"sinc4", 4, sinc},
+ {"lanczos2", 2, lanczos},
+ {"lanczos3", 3, lanczos},
+ {"lanczos4", 4, lanczos},
+ {"blackman2", 2, blackman},
+ {"blackman3", 3, blackman},
+ {"blackman4", 4, blackman},
+ {0}
+};
diff --git a/libvo/filter_kernels.h b/libvo/filter_kernels.h
new file mode 100644
index 0000000000..46a392c40a
--- /dev/null
+++ b/libvo/filter_kernels.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * mplayer2 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.
+ *
+ * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_FILTER_KERNELS_H
+#define MPLAYER_FILTER_KERNELS_H
+
+#include <stdbool.h>
+
+struct filter_kernel {
+ const char *name;
+ double radius;
+ double (*weight)(struct filter_kernel *kernel, double x);
+
+ // The filter params can be changed at runtime. Only used by some filters.
+ float params[2];
+ // The following values are set by mp_init_filter() at runtime.
+ // Number of coefficients; equals the rounded up radius multiplied with 2.
+ int size;
+ double inv_scale;
+};
+
+extern const struct filter_kernel mp_filter_kernels[];
+
+const struct filter_kernel *mp_find_filter_kernel(const char *name);
+bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
+ double scale);
+void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w);
+void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array);
+
+#endif /* MPLAYER_FILTER_KERNELS_H */
diff --git a/libvo/gl_common.c b/libvo/gl_common.c
index 3b723493db..621bfa39a8 100644
--- a/libvo/gl_common.c
+++ b/libvo/gl_common.c
@@ -37,6 +37,7 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#include <stdbool.h>
#include <math.h>
#include "talloc.h"
#include "gl_common.h"
@@ -48,6 +49,31 @@
//! \defgroup glgeneral OpenGL general helper functions
+// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL)
+static const char *gl_error_to_string(GLenum error)
+{
+ switch (error) {
+ case GL_INVALID_ENUM: return "INVALID_ENUM";
+ case GL_INVALID_VALUE: return "INVALID_VALUE";
+ case GL_INVALID_OPERATION: return "INVALID_OPERATION";
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ return "INVALID_FRAMEBUFFER_OPERATION";
+ case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY";
+ default: return "unknown";
+ }
+}
+
+void glCheckError(GL *gl, const char *info)
+{
+ for (;;) {
+ GLenum error = gl->GetError();
+ if (error == GL_NO_ERROR)
+ break;
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] %s: OpenGL error %s.\n", info,
+ gl_error_to_string(error));
+ }
+}
+
//! \defgroup glcontext OpenGL context management helper functions
//! \defgroup gltexture OpenGL texture handling helper functions
@@ -74,62 +100,6 @@ void glAdjustAlignment(GL *gl, int stride)
gl->PixelStorei(GL_PACK_ALIGNMENT, gl_alignment);
}
-struct gl_name_map_struct {
- GLint value;
- const char *name;
-};
-
-#undef MAP
-#define MAP(a) {a, # a}
-//! mapping table for the glValName function
-static const struct gl_name_map_struct gl_name_map[] = {
- // internal format
- MAP(GL_R3_G3_B2), MAP(GL_RGB4), MAP(GL_RGB5), MAP(GL_RGB8),
- MAP(GL_RGB10), MAP(GL_RGB12), MAP(GL_RGB16), MAP(GL_RGBA2),
- MAP(GL_RGBA4), MAP(GL_RGB5_A1), MAP(GL_RGBA8), MAP(GL_RGB10_A2),
- MAP(GL_RGBA12), MAP(GL_RGBA16), MAP(GL_LUMINANCE8), MAP(GL_LUMINANCE16),
- MAP(GL_R16),
-
- // format
- MAP(GL_RGB), MAP(GL_RGBA), MAP(GL_RED), MAP(GL_GREEN), MAP(GL_BLUE),
- MAP(GL_ALPHA), MAP(GL_LUMINANCE), MAP(GL_LUMINANCE_ALPHA),
- MAP(GL_COLOR_INDEX),
- // rest 1.2 only
- MAP(GL_BGR), MAP(GL_BGRA),
-
- //type
- MAP(GL_BYTE), MAP(GL_UNSIGNED_BYTE), MAP(GL_SHORT), MAP(GL_UNSIGNED_SHORT),
- MAP(GL_INT), MAP(GL_UNSIGNED_INT), MAP(GL_FLOAT), MAP(GL_DOUBLE),
- MAP(GL_2_BYTES), MAP(GL_3_BYTES), MAP(GL_4_BYTES),
- // rest 1.2 only
- MAP(GL_UNSIGNED_BYTE_3_3_2), MAP(GL_UNSIGNED_BYTE_2_3_3_REV),
- MAP(GL_UNSIGNED_SHORT_5_6_5), MAP(GL_UNSIGNED_SHORT_5_6_5_REV),
- MAP(GL_UNSIGNED_SHORT_4_4_4_4), MAP(GL_UNSIGNED_SHORT_4_4_4_4_REV),
- MAP(GL_UNSIGNED_SHORT_5_5_5_1), MAP(GL_UNSIGNED_SHORT_1_5_5_5_REV),
- MAP(GL_UNSIGNED_INT_8_8_8_8), MAP(GL_UNSIGNED_INT_8_8_8_8_REV),
- MAP(GL_UNSIGNED_INT_10_10_10_2), MAP(GL_UNSIGNED_INT_2_10_10_10_REV),
- {0, 0}
-};
-#undef MAP
-
-/**
- * \brief return the name of an OpenGL constant
- * \param value the constant
- * \return name of the constant or "Unknown format!"
- * \ingroup glgeneral
- */
-const char *glValName(GLint value)
-{
- int i = 0;
-
- while (gl_name_map[i].name) {
- if (gl_name_map[i].value == value)
- return gl_name_map[i].name;
- i++;
- }
- return "Unknown format!";
-}
-
//! always return this format as internal texture format in glFindFormat
#define TEXTUREFORMAT_ALWAYS GL_RGB8
#undef TEXTUREFORMAT_ALWAYS
@@ -218,8 +188,8 @@ int glFindFormat(uint32_t fmt, int have_texture_rg, int *bpp, GLint *gl_texfmt,
// we do not support palettized formats, although the format the
// swscale produces works
case IMGFMT_RGB8:
- gl_format = GL_RGB;
- gl_type = GL_UNSIGNED_BYTE_2_3_3_REV;
+ *gl_format = GL_RGB;
+ *gl_type = GL_UNSIGNED_BYTE_2_3_3_REV;
break;
#endif
case IMGFMT_RGB15:
@@ -232,12 +202,12 @@ int glFindFormat(uint32_t fmt, int have_texture_rg, int *bpp, GLint *gl_texfmt,
break;
#if 0
case IMGFMT_BGR8:
- // special case as red and blue have a differen number of bits.
+ // special case as red and blue have a different number of bits.
// GL_BGR and GL_UNSIGNED_BYTE_3_3_2 isn't supported at least
// by nVidia drivers, and in addition would give more bits to
// blue than to red, which isn't wanted
- gl_format = GL_RGB;
- gl_type = GL_UNSIGNED_BYTE_3_3_2;
+ *gl_format = GL_RGB;
+ *gl_type = GL_UNSIGNED_BYTE_3_3_2;
break;
#endif
case IMGFMT_BGR15:
@@ -295,45 +265,28 @@ typedef struct {
const char *extstr;
const char *funcnames[7];
void *fallback;
+ bool is_gl3;
} extfunc_desc_t;
#define DEF_FUNC_DESC(name) \
- {offsetof(GL, name), NULL, {"gl" # name, NULL}, gl ## name}
+ {offsetof(GL, name), NULL, {"gl" # name}, gl ## name}
#define DEF_EXT_FUNCS(...) __VA_ARGS__
#define DEF_EXT_DESC(name, ext, funcnames) \
{offsetof(GL, name), ext, {DEF_EXT_FUNCS funcnames}}
+// These are mostly handled the same, but needed because at least the MESA
+// headers don't define any function prototypes for these.
+#define DEF_GL3_DESC(name) \
+ {offsetof(GL, name), NULL, {"gl" # name}, NULL, .is_gl3 = true}
static const extfunc_desc_t extfuncs[] = {
// these aren't extension functions but we query them anyway to allow
// different "backends" with one binary
- DEF_FUNC_DESC(Begin),
- DEF_FUNC_DESC(End),
DEF_FUNC_DESC(Viewport),
- DEF_FUNC_DESC(MatrixMode),
- DEF_FUNC_DESC(LoadIdentity),
- DEF_FUNC_DESC(Translated),
- DEF_FUNC_DESC(Scaled),
- DEF_FUNC_DESC(Ortho),
- DEF_FUNC_DESC(Frustum),
- DEF_FUNC_DESC(PushMatrix),
- DEF_FUNC_DESC(PopMatrix),
DEF_FUNC_DESC(Clear),
- DEF_FUNC_DESC(GenLists),
- DEF_FUNC_DESC(DeleteLists),
- DEF_FUNC_DESC(NewList),
- DEF_FUNC_DESC(EndList),
- DEF_FUNC_DESC(CallList),
- DEF_FUNC_DESC(CallLists),
DEF_FUNC_DESC(GenTextures),
DEF_FUNC_DESC(DeleteTextures),
- DEF_FUNC_DESC(TexEnvf),
DEF_FUNC_DESC(TexEnvi),
- DEF_FUNC_DESC(Color4ub),
- DEF_FUNC_DESC(Color3f),
- DEF_FUNC_DESC(Color4f),
DEF_FUNC_DESC(ClearColor),
- DEF_FUNC_DESC(ClearDepth),
- DEF_FUNC_DESC(DepthFunc),
DEF_FUNC_DESC(Enable),
DEF_FUNC_DESC(Disable),
DEF_FUNC_DESC(DrawBuffer),
@@ -349,19 +302,43 @@ static const extfunc_desc_t extfuncs[] = {
DEF_FUNC_DESC(TexParameteri),
DEF_FUNC_DESC(TexParameterf),
DEF_FUNC_DESC(TexParameterfv),
- DEF_FUNC_DESC(TexCoord2f),
- DEF_FUNC_DESC(TexCoord2fv),
- DEF_FUNC_DESC(Vertex2f),
- DEF_FUNC_DESC(Vertex3f),
- DEF_FUNC_DESC(Normal3f),
- DEF_FUNC_DESC(Lightfv),
- DEF_FUNC_DESC(ColorMaterial),
- DEF_FUNC_DESC(ShadeModel),
DEF_FUNC_DESC(GetIntegerv),
+ DEF_FUNC_DESC(GetBooleanv),
DEF_FUNC_DESC(ColorMask),
DEF_FUNC_DESC(ReadPixels),
DEF_FUNC_DESC(ReadBuffer),
+ DEF_FUNC_DESC(DrawArrays),
+ DEF_FUNC_DESC(GetString),
+ DEF_FUNC_DESC(GetError),
+ // legacy GL functions (1.x - 2.x)
+ DEF_FUNC_DESC(Begin),
+ DEF_FUNC_DESC(End),
+ DEF_FUNC_DESC(MatrixMode),
+ DEF_FUNC_DESC(LoadIdentity),
+ DEF_FUNC_DESC(Translated),
+ DEF_FUNC_DESC(Scaled),
+ DEF_FUNC_DESC(Ortho),
+ DEF_FUNC_DESC(PushMatrix),
+ DEF_FUNC_DESC(PopMatrix),
+ DEF_FUNC_DESC(GenLists),
+ DEF_FUNC_DESC(DeleteLists),
+ DEF_FUNC_DESC(NewList),
+ DEF_FUNC_DESC(EndList),
+ DEF_FUNC_DESC(CallList),
+ DEF_FUNC_DESC(CallLists),
+ DEF_FUNC_DESC(Color4ub),
+ DEF_FUNC_DESC(Color4f),
+ DEF_FUNC_DESC(TexCoord2f),
+ DEF_FUNC_DESC(TexCoord2fv),
+ DEF_FUNC_DESC(Vertex2f),
+ DEF_FUNC_DESC(VertexPointer),
+ DEF_FUNC_DESC(ColorPointer),
+ DEF_FUNC_DESC(TexCoordPointer),
+ DEF_FUNC_DESC(EnableClientState),
+ DEF_FUNC_DESC(DisableClientState),
+
+ // OpenGL extension functions
DEF_EXT_DESC(GenBuffers, NULL,
("glGenBuffers", "glGenBuffersARB")),
DEF_EXT_DESC(DeleteBuffers, NULL,
@@ -374,18 +351,6 @@ static const extfunc_desc_t extfuncs[] = {
("glUnmapBuffer", "glUnmapBufferARB")),
DEF_EXT_DESC(BufferData, NULL,
("glBufferData", "glBufferDataARB")),
- DEF_EXT_DESC(BeginFragmentShader, "ATI_fragment_shader",
- ("glBeginFragmentShaderATI")),
- DEF_EXT_DESC(EndFragmentShader, "ATI_fragment_shader",
- ("glEndFragmentShaderATI")),
- DEF_EXT_DESC(SampleMap, "ATI_fragment_shader",
- ("glSampleMapATI")),
- DEF_EXT_DESC(ColorFragmentOp2, "ATI_fragment_shader",
- ("glColorFragmentOp2ATI")),
- DEF_EXT_DESC(ColorFragmentOp3, "ATI_fragment_shader",
- ("glColorFragmentOp3ATI")),
- DEF_EXT_DESC(SetFragmentShaderConstant, "ATI_fragment_shader",
- ("glSetFragmentShaderConstantATI")),
DEF_EXT_DESC(ActiveTexture, NULL,
("glActiveTexture", "glActiveTextureARB")),
DEF_EXT_DESC(BindTexture, NULL,
@@ -400,7 +365,7 @@ static const extfunc_desc_t extfuncs[] = {
("glBindProgramARB")),
DEF_EXT_DESC(ProgramString, "_program",
("glProgramStringARB")),
- DEF_EXT_DESC(GetProgramiv, "_program",
+ DEF_EXT_DESC(GetProgramivARB, "_program",
("glGetProgramivARB")),
DEF_EXT_DESC(ProgramEnvParameter4f, "_program",
("glProgramEnvParameter4fARB")),
@@ -409,6 +374,64 @@ static const extfunc_desc_t extfuncs[] = {
"wglSwapInterval", "wglSwapIntervalEXT")),
DEF_EXT_DESC(TexImage3D, NULL,
("glTexImage3D")),
+
+ // ancient ATI extensions
+ DEF_EXT_DESC(BeginFragmentShader, "ATI_fragment_shader",
+ ("glBeginFragmentShaderATI")),
+ DEF_EXT_DESC(EndFragmentShader, "ATI_fragment_shader",
+ ("glEndFragmentShaderATI")),
+ DEF_EXT_DESC(SampleMap, "ATI_fragment_shader",
+ ("glSampleMapATI")),
+ DEF_EXT_DESC(ColorFragmentOp2, "ATI_fragment_shader",
+ ("glColorFragmentOp2ATI")),
+ DEF_EXT_DESC(ColorFragmentOp3, "ATI_fragment_shader",
+ ("glColorFragmentOp3ATI")),
+ DEF_EXT_DESC(SetFragmentShaderConstant, "ATI_fragment_shader",
+ ("glSetFragmentShaderConstantATI")),
+
+ // GL 3, possibly in GL 2.x as well in form of extensions
+ DEF_GL3_DESC(GenBuffers),
+ DEF_GL3_DESC(DeleteBuffers),
+ DEF_GL3_DESC(BindBuffer),
+ DEF_GL3_DESC(MapBuffer),
+ DEF_GL3_DESC(UnmapBuffer),
+ DEF_GL3_DESC(BufferData),
+ DEF_GL3_DESC(ActiveTexture),
+ DEF_GL3_DESC(BindTexture),
+ DEF_GL3_DESC(GenVertexArrays),
+ DEF_GL3_DESC(BindVertexArray),
+ DEF_GL3_DESC(GetAttribLocation),
+ DEF_GL3_DESC(EnableVertexAttribArray),
+ DEF_GL3_DESC(DisableVertexAttribArray),
+ DEF_GL3_DESC(VertexAttribPointer),
+ DEF_GL3_DESC(DeleteVertexArrays),
+ DEF_GL3_DESC(UseProgram),
+ DEF_GL3_DESC(GetUniformLocation),
+ DEF_GL3_DESC(CompileShader),
+ DEF_GL3_DESC(CreateProgram),
+ DEF_GL3_DESC(CreateShader),
+ DEF_GL3_DESC(ShaderSource),
+ DEF_GL3_DESC(LinkProgram),
+ DEF_GL3_DESC(AttachShader),
+ DEF_GL3_DESC(DeleteShader),
+ DEF_GL3_DESC(DeleteProgram),
+ DEF_GL3_DESC(GetShaderInfoLog),
+ DEF_GL3_DESC(GetShaderiv),
+ DEF_GL3_DESC(GetProgramInfoLog),
+ DEF_GL3_DESC(GetProgramiv),
+ DEF_GL3_DESC(GetStringi),
+ DEF_GL3_DESC(BindAttribLocation),
+ DEF_GL3_DESC(BindFramebuffer),
+ DEF_GL3_DESC(GenFramebuffers),
+ DEF_GL3_DESC(DeleteFramebuffers),
+ DEF_GL3_DESC(CheckFramebufferStatus),
+ DEF_GL3_DESC(FramebufferTexture2D),
+ DEF_GL3_DESC(Uniform1f),
+ DEF_GL3_DESC(Uniform3f),
+ DEF_GL3_DESC(Uniform1i),
+ DEF_GL3_DESC(UniformMatrix3fv),
+ DEF_GL3_DESC(UniformMatrix4x3fv),
+
{-1}
};
@@ -418,43 +441,53 @@ static const extfunc_desc_t extfuncs[] = {
* \param ext2 an extra extension string
*/
static void getFunctions(GL *gl, void *(*getProcAddress)(const GLubyte *),
- const char *ext2)
+ const char *ext2, bool is_gl3)
{
const extfunc_desc_t *dsc;
- const char *extensions;
- char *allexts;
+ char *allexts = talloc_strdup(NULL, ext2 ? ext2 : "");
+
+ *gl = (GL) {0};
if (!getProcAddress)
getProcAddress = (void *)getdladdr;
- // special case, we need glGetString before starting to find the other functions
- gl->GetString = getProcAddress("glGetString");
- if (!gl->GetString)
- gl->GetString = glGetString;
-
- extensions = (const char *)gl->GetString(GL_EXTENSIONS);
- if (!extensions)
- extensions = "";
- if (!ext2)
- ext2 = "";
- allexts = malloc(strlen(extensions) + strlen(ext2) + 2);
- strcpy(allexts, extensions);
- strcat(allexts, " ");
- strcat(allexts, ext2);
+ if (is_gl3) {
+ gl->GetStringi = getProcAddress("glGetStringi");
+ gl->GetIntegerv = getProcAddress("glGetIntegerv");
+
+ if (!(gl->GetStringi && gl->GetIntegerv))
+ return;
+
+ GLint exts;
+ gl->GetIntegerv(GL_NUM_EXTENSIONS, &exts);
+ for (int n = 0; n < exts; n++) {
+ allexts = talloc_asprintf_append(allexts, " %s",
+ gl->GetStringi(GL_EXTENSIONS, n));
+ }
+ } else {
+ gl->GetString = getProcAddress("glGetString");
+ if (!gl->GetString)
+ gl->GetString = glGetString;
+ const char *ext = (char*)gl->GetString(GL_EXTENSIONS);
+ allexts = talloc_asprintf_append(allexts, " %s", ext);
+ }
+
mp_msg(MSGT_VO, MSGL_DBG2, "OpenGL extensions string:\n%s\n", allexts);
for (dsc = extfuncs; dsc->offset >= 0; dsc++) {
void *ptr = NULL;
- int i;
if (!dsc->extstr || strstr(allexts, dsc->extstr)) {
- for (i = 0; !ptr && dsc->funcnames[i]; i++)
+ for (int i = 0; !ptr && dsc->funcnames[i]; i++)
ptr = getProcAddress((const GLubyte *)dsc->funcnames[i]);
}
if (!ptr)
ptr = dsc->fallback;
+ if (!ptr && !dsc->extstr && (!dsc->is_gl3 || is_gl3))
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] OpenGL function not found: %s\n",
+ dsc->funcnames[0]);
void **funcptr = (void**)(((char*)gl) + dsc->offset);
*funcptr = ptr;
}
- free(allexts);
+ talloc_free(allexts);
}
/**
@@ -859,11 +892,41 @@ static void gen_spline_lookup_tex(GL *gl, GLenum unit)
free(tex);
}
+#define NOISE_RES 2048
+
+/**
+ * \brief creates the 1D lookup texture needed to generate pseudo-random numbers.
+ * \param unit texture unit to attach texture to
+ */
+static void gen_noise_lookup_tex(GL *gl, GLenum unit) {
+ GLfloat *tex = calloc(NOISE_RES, sizeof(*tex));
+ uint32_t lcg = 0x79381c11;
+ int i;
+ for (i = 0; i < NOISE_RES; i++)
+ tex[i] = (double)i / (NOISE_RES - 1);
+ for (i = 0; i < NOISE_RES - 1; i++) {
+ int remain = NOISE_RES - i;
+ int idx = i + (lcg >> 16) % remain;
+ GLfloat tmp = tex[i];
+ tex[i] = tex[idx];
+ tex[idx] = tmp;
+ lcg = lcg * 1664525 + 1013904223;
+ }
+ gl->ActiveTexture(unit);
+ gl->TexImage1D(GL_TEXTURE_1D, 0, 1, NOISE_RES, 0, GL_RED, GL_FLOAT, tex);
+ gl->TexParameterf(GL_TEXTURE_1D, GL_TEXTURE_PRIORITY, 1.0);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ gl->ActiveTexture(GL_TEXTURE0);
+ free(tex);
+}
+
#define SAMPLE(dest, coord, texture) \
"TEX textemp, " coord ", " texture ", $tex_type;\n" \
"MOV " dest ", textemp.r;\n"
-static const char *bilin_filt_template =
+static const char bilin_filt_template[] =
SAMPLE("yuv.$out_comp","fragment.texcoord[$in_tex]","texture[$in_tex]");
#define BICUB_FILT_MAIN \
@@ -880,7 +943,7 @@ static const char *bilin_filt_template =
/* x-interpolation */ \
"LRP yuv.$out_comp, parmx.b, a.bbbb, a.aaaa;\n"
-static const char *bicub_filt_template_2D =
+static const char bicub_filt_template_2D[] =
"MAD coord.xy, fragment.texcoord[$in_tex], {$texw, $texh}, {0.5, 0.5};\n"
"TEX parmx, coord.x, texture[$texs], 1D;\n"
"MUL cdelta.xz, parmx.rrgg, {-$ptw, 0, $ptw, 0};\n"
@@ -888,7 +951,7 @@ static const char *bicub_filt_template_2D =
"MUL cdelta.yw, parmy.rrgg, {0, -$pth, 0, $pth};\n"
BICUB_FILT_MAIN;
-static const char *bicub_filt_template_RECT =
+static const char bicub_filt_template_RECT[] =
"ADD coord, fragment.texcoord[$in_tex], {0.5, 0.5};\n"
"TEX parmx, coord.x, texture[$texs], 1D;\n"
"MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};\n"
@@ -906,7 +969,7 @@ static const char *bicub_filt_template_RECT =
"ADD "t ".x, "t ".xxxx, "s ";\n" \
"SUB "t ".y, "t ".yyyy, "s ";\n"
-static const char *bicub_notex_filt_template_2D =
+static const char bicub_notex_filt_template_2D[] =
"MAD coord.xy, fragment.texcoord[$in_tex], {$texw, $texh}, {0.5, 0.5};\n"
"FRC coord.xy, coord.xyxy;\n"
CALCWEIGHTS("parmx", "coord.xxxx")
@@ -915,7 +978,7 @@ static const char *bicub_notex_filt_template_2D =
"MUL cdelta.yw, parmy.rrgg, {0, -$pth, 0, $pth};\n"
BICUB_FILT_MAIN;
-static const char *bicub_notex_filt_template_RECT =
+static const char bicub_notex_filt_template_RECT[] =
"ADD coord, fragment.texcoord[$in_tex], {0.5, 0.5};\n"
"FRC coord.xy, coord.xyxy;\n"
CALCWEIGHTS("parmx", "coord.xxxx")
@@ -932,19 +995,19 @@ static const char *bicub_notex_filt_template_RECT =
/* x-interpolation */ \
"LRP yuv.$out_comp, parmx.b, a.rrrr, b.rrrr;\n"
-static const char *bicub_x_filt_template_2D =
+static const char bicub_x_filt_template_2D[] =
"MAD coord.x, fragment.texcoord[$in_tex], {$texw}, {0.5};\n"
"TEX parmx, coord, texture[$texs], 1D;\n"
"MUL cdelta.xyz, parmx.rrgg, {-$ptw, 0, $ptw};\n"
BICUB_X_FILT_MAIN;
-static const char *bicub_x_filt_template_RECT =
+static const char bicub_x_filt_template_RECT[] =
"ADD coord.x, fragment.texcoord[$in_tex], {0.5};\n"
"TEX parmx, coord, texture[$texs], 1D;\n"
"MUL cdelta.xyz, parmx.rrgg, {-1, 0, 1};\n"
BICUB_X_FILT_MAIN;
-static const char *unsharp_filt_template =
+static const char unsharp_filt_template[] =
"PARAM dcoord$out_comp = {$ptw_05, $pth_05, $ptw_05, -$pth_05};\n"
"ADD coord, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n"
"SUB coord2, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n"
@@ -959,7 +1022,7 @@ static const char *unsharp_filt_template =
"MAD textemp.r, b.r, {$strength}, a.r;\n"
"MOV yuv.$out_comp, textemp.r;\n";
-static const char *unsharp_filt_template2 =
+static const char unsharp_filt_template2[] =
"PARAM dcoord$out_comp = {$ptw_12, $pth_12, $ptw_12, -$pth_12};\n"
"PARAM dcoord2$out_comp = {$ptw_15, 0, 0, $pth_15};\n"
"ADD coord, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n"
@@ -984,7 +1047,7 @@ static const char *unsharp_filt_template2 =
"MAD textemp.r, b.r, {$strength}, a.r;\n"
"MOV yuv.$out_comp, textemp.r;\n";
-static const char *yuv_prog_template =
+static const char yuv_prog_template[] =
"PARAM ycoef = {$cm11, $cm21, $cm31};\n"
"PARAM ucoef = {$cm12, $cm22, $cm32};\n"
"PARAM vcoef = {$cm13, $cm23, $cm33};\n"
@@ -992,10 +1055,9 @@ static const char *yuv_prog_template =
"TEMP res;\n"
"MAD res.rgb, yuv.rrrr, ycoef, offsets;\n"
"MAD res.rgb, yuv.gggg, ucoef, res;\n"
- "MAD result.color.rgb, yuv.bbbb, vcoef, res;\n"
- "END";
+ "MAD res.rgb, yuv.bbbb, vcoef, res;\n";
-static const char *yuv_pow_prog_template =
+static const char yuv_pow_prog_template[] =
"PARAM ycoef = {$cm11, $cm21, $cm31};\n"
"PARAM ucoef = {$cm12, $cm22, $cm32};\n"
"PARAM vcoef = {$cm13, $cm23, $cm33};\n"
@@ -1005,12 +1067,11 @@ static const char *yuv_pow_prog_template =
"MAD res.rgb, yuv.rrrr, ycoef, offsets;\n"
"MAD res.rgb, yuv.gggg, ucoef, res;\n"
"MAD_SAT res.rgb, yuv.bbbb, vcoef, res;\n"
- "POW result.color.r, res.r, gamma.r;\n"
- "POW result.color.g, res.g, gamma.g;\n"
- "POW result.color.b, res.b, gamma.b;\n"
- "END";
+ "POW res.r, res.r, gamma.r;\n"
+ "POW res.g, res.g, gamma.g;\n"
+ "POW res.b, res.b, gamma.b;\n";
-static const char *yuv_lookup_prog_template =
+static const char yuv_lookup_prog_template[] =
"PARAM ycoef = {$cm11, $cm21, $cm31, 0};\n"
"PARAM ucoef = {$cm12, $cm22, $cm32, 0};\n"
"PARAM vcoef = {$cm13, $cm23, $cm33, 0};\n"
@@ -1019,16 +1080,23 @@ static const char *yuv_lookup_prog_template =
"MAD res, yuv.rrrr, ycoef, offsets;\n"
"MAD res.rgb, yuv.gggg, ucoef, res;\n"
"MAD res.rgb, yuv.bbbb, vcoef, res;\n"
- "TEX result.color.r, res.raaa, texture[$conv_tex0], 2D;\n"
+ "TEX res.r, res.raaa, texture[$conv_tex0], 2D;\n"
"ADD res.a, res.a, 0.25;\n"
- "TEX result.color.g, res.gaaa, texture[$conv_tex0], 2D;\n"
+ "TEX res.g, res.gaaa, texture[$conv_tex0], 2D;\n"
"ADD res.a, res.a, 0.25;\n"
- "TEX result.color.b, res.baaa, texture[$conv_tex0], 2D;\n"
- "END";
+ "TEX res.b, res.baaa, texture[$conv_tex0], 2D;\n";
-static const char *yuv_lookup3d_prog_template =
- "TEX result.color, yuv, texture[$conv_tex0], 3D;\n"
- "END";
+static const char yuv_lookup3d_prog_template[] =
+ "TEMP res;\n"
+ "TEX res, yuv, texture[$conv_tex0], 3D;\n";
+
+static const char noise_filt_template[] =
+ "MUL coord.xy, fragment.texcoord[0], {$noise_sx, $noise_sy};\n"
+ "TEMP rand;\n"
+ "TEX rand.r, coord.x, texture[$noise_filt_tex], 1D;\n"
+ "ADD rand.r, rand.r, coord.y;\n"
+ "TEX rand.r, rand.r, texture[$noise_filt_tex], 1D;\n"
+ "MAD res.rgb, rand.rrrr, {$noise_str, $noise_str, $noise_str}, res;\n";
/**
* \brief creates and initializes helper textures needed for scaling texture read
@@ -1236,12 +1304,12 @@ int loadGPUProgram(GL *gl, GLenum target, char *prog)
gl->GetString(GL_PROGRAM_ERROR_STRING), &prog[err]);
return 0;
}
- if (!gl->GetProgramiv || !mp_msg_test(MSGT_VO, MSGL_DBG2))
+ if (!gl->GetProgramivARB || !mp_msg_test(MSGT_VO, MSGL_DBG2))
return 1;
mp_msg(MSGT_VO, MSGL_V, "[gl] Program statistics:\n");
for (i = 0; progstats[i].name; i++) {
- gl->GetProgramiv(target, progstats[i].cur, &cur);
- gl->GetProgramiv(target, progstats[i].max, &max);
+ gl->GetProgramivARB(target, progstats[i].cur, &cur);
+ gl->GetProgramivARB(target, progstats[i].max, &max);
mp_msg(MSGT_VO, MSGL_V, "[gl] %s: %i/%i\n", progstats[i].name, cur,
max);
}
@@ -1273,10 +1341,12 @@ static void glSetupYUVFragprog(GL *gl, gl_conversion_params_t *params)
char lum_scale_texs[1];
char chrom_scale_texs[1];
char conv_texs[1];
+ char filt_texs[1] = {0};
GLint i;
// this is the conversion matrix, with y, u, v factors
// for red, green, blue and the constant offsets
float yuv2rgb[3][4];
+ int noise = params->noise_strength != 0;
create_conv_textures(gl, params, &cur_texu, conv_texs);
create_scaler_textures(gl, YUV_LUM_SCALER(type), &cur_texu, lum_scale_texs);
if (YUV_CHROM_SCALER(type) == YUV_LUM_SCALER(type))
@@ -1284,6 +1354,12 @@ static void glSetupYUVFragprog(GL *gl, gl_conversion_params_t *params)
else
create_scaler_textures(gl, YUV_CHROM_SCALER(type), &cur_texu,
chrom_scale_texs);
+
+ if (noise) {
+ gen_noise_lookup_tex(gl, cur_texu);
+ filt_texs[0] = '0' + cur_texu++;
+ }
+
gl->GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &i);
if (i < cur_texu)
mp_msg(MSGT_VO, MSGL_ERR,
@@ -1332,6 +1408,25 @@ static void glSetupYUVFragprog(GL *gl, gl_conversion_params_t *params)
replace_var_float(prog, "gamma_g", (float)1.0 / params->csp_params.ggamma);
replace_var_float(prog, "gamma_b", (float)1.0 / params->csp_params.bgamma);
replace_var_char(prog, "conv_tex0", conv_texs[0]);
+
+ if (noise) {
+ // 1.0 strength is suitable for dithering 8 to 6 bit
+ double str = params->noise_strength * (1.0 / 64);
+ double scale_x = (double)NOISE_RES / texw;
+ double scale_y = (double)NOISE_RES / texh;
+ if (rect) {
+ scale_x /= texw;
+ scale_y /= texh;
+ }
+ append_template(prog, noise_filt_template);
+ replace_var_float(prog, "noise_sx", scale_x);
+ replace_var_float(prog, "noise_sy", scale_y);
+ replace_var_char(prog, "noise_filt_tex", filt_texs[0]);
+ replace_var_float(prog, "noise_str", str);
+ }
+
+ append_template(prog, "MOV result.color.rgb, res;\nEND");
+
mp_msg(MSGT_VO, MSGL_DBG2, "[gl] generated fragment program:\n%s\n",
yuv_prog);
loadGPUProgram(gl, GL_FRAGMENT_PROGRAM, yuv_prog);
@@ -1619,16 +1714,29 @@ void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h,
static int create_window_cocoa(struct MPGLContext *ctx, uint32_t d_width,
uint32_t d_height, uint32_t flags)
{
- if (vo_cocoa_create_window(ctx->vo, d_width, d_height, flags) == 0) {
+ if (vo_cocoa_create_window(ctx->vo, d_width, d_height, flags, 0) == 0) {
return SET_WINDOW_OK;
} else {
return SET_WINDOW_FAILED;
}
}
+
+static int create_window_cocoa_gl3(struct MPGLContext *ctx, int gl_flags,
+ int gl_version, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ int rv = vo_cocoa_create_window(ctx->vo, d_width, d_height, flags, 1);
+ getFunctions(ctx->gl, (void *)vo_cocoa_glgetaddr, NULL, true);
+ ctx->depth_r = vo_cocoa_cgl_color_size();
+ ctx->depth_g = vo_cocoa_cgl_color_size();
+ ctx->depth_b = vo_cocoa_cgl_color_size();
+ return rv;
+}
+
static int setGlWindow_cocoa(MPGLContext *ctx)
{
vo_cocoa_change_attributes(ctx->vo);
- getFunctions(ctx->gl, (void *)vo_cocoa_glgetaddr, NULL);
+ getFunctions(ctx->gl, (void *)vo_cocoa_glgetaddr, NULL, false);
if (!ctx->gl->SwapInterval)
ctx->gl->SwapInterval = vo_cocoa_swap_interval;
return SET_WINDOW_OK;
@@ -1660,12 +1768,18 @@ static void cocoa_fullscreen(struct vo *vo)
#endif
#ifdef CONFIG_GL_WIN32
+#include <windows.h>
#include "w32_common.h"
+struct w32_context {
+ int vinfo;
+ HGLRC context;
+};
+
static int create_window_w32(struct MPGLContext *ctx, uint32_t d_width,
uint32_t d_height, uint32_t flags)
{
- if (!vo_w32_config(d_width, d_height, flags))
+ if (!vo_w32_config(ctx->vo, d_width, d_height, flags))
return -1;
return 0;
}
@@ -1686,13 +1800,112 @@ static void *w32gpa(const GLubyte *procName)
return GetProcAddress(oglmod, procName);
}
+static int create_window_w32_gl3(struct MPGLContext *ctx, int gl_flags,
+ int gl_version, uint32_t d_width,
+ uint32_t d_height, uint32_t flags) {
+ if (!vo_w32_config(ctx->vo, d_width, d_height, flags))
+ return -1;
+
+ struct w32_context *w32_ctx = ctx->priv;
+ HGLRC *context = &w32_ctx->context;
+
+ if (*context) // reuse existing context
+ return 0; // not reusing it breaks gl3!
+
+ HWND win = ctx->vo->w32->window;
+ HDC windc = vo_w32_get_dc(ctx->vo, win);
+ HGLRC new_context = 0;
+
+ new_context = wglCreateContext(windc);
+ if (!new_context) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GL context!\n");
+ return -1;
+ }
+
+ // set context
+ if (!wglMakeCurrent(windc, new_context)) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL context!\n");
+ goto out;
+ }
+
+ const char *(GLAPIENTRY *wglGetExtensionsStringARB)(HDC hdc)
+ = w32gpa((const GLubyte*)"wglGetExtensionsStringARB");
+
+ if (!wglGetExtensionsStringARB)
+ goto unsupported;
+
+ const char *wgl_exts = wglGetExtensionsStringARB(windc);
+ if (!strstr(wgl_exts, "WGL_ARB_create_context"))
+ goto unsupported;
+
+ HGLRC (GLAPIENTRY *wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext,
+ const int *attribList)
+ = w32gpa((const GLubyte*)"wglCreateContextAttribsARB");
+
+ if (!wglCreateContextAttribsARB)
+ goto unsupported;
+
+ int attribs[] = {
+ WGL_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version),
+ WGL_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version),
+ WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
+ WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
+ 0
+ };
+
+ *context = wglCreateContextAttribsARB(windc, 0, attribs);
+ if (! *context) {
+ // NVidia, instead of ignoring WGL_CONTEXT_FLAGS_ARB, will error out if
+ // it's present on pre-3.2 contexts.
+ // Remove it from attribs and retry the context creation.
+ attribs[6] = attribs[7] = 0;
+ *context = wglCreateContextAttribsARB(windc, 0, attribs);
+ }
+ if (! *context) {
+ int err = GetLastError();
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create an OpenGL 3.x"
+ " context: error 0x%x\n", err);
+ goto out;
+ }
+
+ wglMakeCurrent(NULL, NULL);
+ wglDeleteContext(new_context);
+
+ if (!wglMakeCurrent(windc, *context)) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL3 context!\n");
+ wglDeleteContext(*context);
+ return -1;
+ }
+
+ /* update function pointers */
+ getFunctions(ctx->gl, w32gpa, NULL, true);
+
+ int pfmt = GetPixelFormat(windc);
+ PIXELFORMATDESCRIPTOR pfd;
+ if (DescribePixelFormat(windc, pfmt, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) {
+ ctx->depth_r = pfd.cRedBits;
+ ctx->depth_g = pfd.cGreenBits;
+ ctx->depth_b = pfd.cBlueBits;
+ }
+
+ return 0;
+
+unsupported:
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] The current OpenGL implementation does"
+ " not support OpenGL 3.x \n");
+out:
+ wglDeleteContext(new_context);
+ return -1;
+}
+
static int setGlWindow_w32(MPGLContext *ctx)
{
- HWND win = vo_w32_window;
- int *vinfo = &ctx->vinfo.w32;
- HGLRC *context = &ctx->context.w32;
+ HWND win = ctx->vo->w32->window;
+ struct w32_context *w32_ctx = ctx->priv;
+ int *vinfo = &w32_ctx->vinfo;
+ HGLRC *context = &w32_ctx->context;
int new_vinfo;
- HDC windc = vo_w32_get_dc(win);
+ HDC windc = vo_w32_get_dc(ctx->vo, win);
HGLRC new_context = 0;
int keep_context = 0;
int res = SET_WINDOW_FAILED;
@@ -1725,7 +1938,6 @@ static int setGlWindow_w32(MPGLContext *ctx)
}
// set new values
- vo_w32_window = win;
{
RECT rect;
GetClientRect(win, &rect);
@@ -1737,7 +1949,8 @@ static int setGlWindow_w32(MPGLContext *ctx)
wglDeleteContext(*context);
*context = new_context;
*vinfo = new_vinfo;
- getFunctions(gl, w32gpa, NULL);
+
+ getFunctions(ctx->gl, w32gpa, NULL, false);
// and inform that reinit is neccessary
res = SET_WINDOW_REINIT;
@@ -1745,14 +1958,15 @@ static int setGlWindow_w32(MPGLContext *ctx)
res = SET_WINDOW_OK;
out:
- vo_w32_release_dc(win, windc);
+ vo_w32_release_dc(ctx->vo, win, windc);
return res;
}
static void releaseGlContext_w32(MPGLContext *ctx)
{
- int *vinfo = &ctx->vinfo.w32;
- HGLRC *context = &ctx->context.w32;
+ struct w32_context *w32_ctx = ctx->priv;
+ int *vinfo = &w32_ctx->vinfo;
+ HGLRC *context = &w32_ctx->context;
*vinfo = 0;
if (*context) {
wglMakeCurrent(0, 0);
@@ -1763,21 +1977,22 @@ static void releaseGlContext_w32(MPGLContext *ctx)
static void swapGlBuffers_w32(MPGLContext *ctx)
{
- HDC vo_hdc = vo_w32_get_dc(vo_w32_window);
+ HDC vo_hdc = vo_w32_get_dc(ctx->vo, ctx->vo->w32->window);
SwapBuffers(vo_hdc);
- vo_w32_release_dc(vo_w32_window, vo_hdc);
+ vo_w32_release_dc(ctx->vo, ctx->vo->w32->window, vo_hdc);
}
-
-//trivial wrappers (w32 code uses old vo API)
-static void new_vo_w32_ontop(struct vo *vo) { vo_w32_ontop(); }
-static void new_vo_w32_border(struct vo *vo) { vo_w32_border(); }
-static void new_vo_w32_fullscreen(struct vo *vo) { vo_w32_fullscreen(); }
-static int new_vo_w32_check_events(struct vo *vo) { return vo_w32_check_events(); }
-static void new_w32_update_xinerama_info(struct vo *vo) { w32_update_xinerama_info(); }
#endif
+
#ifdef CONFIG_GL_X11
+#include <X11/Xlib.h>
+#include <GL/glx.h>
#include "x11_common.h"
+struct glx_context {
+ XVisualInfo *vinfo;
+ GLXContext context;
+};
+
static int create_window_x11(struct MPGLContext *ctx, uint32_t d_width,
uint32_t d_height, uint32_t flags)
{
@@ -1832,21 +2047,6 @@ static XVisualInfo *getWindowVisualInfo(MPGLContext *ctx, Window win)
return XGetVisualInfo(ctx->vo->x11->display, VisualIDMask, &vinfo_template, &tmp);
}
-static void appendstr(char **dst, const char *str)
-{
- int newsize;
- char *newstr;
- if (!str)
- return;
- newsize = strlen(*dst) + 1 + strlen(str) + 1;
- newstr = realloc(*dst, newsize);
- if (!newstr)
- return;
- *dst = newstr;
- strcat(*dst, " ");
- strcat(*dst, str);
-}
-
/**
* \brief Changes the window in which video is displayed.
* If possible only transfers the context to the new window, otherwise
@@ -1861,8 +2061,9 @@ static void appendstr(char **dst, const char *str)
*/
static int setGlWindow_x11(MPGLContext *ctx)
{
- XVisualInfo **vinfo = &ctx->vinfo.x11;
- GLXContext *context = &ctx->context.x11;
+ struct glx_context *glx_context = ctx->priv;
+ XVisualInfo **vinfo = &glx_context->vinfo;
+ GLXContext *context = &glx_context->context;
Display *display = ctx->vo->x11->display;
Window win = ctx->vo->x11->window;
XVisualInfo *new_vinfo;
@@ -1907,8 +2108,6 @@ static int setGlWindow_x11(MPGLContext *ctx)
vo_x11_update_geometry(ctx->vo, 1);
if (!keep_context) {
void *(*getProcAddress)(const GLubyte *);
- const char *(*glXExtStr)(Display *, int);
- char *glxstr = strdup("");
if (*context)
glXDestroyContext(display, *context);
*context = new_context;
@@ -1918,25 +2117,21 @@ static int setGlWindow_x11(MPGLContext *ctx)
getProcAddress = getdladdr("glXGetProcAddress");
if (!getProcAddress)
getProcAddress = getdladdr("glXGetProcAddressARB");
- glXExtStr = getdladdr("glXQueryExtensionsString");
- if (glXExtStr)
- appendstr(&glxstr, glXExtStr(display, DefaultScreen(display)));
- glXExtStr = getdladdr("glXGetClientString");
- if (glXExtStr)
- appendstr(&glxstr, glXExtStr(display, GLX_EXTENSIONS));
- glXExtStr = getdladdr("glXGetServerString");
+
+ const char *glxstr = "";
+ const char *(*glXExtStr)(Display *, int)
+ = getdladdr("glXQueryExtensionsString");
if (glXExtStr)
- appendstr(&glxstr, glXExtStr(display, GLX_EXTENSIONS));
+ glxstr = glXExtStr(display, ctx->vo->x11->screen);
- getFunctions(gl, getProcAddress, glxstr);
+ getFunctions(gl, getProcAddress, glxstr, false);
if (!gl->GenPrograms && gl->GetString &&
getProcAddress &&
strstr(gl->GetString(GL_EXTENSIONS), "GL_ARB_vertex_program")) {
mp_msg(MSGT_VO, MSGL_WARN,
"Broken glXGetProcAddress detected, trying workaround\n");
- getFunctions(gl, NULL, glxstr);
+ getFunctions(gl, NULL, glxstr, false);
}
- free(glxstr);
// and inform that reinit is neccessary
return SET_WINDOW_REINIT;
@@ -1944,14 +2139,154 @@ static int setGlWindow_x11(MPGLContext *ctx)
return SET_WINDOW_OK;
}
+// The GL3 initialization code roughly follows/copies from:
+// http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)
+// but also uses some of the old code.
+
+static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs)
+{
+ int fbcount;
+ GLXFBConfig *fbc = glXChooseFBConfig(vo->x11->display, vo->x11->screen,
+ attribs, &fbcount);
+ if (!fbc)
+ return NULL;
+
+ // The list in fbc is sorted (so that the first element is the best).
+ GLXFBConfig fbconfig = fbc[0];
+
+ XFree(fbc);
+
+ return fbconfig;
+}
+
+typedef GLXContext (*glXCreateContextAttribsARBProc)
+ (Display*, GLXFBConfig, GLXContext, Bool, const int*);
+
+static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags,
+ int gl_version, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ struct vo *vo = ctx->vo;
+ struct glx_context *glx_ctx = ctx->priv;
+
+ if (glx_ctx->context) {
+ // GL context and window already exist.
+ // Only update window geometry etc.
+ Colormap colormap = XCreateColormap(vo->x11->display, vo->x11->rootwin,
+ glx_ctx->vinfo->visual, AllocNone);
+ vo_x11_create_vo_window(vo, glx_ctx->vinfo, vo->dx, vo->dy, d_width,
+ d_height, flags, colormap, "gl");
+ XFreeColormap(vo->x11->display, colormap);
+ return SET_WINDOW_OK;
+ }
+
+ int glx_major, glx_minor;
+
+ // FBConfigs were added in GLX version 1.3.
+ if (!glXQueryVersion(vo->x11->display, &glx_major, &glx_minor) ||
+ (MPGL_VER(glx_major, glx_minor) < MPGL_VER(1, 3)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] GLX version older than 1.3.\n");
+ return SET_WINDOW_FAILED;
+ }
+
+ const int glx_attribs_stereo_value_idx = 1; // index of GLX_STEREO + 1
+ int glx_attribs[] = {
+ GLX_STEREO, False,
+ GLX_X_RENDERABLE, True,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ GLX_DOUBLEBUFFER, True,
+ None
+ };
+ GLXFBConfig fbc = NULL;
+ if (flags & VOFLAG_STEREO) {
+ glx_attribs[glx_attribs_stereo_value_idx] = True;
+ fbc = select_fb_config(vo, glx_attribs);
+ if (!fbc) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Could not find a stereo visual,"
+ " 3D will probably not work!\n");
+ glx_attribs[glx_attribs_stereo_value_idx] = False;
+ }
+ }
+ if (!fbc)
+ fbc = select_fb_config(vo, glx_attribs);
+ if (!fbc) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] no GLX support present\n");
+ return SET_WINDOW_FAILED;
+ }
+
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_RED_SIZE, &ctx->depth_r);
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_GREEN_SIZE, &ctx->depth_g);
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_BLUE_SIZE, &ctx->depth_b);
+
+ XVisualInfo *vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] GLX chose visual with ID 0x%x\n",
+ (int)vinfo->visualid);
+ Colormap colormap = XCreateColormap(vo->x11->display, vo->x11->rootwin,
+ vinfo->visual, AllocNone);
+ vo_x11_create_vo_window(vo, vinfo, vo->dx, vo->dy, d_width, d_height,
+ flags, colormap, "gl");
+ XFreeColormap(vo->x11->display, colormap);
+
+ glXCreateContextAttribsARBProc glXCreateContextAttribsARB =
+ (glXCreateContextAttribsARBProc)
+ glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB");
+
+ const char *glxstr = "";
+ const char *(*glXExtStr)(Display *, int)
+ = getdladdr("glXQueryExtensionsString");
+ if (glXExtStr)
+ glxstr = glXExtStr(vo->x11->display, vo->x11->screen);
+ bool have_ctx_ext = glxstr && !!strstr(glxstr, "GLX_ARB_create_context");
+
+ if (!(have_ctx_ext && glXCreateContextAttribsARB)) {
+ XFree(vinfo);
+ return SET_WINDOW_FAILED;
+ }
+
+ int context_attribs[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version),
+ GLX_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version),
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
+ | (gl_flags & MPGLFLAG_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
+ None
+ };
+ GLXContext context = glXCreateContextAttribsARB(vo->x11->display, fbc, 0,
+ True, context_attribs);
+ if (!context) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GLX context!\n");
+ XFree(vinfo);
+ return SET_WINDOW_FAILED;
+ }
+
+ // set context
+ if (!glXMakeCurrent(vo->x11->display, vo->x11->window, context)) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GLX context!\n");
+ glXDestroyContext(vo->x11->display, context);
+ XFree(vinfo);
+ return SET_WINDOW_FAILED;
+ }
+
+ glx_ctx->vinfo = vinfo;
+ glx_ctx->context = context;
+
+ getFunctions(ctx->gl, (void *)glXGetProcAddress, glxstr, true);
+
+ return SET_WINDOW_REINIT;
+}
+
/**
* \brief free the VisualInfo and GLXContext of an OpenGL context.
* \ingroup glcontext
*/
static void releaseGlContext_x11(MPGLContext *ctx)
{
- XVisualInfo **vinfo = &ctx->vinfo.x11;
- GLXContext *context = &ctx->context.x11;
+ struct glx_context *glx_ctx = ctx->priv;
+ XVisualInfo **vinfo = &glx_ctx->vinfo;
+ GLXContext *context = &glx_ctx->context;
Display *display = ctx->vo->x11->display;
GL *gl = ctx->gl;
if (*vinfo)
@@ -1998,7 +2333,7 @@ static int setGlWindow_sdl(MPGLContext *ctx)
if (sdl_set_mode(0, SDL_OPENGL | SDL_RESIZABLE) < 0)
return SET_WINDOW_FAILED;
SDL_GL_LoadLibrary(NULL);
- getFunctions(ctx->gl, sdlgpa, NULL);
+ getFunctions(ctx->gl, sdlgpa, NULL, false);
return SET_WINDOW_OK;
}
@@ -2021,9 +2356,40 @@ static int sdl_check_events(struct vo *vo)
static void new_sdl_update_xinerama_info(struct vo *vo) { sdl_update_xinerama_info(); }
static void new_vo_sdl_fullscreen(struct vo *vo) { vo_sdl_fullscreen(); }
+static void new_vo_sdl_uninit(struct vo *vo) { vo_sdl_uninit(); }
#endif
+struct backend {
+ const char *name;
+ enum MPGLType type;
+};
+
+static struct backend backends[] = {
+ {"auto", GLTYPE_AUTO},
+ {"cocoa", GLTYPE_COCOA},
+ {"win", GLTYPE_W32},
+ {"x11", GLTYPE_X11},
+ {"sdl", GLTYPE_SDL},
+ // mplayer-svn aliases (note that mplayer-svn couples these with the numeric
+ // values of the internal GLTYPE_* constants)
+ {"-1", GLTYPE_AUTO},
+ { "0", GLTYPE_W32},
+ { "1", GLTYPE_X11},
+ { "2", GLTYPE_SDL},
+
+ {0}
+};
+
+int mpgl_find_backend(const char *name)
+{
+ for (const struct backend *entry = backends; entry->name; entry++) {
+ if (strcmp(entry->name, name) == 0)
+ return entry->type;
+ }
+ return -1;
+}
+
MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo)
{
MPGLContext *ctx;
@@ -2047,6 +2413,7 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo)
#ifdef CONFIG_GL_COCOA
case GLTYPE_COCOA:
ctx->create_window = create_window_cocoa;
+ ctx->create_window_gl3 = create_window_cocoa_gl3;
ctx->setGlWindow = setGlWindow_cocoa;
ctx->releaseGlContext = releaseGlContext_cocoa;
ctx->swapGlBuffers = swapGlBuffers_cocoa;
@@ -2054,31 +2421,35 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo)
ctx->update_xinerama_info = cocoa_update_xinerama_info;
ctx->fullscreen = cocoa_fullscreen;
ctx->ontop = vo_cocoa_ontop;
+ ctx->vo_uninit = vo_cocoa_uninit;
if (vo_cocoa_init(vo))
return ctx;
break;
#endif
#ifdef CONFIG_GL_WIN32
case GLTYPE_W32:
+ ctx->priv = talloc_zero(ctx, struct w32_context);
ctx->create_window = create_window_w32;
+ ctx->create_window_gl3 = create_window_w32_gl3;
ctx->setGlWindow = setGlWindow_w32;
ctx->releaseGlContext = releaseGlContext_w32;
ctx->swapGlBuffers = swapGlBuffers_w32;
- ctx->update_xinerama_info = new_w32_update_xinerama_info;
- ctx->border = new_vo_w32_border;
- ctx->check_events = new_vo_w32_check_events;
- ctx->fullscreen = new_vo_w32_fullscreen;
- ctx->ontop = new_vo_w32_ontop;
- //the win32 code is hardcoded to use the deprecated vo API
- global_vo = vo;
- if (vo_w32_init())
+ ctx->update_xinerama_info = w32_update_xinerama_info;
+ ctx->border = vo_w32_border;
+ ctx->check_events = vo_w32_check_events;
+ ctx->fullscreen = vo_w32_fullscreen;
+ ctx->ontop = vo_w32_ontop;
+ ctx->vo_uninit = vo_w32_uninit;
+ if (vo_w32_init(vo))
return ctx;
break;
#endif
#ifdef CONFIG_GL_X11
case GLTYPE_X11:
+ ctx->priv = talloc_zero(ctx, struct glx_context);
ctx->create_window = create_window_x11;
ctx->setGlWindow = setGlWindow_x11;
+ ctx->create_window_gl3 = create_window_x11_gl3;
ctx->releaseGlContext = releaseGlContext_x11;
ctx->swapGlBuffers = swapGlBuffers_x11;
ctx->update_xinerama_info = update_xinerama_info;
@@ -2086,6 +2457,7 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo)
ctx->check_events = vo_x11_check_events;
ctx->fullscreen = vo_x11_fullscreen;
ctx->ontop = vo_x11_ontop;
+ ctx->vo_uninit = vo_x11_uninit;
if (vo_init(vo))
return ctx;
break;
@@ -2099,6 +2471,7 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo)
ctx->update_xinerama_info = new_sdl_update_xinerama_info;
ctx->check_events = sdl_check_events;
ctx->fullscreen = new_vo_sdl_fullscreen;
+ ctx->vo_uninit = new_vo_sdl_uninit;
//the SDL code is hardcoded to use the deprecated vo API
global_vo = vo;
if (vo_sdl_init())
@@ -2110,32 +2483,45 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo)
return NULL;
}
+int create_mpglcontext(struct MPGLContext *ctx, int gl_flags, int gl_version,
+ uint32_t d_width, uint32_t d_height, uint32_t flags)
+{
+ if (gl_version < MPGL_VER(3, 0)) {
+ if (ctx->create_window(ctx, d_width, d_height, flags) < 0)
+ return SET_WINDOW_FAILED;
+ return ctx->setGlWindow(ctx);
+ } else {
+ if (!ctx->create_window_gl3) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] OpenGL 3.x context creation not "
+ "implemented.\n");
+ return SET_WINDOW_FAILED;
+ }
+ return ctx->create_window_gl3(ctx, gl_flags, gl_version, d_width,
+ d_height, flags);
+ }
+}
+
void uninit_mpglcontext(MPGLContext *ctx)
{
if (!ctx)
return;
ctx->releaseGlContext(ctx);
- switch (ctx->type) {
-#ifdef CONFIG_GL_COCOA
- case GLTYPE_COCOA:
- vo_cocoa_uninit(ctx->vo);
- break;
-#endif
-#ifdef CONFIG_GL_WIN32
- case GLTYPE_W32:
- vo_w32_uninit();
- break;
-#endif
-#ifdef CONFIG_GL_X11
- case GLTYPE_X11:
- vo_x11_uninit(ctx->vo);
- break;
-#endif
-#ifdef CONFIG_GL_SDL
- case GLTYPE_SDL:
- vo_sdl_uninit();
- break;
-#endif
- }
+ ctx->vo_uninit(ctx->vo);
talloc_free(ctx);
}
+
+void mp_log_source(int mod, int lev, const char *src)
+{
+ int line = 1;
+ if (!src)
+ return;
+ while (*src) {
+ const char *end = strchr(src, '\n');
+ const char *next = end + 1;
+ if (!end)
+ next = end = src + strlen(src);
+ mp_msg(mod, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
+ line++;
+ src = next;
+ }
+}
diff --git a/libvo/gl_common.h b/libvo/gl_common.h
index b02e1d2b8d..f0e5912dd9 100644
--- a/libvo/gl_common.h
+++ b/libvo/gl_common.h
@@ -33,221 +33,16 @@
#include "video_out.h"
#include "csputils.h"
-#ifdef CONFIG_GL_WIN32
-#include <windows.h>
-#include "w32_common.h"
-#endif
-#ifdef CONFIG_GL_X11
-#include <X11/Xlib.h>
-#include <GL/glx.h>
-#include "x11_common.h"
-// This old-vo wrapper macro would conflict with the struct member
-#undef update_xinerama_info
-#endif
#include <GL/gl.h>
+#include <GL/glext.h>
-// workaround for some gl.h headers
-#ifndef GLAPIENTRY
-#ifdef APIENTRY
-#define GLAPIENTRY APIENTRY
-#elif defined(CONFIG_GL_WIN32)
-#define GLAPIENTRY __stdcall
-#else
-#define GLAPIENTRY
-#endif
-#endif
-
-/**
- * \defgroup glextdefines OpenGL extension defines
- *
- * conditionally define all extension defines used.
- * vendor specific extensions should be marked as such
- * (e.g. _NV), _ARB is not used to ease readability.
- * \{
- */
-#ifndef GL_TEXTURE_3D
-#define GL_TEXTURE_3D 0x806F
-#endif
-#ifndef GL_TEXTURE_WRAP_R
-#define GL_TEXTURE_WRAP_R 0x8072
-#endif
-#ifndef GL_CLAMP_TO_EDGE
-#define GL_CLAMP_TO_EDGE 0x812F
-#endif
-#ifndef GL_GENERATE_MIPMAP
-#define GL_GENERATE_MIPMAP 0x8191
-#endif
-#ifndef GL_TEXT_FRAGMENT_SHADER_ATI
-#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200
-#endif
-#ifndef GL_FRAGMENT_SHADER_ATI
-#define GL_FRAGMENT_SHADER_ATI 0x8920
-#endif
-#ifndef GL_NUM_FRAGMENT_REGISTERS_ATI
-#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E
-#endif
-#ifndef GL_REG_0_ATI
-#define GL_REG_0_ATI 0x8921
-#endif
-#ifndef GL_REG_1_ATI
-#define GL_REG_1_ATI 0x8922
-#endif
-#ifndef GL_REG_2_ATI
-#define GL_REG_2_ATI 0x8923
-#endif
-#ifndef GL_CON_0_ATI
-#define GL_CON_0_ATI 0x8941
-#endif
-#ifndef GL_CON_1_ATI
-#define GL_CON_1_ATI 0x8942
-#endif
-#ifndef GL_CON_2_ATI
-#define GL_CON_2_ATI 0x8943
-#endif
-#ifndef GL_CON_3_ATI
-#define GL_CON_3_ATI 0x8944
-#endif
-#ifndef GL_ADD_ATI
-#define GL_ADD_ATI 0x8963
-#endif
-#ifndef GL_MUL_ATI
-#define GL_MUL_ATI 0x8964
-#endif
-#ifndef GL_MAD_ATI
-#define GL_MAD_ATI 0x8968
-#endif
-#ifndef GL_SWIZZLE_STR_ATI
-#define GL_SWIZZLE_STR_ATI 0x8976
-#endif
-#ifndef GL_4X_BIT_ATI
-#define GL_4X_BIT_ATI 2
-#endif
-#ifndef GL_8X_BIT_ATI
-#define GL_8X_BIT_ATI 4
-#endif
-#ifndef GL_BIAS_BIT_ATI
-#define GL_BIAS_BIT_ATI 8
-#endif
-#ifndef GL_MAX_TEXTURE_UNITS
-#define GL_MAX_TEXTURE_UNITS 0x84E2
-#endif
-#ifndef GL_TEXTURE0
-#define GL_TEXTURE0 0x84C0
-#endif
-#ifndef GL_TEXTURE1
-#define GL_TEXTURE1 0x84C1
-#endif
-#ifndef GL_TEXTURE2
-#define GL_TEXTURE2 0x84C2
-#endif
-#ifndef GL_TEXTURE3
-#define GL_TEXTURE3 0x84C3
-#endif
-#ifndef GL_TEXTURE_RECTANGLE
-#define GL_TEXTURE_RECTANGLE 0x84F5
-#endif
-#ifndef GL_PIXEL_UNPACK_BUFFER
-#define GL_PIXEL_UNPACK_BUFFER 0x88EC
-#endif
-#ifndef GL_STREAM_DRAW
-#define GL_STREAM_DRAW 0x88E0
-#endif
-#ifndef GL_DYNAMIC_DRAW
-#define GL_DYNAMIC_DRAW 0x88E8
-#endif
-#ifndef GL_WRITE_ONLY
-#define GL_WRITE_ONLY 0x88B9
-#endif
-#ifndef GL_BGR
-#define GL_BGR 0x80E0
-#endif
-#ifndef GL_BGRA
-#define GL_BGRA 0x80E1
-#endif
-#ifndef GL_UNSIGNED_BYTE_3_3_2
-#define GL_UNSIGNED_BYTE_3_3_2 0x8032
-#endif
-#ifndef GL_UNSIGNED_BYTE_2_3_3_REV
-#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
-#endif
-#ifndef GL_UNSIGNED_SHORT_4_4_4_4
-#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
-#endif
-#ifndef GL_UNSIGNED_SHORT_4_4_4_4_REV
-#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
-#endif
-#ifndef GL_UNSIGNED_SHORT_5_6_5
-#define GL_UNSIGNED_SHORT_5_6_5 0x8363
-#endif
-#ifndef GL_UNSIGNED_INT_8_8_8_8
-#define GL_UNSIGNED_INT_8_8_8_8 0x8035
-#endif
-#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
-#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
-#endif
-#ifndef GL_UNSIGNED_SHORT_5_6_5_REV
-#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
-#endif
-#ifndef GL_UNSIGNED_INT_10_10_10_2
-#define GL_UNSIGNED_INT_10_10_10_2 0x8036
-#endif
-#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
-#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
-#endif
-#ifndef GL_UNSIGNED_SHORT_5_5_5_1
-#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
-#endif
-#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV
-#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
-#endif
-#ifndef GL_UNSIGNED_SHORT_8_8
-#define GL_UNSIGNED_SHORT_8_8 0x85BA
-#endif
-#ifndef GL_UNSIGNED_SHORT_8_8_REV
-#define GL_UNSIGNED_SHORT_8_8_REV 0x85BB
-#endif
-#ifndef GL_YCBCR_MESA
-#define GL_YCBCR_MESA 0x8757
-#endif
-#ifndef GL_RGB32F
-#define GL_RGB32F 0x8815
-#endif
-#ifndef GL_FLOAT_RGB32_NV
-#define GL_FLOAT_RGB32_NV 0x8889
-#endif
-#ifndef GL_LUMINANCE16
-#define GL_LUMINANCE16 0x8042
-#endif
-#ifndef GL_R16
-#define GL_R16 0x822A
-#endif
-#ifndef GL_UNPACK_CLIENT_STORAGE_APPLE
-#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2
-#endif
-#ifndef GL_FRAGMENT_PROGRAM
-#define GL_FRAGMENT_PROGRAM 0x8804
-#endif
-#ifndef GL_PROGRAM_FORMAT_ASCII
-#define GL_PROGRAM_FORMAT_ASCII 0x8875
-#endif
-#ifndef GL_PROGRAM_ERROR_POSITION
-#define GL_PROGRAM_ERROR_POSITION 0x864B
-#endif
-#ifndef GL_MAX_TEXTURE_IMAGE_UNITS
-#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
-#endif
-#ifndef GL_PROGRAM_ERROR_STRING
-#define GL_PROGRAM_ERROR_STRING 0x8874
-#endif
-/** \} */ // end of glextdefines group
+#include "libvo/gl_header_fixes.h"
struct GL;
typedef struct GL GL;
void glAdjustAlignment(GL *gl, int stride);
-const char *glValName(GLint value);
-
int glFindFormat(uint32_t format, int have_texture_rg, int *bpp,
GLint *gl_texfmt, GLenum *gl_format, GLenum *gl_type);
int glFmt2bpp(GLenum format, GLenum type);
@@ -265,6 +60,7 @@ void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h,
GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th,
int sx, int sy, int rect_tex, int is_yv12, int flip);
int loadGPUProgram(GL *gl, GLenum target, char *prog);
+void glCheckError(GL *gl, const char *info);
/** \addtogroup glconversion
* \{ */
@@ -335,6 +131,7 @@ typedef struct {
int chrom_texw;
int chrom_texh;
float filter_strength;
+ float noise_strength;
} gl_conversion_params_t;
int glAutodetectYUVConversion(GL *gl);
@@ -368,39 +165,55 @@ enum MPGLType {
GLTYPE_SDL,
};
+enum {
+ MPGLFLAG_DEBUG = 1,
+};
+
+#define MPGL_VER(major, minor) (((major) << 16) | (minor))
+#define MPGL_VER_GET_MAJOR(ver) ((ver) >> 16)
+#define MPGL_VER_GET_MINOR(ver) ((ver) & ((1 << 16) - 1))
+
typedef struct MPGLContext {
GL *gl;
enum MPGLType type;
struct vo *vo;
- union {
- int w32;
-#ifdef CONFIG_GL_X11
- XVisualInfo *x11;
-#endif
- } vinfo;
- union {
-#ifdef CONFIG_GL_WIN32
- HGLRC w32;
-#endif
-#ifdef CONFIG_GL_X11
- GLXContext x11;
-#endif
- } context;
+ void *priv;
+ // Bit size of each component in the created framebuffer. 0 if unknown.
+ int depth_r, depth_g, depth_b;
int (*create_window)(struct MPGLContext *ctx, uint32_t d_width,
uint32_t d_height, uint32_t flags);
int (*setGlWindow)(struct MPGLContext *);
void (*releaseGlContext)(struct MPGLContext *);
void (*swapGlBuffers)(struct MPGLContext *);
- void (*update_xinerama_info)(struct vo *vo);
- void (*border)(struct vo *vo);
int (*check_events)(struct vo *vo);
void (*fullscreen)(struct vo *vo);
+ void (*vo_uninit)(struct vo *vo);
+ // only available if GL3 context creation is supported
+ // gl_flags: bitfield of MPGLFLAG_* constants
+ // gl_version: requested OpenGL version number (use MPGL_VER())
+ // return value is one of the SET_WINDOW_* constants
+ int (*create_window_gl3)(struct MPGLContext *ctx, int gl_flags,
+ int gl_version, uint32_t d_width,
+ uint32_t d_height, uint32_t flags);
+ // optional
void (*ontop)(struct vo *vo);
+ void (*border)(struct vo *vo);
+ void (*update_xinerama_info)(struct vo *vo);
} MPGLContext;
+int mpgl_find_backend(const char *name);
+
MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo);
void uninit_mpglcontext(MPGLContext *ctx);
+// calls create_window_gl3 or create_window+setGlWindow
+int create_mpglcontext(struct MPGLContext *ctx, int gl_flags, int gl_version,
+ uint32_t d_width, uint32_t d_height, uint32_t flags);
+
+// print a multi line string with line numbers (e.g. for shader sources)
+// mod, lev: module and log level, as in mp_msg()
+void mp_log_source(int mod, int lev, const char *src);
+
//function pointers loaded from the OpenGL library
struct GL {
void (GLAPIENTRY *Begin)(GLenum);
@@ -411,7 +224,6 @@ struct GL {
void (GLAPIENTRY *Translated)(double, double, double);
void (GLAPIENTRY *Scaled)(double, double, double);
void (GLAPIENTRY *Ortho)(double, double, double, double, double,double);
- void (GLAPIENTRY *Frustum)(double, double, double, double, double, double);
void (GLAPIENTRY *PushMatrix)(void);
void (GLAPIENTRY *PopMatrix)(void);
void (GLAPIENTRY *Clear)(GLbitfield);
@@ -423,14 +235,10 @@ struct GL {
void (GLAPIENTRY *CallLists)(GLsizei, GLenum, const GLvoid *);
void (GLAPIENTRY *GenTextures)(GLsizei, GLuint *);
void (GLAPIENTRY *DeleteTextures)(GLsizei, const GLuint *);
- void (GLAPIENTRY *TexEnvf)(GLenum, GLenum, GLfloat);
void (GLAPIENTRY *TexEnvi)(GLenum, GLenum, GLint);
void (GLAPIENTRY *Color4ub)(GLubyte, GLubyte, GLubyte, GLubyte);
- void (GLAPIENTRY *Color3f)(GLfloat, GLfloat, GLfloat);
void (GLAPIENTRY *Color4f)(GLfloat, GLfloat, GLfloat, GLfloat);
void (GLAPIENTRY *ClearColor)(GLclampf, GLclampf, GLclampf, GLclampf);
- void (GLAPIENTRY *ClearDepth)(GLclampd);
- void (GLAPIENTRY *DepthFunc)(GLenum);
void (GLAPIENTRY *Enable)(GLenum);
void (GLAPIENTRY *Disable)(GLenum);
const GLubyte *(GLAPIENTRY * GetString)(GLenum);
@@ -454,16 +262,20 @@ struct GL {
void (GLAPIENTRY *TexCoord2f)(GLfloat, GLfloat);
void (GLAPIENTRY *TexCoord2fv)(const GLfloat *);
void (GLAPIENTRY *Vertex2f)(GLfloat, GLfloat);
- void (GLAPIENTRY *Vertex3f)(GLfloat, GLfloat, GLfloat);
- void (GLAPIENTRY *Normal3f)(GLfloat, GLfloat, GLfloat);
- void (GLAPIENTRY *Lightfv)(GLenum, GLenum, const GLfloat *);
- void (GLAPIENTRY *ColorMaterial)(GLenum, GLenum);
- void (GLAPIENTRY *ShadeModel)(GLenum);
void (GLAPIENTRY *GetIntegerv)(GLenum, GLint *);
+ void (GLAPIENTRY *GetBooleanv)(GLenum, GLboolean *);
void (GLAPIENTRY *ColorMask)(GLboolean, GLboolean, GLboolean, GLboolean);
void (GLAPIENTRY *ReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum,
GLenum, GLvoid *);
void (GLAPIENTRY *ReadBuffer)(GLenum);
+ void (GLAPIENTRY *VertexPointer)(GLint, GLenum, GLsizei, const GLvoid *);
+ void (GLAPIENTRY *ColorPointer)(GLint, GLenum, GLsizei, const GLvoid *);
+ void (GLAPIENTRY *TexCoordPointer)(GLint, GLenum, GLsizei, const GLvoid *);
+ void (GLAPIENTRY *DrawArrays)(GLenum, GLint, GLsizei);
+ void (GLAPIENTRY *EnableClientState)(GLenum);
+ void (GLAPIENTRY *DisableClientState)(GLenum);
+ GLenum (GLAPIENTRY *GetError)(void);
+
// OpenGL extension functions
void (GLAPIENTRY *GenBuffers)(GLsizei, GLuint *);
@@ -472,15 +284,6 @@ struct GL {
GLvoid * (GLAPIENTRY * MapBuffer)(GLenum, GLenum);
GLboolean (GLAPIENTRY *UnmapBuffer)(GLenum);
void (GLAPIENTRY *BufferData)(GLenum, intptr_t, const GLvoid *, GLenum);
- void (GLAPIENTRY *BeginFragmentShader)(void);
- void (GLAPIENTRY *EndFragmentShader)(void);
- void (GLAPIENTRY *SampleMap)(GLuint, GLuint, GLenum);
- void (GLAPIENTRY *ColorFragmentOp2)(GLenum, GLuint, GLuint, GLuint, GLuint,
- GLuint, GLuint, GLuint, GLuint, GLuint);
- void (GLAPIENTRY *ColorFragmentOp3)(GLenum, GLuint, GLuint, GLuint, GLuint,
- GLuint, GLuint, GLuint, GLuint, GLuint,
- GLuint, GLuint, GLuint);
- void (GLAPIENTRY *SetFragmentShaderConstant)(GLuint, const GLfloat *);
void (GLAPIENTRY *ActiveTexture)(GLenum);
void (GLAPIENTRY *BindTexture)(GLenum, GLuint);
void (GLAPIENTRY *MultiTexCoord2f)(GLenum, GLfloat, GLfloat);
@@ -488,13 +291,66 @@ struct GL {
void (GLAPIENTRY *DeletePrograms)(GLsizei, const GLuint *);
void (GLAPIENTRY *BindProgram)(GLenum, GLuint);
void (GLAPIENTRY *ProgramString)(GLenum, GLenum, GLsizei, const GLvoid *);
- void (GLAPIENTRY *GetProgramiv)(GLenum, GLenum, GLint *);
+ void (GLAPIENTRY *GetProgramivARB)(GLenum, GLenum, GLint *);
void (GLAPIENTRY *ProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat,
GLfloat, GLfloat);
int (GLAPIENTRY *SwapInterval)(int);
void (GLAPIENTRY *TexImage3D)(GLenum, GLint, GLenum, GLsizei, GLsizei,
GLsizei, GLint, GLenum, GLenum,
const GLvoid *);
+
+ // ancient ATI extensions
+ void (GLAPIENTRY *BeginFragmentShader)(void);
+ void (GLAPIENTRY *EndFragmentShader)(void);
+ void (GLAPIENTRY *SampleMap)(GLuint, GLuint, GLenum);
+ void (GLAPIENTRY *ColorFragmentOp2)(GLenum, GLuint, GLuint, GLuint, GLuint,
+ GLuint, GLuint, GLuint, GLuint, GLuint);
+ void (GLAPIENTRY *ColorFragmentOp3)(GLenum, GLuint, GLuint, GLuint, GLuint,
+ GLuint, GLuint, GLuint, GLuint, GLuint,
+ GLuint, GLuint, GLuint);
+ void (GLAPIENTRY *SetFragmentShaderConstant)(GLuint, const GLfloat *);
+
+
+ // GL 3, possibly in GL 2.x as well in form of extensions
+ void (GLAPIENTRY *GenVertexArrays)(GLsizei, GLuint *);
+ void (GLAPIENTRY *BindVertexArray)(GLuint);
+ GLint (GLAPIENTRY *GetAttribLocation)(GLuint, const GLchar *);
+ void (GLAPIENTRY *EnableVertexAttribArray)(GLuint);
+ void (GLAPIENTRY *DisableVertexAttribArray)(GLuint);
+ void (GLAPIENTRY *VertexAttribPointer)(GLuint, GLint, GLenum, GLboolean,
+ GLsizei, const GLvoid *);
+ void (GLAPIENTRY *DeleteVertexArrays)(GLsizei, const GLuint *);
+ void (GLAPIENTRY *UseProgram)(GLuint);
+ GLint (GLAPIENTRY *GetUniformLocation)(GLuint, const GLchar *);
+ void (GLAPIENTRY *CompileShader)(GLuint);
+ GLuint (GLAPIENTRY *CreateProgram)(void);
+ GLuint (GLAPIENTRY *CreateShader)(GLenum);
+ void (GLAPIENTRY *ShaderSource)(GLuint, GLsizei, const GLchar **,
+ const GLint *);
+ void (GLAPIENTRY *LinkProgram)(GLuint);
+ void (GLAPIENTRY *AttachShader)(GLuint, GLuint);
+ void (GLAPIENTRY *DeleteShader)(GLuint);
+ void (GLAPIENTRY *DeleteProgram)(GLuint);
+ void (GLAPIENTRY *GetShaderInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *);
+ void (GLAPIENTRY *GetShaderiv)(GLuint, GLenum, GLint *);
+ void (GLAPIENTRY *GetProgramInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *);
+ void (GLAPIENTRY *GetProgramiv)(GLenum, GLenum, GLint *);
+ const GLubyte* (GLAPIENTRY *GetStringi)(GLenum, GLuint);
+ void (GLAPIENTRY *BindAttribLocation)(GLuint, GLuint, const GLchar *);
+ void (GLAPIENTRY *BindFramebuffer)(GLenum, GLuint);
+ void (GLAPIENTRY *GenFramebuffers)(GLsizei, GLuint *);
+ void (GLAPIENTRY *DeleteFramebuffers)(GLsizei, const GLuint *);
+ GLenum (GLAPIENTRY *CheckFramebufferStatus)(GLenum);
+ void (GLAPIENTRY *FramebufferTexture2D)(GLenum, GLenum, GLenum, GLuint,
+ GLint);
+
+ void (GLAPIENTRY *Uniform1f)(GLint, GLfloat);
+ void (GLAPIENTRY *Uniform3f)(GLint, GLfloat, GLfloat, GLfloat);
+ void (GLAPIENTRY *Uniform1i)(GLint, GLint);
+ void (GLAPIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean,
+ const GLfloat *);
+ void (GLAPIENTRY *UniformMatrix4x3fv)(GLint, GLsizei, GLboolean,
+ const GLfloat *);
};
#endif /* MPLAYER_GL_COMMON_H */
diff --git a/libvo/gl_header_fixes.h b/libvo/gl_header_fixes.h
new file mode 100644
index 0000000000..c6f260b289
--- /dev/null
+++ b/libvo/gl_header_fixes.h
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ *
+ * You can alternatively redistribute this file 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.
+ */
+
+// workaround for some gl.h headers
+#ifndef GLAPIENTRY
+#ifdef APIENTRY
+#define GLAPIENTRY APIENTRY
+#elif defined(CONFIG_GL_WIN32)
+#define GLAPIENTRY __stdcall
+#else
+#define GLAPIENTRY
+#endif
+#endif
+
+/**
+ * \defgroup glextdefines OpenGL extension defines
+ *
+ * conditionally define all extension defines used.
+ * vendor specific extensions should be marked as such
+ * (e.g. _NV), _ARB is not used to ease readability.
+ * \{
+ */
+#ifndef GL_TEXTURE_3D
+#define GL_TEXTURE_3D 0x806F
+#endif
+#ifndef GL_TEXTURE_WRAP_R
+#define GL_TEXTURE_WRAP_R 0x8072
+#endif
+#ifndef GL_CLAMP_TO_EDGE
+#define GL_CLAMP_TO_EDGE 0x812F
+#endif
+#ifndef GL_GENERATE_MIPMAP
+#define GL_GENERATE_MIPMAP 0x8191
+#endif
+#ifndef GL_TEXT_FRAGMENT_SHADER_ATI
+#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200
+#endif
+#ifndef GL_FRAGMENT_SHADER_ATI
+#define GL_FRAGMENT_SHADER_ATI 0x8920
+#endif
+#ifndef GL_NUM_FRAGMENT_REGISTERS_ATI
+#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E
+#endif
+#ifndef GL_REG_0_ATI
+#define GL_REG_0_ATI 0x8921
+#endif
+#ifndef GL_REG_1_ATI
+#define GL_REG_1_ATI 0x8922
+#endif
+#ifndef GL_REG_2_ATI
+#define GL_REG_2_ATI 0x8923
+#endif
+#ifndef GL_CON_0_ATI
+#define GL_CON_0_ATI 0x8941
+#endif
+#ifndef GL_CON_1_ATI
+#define GL_CON_1_ATI 0x8942
+#endif
+#ifndef GL_CON_2_ATI
+#define GL_CON_2_ATI 0x8943
+#endif
+#ifndef GL_CON_3_ATI
+#define GL_CON_3_ATI 0x8944
+#endif
+#ifndef GL_ADD_ATI
+#define GL_ADD_ATI 0x8963
+#endif
+#ifndef GL_MUL_ATI
+#define GL_MUL_ATI 0x8964
+#endif
+#ifndef GL_MAD_ATI
+#define GL_MAD_ATI 0x8968
+#endif
+#ifndef GL_SWIZZLE_STR_ATI
+#define GL_SWIZZLE_STR_ATI 0x8976
+#endif
+#ifndef GL_4X_BIT_ATI
+#define GL_4X_BIT_ATI 2
+#endif
+#ifndef GL_8X_BIT_ATI
+#define GL_8X_BIT_ATI 4
+#endif
+#ifndef GL_BIAS_BIT_ATI
+#define GL_BIAS_BIT_ATI 8
+#endif
+#ifndef GL_MAX_TEXTURE_UNITS
+#define GL_MAX_TEXTURE_UNITS 0x84E2
+#endif
+#ifndef GL_TEXTURE0
+#define GL_TEXTURE0 0x84C0
+#endif
+#ifndef GL_TEXTURE1
+#define GL_TEXTURE1 0x84C1
+#endif
+#ifndef GL_TEXTURE2
+#define GL_TEXTURE2 0x84C2
+#endif
+#ifndef GL_TEXTURE3
+#define GL_TEXTURE3 0x84C3
+#endif
+#ifndef GL_TEXTURE_RECTANGLE
+#define GL_TEXTURE_RECTANGLE 0x84F5
+#endif
+#ifndef GL_PIXEL_UNPACK_BUFFER
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#endif
+#ifndef GL_STREAM_DRAW
+#define GL_STREAM_DRAW 0x88E0
+#endif
+#ifndef GL_DYNAMIC_DRAW
+#define GL_DYNAMIC_DRAW 0x88E8
+#endif
+#ifndef GL_WRITE_ONLY
+#define GL_WRITE_ONLY 0x88B9
+#endif
+#ifndef GL_BGR
+#define GL_BGR 0x80E0
+#endif
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+#ifndef GL_UNSIGNED_BYTE_3_3_2
+#define GL_UNSIGNED_BYTE_3_3_2 0x8032
+#endif
+#ifndef GL_UNSIGNED_BYTE_2_3_3_REV
+#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
+#endif
+#ifndef GL_UNSIGNED_SHORT_4_4_4_4
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#endif
+#ifndef GL_UNSIGNED_SHORT_4_4_4_4_REV
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#endif
+#ifndef GL_UNSIGNED_SHORT_5_6_5
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#endif
+#ifndef GL_UNSIGNED_INT_8_8_8_8
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#endif
+#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+#ifndef GL_UNSIGNED_SHORT_5_6_5_REV
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#endif
+#ifndef GL_UNSIGNED_INT_10_10_10_2
+#define GL_UNSIGNED_INT_10_10_10_2 0x8036
+#endif
+#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+#endif
+#ifndef GL_UNSIGNED_SHORT_5_5_5_1
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#endif
+#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#endif
+#ifndef GL_UNSIGNED_SHORT_8_8
+#define GL_UNSIGNED_SHORT_8_8 0x85BA
+#endif
+#ifndef GL_UNSIGNED_SHORT_8_8_REV
+#define GL_UNSIGNED_SHORT_8_8_REV 0x85BB
+#endif
+#ifndef GL_YCBCR_MESA
+#define GL_YCBCR_MESA 0x8757
+#endif
+#ifndef GL_RGB32F
+#define GL_RGB32F 0x8815
+#endif
+#ifndef GL_FLOAT_RGB32_NV
+#define GL_FLOAT_RGB32_NV 0x8889
+#endif
+#ifndef GL_LUMINANCE16
+#define GL_LUMINANCE16 0x8042
+#endif
+#ifndef GL_R16
+#define GL_R16 0x822A
+#endif
+#ifndef GL_UNPACK_CLIENT_STORAGE_APPLE
+#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2
+#endif
+#ifndef GL_FRAGMENT_PROGRAM
+#define GL_FRAGMENT_PROGRAM 0x8804
+#endif
+#ifndef GL_PROGRAM_FORMAT_ASCII
+#define GL_PROGRAM_FORMAT_ASCII 0x8875
+#endif
+#ifndef GL_PROGRAM_ERROR_POSITION
+#define GL_PROGRAM_ERROR_POSITION 0x864B
+#endif
+#ifndef GL_MAX_TEXTURE_IMAGE_UNITS
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#endif
+#ifndef GL_PROGRAM_ERROR_STRING
+#define GL_PROGRAM_ERROR_STRING 0x8874
+#endif
+/** \} */ // end of glextdefines group
+
+
+#if defined(CONFIG_GL_WIN32) && !defined(WGL_CONTEXT_MAJOR_VERSION_ARB)
+/* these are supposed to be defined in wingdi.h but mingw's is too old */
+/* only the bits actually used by mplayer are defined */
+/* reference: http://www.opengl.org/registry/specs/ARB/wgl_create_context.txt */
+
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#endif
diff --git a/libvo/mga_template.c b/libvo/mga_template.c
index 6886ecd8f7..274be76098 100644
--- a/libvo/mga_template.c
+++ b/libvo/mga_template.c
@@ -189,7 +189,7 @@ draw_image(mp_image_t *mpi){
if(mpi->flags&MP_IMGFLAG_PLANAR){
// copy planar:
- draw_slice(mpi->planes,mpi->stride,mpi->w,mpi->h,mpi->x,mpi->y);
+ draw_slice(mpi->planes,mpi->stride,mpi->w,mpi->h,0,0);
} else {
// copy packed:
mem2agpcpy_pic(vid_data, mpi->planes[0], // dst,src
diff --git a/libvo/video_out.c b/libvo/video_out.c
index d0cd991e8b..b49e5b095f 100644
--- a/libvo/video_out.c
+++ b/libvo/video_out.c
@@ -83,7 +83,7 @@ extern struct vo_driver video_out_vdpau;
extern struct vo_driver video_out_xv;
extern struct vo_driver video_out_gl_nosw;
extern struct vo_driver video_out_gl;
-extern struct vo_driver video_out_gl_sdl;
+extern struct vo_driver video_out_gl3;
extern struct vo_driver video_out_dga;
extern struct vo_driver video_out_sdl;
extern struct vo_driver video_out_3dfx;
@@ -102,6 +102,7 @@ extern struct vo_driver video_out_caca;
extern struct vo_driver video_out_mpegpes;
extern struct vo_driver video_out_yuv4mpeg;
extern struct vo_driver video_out_direct3d;
+extern struct vo_driver video_out_direct3d_shaders;
extern struct vo_driver video_out_directx;
extern struct vo_driver video_out_kva;
extern struct vo_driver video_out_dxr3;
@@ -128,12 +129,13 @@ const struct vo_driver *video_out_drivers[] =
#ifdef CONFIG_TDFX_VID
&video_out_tdfx_vid,
#endif
-#ifdef CONFIG_DIRECTX
- &video_out_directx,
-#endif
#ifdef CONFIG_DIRECT3D
+ &video_out_direct3d_shaders,
&video_out_direct3d,
#endif
+#ifdef CONFIG_DIRECTX
+ &video_out_directx,
+#endif
#ifdef CONFIG_KVA
&video_out_kva,
#endif
@@ -167,22 +169,19 @@ const struct vo_driver *video_out_drivers[] =
#ifdef CONFIG_XV
&video_out_xv,
#endif
-#ifdef CONFIG_X11
#ifdef CONFIG_GL
- &video_out_gl_nosw,
+ &video_out_gl3,
+#if !defined CONFIG_GL_COCOA
+ &video_out_gl,
#endif
+#endif
+#ifdef CONFIG_X11
&video_out_x11,
&video_out_xover,
#endif
#ifdef CONFIG_SDL
&video_out_sdl,
#endif
-#if (defined CONFIG_GL && !defined CONFIG_GL_COCOA)
- &video_out_gl,
-#endif
-#ifdef CONFIG_GL_SDL
- &video_out_gl_sdl,
-#endif
#ifdef CONFIG_DGA
&video_out_dga,
#endif
@@ -222,6 +221,9 @@ const struct vo_driver *video_out_drivers[] =
#endif
&video_out_null,
// should not be auto-selected
+#ifdef CONFIG_SHAREDBUFFER
+ &video_out_sharedbuffer,
+#endif
#ifdef CONFIG_DIRECTFB
// vo directfb can call exit() if initialization fails
&video_out_directfb,
@@ -246,8 +248,10 @@ const struct vo_driver *video_out_drivers[] =
#ifdef CONFIG_MD5SUM
&video_out_md5sum,
#endif
-#ifdef CONFIG_SHAREDBUFFER
- &video_out_sharedbuffer,
+#ifdef CONFIG_X11
+#ifdef CONFIG_GL
+ &video_out_gl_nosw,
+#endif
#endif
NULL
};
diff --git a/libvo/video_out.h b/libvo/video_out.h
index 10a3891b7b..df27d373fe 100644
--- a/libvo/video_out.h
+++ b/libvo/video_out.h
@@ -279,6 +279,7 @@ struct vo {
void *priv;
struct MPOpts *opts;
struct vo_x11_state *x11;
+ struct vo_w32_state *w32;
struct mp_fifo *key_fifo;
struct input_ctx *input_ctx;
int event_fd; // check_events() should be called when this has input
diff --git a/libvo/vo_direct3d.c b/libvo/vo_direct3d.c
index 2fb40d0398..fb2f58f5cc 100644
--- a/libvo/vo_direct3d.c
+++ b/libvo/vo_direct3d.c
@@ -21,10 +21,20 @@
#include <windows.h>
#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdbool.h>
+#include <assert.h>
#include <d3d9.h>
#include "config.h"
+#include "options.h"
+#include "subopt-helper.h"
+#include "talloc.h"
#include "video_out.h"
-#include "video_out_internal.h"
+#include "libmpcodecs/vfcap.h"
+#include "csputils.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/img_format.h"
#include "fastmemcpy.h"
#include "mp_msg.h"
#include "aspect.h"
@@ -32,28 +42,100 @@
#include "libavutil/common.h"
#include "sub/font_load.h"
#include "sub/sub.h"
+#include "eosd_packer.h"
-static const vo_info_t info =
-{
- "Direct3D 9 Renderer",
- "direct3d",
- "Georgi Petrov (gogothebee) <gogothebee@gmail.com>",
- ""
-};
+// shaders generated by fxc.exe from d3d_shader_yuv.hlsl
+#include "d3d_shader_yuv.h"
+#include "d3d_shader_yuv_2ch.h"
-/*
- * Link essential libvo functions: preinit, config, control, draw_frame,
- * draw_slice, draw_osd, flip_page, check_events, uninit and
- * the structure info.
- */
-const LIBVO_EXTERN(direct3d)
+// TODO: beg someone to add this (there is already IMGFMT_Y8)
+// equals MAKEFOURCC('Y', '1', '6', ' ')
+#define IMGFMT_Y16 0x20363159
+#define IMGFMT_A8Y8 MAKEFOURCC('A', '8', 'Y', '8')
+
+#define IMGFMT_IS_Y(x) ((x) == IMGFMT_Y8 || (x) == IMGFMT_Y16 || (x) == IMGFMT_A8Y8)
+#define IMGFMT_Y_DEPTH(x) ((x) == IMGFMT_Y8 ? 8 : 16)
+
+#define DEVTYPE D3DDEVTYPE_HAL
+//#define DEVTYPE D3DDEVTYPE_REF
+
+
+#define D3DFVF_OSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
+
+typedef struct {
+ float x, y, z; /* Position of vertex in 3D space */
+ float tu, tv; /* Texture coordinates */
+} vertex_osd;
+
+#define D3DFVF_EOSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE)
+
+typedef struct {
+ float x, y, z;
+ D3DCOLOR color;
+ float tu, tv;
+} vertex_eosd;
+
+#define D3DFVF_VIDEO_VERTEX (D3DFVF_XYZ | D3DFVF_TEX3)
+
+typedef struct {
+ float x, y, z;
+ // pairs of texture coordinates for up to 3 planes
+ float t[3][2];
+} vertex_video;
+
+
+struct d3dtex {
+ // user-requested size
+ int w, h;
+ // allocated texture size
+ int tex_w, tex_h;
+ // D3DPOOL_SYSTEMMEM (or others) texture:
+ // - can be locked in order to write (and even read) data
+ // - can _not_ (probably) be used as texture for rendering
+ // This is always non-NULL if d3dtex_allocate succeeds.
+ IDirect3DTexture9 *system;
+ // D3DPOOL_DEFAULT texture:
+ // - can't be locked (Probably.)
+ // - must be used for rendering
+ // This will be NULL on systems with device_texture_sys != 0.
+ IDirect3DTexture9 *device;
+};
+
+struct texplane {
+ int bytes_per_pixel;
+ int bits_per_pixel;
+ // chroma shifts
+ // e.g. get the plane's width in pixels with (priv->src_width >> shift_x)
+ int shift_x, shift_y;
+ D3DFORMAT d3d_format;
+ struct d3dtex texture;
+ // temporary locking during uploading the frame (e.g. for draw_slice)
+ D3DLOCKED_RECT locked_rect;
+ // value used to clear the image with memset (YUV chroma planes do not use
+ // the value 0 for this)
+ uint8_t clearval;
+};
/* Global variables "priv" structure. I try to keep their count low.
*/
-static struct global_priv {
- int is_paused; /**< 1 = Movie is paused,
- 0 = Movie is not paused */
+typedef struct d3d_priv {
+ int opt_prefer_stretchrect;
+ int opt_disable_textures;
+ int opt_disable_stretchrect;
+ int opt_disable_shaders;
+ int opt_only_8bit;
+ int opt_disable_eosd;
+ int opt_disable_texture_align;
+ // debugging
+ int opt_force_power_of_2;
+ int opt_texture_memory;
+ int opt_swap_discard;
+ int opt_exact_backbuffer;
+ int opt_16bit_textures;
+
+ struct vo *vo;
+
int is_clear_needed; /**< 1 = Clear the backbuffer before StretchRect
0 = (default) Don't clear it */
D3DLOCKED_RECT locked_rect; /**< The locked offscreen surface */
@@ -63,8 +145,22 @@ static struct global_priv {
fullscreen */
int src_width; /**< Source (movie) width */
int src_height; /**< Source (movie) heigth */
+ int src_d_width; /**< Source (movie) aspect corrected width */
+ int src_d_height; /**< Source (movie) aspect corrected heigth */
int border_x; /**< horizontal border value for OSD */
int border_y; /**< vertical border value for OSD */
+ int image_format; /**< mplayer image format */
+ bool use_textures; /**< use 3D texture rendering, instead of
+ StretchRect */
+ bool use_shaders; /**< use shader for YUV color conversion
+ (or possibly for RGB video equalizers) */
+ bool use_2ch_hack; /**< 2 byte YUV formats use 2 channel hack */
+
+ int plane_count;
+ struct texplane planes[3];
+
+ IDirect3DPixelShader9 *pixel_shader;
+ const BYTE *pixel_shader_data;
D3DFORMAT movie_src_fmt; /**< Movie colorspace format (depends on
the movie's codec) */
@@ -76,14 +172,14 @@ static struct global_priv {
LPDIRECT3D9 d3d_handle; /**< Direct3D Handle */
LPDIRECT3DDEVICE9 d3d_device; /**< The Direct3D Adapter */
+ bool d3d_in_scene; /**< BeginScene was called, EndScene not */
IDirect3DSurface9 *d3d_surface; /**< Offscreen Direct3D Surface. MPlayer
renders inside it. Uses colorspace
priv->movie_src_fmt */
- IDirect3DTexture9 *d3d_texture_osd; /**< Direct3D Texture. Uses RGBA */
- IDirect3DTexture9 *d3d_texture_system; /**< Direct3D Texture. System memory
- cannot lock a normal texture. Uses RGBA */
+ struct d3dtex texture_osd; /**< RGBA */
IDirect3DSurface9 *d3d_backbuf; /**< Video card's back buffer (used to
display next frame) */
+ struct d3dtex texture_eosd; /**< A8 */
int cur_backbuf_width; /**< Current backbuffer width */
int cur_backbuf_height; /**< Current backbuffer height */
int is_osd_populated; /**< 1 = OSD texture has something to display,
@@ -96,48 +192,82 @@ static struct global_priv {
0 = device requires shadow */
int max_texture_width; /**< from the device capabilities */
int max_texture_height; /**< from the device capabilities */
- int osd_width; /**< current width of the OSD */
- int osd_height; /**< current height of the OSD */
- int osd_texture_width; /**< current width of the OSD texture */
- int osd_texture_height; /**< current height of the OSD texture */
-} *priv;
-typedef struct {
+ D3DMATRIX d3d_colormatrix;
+ float d3d_depth_vector[4];
+ struct mp_csp_details colorspace;
+ struct mp_csp_equalizer video_eq;
+
+ struct eosd_packer *eosd; /**< EOSD packer (image positions etc.) */
+ vertex_eosd *eosd_vb; /**< temporary memory for D3D when rendering EOSD */
+} d3d_priv;
+
+struct fmt_entry {
const unsigned int mplayer_fmt; /**< Given by MPlayer */
const D3DFORMAT fourcc; /**< Required by D3D's test function */
-} struct_fmt_table;
+};
/* Map table from reported MPlayer format to the required
fourcc. This is needed to perform the format query. */
-static const struct_fmt_table fmt_table[] = {
+static const struct fmt_entry fmt_table[] = {
+ // planar YUV
{IMGFMT_YV12, MAKEFOURCC('Y','V','1','2')},
{IMGFMT_I420, MAKEFOURCC('I','4','2','0')},
{IMGFMT_IYUV, MAKEFOURCC('I','Y','U','V')},
{IMGFMT_YVU9, MAKEFOURCC('Y','V','U','9')},
+ // packed YUV
{IMGFMT_YUY2, D3DFMT_YUY2},
{IMGFMT_UYVY, D3DFMT_UYVY},
+ // packed RGB
{IMGFMT_BGR32, D3DFMT_X8R8G8B8},
{IMGFMT_RGB32, D3DFMT_X8B8G8R8},
{IMGFMT_BGR24, D3DFMT_R8G8B8}, //untested
{IMGFMT_BGR16, D3DFMT_R5G6B5},
{IMGFMT_BGR15, D3DFMT_X1R5G5B5},
{IMGFMT_BGR8 , D3DFMT_R3G3B2}, //untested
+ // grayscale (can be considered both packed and planar)
+ {IMGFMT_Y8, D3DFMT_L8},
+ {IMGFMT_Y16, D3DFMT_L16},
+ {IMGFMT_A8Y8, D3DFMT_A8L8},
+ {0},
};
-#define DISPLAY_FORMAT_TABLE_ENTRIES (sizeof(fmt_table) / sizeof(fmt_table[0]))
-#define D3DFVF_MY_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
+static void generate_eosd(d3d_priv *priv, mp_eosd_images_t *);
+static void draw_eosd(d3d_priv *priv);
+static void update_colorspace(d3d_priv *priv);
+static void d3d_clear_video_textures(d3d_priv *priv);
+static bool resize_d3d(d3d_priv *priv);
+static uint32_t d3d_draw_frame(d3d_priv *priv);
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y);
+static void uninit(struct vo *vo);
+static void draw_osd(struct vo *vo, struct osd_state *osd);
+static void flip_page(struct vo *vo);
+static mp_image_t *get_screenshot(d3d_priv *priv);
+static mp_image_t *get_window_screenshot(d3d_priv *priv);
-typedef struct {
- float x, y, z; /* Position of vertex in 3D space */
- float tu, tv; /* Texture coordinates */
-} struct_vertex;
-typedef enum back_buffer_action {
- BACKBUFFER_CREATE,
- BACKBUFFER_RESET
-} back_buffer_action_e;
+static void d3d_matrix_identity(D3DMATRIX *m)
+{
+ memset(m, 0, sizeof(D3DMATRIX));
+ m->_11 = m->_22 = m->_33 = m->_44 = 1.0f;
+}
+
+static void d3d_matrix_ortho(D3DMATRIX *m, float left, float right,
+ float bottom, float top)
+{
+ d3d_matrix_identity(m);
+ m->_11 = 2.0f / (right - left);
+ m->_22 = 2.0f / (top - bottom);
+ m->_33 = 1.0f;
+ m->_41 = -(right + left) / (right - left);
+ m->_42 = -(top + bottom) / (top - bottom);
+ m->_43 = 0;
+ m->_44 = 1.0f;
+}
+
/****************************************************************************
* *
* *
@@ -148,15 +278,28 @@ typedef enum back_buffer_action {
* *
****************************************************************************/
+static bool d3d_begin_scene(d3d_priv *priv)
+{
+ if (!priv->d3d_in_scene) {
+ if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>BeginScene failed.\n");
+ return false;
+ }
+ priv->d3d_in_scene = true;
+ }
+ return true;
+}
+
/** @brief Calculate scaled fullscreen movie rectangle with
* preserved aspect ratio.
*/
-static void calc_fs_rect(void)
+static void calc_fs_rect(d3d_priv *priv)
{
struct vo_rect src_rect;
struct vo_rect dst_rect;
struct vo_rect borders;
- calc_src_dst_rects(priv->src_width, priv->src_height, &src_rect, &dst_rect, &borders, NULL);
+ calc_src_dst_rects(priv->vo, priv->src_width, priv->src_height, &src_rect,
+ &dst_rect, &borders, NULL);
priv->fs_movie_rect.left = dst_rect.left;
priv->fs_movie_rect.right = dst_rect.right;
@@ -170,7 +313,7 @@ static void calc_fs_rect(void)
priv->border_y = borders.top;
mp_msg(MSGT_VO, MSGL_V,
- "<vo_direct3d>Fullscreen movie rectangle: t: %ld, l: %ld, r: %ld, b:%ld\n",
+ "<vo_direct3d>Video rectangle: t: %ld, l: %ld, r: %ld, b:%ld\n",
priv->fs_movie_rect.top, priv->fs_movie_rect.left,
priv->fs_movie_rect.right, priv->fs_movie_rect.bottom);
@@ -181,54 +324,285 @@ static void calc_fs_rect(void)
priv->is_clear_needed = 1;
}
-/** @brief Destroy D3D Offscreen and Backbuffer surfaces.
- */
-static void destroy_d3d_surfaces(void)
+// Adjust the texture size *width/*height to fit the requirements of the D3D
+// device. The texture size is only increased.
+static void d3d_fix_texture_size(d3d_priv *priv, int *width, int *height)
{
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>destroy_d3d_surfaces called.\n");
- /* Let's destroy the old (if any) D3D Surfaces */
+ int tex_width = *width;
+ int tex_height = *height;
+
+ // avoid nasty special cases with 0-sized textures and texture sizes
+ tex_width = FFMAX(tex_width, 1);
+ tex_height = FFMAX(tex_height, 1);
+
+ if (priv->device_caps_power2_only) {
+ tex_width = 1;
+ tex_height = 1;
+ while (tex_width < *width) tex_width <<= 1;
+ while (tex_height < *height) tex_height <<= 1;
+ }
+ if (priv->device_caps_square_only)
+ /* device only supports square textures */
+ tex_width = tex_height = FFMAX(tex_width, tex_height);
+ // better round up to a multiple of 16
+ if (!priv->opt_disable_texture_align) {
+ tex_width = (tex_width + 15) & ~15;
+ tex_height = (tex_height + 15) & ~15;
+ }
+
+ *width = tex_width;
+ *height = tex_height;
+}
+
+static void d3dtex_release(d3d_priv *priv, struct d3dtex *tex)
+{
+ if (tex->system)
+ IDirect3DTexture9_Release(tex->system);
+ tex->system = NULL;
+
+ if (tex->device)
+ IDirect3DTexture9_Release(tex->device);
+ tex->device = NULL;
+
+ tex->tex_w = tex->tex_h = 0;
+}
+
+static bool d3dtex_allocate(d3d_priv *priv, struct d3dtex *tex, D3DFORMAT fmt,
+ int w, int h)
+{
+ d3dtex_release(priv, tex);
+
+ tex->w = w;
+ tex->h = h;
+
+ int tw = w, th = h;
+ d3d_fix_texture_size(priv, &tw, &th);
+
+ int memtype = D3DPOOL_SYSTEMMEM;
+ switch (priv->opt_texture_memory) {
+ case 1: memtype = D3DPOOL_MANAGED; break;
+ case 2: memtype = D3DPOOL_DEFAULT; break;
+ }
+
+ if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, tw, th, 1,
+ D3DUSAGE_DYNAMIC, fmt, memtype, &tex->system, NULL)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Allocating %dx%d texture in system RAM failed.\n",
+ w, h);
+ goto error_exit;
+ }
+
+ if (!priv->device_texture_sys && !priv->opt_texture_memory) {
+ if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, tw, th, 1,
+ D3DUSAGE_DYNAMIC, fmt, D3DPOOL_DEFAULT, &tex->device, NULL)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Allocating %dx%d texture in video RAM failed.\n",
+ w, h);
+ goto error_exit;
+ }
+ }
+
+ tex->tex_w = tw;
+ tex->tex_h = th;
+
+ return true;
+
+error_exit:
+ d3dtex_release(priv, tex);
+ return false;
+}
- if (priv->locked_rect.pBits)
- IDirect3DSurface9_UnlockRect(priv->d3d_surface);
+static IDirect3DBaseTexture9 *d3dtex_get_render_texture(d3d_priv *priv,
+ struct d3dtex *tex)
+{
+ return (IDirect3DBaseTexture9 *)(tex->device ? tex->device : tex->system);
+}
+
+// Copy system texture contents to device texture.
+static bool d3dtex_update(d3d_priv *priv, struct d3dtex *tex)
+{
+ if (!tex->device)
+ return true;
+ return !FAILED(IDirect3DDevice9_UpdateTexture(priv->d3d_device,
+ (IDirect3DBaseTexture9 *)tex->system,
+ (IDirect3DBaseTexture9 *)tex->device));
+}
+
+static void d3d_unlock_video_objects(d3d_priv *priv)
+{
+ bool any_failed = false;
+
+ if (priv->locked_rect.pBits) {
+ if (FAILED(IDirect3DSurface9_UnlockRect(priv->d3d_surface)))
+ any_failed = true;
+ }
priv->locked_rect.pBits = NULL;
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+ if (plane->locked_rect.pBits) {
+ if (FAILED(IDirect3DTexture9_UnlockRect(plane->texture.system, 0)))
+ any_failed = true;
+ }
+ plane->locked_rect.pBits = NULL;
+ }
+
+ if (any_failed) {
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Unlocking video objects failed.\n");
+ }
+}
+
+// Free video surface/textures, shaders, etc.
+static void d3d_destroy_video_objects(d3d_priv *priv)
+{
+ d3d_unlock_video_objects(priv);
+
if (priv->d3d_surface)
IDirect3DSurface9_Release(priv->d3d_surface);
priv->d3d_surface = NULL;
- /* kill the OSD texture and its shadow copy */
- if (priv->d3d_texture_osd)
- IDirect3DTexture9_Release(priv->d3d_texture_osd);
- priv->d3d_texture_osd = NULL;
+ for (int n = 0; n < priv->plane_count; n++) {
+ d3dtex_release(priv, &priv->planes[n].texture);
+ }
- if (priv->d3d_texture_system)
- IDirect3DTexture9_Release(priv->d3d_texture_system);
- priv->d3d_texture_system = NULL;
+ if (priv->pixel_shader)
+ IDirect3DPixelShader9_Release(priv->pixel_shader);
+ priv->pixel_shader = NULL;
+}
+
+/** @brief Destroy D3D Offscreen and Backbuffer surfaces.
+ */
+static void destroy_d3d_surfaces(d3d_priv *priv)
+{
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>destroy_d3d_surfaces called.\n");
+
+ d3d_destroy_video_objects(priv);
+
+ d3dtex_release(priv, &priv->texture_osd);
if (priv->d3d_backbuf)
IDirect3DSurface9_Release(priv->d3d_backbuf);
priv->d3d_backbuf = NULL;
+
+ d3dtex_release(priv, &priv->texture_eosd);
+
+ if (priv->eosd)
+ eosd_packer_reinit(priv->eosd, 0, 0);
+
+ priv->d3d_in_scene = false;
}
-/** @brief Create D3D Offscreen and Backbuffer surfaces. Each
- * surface is created only if it's not already present.
- * @return 1 on success, 0 on failure
- */
-static int create_d3d_surfaces(void)
+// Allocate video surface or textures, and create shaders if needed.
+static bool d3d_configure_video_objects(d3d_priv *priv)
{
- int osd_width = vo_dwidth, osd_height = vo_dheight;
- int tex_width = osd_width, tex_height = osd_height;
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>create_d3d_surfaces called.\n");
+ int n;
+ bool need_clear = false;
+
+ assert(priv->image_format != 0);
+
+ if (priv->use_textures) {
+ for (n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ if (!plane->texture.system) {
+ if (!d3dtex_allocate(priv,
+ &plane->texture,
+ plane->d3d_format,
+ priv->src_width >> plane->shift_x,
+ priv->src_height >> plane->shift_y))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Allocating plane %d"
+ " failed.\n", n);
+ return false;
+ }
+
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Allocated plane %d:"
+ " %d bit, shift=%d/%d size=%d/%d (%d/%d).\n", n,
+ plane->bits_per_pixel,
+ plane->shift_x, plane->shift_y,
+ plane->texture.w, plane->texture.h,
+ plane->texture.tex_w, plane->texture.tex_h);
+
+ need_clear = true;
+ }
+ }
- if (!priv->d3d_surface &&
- FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(
- priv->d3d_device, priv->src_width, priv->src_height,
- priv->movie_src_fmt, D3DPOOL_DEFAULT, &priv->d3d_surface, NULL))) {
- mp_msg(MSGT_VO, MSGL_ERR,
- "<vo_direct3d>Allocating offscreen surface failed.\n");
- return 0;
+ if (need_clear)
+ d3d_clear_video_textures(priv);
+
+ if (priv->pixel_shader_data) {
+ if (!priv->pixel_shader &&
+ FAILED(IDirect3DDevice9_CreatePixelShader(priv->d3d_device,
+ (DWORD *)priv->pixel_shader_data, &priv->pixel_shader)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Failed to create "
+ "YUV conversion pixel shader.\n");
+ return false;
+ }
+ }
+
+ } else {
+
+ if (!priv->d3d_surface &&
+ FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(
+ priv->d3d_device, priv->src_width, priv->src_height,
+ priv->movie_src_fmt, D3DPOOL_DEFAULT, &priv->d3d_surface, NULL)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Allocating offscreen surface failed.\n");
+ return false;
+ }
}
+ return true;
+}
+
+static bool d3d_lock_video_textures(d3d_priv *priv)
+{
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ if (!plane->locked_rect.pBits) {
+ if (FAILED(IDirect3DTexture9_LockRect(plane->texture.system, 0,
+ &plane->locked_rect, NULL, 0)))
+ {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Texture lock failure.\n");
+ d3d_unlock_video_objects(priv);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static void d3d_clear_video_textures(d3d_priv *priv)
+{
+ if (!d3d_lock_video_textures(priv))
+ return;
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+ memset(plane->locked_rect.pBits, plane->clearval,
+ plane->locked_rect.Pitch * plane->texture.tex_h);
+ }
+
+ d3d_unlock_video_objects(priv);
+}
+
+// Recreate and initialize D3D objects if necessary. The amount of work that
+// needs to be done can be quite different: it could be that full initialization
+// is required, or that some objects need to be created, or that nothing is
+// done.
+static bool create_d3d_surfaces(d3d_priv *priv)
+{
+ int osd_width = priv->vo->dwidth, osd_height = priv->vo->dheight;
+ int tex_width = osd_width, tex_height = osd_height;
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>create_d3d_surfaces called.\n");
+
if (!priv->d3d_backbuf &&
FAILED(IDirect3DDevice9_GetBackBuffer(priv->d3d_device, 0, 0,
D3DBACKBUFFER_TYPE_MONO,
@@ -237,92 +611,150 @@ static int create_d3d_surfaces(void)
return 0;
}
- /* calculate the best size for the OSD depending on the factors from the device */
- if (priv->device_caps_power2_only) {
- tex_width = 1;
- tex_height = 1;
- while (tex_width < osd_width ) tex_width <<= 1;
- while (tex_height < osd_height) tex_height <<= 1;
- }
- if (priv->device_caps_square_only)
- /* device only supports square textures */
- tex_width = tex_height = tex_width > tex_height ? tex_width : tex_height;
- // better round up to a multiple of 16
- tex_width = (tex_width + 15) & ~15;
- tex_height = (tex_height + 15) & ~15;
+ if (!d3d_configure_video_objects(priv))
+ return 0;
+
+ /* create OSD */
+
+ d3d_fix_texture_size(priv, &tex_width, &tex_height);
- // make sure we respect the size limits without breaking aspect or pow2-requirements
- while (tex_width > priv->max_texture_width || tex_height > priv->max_texture_height) {
+ while (tex_width > priv->max_texture_width
+ || tex_height > priv->max_texture_height)
+ {
osd_width >>= 1;
osd_height >>= 1;
tex_width >>= 1;
tex_height >>= 1;
}
- priv->osd_width = osd_width;
- priv->osd_height = osd_height;
- priv->osd_texture_width = tex_width;
- priv->osd_texture_height = tex_height;
+ if (priv->texture_osd.tex_w < tex_width
+ || priv->texture_osd.tex_h < tex_height)
+ {
+ d3dtex_release(priv, &priv->texture_osd);
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>OSD texture size (%dx%d), requested (%dx%d).\n",
- vo_dwidth, vo_dheight, priv->osd_texture_width, priv->osd_texture_height);
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>OSD texture size (%dx%d),"
+ " requested (%dx%d).\n", osd_width, osd_height, tex_width,
+ tex_height);
- /* create OSD */
- if (!priv->d3d_texture_system &&
- FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device,
- priv->osd_texture_width,
- priv->osd_texture_height,
- 1,
- D3DUSAGE_DYNAMIC,
- D3DFMT_A8L8,
- D3DPOOL_SYSTEMMEM,
- &priv->d3d_texture_system,
- NULL))) {
- mp_msg(MSGT_VO,MSGL_ERR,
- "<vo_direct3d>Allocating OSD texture in system RAM failed.\n");
- return 0;
- }
-
- if (!priv->device_texture_sys) {
- /* only create if we need a shadow version on the external device */
- if (!priv->d3d_texture_osd &&
- FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device,
- priv->osd_texture_width,
- priv->osd_texture_height,
- 1,
- D3DUSAGE_DYNAMIC,
- D3DFMT_A8L8,
- D3DPOOL_DEFAULT,
- &priv->d3d_texture_osd,
- NULL))) {
- mp_msg(MSGT_VO,MSGL_ERR,
- "<vo_direct3d>Allocating OSD texture in video RAM failed.\n");
+ if (!d3dtex_allocate(priv, &priv->texture_osd, D3DFMT_A8L8, tex_width,
+ tex_height))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Allocating OSD texture failed.\n");
return 0;
}
}
+ priv->texture_osd.w = osd_width;
+ priv->texture_osd.h = osd_height;
+
/* setup default renderstate */
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_SRCBLEND, D3DBLEND_ONE);
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHAFUNC, D3DCMP_GREATER);
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHAREF, (DWORD)0x0);
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_LIGHTING, FALSE);
- IDirect3DDevice9_SetSamplerState(priv->d3d_device, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
- IDirect3DDevice9_SetSamplerState(priv->d3d_device, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHAFUNC, D3DCMP_GREATER);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHAREF, (DWORD)0x0);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_LIGHTING, FALSE);
+
+ // we use up to 3 samplers for up to 3 YUV planes
+ for (int n = 0; n < 3; n++) {
+ IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_MINFILTER,
+ D3DTEXF_LINEAR);
+ IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_MAGFILTER,
+ D3DTEXF_LINEAR);
+ IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_ADDRESSU,
+ D3DTADDRESS_CLAMP);
+ IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_ADDRESSV,
+ D3DTADDRESS_CLAMP);
+ }
+
+ if (priv->eosd && !priv->texture_eosd.system)
+ eosd_packer_reinit(priv->eosd, priv->max_texture_width,
+ priv->max_texture_height);
return 1;
}
+static bool init_d3d(d3d_priv *priv)
+{
+ D3DDISPLAYMODE disp_mode;
+ D3DCAPS9 disp_caps;
+ DWORD texture_caps;
+ DWORD dev_caps;
+
+ priv->d3d_handle = priv->pDirect3DCreate9(D3D_SDK_VERSION);
+ if (!priv->d3d_handle) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Initializing Direct3D failed.\n");
+ return false;
+ }
+
+ if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ &disp_mode))) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Reading display mode failed.\n");
+ return false;
+ }
+
+ priv->desktop_fmt = disp_mode.Format;
+ priv->cur_backbuf_width = disp_mode.Width;
+ priv->cur_backbuf_height = disp_mode.Height;
+
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Setting backbuffer dimensions to (%dx%d).\n",
+ disp_mode.Width, disp_mode.Height);
+
+ if (FAILED(IDirect3D9_GetDeviceCaps(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ DEVTYPE,
+ &disp_caps)))
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Reading display capabilities failed.\n");
+ return false;
+ }
+
+ /* Store relevant information reguarding caps of device */
+ texture_caps = disp_caps.TextureCaps;
+ dev_caps = disp_caps.DevCaps;
+ priv->device_caps_power2_only = (texture_caps & D3DPTEXTURECAPS_POW2) &&
+ !(texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL);
+ priv->device_caps_square_only = texture_caps & D3DPTEXTURECAPS_SQUAREONLY;
+ priv->device_texture_sys = dev_caps & D3DDEVCAPS_TEXTURESYSTEMMEMORY;
+ priv->max_texture_width = disp_caps.MaxTextureWidth;
+ priv->max_texture_height = disp_caps.MaxTextureHeight;
+
+ if (priv->opt_force_power_of_2)
+ priv->device_caps_power2_only = 1;
+
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>device_caps_power2_only %d, device_caps_square_only %d\n"
+ "<vo_direct3d>device_texture_sys %d\n"
+ "<vo_direct3d>max_texture_width %d, max_texture_height %d\n",
+ priv->device_caps_power2_only, priv->device_caps_square_only,
+ priv->device_texture_sys, priv->max_texture_width,
+ priv->max_texture_height);
+
+ return true;
+}
+
/** @brief Fill D3D Presentation parameters
*/
-static void fill_d3d_presentparams(D3DPRESENT_PARAMETERS *present_params)
+static void fill_d3d_presentparams(d3d_priv *priv,
+ D3DPRESENT_PARAMETERS *present_params)
{
/* Prepare Direct3D initialization parameters. */
memset(present_params, 0, sizeof(D3DPRESENT_PARAMETERS));
present_params->Windowed = TRUE;
- present_params->SwapEffect = D3DSWAPEFFECT_COPY;
+ present_params->SwapEffect =
+ priv->opt_swap_discard ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
present_params->Flags = D3DPRESENTFLAG_VIDEO;
- present_params->hDeviceWindow = vo_w32_window; /* w32_common var */
+ present_params->hDeviceWindow = priv->vo->w32->window;
present_params->BackBufferWidth = priv->cur_backbuf_width;
present_params->BackBufferHeight = priv->cur_backbuf_height;
present_params->MultiSampleType = D3DMULTISAMPLE_NONE;
@@ -333,177 +765,133 @@ static void fill_d3d_presentparams(D3DPRESENT_PARAMETERS *present_params)
}
-/** @brief Create a new backbuffer. Create or Reset the D3D
- * device.
- * @return 1 on success, 0 on failure
- */
-static int change_d3d_backbuffer(back_buffer_action_e action)
+// Create a new backbuffer. Create or Reset the D3D device.
+static bool change_d3d_backbuffer(d3d_priv *priv)
{
D3DPRESENT_PARAMETERS present_params;
- destroy_d3d_surfaces();
+ int window_w = priv->vo->dwidth;
+ int window_h = priv->vo->dheight;
/* Grow the backbuffer in the required dimension. */
- if (vo_dwidth > priv->cur_backbuf_width)
- priv->cur_backbuf_width = vo_dwidth;
+ if (window_w > priv->cur_backbuf_width)
+ priv->cur_backbuf_width = window_w;
+
+ if (window_h > priv->cur_backbuf_height)
+ priv->cur_backbuf_height = window_h;
- if (vo_dheight > priv->cur_backbuf_height)
- priv->cur_backbuf_height = vo_dheight;
+ if (priv->opt_exact_backbuffer) {
+ priv->cur_backbuf_width = window_w;
+ priv->cur_backbuf_height = window_h;
+ }
/* The grown backbuffer dimensions are ready and fill_d3d_presentparams
* will use them, so we can reset the device.
*/
- fill_d3d_presentparams(&present_params);
-
- /* vo_w32_window is w32_common variable. It's a handle to the window. */
- if (action == BACKBUFFER_CREATE &&
- FAILED(IDirect3D9_CreateDevice(priv->d3d_handle,
- D3DADAPTER_DEFAULT,
- D3DDEVTYPE_HAL, vo_w32_window,
- D3DCREATE_SOFTWARE_VERTEXPROCESSING,
- &present_params, &priv->d3d_device))) {
+ fill_d3d_presentparams(priv, &present_params);
+
+ if (!priv->d3d_device) {
+ if (FAILED(IDirect3D9_CreateDevice(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ DEVTYPE, priv->vo->w32->window,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING
+ | D3DCREATE_FPU_PRESERVE,
+ &present_params, &priv->d3d_device)))
+ {
mp_msg(MSGT_VO, MSGL_V,
"<vo_direct3d>Creating Direct3D device failed.\n");
- return 0;
- }
-
- if (action == BACKBUFFER_RESET &&
- FAILED(IDirect3DDevice9_Reset(priv->d3d_device, &present_params))) {
+ return 0;
+ }
+ } else {
+ if (FAILED(IDirect3DDevice9_Reset(priv->d3d_device, &present_params))) {
mp_msg(MSGT_VO, MSGL_ERR,
"<vo_direct3d>Reseting Direct3D device failed.\n");
- return 0;
+ return 0;
+ }
}
mp_msg(MSGT_VO, MSGL_V,
"<vo_direct3d>New backbuffer (%dx%d), VO (%dx%d)\n",
present_params.BackBufferWidth, present_params.BackBufferHeight,
- vo_dwidth, vo_dheight);
-
- return 1;
-}
-
-/** @brief Configure initial Direct3D context. The first
- * function called to initialize the D3D context.
- * @return 1 on success, 0 on failure
- */
-static int configure_d3d(void)
-{
- D3DDISPLAYMODE disp_mode;
- D3DVIEWPORT9 vp = {0, 0, vo_dwidth, vo_dheight, 0, 1};
-
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>configure_d3d called.\n");
-
- destroy_d3d_surfaces();
-
- /* Get the current desktop display mode, so we can set up a back buffer
- * of the same format. */
- if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle,
- D3DADAPTER_DEFAULT,
- &disp_mode))) {
- mp_msg(MSGT_VO, MSGL_ERR,
- "<vo_direct3d>Reading adapter display mode failed.\n");
- return 0;
- }
-
- /* Write current Desktop's colorspace format in the global storage. */
- priv->desktop_fmt = disp_mode.Format;
-
- if (!change_d3d_backbuffer(BACKBUFFER_CREATE))
- return 0;
-
- if (!create_d3d_surfaces())
- return 0;
-
- if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device,
- &vp))) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Setting viewport failed.\n");
- return 0;
- }
-
- calc_fs_rect();
+ window_w, window_h);
return 1;
}
/** @brief Reconfigure the whole Direct3D. Called only
- * when the video adapter becomes uncooperative.
+ * when the video adapter becomes uncooperative. ("Lost" devices)
* @return 1 on success, 0 on failure
*/
-static int reconfigure_d3d(void)
+static int reconfigure_d3d(d3d_priv *priv)
{
mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>reconfigure_d3d called.\n");
- /* Destroy the offscreen, OSD and backbuffer surfaces */
- destroy_d3d_surfaces();
+ destroy_d3d_surfaces(priv);
- /* Destroy the D3D Device */
if (priv->d3d_device)
IDirect3DDevice9_Release(priv->d3d_device);
priv->d3d_device = NULL;
- /* Stop the whole Direct3D */
- IDirect3D9_Release(priv->d3d_handle);
-
- /* Initialize Direct3D from the beginning */
- priv->d3d_handle = priv->pDirect3DCreate9(D3D_SDK_VERSION);
- if (!priv->d3d_handle) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Initializing Direct3D failed.\n");
+ // Force complete destruction of the D3D state.
+ // Note: this step could be omitted. The resize_d3d call below would detect
+ // that d3d_device is NULL, and would properly recreate it. I'm not sure why
+ // the following code to release and recreate the d3d_handle exists.
+ if (priv->d3d_handle)
+ IDirect3D9_Release(priv->d3d_handle);
+ priv->d3d_handle = NULL;
+ if (!init_d3d(priv))
return 0;
- }
- /* Configure Direct3D */
- if (!configure_d3d())
+ // Proper re-initialization.
+ if (!resize_d3d(priv))
return 0;
return 1;
}
-/** @brief Resize Direct3D context on window resize.
- * @return 1 on success, 0 on failure
- */
-static int resize_d3d(void)
+// Resize Direct3D context on window resize.
+// This function also is called when major initializations need to be done.
+static bool resize_d3d(d3d_priv *priv)
{
- D3DVIEWPORT9 vp = {0, 0, vo_dwidth, vo_dheight, 0, 1};
+ D3DVIEWPORT9 vp = {0, 0, priv->vo->dwidth, priv->vo->dheight, 0, 1};
mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>resize_d3d called.\n");
/* Make sure that backbuffer is large enough to accomodate the new
viewport dimensions. Grow it if necessary. */
- if (vo_dwidth > priv->cur_backbuf_width ||
- vo_dheight > priv->cur_backbuf_height) {
- if (!change_d3d_backbuffer(BACKBUFFER_RESET))
- return 0;
- }
-
- /* Destroy the OSD textures. They should always match the new dimensions
- * of the onscreen window, so on each resize we need new OSD dimensions.
- */
-
- if (priv->d3d_texture_osd)
- IDirect3DTexture9_Release(priv->d3d_texture_osd);
- priv->d3d_texture_osd = NULL;
+ bool backbuf_resize = priv->vo->dwidth > priv->cur_backbuf_width ||
+ priv->vo->dheight > priv->cur_backbuf_height;
- if (priv->d3d_texture_system)
- IDirect3DTexture9_Release(priv->d3d_texture_system);
- priv->d3d_texture_system = NULL;
+ if (priv->opt_exact_backbuffer) {
+ backbuf_resize = priv->vo->dwidth != priv->cur_backbuf_width ||
+ priv->vo->dheight != priv->cur_backbuf_height;
+ }
+ if (backbuf_resize || !priv->d3d_device)
+ {
+ destroy_d3d_surfaces(priv);
+ if (!change_d3d_backbuffer(priv))
+ return 0;
+ }
- /* Recreate the OSD. The function will observe that the offscreen plain
- * surface and the backbuffer are not destroyed and will skip their creation,
- * effectively recreating only the OSD.
- */
+ if (!priv->d3d_device)
+ return 1;
- if (!create_d3d_surfaces())
+ if (!create_d3d_surfaces(priv))
return 0;
- if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device,
- &vp))) {
+ if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device, &vp))) {
mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Setting viewport failed.\n");
return 0;
}
- calc_fs_rect();
+ // so that screen coordinates map to D3D ones
+ D3DMATRIX view;
+ d3d_matrix_ortho(&view, 0.5f, vp.Width + 0.5f, vp.Height + 0.5f, 0.5f);
+ IDirect3DDevice9_SetTransform(priv->d3d_device, D3DTS_VIEW, &view);
+
+ calc_fs_rect(priv);
#ifdef CONFIG_FREETYPE
// font needs to be adjusted
@@ -512,23 +900,23 @@ static int resize_d3d(void)
// OSD needs to be drawn fresh for new size
vo_osd_changed(OSDTYPE_OSD);
+ priv->vo->want_redraw = true;
+
return 1;
}
/** @brief Uninitialize Direct3D and close the window.
*/
-static void uninit_d3d(void)
+static void uninit_d3d(d3d_priv *priv)
{
mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>uninit_d3d called.\n");
- destroy_d3d_surfaces();
+ destroy_d3d_surfaces(priv);
- /* Destroy the D3D Device */
if (priv->d3d_device)
IDirect3DDevice9_Release(priv->d3d_device);
priv->d3d_device = NULL;
- /* Stop the whole D3D. */
if (priv->d3d_handle) {
mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Stopping Direct3D.\n");
IDirect3D9_Release(priv->d3d_handle);
@@ -536,26 +924,38 @@ static void uninit_d3d(void)
priv->d3d_handle = NULL;
}
+static uint32_t d3d_upload_and_render_frame_texture(d3d_priv *priv,
+ mp_image_t *mpi)
+{
+ if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK))
+ draw_slice(priv->vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
+
+ d3d_unlock_video_objects(priv);
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ d3dtex_update(priv, &priv->planes[n].texture);
+ }
+
+ return d3d_draw_frame(priv);
+}
+
/** @brief Render a frame on the screen.
* @param mpi mpi structure with the decoded frame inside
* @return VO_TRUE on success, VO_ERROR on failure
*/
-static uint32_t render_d3d_frame(mp_image_t *mpi)
+static uint32_t d3d_upload_and_render_frame(d3d_priv *priv, mp_image_t *mpi)
{
- /* Uncomment when direct rendering is implemented.
- * if (mpi->flags & MP_IMGFLAG_DIRECT) ...
- */
-
- /* If the D3D device is uncooperative (not initialized), return success.
- The device will be probed for reinitialization in the next flip_page() */
if (!priv->d3d_device)
return VO_TRUE;
+ if (priv->use_textures)
+ return d3d_upload_and_render_frame_texture(priv, mpi);
+
if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
goto skip_upload;
if (mpi->flags & MP_IMGFLAG_PLANAR) { /* Copy a planar frame. */
- draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
+ draw_slice(priv->vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
goto skip_upload;
}
@@ -572,77 +972,315 @@ static uint32_t render_d3d_frame(mp_image_t *mpi)
mpi->height, priv->locked_rect.Pitch, mpi->stride[0]);
skip_upload:
- /* This unlock is used for both slice_draw path and render_d3d_frame path. */
- if (FAILED(IDirect3DSurface9_UnlockRect(priv->d3d_surface))) {
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Surface unlock failed.\n");
- return VO_ERROR;
- }
- priv->locked_rect.pBits = NULL;
+ d3d_unlock_video_objects(priv);
+
+ return d3d_draw_frame(priv);
+}
+
+static uint32_t d3d_draw_frame(d3d_priv *priv)
+{
+ int n;
- if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>BeginScene failed.\n");
+ if (!priv->d3d_device)
+ return VO_TRUE;
+
+ if (!d3d_begin_scene(priv))
return VO_ERROR;
- }
- if (priv->is_clear_needed) {
+ if (priv->is_clear_needed || priv->opt_swap_discard) {
IDirect3DDevice9_Clear(priv->d3d_device, 0, NULL,
D3DCLEAR_TARGET, 0, 0, 0);
priv->is_clear_needed = 0;
}
- if (FAILED(IDirect3DDevice9_StretchRect(priv->d3d_device,
- priv->d3d_surface,
- &priv->fs_panscan_rect,
- priv->d3d_backbuf,
- &priv->fs_movie_rect,
- D3DTEXF_LINEAR))) {
- mp_msg(MSGT_VO, MSGL_ERR,
- "<vo_direct3d>Copying frame to the backbuffer failed.\n");
- return VO_ERROR;
- }
+ if (priv->use_textures) {
- if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>EndScene failed.\n");
- return VO_ERROR;
+ for (n = 0; n < priv->plane_count; n++) {
+ IDirect3DDevice9_SetTexture(priv->d3d_device, n,
+ d3dtex_get_render_texture(priv, &priv->planes[n].texture));
+ }
+
+ RECT rm = priv->fs_movie_rect;
+ RECT rs = priv->fs_panscan_rect;
+
+ vertex_video vb[] = {
+ { rm.left, rm.top, 0.0f},
+ { rm.right, rm.top, 0.0f},
+ { rm.left, rm.bottom, 0.0f},
+ { rm.right, rm.bottom, 0.0f}
+ };
+
+ float texc[4][2] = {
+ { rs.left, rs.top},
+ { rs.right, rs.top},
+ { rs.left, rs.bottom},
+ { rs.right, rs.bottom}
+ };
+
+ for (n = 0; n < priv->plane_count; n++) {
+ float s_x = (1.0f / (1 << priv->planes[n].shift_x))
+ / priv->planes[n].texture.tex_w;
+ float s_y = (1.0f / (1 << priv->planes[n].shift_y))
+ / priv->planes[n].texture.tex_h;
+ for (int i = 0; i < 4; i++) {
+ vb[i].t[n][0] = texc[i][0] * s_x;
+ vb[i].t[n][1] = texc[i][1] * s_y;
+ }
+ }
+
+ if (priv->pixel_shader) {
+ IDirect3DDevice9_SetPixelShader(priv->d3d_device, priv->pixel_shader);
+ IDirect3DDevice9_SetPixelShaderConstantF(priv->d3d_device, 0,
+ &priv->d3d_colormatrix._11,
+ 4);
+ IDirect3DDevice9_SetPixelShaderConstantF(priv->d3d_device, 5,
+ priv->d3d_depth_vector,
+ 1);
+ }
+
+ IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_VIDEO_VERTEX);
+ IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP,
+ 2, &vb[0], sizeof(vertex_video));
+
+ IDirect3DDevice9_SetPixelShader(priv->d3d_device, NULL);
+
+ for (n = 0; n < priv->plane_count; n++) {
+ IDirect3DDevice9_SetTexture(priv->d3d_device, n, NULL);
+ }
+
+ } else {
+ if (FAILED(IDirect3DDevice9_StretchRect(priv->d3d_device,
+ priv->d3d_surface,
+ &priv->fs_panscan_rect,
+ priv->d3d_backbuf,
+ &priv->fs_movie_rect,
+ D3DTEXF_LINEAR))) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Copying frame to the backbuffer failed.\n");
+ return VO_ERROR;
+ }
}
return VO_TRUE;
}
+// Return the high byte of the value that represents white in chroma (U/V)
+static int get_chroma_clear_val(int bit_depth)
+{
+ return 1 << (bit_depth - 1 & 7);
+}
-/** @brief Query if movie colorspace is supported by the HW.
- * @return 0 on failure, device capabilities (not probed
- * currently) on success.
- */
-static int query_format(uint32_t movie_fmt)
+// this macro is supposed to work on all formats supported by 3D rendering, and
+// that produce "reasonable" output (i.e. no mixed up colors)
+#define IMGFMT_IS_ANY_RND(x) \
+ (IMGFMT_IS_BGR(x) || IMGFMT_IS_RGB(x) || IMGFMT_IS_Y(x))
+
+// pixel size in bit for any IMGFMT_IS_ANY_RND(x)==true
+// we assume that the actual pixel strides are always aligned on bytes
+static int imgfmt_any_rnd_depth(int fmt)
{
- int i;
- for (i = 0; i < DISPLAY_FORMAT_TABLE_ENTRIES; i++) {
- if (fmt_table[i].mplayer_fmt == movie_fmt) {
- /* Test conversion from Movie colorspace to
- * display's target colorspace. */
- if (FAILED(IDirect3D9_CheckDeviceFormatConversion(priv->d3d_handle,
- D3DADAPTER_DEFAULT,
- D3DDEVTYPE_HAL,
- fmt_table[i].fourcc,
- priv->desktop_fmt))) {
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Rejected image format: %s\n",
- vo_format_name(fmt_table[i].mplayer_fmt));
+ if (IMGFMT_IS_BGR(fmt))
+ return IMGFMT_BGR_DEPTH(fmt);
+ if (IMGFMT_IS_RGB(fmt))
+ return IMGFMT_RGB_DEPTH(fmt);
+ if (IMGFMT_IS_Y(fmt))
+ return IMGFMT_Y_DEPTH(fmt);
+ assert(false);
+ return 0;
+}
+
+static D3DFORMAT check_format(d3d_priv *priv, uint32_t movie_fmt,
+ bool as_texture)
+{
+ const char *type = as_texture ? "texture rendering" : "StretchRect";
+ const struct fmt_entry *cur = &fmt_table[0];
+
+ // Don't try to handle weird packed texture formats (although I don't know
+ // if D3D9 would even accept any such format for 3D rendering; and we
+ // certainly don't try any tricks like matching it to RGB formats and
+ // applying a YUV conversion matrix)
+ if (as_texture && !IMGFMT_IS_ANY_RND(movie_fmt))
+ return 0;
+
+ while (cur->mplayer_fmt) {
+ if (cur->mplayer_fmt == movie_fmt) {
+ HRESULT res;
+ if (as_texture) {
+ res = IDirect3D9_CheckDeviceFormat(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ DEVTYPE,
+ priv->desktop_fmt,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER,
+ D3DRTYPE_TEXTURE,
+ cur->fourcc);
+ } else {
+ /* Test conversion from Movie colorspace to
+ * display's target colorspace. */
+ res = IDirect3D9_CheckDeviceFormatConversion(priv->d3d_handle,
+ D3DADAPTER_DEFAULT,
+ DEVTYPE,
+ cur->fourcc,
+ priv->desktop_fmt);
+ }
+ if (FAILED(res)) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Rejected image format "
+ "(%s): %s\n", type, vo_format_name(cur->mplayer_fmt));
return 0;
}
- priv->movie_src_fmt = fmt_table[i].fourcc;
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Accepted image format: %s\n",
- vo_format_name(fmt_table[i].mplayer_fmt));
- return (VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
- | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN);
+ mp_msg(MSGT_VO, MSGL_DBG2, "<vo_direct3d>Accepted image format "
+ "(%s): %s\n", type, vo_format_name(cur->mplayer_fmt));
+ return cur->fourcc;
}
+ cur++;
}
return 0;
}
+// Check whether YUV conversion with shaders can be done.
+// Returns the format the individual planes should use (or 0 on failure)
+static D3DFORMAT check_shader_conversion(d3d_priv *priv, uint32_t fmt)
+{
+ if (priv->opt_disable_shaders)
+ return 0;
+ int component_bits;
+ if (!mp_get_chroma_shift(fmt, NULL, NULL, &component_bits))
+ return 0;
+ if (component_bits < 8 || component_bits > 16)
+ return 0;
+ bool is_8bit = component_bits == 8;
+ if (!is_8bit && priv->opt_only_8bit)
+ return 0;
+ int texfmt = IMGFMT_Y8;
+ if (!is_8bit) {
+ if (priv->opt_16bit_textures)
+ texfmt = IMGFMT_Y16;
+ else
+ texfmt = IMGFMT_A8Y8;
+ }
+ return check_format(priv, texfmt, true);
+}
+
+// Return if the image format can be used. If it can, decide which rendering
+// and conversion mode to use.
+// If initialize is true, actually setup all variables to use the picked
+// rendering mode.
+static bool init_rendering_mode(d3d_priv *priv, uint32_t fmt, bool initialize)
+{
+ int n;
+ int blit_d3dfmt = check_format(priv, fmt, false);
+ int texture_d3dfmt = check_format(priv, fmt, true);
+ int shader_d3dfmt = check_shader_conversion(priv, fmt);
+
+ if (priv->opt_disable_textures)
+ texture_d3dfmt = 0;
+ if (priv->opt_disable_shaders)
+ shader_d3dfmt = 0;
+ if (priv->opt_disable_stretchrect)
+ blit_d3dfmt = 0;
+
+ if (!(blit_d3dfmt || shader_d3dfmt || texture_d3dfmt))
+ return false;
+
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Accepted rendering methods for "
+ "format='%s': StretchRect=%#x, Texture=%#x, Texture+Shader=%#x.\n",
+ vo_format_name(fmt), blit_d3dfmt, texture_d3dfmt, shader_d3dfmt);
+
+ if (!initialize)
+ return true;
+
+ // initialization doesn't fail beyond this point
+
+ priv->use_shaders = false;
+ priv->use_textures = false;
+ priv->use_2ch_hack = false;
+ priv->movie_src_fmt = 0;
+ priv->pixel_shader_data = NULL;
+ priv->plane_count = 0;
+ priv->image_format = fmt;
+
+ if (blit_d3dfmt && priv->opt_prefer_stretchrect)
+ texture_d3dfmt = shader_d3dfmt = 0;
+
+ if (texture_d3dfmt) {
+ priv->use_textures = true;
+ } else if (shader_d3dfmt) {
+ priv->use_textures = true;
+ priv->use_shaders = true;
+ } else {
+ assert(!!blit_d3dfmt);
+ }
+
+ if (priv->use_textures) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using 3D rendering.\n");
+
+ struct texplane *planes = &priv->planes[0];
+ planes[0].shift_x = planes[0].shift_y = 0;
+ planes[0].clearval = 0;
+
+ if (!priv->use_shaders) {
+ assert(IMGFMT_IS_ANY_RND(priv->image_format));
+ priv->plane_count = 1;
+ planes[0].bits_per_pixel = imgfmt_any_rnd_depth(priv->image_format);
+ planes[0].d3d_format = texture_d3dfmt;
+ } else {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using YUV shaders.\n");
+
+ int sx, sy, component_bits;
+ mp_get_chroma_shift(priv->image_format, &sx, &sy, &component_bits);
+ priv->plane_count = 3;
+ for (n = 0; n < 3; n++) {
+ planes[n].d3d_format = shader_d3dfmt;
+ planes[n].bits_per_pixel = component_bits;
+ if (n > 0) {
+ planes[n].shift_x = sx;
+ planes[n].shift_y = sy;
+ planes[n].clearval = get_chroma_clear_val(component_bits);
+ }
+ }
+ if (shader_d3dfmt != D3DFMT_A8L8) {
+ priv->pixel_shader_data = d3d_shader_yuv;
+ } else {
+ mp_msg(MSGT_VO, MSGL_WARN, "<vo_direct3d>Using YUV 2ch hack.\n");
+
+ priv->pixel_shader_data = d3d_shader_yuv_2ch;
+ priv->use_2ch_hack = true;
+ }
+ }
+
+ for (n = 0; n < priv->plane_count; n++) {
+ planes[n].bytes_per_pixel = (planes[n].bits_per_pixel + 7) / 8;
+ }
+
+ } else {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using StretchRect.\n");
+
+ priv->movie_src_fmt = blit_d3dfmt;
+ }
+
+ update_colorspace(priv);
+
+ return true;
+}
+
+/** @brief Query if movie colorspace is supported by the HW.
+ * @return 0 on failure, device capabilities (not probed
+ * currently) on success.
+ */
+static int query_format(d3d_priv *priv, uint32_t movie_fmt)
+{
+ if (!init_rendering_mode(priv, movie_fmt, false))
+ return 0;
+
+ int eosd_caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
+ | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
+ if (priv->eosd)
+ eosd_caps |= VFCAP_EOSD | VFCAP_EOSD_UNSCALED;
+ return eosd_caps;
+}
+
/****************************************************************************
* *
* *
@@ -653,8 +1291,111 @@ static int query_format(uint32_t movie_fmt)
* *
****************************************************************************/
+static void get_2ch_depth_multiplier(int depth, float *out_f1, float *out_f2) {
+ // How to get these values:
+ // The suffix i8 and i16 is for values with 8/16 bit fixed point numbers.
+ // The suffix f is for float, ideally in the range 0.0-1.0.
+ // c_i8 is a two component vector, sampled from a two channel texture.
+ // (c_i8.x is the low byte, c_i8.y is the high byte)
+ // r_f is the resulting color scalar value.
+ //
+ // c_i8 = c_f * (2^8-1)
+ // r_i16 = c_i8.x + c_i8.y * 2^8
+ // r_f = r_i16 / (2^16-1)
+ // = c_f.x * (2^8-1) / (2^16-1) + c_f.y * (2^8-1) * 2^8 / (2^16-1)
+ // = c_f.x * ((2^8-1) / (2^16-1)) + c_f.y * (2^8 * ((2^8-1) / (2^16-1)))
+ // out = ((2^8-1) / (2^16-1), 2^8 * ((2^8-1) / (2^16-1)))
+ // The result color is r_f = dot(c_f, out).
+ // Same goes for other bit depth, such as 10 bit. Assuming (2^depth-1) is
+ // the maximum possible value at that depth, you have to scale the value
+ // r_i16 with it, the factor (2^16-1) in the formula above has to be
+ // replaced with (2^depth-1).
+ float factor = (float)((1 << 8) - 1) / (float)((1 << depth) - 1);
+ *out_f1 = factor;
+ *out_f2 = 256.0 * factor;
+}
+
+static void update_colorspace(d3d_priv *priv)
+{
+ float coeff[3][4];
+ struct mp_csp_params csp = { .colorspace = priv->colorspace };
+ mp_csp_copy_equalizer_values(&csp, &priv->video_eq);
+
+ if (priv->use_shaders) {
+ if (!priv->use_2ch_hack) {
+ csp.input_bits = priv->planes[0].bits_per_pixel;
+ csp.texture_bits = (csp.input_bits + 7) & ~7;
+ } else {
+ float f1, f2;
+ get_2ch_depth_multiplier(priv->planes[0].bits_per_pixel, &f1, &f2);
+ priv->d3d_depth_vector[0] = f1;
+ priv->d3d_depth_vector[1] = f2;
+ priv->d3d_depth_vector[2] = priv->d3d_depth_vector[3] = 0;
+ // no change
+ csp.input_bits = 8;
+ csp.texture_bits = 8;
+ }
+ mp_get_yuv2rgb_coeffs(&csp, coeff);
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 4; col++) {
+ priv->d3d_colormatrix.m[row][col] = coeff[row][col];
+ }
+ }
+ }
+}
+const char *options_help_text = "-vo direct3d command line help:\n"
+"Example: -vo direct3d:disable-eosd:disable-textures\n"
+"Options:\n"
+" prefer-stretchrect\n"
+" Use IDirect3DDevice9::StretchRect over other methods if possible.\n"
+" disable-stretchrect\n"
+" Never render the video using IDirect3DDevice9::StretchRect.\n"
+" disable-textures\n"
+" Never render the video using D3D texture rendering. (Rendering with\n"
+" textures + shader will still be allowed. Add disable-shaders to\n"
+" completely disable video rendering with textures.)\n"
+" disable-shaders\n"
+" Never use shaders when rendering video.\n"
+" only-8bit\n"
+" Never render YUV video with more than 8 bits per component.\n"
+" (Using this flag will force software conversion to 8 bit.)\n"
+" disable-eosd\n"
+" Disable EOSD rendering for subtitles.\n"
+" (Using this flag might force the insertion of the 'ass' video filter,\n"
+" which will render the subtitles in software.)\n"
+" disable-texture-align\n"
+" Normally texture sizes are always aligned to 16. With this option\n"
+" enabled, the video texture will always have exactly the same size as\n"
+" the video itself.\n"
+"Debug options. These might be incorrect, might be removed in the future, might\n"
+"crash, might cause slow downs, etc. Contact the developers if you actually need\n"
+"any of these for performance or proper operation.\n"
+" force-power-of-2\n"
+" Always force textures to power of 2, even if the device reports\n"
+" non-power-of-2 texture sizes as supported.\n"
+" texture-memory=N\n"
+" Only affects operation with shaders/texturing enabled, and (E)OSD.\n"
+" Values for N:\n"
+" 0 default, will often use an additional shadow texture + copy\n"
+" 1 use D3DPOOL_MANAGED\n"
+" 2 use D3DPOOL_DEFAULT\n"
+" 3 use D3DPOOL_SYSTEMMEM, but without shadow texture\n"
+" swap-discard\n"
+" Use D3DSWAPEFFECT_DISCARD, which might be faster.\n"
+" Might be slower too, as it must (?) clear every frame.\n"
+" exact-backbuffer\n"
+" Always resize the backbuffer to window size.\n"
+" no16bit-textures\n"
+" Don't use textures with a 16 bit color channel for YUV formats that\n"
+" use more than 8 bits per component. Instead, use D3DFMT_A8L8 textures\n"
+" and compute the values sampled from the 2 channels back into one.\n"
+" Might be slower, since the shader becomes slightly more complicated.\n"
+" Might work better, if your drivers either don't support D3DFMT_L16,\n"
+" or if either the texture unit or the shaders don't operate in at least\n"
+" 16 bit precision.\n"
+"";
/** @brief libvo Callback: Preinitialize the video card.
* Preinit the hardware just enough to be queried about
@@ -663,147 +1404,184 @@ static int query_format(uint32_t movie_fmt)
* @return 0 on success, -1 on failure
*/
-static int preinit(const char *arg)
+static int preinit_internal(struct vo *vo, const char *arg, bool allow_shaders)
{
- D3DDISPLAYMODE disp_mode;
- D3DCAPS9 disp_caps;
- DWORD texture_caps;
- DWORD dev_caps;
+ d3d_priv *priv = talloc_zero(vo, d3d_priv);
+ vo->priv = priv;
- /* Set to zero all global variables. */
- priv = calloc(1, sizeof(struct global_priv));
- if (!priv) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Allocating private memory failed.\n");
- goto err_out;
- }
+ *priv = (d3d_priv) {
+ .vo = vo,
- /* FIXME
- > Please use subopt-helper.h for this, see vo_gl.c:preinit for
- > an example of how to use it.
- */
+ .opt_16bit_textures = true,
- priv->d3d9_dll = LoadLibraryA("d3d9.dll");
- if (!priv->d3d9_dll) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Unable to dynamically load d3d9.dll\n");
- goto err_out;
- }
+ .colorspace = MP_CSP_DETAILS_DEFAULTS,
+ .video_eq = { MP_CSP_EQ_CAPS_COLORMATRIX },
+ };
- priv->pDirect3DCreate9 = (void *)GetProcAddress(priv->d3d9_dll, "Direct3DCreate9");
- if (!priv->pDirect3DCreate9) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Unable to find entry point of Direct3DCreate9\n");
- goto err_out;
+ if (!allow_shaders) {
+ priv->opt_disable_shaders = priv->opt_disable_textures = true;
}
- priv->d3d_handle = priv->pDirect3DCreate9(D3D_SDK_VERSION);
- if (!priv->d3d_handle) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Initializing Direct3D failed.\n");
- goto err_out;
+ const opt_t subopts[] = {
+ {"prefer-stretchrect", OPT_ARG_BOOL, &priv->opt_prefer_stretchrect},
+ {"disable-textures", OPT_ARG_BOOL, &priv->opt_disable_textures},
+ {"disable-stretchrect", OPT_ARG_BOOL, &priv->opt_disable_stretchrect},
+ {"disable-shaders", OPT_ARG_BOOL, &priv->opt_disable_shaders},
+ {"only-8bit", OPT_ARG_BOOL, &priv->opt_only_8bit},
+ {"disable-eosd", OPT_ARG_BOOL, &priv->opt_disable_eosd},
+ {"force-power-of-2", OPT_ARG_BOOL, &priv->opt_force_power_of_2},
+ {"disable-texture-align", OPT_ARG_BOOL, &priv->opt_disable_texture_align},
+ {"texture-memory", OPT_ARG_INT, &priv->opt_texture_memory},
+ {"swap-discard", OPT_ARG_BOOL, &priv->opt_swap_discard},
+ {"exact-backbuffer", OPT_ARG_BOOL, &priv->opt_exact_backbuffer},
+ {"16bit-textures", OPT_ARG_BOOL, &priv->opt_16bit_textures},
+ {NULL}
+ };
+ if (subopt_parse(arg, subopts) != 0) {
+ mp_msg(MSGT_VO, MSGL_FATAL, options_help_text);
+ return -1;
}
- if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle,
- D3DADAPTER_DEFAULT,
- &disp_mode))) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Reading display mode failed.\n");
+ if (!priv->opt_disable_eosd)
+ priv->eosd = eosd_packer_create(priv);
+
+ priv->d3d9_dll = LoadLibraryA("d3d9.dll");
+ if (!priv->d3d9_dll) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Unable to dynamically load d3d9.dll\n");
goto err_out;
}
- /* Store in priv->desktop_fmt the user desktop's colorspace. Usually XRGB. */
- priv->desktop_fmt = disp_mode.Format;
- priv->cur_backbuf_width = disp_mode.Width;
- priv->cur_backbuf_height = disp_mode.Height;
-
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Setting backbuffer dimensions to (%dx%d).\n",
- disp_mode.Width, disp_mode.Height);
-
- if (FAILED(IDirect3D9_GetDeviceCaps(priv->d3d_handle,
- D3DADAPTER_DEFAULT,
- D3DDEVTYPE_HAL,
- &disp_caps))) {
- mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Reading display capabilities failed.\n");
+ priv->pDirect3DCreate9 = (void *)GetProcAddress(priv->d3d9_dll,
+ "Direct3DCreate9");
+ if (!priv->pDirect3DCreate9) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "<vo_direct3d>Unable to find entry point of Direct3DCreate9\n");
goto err_out;
}
- /* Store relevant information reguarding caps of device */
- texture_caps = disp_caps.TextureCaps;
- dev_caps = disp_caps.DevCaps;
- priv->device_caps_power2_only = (texture_caps & D3DPTEXTURECAPS_POW2) &&
- !(texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL);
- priv->device_caps_square_only = texture_caps & D3DPTEXTURECAPS_SQUAREONLY;
- priv->device_texture_sys = dev_caps & D3DDEVCAPS_TEXTURESYSTEMMEMORY;
- priv->max_texture_width = disp_caps.MaxTextureWidth;
- priv->max_texture_height = disp_caps.MaxTextureHeight;
-
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>device_caps_power2_only %d, device_caps_square_only %d\n"
- "<vo_direct3d>device_texture_sys %d\n"
- "<vo_direct3d>max_texture_width %d, max_texture_height %d\n",
- priv->device_caps_power2_only, priv->device_caps_square_only,
- priv->device_texture_sys, priv->max_texture_width,
- priv->max_texture_height);
+ if (!init_d3d(priv))
+ goto err_out;
/* w32_common framework call. Configures window on the screen, gets
* fullscreen dimensions and does other useful stuff.
*/
- if (!vo_w32_init()) {
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Configuring onscreen window failed.\n");
+ if (!vo_w32_init(vo)) {
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Configuring onscreen window failed.\n");
goto err_out;
}
return 0;
err_out:
- uninit();
+ uninit(vo);
return -1;
}
+static int preinit_standard(struct vo *vo, const char *arg)
+{
+ return preinit_internal(vo, arg, false);
+}
+static int preinit_shaders(struct vo *vo, const char *arg)
+{
+ return preinit_internal(vo, arg, true);
+}
/** @brief libvo Callback: Handle control requests.
* @return VO_TRUE on success, VO_NOTIMPL when not implemented
*/
-static int control(uint32_t request, void *data)
+static int control(struct vo *vo, uint32_t request, void *data)
{
+ d3d_priv *priv = vo->priv;
+
switch (request) {
case VOCTRL_QUERY_FORMAT:
- return query_format(*(uint32_t*) data);
- case VOCTRL_GET_IMAGE: /* Direct Rendering. Not implemented yet. */
+ return query_format(priv, *(uint32_t*) data);
+ case VOCTRL_GET_IMAGE:
mp_msg(MSGT_VO, MSGL_V,
"<vo_direct3d>Direct Rendering request. Not implemented yet.\n");
return VO_NOTIMPL;
case VOCTRL_DRAW_IMAGE:
- return render_d3d_frame(data);
+ return d3d_upload_and_render_frame(priv, data);
case VOCTRL_FULLSCREEN:
- vo_w32_fullscreen();
- resize_d3d();
+ vo_w32_fullscreen(vo);
+ resize_d3d(priv);
return VO_TRUE;
case VOCTRL_RESET:
return VO_NOTIMPL;
- case VOCTRL_PAUSE:
- priv->is_paused = 1;
+ case VOCTRL_REDRAW_FRAME:
+ priv->is_clear_needed = 1;
+ d3d_draw_frame(priv);
return VO_TRUE;
- case VOCTRL_RESUME:
- priv->is_paused = 0;
+ case VOCTRL_SET_YUV_COLORSPACE:
+ priv->colorspace = *(struct mp_csp_details *)data;
+ update_colorspace(priv);
+ vo->want_redraw = true;
return VO_TRUE;
- case VOCTRL_SET_EQUALIZER:
- return VO_NOTIMPL;
- case VOCTRL_GET_EQUALIZER:
- return VO_NOTIMPL;
+ case VOCTRL_GET_YUV_COLORSPACE:
+ if (!priv->use_shaders)
+ break; // no idea what the heck D3D YUV uses
+ *(struct mp_csp_details *)data = priv->colorspace;
+ return VO_TRUE;
+ case VOCTRL_SET_EQUALIZER: {
+ if (!priv->use_shaders)
+ break;
+ struct voctrl_set_equalizer_args *args = data;
+ if (mp_csp_equalizer_set(&priv->video_eq, args->name, args->value) < 0)
+ return VO_NOTIMPL;
+ update_colorspace(priv);
+ vo->want_redraw = true;
+ return VO_TRUE;
+ }
+ case VOCTRL_GET_EQUALIZER: {
+ if (!priv->use_shaders)
+ break;
+ struct voctrl_get_equalizer_args *args = data;
+ return mp_csp_equalizer_get(&priv->video_eq, args->name, args->valueptr)
+ >= 0 ? VO_TRUE : VO_NOTIMPL;
+ }
case VOCTRL_ONTOP:
- vo_w32_ontop();
+ vo_w32_ontop(vo);
return VO_TRUE;
case VOCTRL_BORDER:
- vo_w32_border();
- resize_d3d();
+ vo_w32_border(vo);
return VO_TRUE;
case VOCTRL_UPDATE_SCREENINFO:
- w32_update_xinerama_info();
+ w32_update_xinerama_info(vo);
return VO_TRUE;
case VOCTRL_SET_PANSCAN:
- calc_fs_rect();
+ calc_fs_rect(priv);
return VO_TRUE;
case VOCTRL_GET_PANSCAN:
return VO_TRUE;
+ case VOCTRL_DRAW_EOSD:
+ if (!data)
+ return VO_FALSE;
+ assert(priv->eosd);
+ generate_eosd(priv, data);
+ draw_eosd(priv);
+ return VO_TRUE;
+ case VOCTRL_GET_EOSD_RES: {
+ assert(priv->eosd);
+ struct mp_eosd_res *r = data;
+ r->w = vo->dwidth;
+ r->h = vo->dheight;
+ r->ml = r->mr = priv->border_x;
+ r->mt = r->mb = priv->border_y;
+ return VO_TRUE;
}
- return VO_FALSE;
+ case VOCTRL_SCREENSHOT: {
+ struct voctrl_screenshot_args *args = data;
+ if (args->full_window)
+ args->out_image = get_window_screenshot(priv);
+ else
+ args->out_image = get_screenshot(priv);
+ return !!args->out_image;
+ }
+ }
+ return VO_NOTIMPL;
}
/** @brief libvo Callback: Configre the Direct3D adapter.
@@ -812,40 +1590,39 @@ static int control(uint32_t request, void *data)
* @param d_width Screen (destination) width
* @param d_height Screen (destination) height
* @param options Options bitmap
- * @param title Window title
* @param format Movie colorspace format (using MPlayer's
* defines, e.g. IMGFMT_YUY2)
* @return 0 on success, VO_ERROR on failure
*/
-static int config(uint32_t width, uint32_t height, uint32_t d_width,
- uint32_t d_height, uint32_t options, char *title,
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t options,
uint32_t format)
{
-
- priv->src_width = width;
- priv->src_height = height;
+ d3d_priv *priv = vo->priv;
/* w32_common framework call. Creates window on the screen with
* the given coordinates.
*/
- if (!vo_w32_config(d_width, d_height, options)) {
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Creating onscreen window failed.\n");
+ if (!vo_w32_config(vo, d_width, d_height, options)) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Creating window failed.\n");
return VO_ERROR;
}
- /* "config" may be called several times, so if this is not the first
- * call, we should destroy Direct3D adapter and surfaces before
- * calling configure_d3d, which will create them again.
- */
+ priv->src_d_width = d_width;
+ priv->src_d_height = d_height;
- destroy_d3d_surfaces();
+ if ((priv->image_format != format)
+ || (priv->src_width != width)
+ || (priv->src_height != height))
+ {
+ d3d_destroy_video_objects(priv);
- /* Destroy the D3D Device */
- if (priv->d3d_device)
- IDirect3DDevice9_Release(priv->d3d_device);
- priv->d3d_device = NULL;
+ priv->src_width = width;
+ priv->src_height = height;
+ init_rendering_mode(priv, format, true);
+ }
- if (!configure_d3d())
+ if (!resize_d3d(priv))
return VO_ERROR;
return 0; /* Success */
@@ -854,68 +1631,100 @@ static int config(uint32_t width, uint32_t height, uint32_t d_width,
/** @brief libvo Callback: Flip next already drawn frame on the
* screen.
*/
-static void flip_page(void)
+static void flip_page(struct vo *vo)
{
- RECT rect = {0, 0, vo_dwidth, vo_dheight};
+ d3d_priv *priv = vo->priv;
+
+ if (priv->d3d_device && priv->d3d_in_scene) {
+ if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>EndScene failed.\n");
+ }
+ }
+ priv->d3d_in_scene = false;
+
+ RECT rect = {0, 0, vo->dwidth, vo->dheight};
if (!priv->d3d_device ||
FAILED(IDirect3DDevice9_Present(priv->d3d_device, &rect, 0, 0, 0))) {
mp_msg(MSGT_VO, MSGL_V,
"<vo_direct3d>Trying to reinitialize uncooperative video adapter.\n");
- if (!reconfigure_d3d()) {
+ if (!reconfigure_d3d(priv)) {
mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Reinitialization failed.\n");
return;
+ } else {
+ mp_msg(MSGT_VO, MSGL_V,
+ "<vo_direct3d>Video adapter reinitialized.\n");
}
- else
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Video adapter reinitialized.\n");
}
}
/** @brief libvo Callback: Uninitializes all pointers and closes
* all D3D related stuff,
*/
-static void uninit(void)
+static void uninit(struct vo *vo)
{
+ d3d_priv *priv = vo->priv;
+
mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>uninit called.\n");
- uninit_d3d();
- vo_w32_uninit(); /* w32_common framework call */
+ uninit_d3d(priv);
+ vo_w32_uninit(vo);
if (priv->d3d9_dll)
FreeLibrary(priv->d3d9_dll);
priv->d3d9_dll = NULL;
- free(priv);
- priv = NULL;
}
/** @brief libvo Callback: Handles video window events.
*/
-static void check_events(void)
+static void check_events(struct vo *vo)
{
- int flags;
- /* w32_common framework call. Handles video window events.
- * Updates global libvo's vo_dwidth/vo_dheight upon resize
- * with the new window width/height.
- */
- flags = vo_w32_check_events();
+ d3d_priv *priv = vo->priv;
+
+ int flags = vo_w32_check_events(vo);
if (flags & VO_EVENT_RESIZE)
- resize_d3d();
+ resize_d3d(priv);
- if ((flags & VO_EVENT_EXPOSE) && priv->is_paused)
- flip_page();
+ if (flags & VO_EVENT_EXPOSE)
+ vo->want_redraw = true;
+}
+
+static int draw_slice_textures(d3d_priv *priv, uint8_t *src[], int stride[],
+ int w, int h, int x, int y)
+{
+ if (!d3d_lock_video_textures(priv))
+ return VO_FALSE;
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ int dst_stride = plane->locked_rect.Pitch;
+ uint8_t *pdst = (uint8_t*)plane->locked_rect.pBits
+ + (y >> plane->shift_y) * dst_stride
+ + (x >> plane->shift_x) * plane->bytes_per_pixel;
+
+ memcpy_pic(pdst, src[n], (w >> plane->shift_x) * plane->bytes_per_pixel,
+ h >> plane->shift_y, dst_stride, stride[n]);
+ }
+
+ return 0;
}
/** @brief libvo Callback: Draw slice
* @return 0 on success
*/
-static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y )
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y)
{
+ d3d_priv *priv = vo->priv;
+
+ if (!priv->d3d_device)
+ return 0;
+
char *my_src; /**< Pointer to the source image */
char *dst; /**< Pointer to the destination image */
int uv_stride; /**< Stride of the U/V planes */
- /* If the D3D device is uncooperative (not initialized), return success.
- The device will be probed for reinitialization in the next flip_page() */
- if (!priv->d3d_device)
- return 0;
+ if (priv->use_textures)
+ return draw_slice_textures(priv, src, stride, w, h, x, y);
/* Lock the offscreen surface if it's not already locked. */
if (!priv->locked_rect.pBits) {
@@ -964,12 +1773,175 @@ static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y )
return 0; /* Success */
}
-/** @brief libvo Callback: Unused function
- */
-static int draw_frame(uint8_t *src[])
+static bool get_screenshot_from_surface(d3d_priv *priv, mp_image_t *image)
{
- mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>draw_frame called.\n");
- return VO_FALSE;
+ if (!priv->locked_rect.pBits) {
+ if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface,
+ &priv->locked_rect, NULL, 0))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Surface lock failed.\n");
+ return false;
+ }
+ }
+
+ if (image->flags & MP_IMGFLAG_PLANAR) {
+ char *src;
+ int w = priv->src_width;
+ int h = priv->src_height;
+ int swapped = priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2');
+ int plane1 = swapped ? 2 : 1;
+ int plane2 = swapped ? 1 : 2;
+
+ int uv_stride = priv->locked_rect.Pitch / 2;
+
+ /* Copy Y */
+ src = priv->locked_rect.pBits;
+ memcpy_pic(image->planes[0], src, w, h, priv->locked_rect.Pitch,
+ image->stride[0]);
+
+ w /= 2;
+ h /= 2;
+
+ /* Copy U */
+ src = priv->locked_rect.pBits;
+ src = src + priv->locked_rect.Pitch * priv->src_height;
+
+ memcpy_pic(image->planes[plane1], src, w, h, uv_stride,
+ image->stride[1]);
+
+ /* Copy V */
+ src = priv->locked_rect.pBits;
+ src = src + priv->locked_rect.Pitch * priv->src_height
+ + uv_stride * (priv->src_height / 2);
+
+ memcpy_pic(image->planes[plane2], src, w, h, uv_stride,
+ image->stride[2]);
+ } else {
+ // packed YUV or RGB
+ memcpy_pic(image->planes[0], priv->locked_rect.pBits, image->stride[0],
+ image->height, priv->locked_rect.Pitch, image->stride[0]);
+ }
+
+ d3d_unlock_video_objects(priv);
+ return true;
+}
+
+static bool get_screenshot_from_texture(d3d_priv *priv, mp_image_t *image)
+{
+ if (!d3d_lock_video_textures(priv)) {
+ d3d_unlock_video_objects(priv);
+ return false;
+ }
+
+ assert(image->num_planes == priv->plane_count);
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ int width = priv->src_width >> plane->shift_x;
+ int height = priv->src_height >> plane->shift_y;
+
+ memcpy_pic(image->planes[n], plane->locked_rect.pBits,
+ width * plane->bytes_per_pixel, height,
+ image->stride[n], plane->locked_rect.Pitch);
+ }
+
+ return true;
+}
+
+static mp_image_t *get_screenshot(d3d_priv *priv)
+{
+ if (!priv->d3d_device)
+ return NULL;
+
+ mp_image_t *image = alloc_mpi(priv->src_width, priv->src_height,
+ priv->image_format);
+
+ bool res;
+
+ if (priv->use_textures)
+ res = get_screenshot_from_texture(priv, image);
+ else
+ res = get_screenshot_from_surface(priv, image);
+
+ if (!res) {
+ free_mp_image(image);
+ return NULL;
+ }
+
+ image->w = priv->src_d_width;
+ image->h = priv->src_d_height;
+
+ return image;
+}
+
+static mp_image_t *get_window_screenshot(d3d_priv *priv)
+{
+ D3DDISPLAYMODE mode;
+ mp_image_t *image = NULL;
+ RECT window_rc;
+ RECT screen_rc;
+ RECT visible;
+ POINT pt;
+ D3DLOCKED_RECT locked_rect;
+ int width, height;
+
+ if (FAILED(IDirect3DDevice9_GetDisplayMode(priv->d3d_device, 0, &mode))) {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>GetDisplayMode failed.\n");
+ goto error_exit;
+ }
+
+ IDirect3DSurface9 *surface = NULL;
+ if (FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(priv->d3d_device,
+ mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface,
+ NULL)))
+ {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>Couldn't create surface.\n");
+ goto error_exit;
+ }
+
+ if (FAILED(IDirect3DDevice9_GetFrontBufferData(priv->d3d_device, 0,
+ surface)))
+ {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>Couldn't copy frontbuffer.\n");
+ goto error_exit;
+ }
+
+ GetClientRect(priv->vo->w32->window, &window_rc);
+ pt = (POINT) { 0, 0 };
+ ClientToScreen(priv->vo->w32->window, &pt);
+ window_rc.left = pt.x;
+ window_rc.top = pt.y;
+ window_rc.right += window_rc.left;
+ window_rc.bottom += window_rc.top;
+
+ screen_rc = (RECT) { 0, 0, mode.Width, mode.Height };
+
+ if (!IntersectRect(&visible, &screen_rc, &window_rc))
+ goto error_exit;
+ width = visible.right - visible.left;
+ height = visible.bottom - visible.top;
+ if (width < 1 || height < 1)
+ goto error_exit;
+
+ image = alloc_mpi(width, height, IMGFMT_BGR32);
+
+ IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
+
+ memcpy_pic(image->planes[0], (char*)locked_rect.pBits + visible.top *
+ locked_rect.Pitch + visible.left * 4, width * 4, height,
+ image->stride[0], locked_rect.Pitch);
+
+ IDirect3DSurface9_UnlockRect(surface);
+ IDirect3DSurface9_Release(surface);
+
+ return image;
+
+error_exit:
+ if (image)
+ free_mp_image(image);
+ if (surface)
+ IDirect3DSurface9_Release(surface);
+ return NULL;
}
/** @brief Maps MPlayer alpha to D3D
@@ -996,120 +1968,293 @@ static void vo_draw_alpha_l8a8(int w, int h, unsigned char* src,
}
}
+struct draw_osd_closure {
+ d3d_priv *priv;
+ D3DLOCKED_RECT locked_rect;
+};
+
/** @brief Callback function to render the OSD to the texture
*/
-static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src,
- unsigned char *srca, int stride)
+static void draw_alpha(void *pctx, int x0, int y0, int w, int h,
+ unsigned char *src, unsigned char *srca, int stride)
{
- D3DLOCKED_RECT locked_rect; /**< Offscreen surface we lock in order
- to copy MPlayer's frame inside it.*/
-
- if (FAILED(IDirect3DTexture9_LockRect(priv->d3d_texture_system, 0,
- &locked_rect, NULL, 0))) {
- mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>OSD texture lock failed.\n");
- return;
- }
+ struct draw_osd_closure *ctx = pctx;
+ D3DLOCKED_RECT locked_rect = ctx->locked_rect;
vo_draw_alpha_l8a8(w, h, src, srca, stride,
- (unsigned char *)locked_rect.pBits + locked_rect.Pitch*y0 + 2*x0, locked_rect.Pitch);
+ (unsigned char *)locked_rect.pBits + locked_rect.Pitch * y0 + 2 * x0,
+ locked_rect.Pitch);
- /* this unlock is used for both slice_draw path and D3DRenderFrame path */
- if (FAILED(IDirect3DTexture9_UnlockRect(priv->d3d_texture_system, 0))) {
- mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>OSD texture unlock failed.\n");
- return;
- }
-
- priv->is_osd_populated = 1;
+ ctx->priv->is_osd_populated = 1;
}
/** @brief libvo Callback: Draw OSD/Subtitles,
*/
-static void draw_osd(void)
+static void draw_osd(struct vo *vo, struct osd_state *osd)
{
- // we can not render OSD if we lost the device e.g. because it was uncooperative
+ d3d_priv *priv = vo->priv;
+
if (!priv->d3d_device)
return;
if (vo_osd_changed(0)) {
- D3DLOCKED_RECT locked_rect; /**< Offscreen surface we lock in order
- to copy MPlayer's frame inside it.*/
+ struct draw_osd_closure ctx = { priv };
/* clear the OSD */
- if (FAILED(IDirect3DTexture9_LockRect(priv->d3d_texture_system, 0,
- &locked_rect, NULL, 0))) {
+ if (FAILED(IDirect3DTexture9_LockRect(priv->texture_osd.system, 0,
+ &ctx.locked_rect, NULL, 0))) {
mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture lock failed.\n");
return;
}
/* clear the whole texture to avoid issues due to interpolation */
- memset(locked_rect.pBits, 0, locked_rect.Pitch * priv->osd_texture_height);
-
- /* this unlock is used for both slice_draw path and D3DRenderFrame path */
- if (FAILED(IDirect3DTexture9_UnlockRect(priv->d3d_texture_system, 0))) {
- mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture unlock failed.\n");
- return;
- }
+ memset(ctx.locked_rect.pBits, 0,
+ ctx.locked_rect.Pitch * priv->texture_osd.tex_h);
priv->is_osd_populated = 0;
/* required for if subs are in the boarder region */
priv->is_clear_needed = 1;
- vo_draw_text_ext(priv->osd_width, priv->osd_height, priv->border_x, priv->border_y,
- priv->border_x, priv->border_y, priv->src_width, priv->src_height, draw_alpha);
+ osd_draw_text_ext(osd, priv->texture_osd.w, priv->texture_osd.h,
+ priv->border_x, priv->border_y,
+ priv->border_x, priv->border_y,
+ priv->src_width, priv->src_height, draw_alpha, &ctx);
- if (!priv->device_texture_sys)
- {
- /* only DMA to the shadow if its required */
- if (FAILED(IDirect3DDevice9_UpdateTexture(priv->d3d_device,
- (IDirect3DBaseTexture9 *)priv->d3d_texture_system,
- (IDirect3DBaseTexture9 *)priv->d3d_texture_osd))) {
- mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture transfer failed.\n");
- return;
- }
+ if (FAILED(IDirect3DTexture9_UnlockRect(priv->texture_osd.system, 0))) {
+ mp_msg(MSGT_VO,MSGL_ERR,
+ "<vo_direct3d>OSD texture unlock failed.\n");
+ return;
}
- }
- /* update OSD */
+ d3dtex_update(priv, &priv->texture_osd);
+ }
if (priv->is_osd_populated) {
-
- struct_vertex osd_quad_vb[] = {
- {-1.0f, 1.0f, 0.0f, 0, 0 },
- { 1.0f, 1.0f, 0.0f, 1, 0 },
- {-1.0f,-1.0f, 0.0f, 0, 1 },
- { 1.0f,-1.0f, 0.0f, 1, 1 }
+ float tw = (float)priv->texture_osd.w / priv->texture_osd.tex_w;
+ float th = (float)priv->texture_osd.h / priv->texture_osd.tex_h;
+
+ vertex_osd osd_quad_vb[] = {
+ { 0, 0, 0.0f, 0, 0 },
+ { vo->dwidth, 0, 0.0f, tw, 0 },
+ { 0, vo->dheight, 0.0f, 0, th },
+ { vo->dwidth, vo->dheight, 0.0f, tw, th }
};
- /* calculate the texture coordinates */
- osd_quad_vb[1].tu =
- osd_quad_vb[3].tu = (float)priv->osd_width / priv->osd_texture_width;
- osd_quad_vb[2].tv =
- osd_quad_vb[3].tv = (float)priv->osd_height / priv->osd_texture_height;
+ d3d_begin_scene(priv);
- if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) {
- mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>BeginScene failed.\n");
- return;
- }
-
- /* turn on alpha test */
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHABLENDENABLE, TRUE);
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHATESTENABLE, TRUE);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHABLENDENABLE, TRUE);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHATESTENABLE, TRUE);
- /* need to use a texture here (done here as we may be able to texture from system memory) */
IDirect3DDevice9_SetTexture(priv->d3d_device, 0,
- (IDirect3DBaseTexture9 *)(priv->device_texture_sys
- ? priv->d3d_texture_system : priv->d3d_texture_osd));
+ d3dtex_get_render_texture(priv, &priv->texture_osd));
- IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_MY_VERTEX);
- IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP, 2, osd_quad_vb, sizeof(struct_vertex));
+ IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_OSD_VERTEX);
+ IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP,
+ 2, osd_quad_vb, sizeof(vertex_osd));
- /* turn off alpha test */
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHATESTENABLE, FALSE);
- IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHABLENDENABLE, FALSE);
+ IDirect3DDevice9_SetTexture(priv->d3d_device, 0, NULL);
- if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) {
- mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>EndScene failed.\n");
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHATESTENABLE, FALSE);
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHABLENDENABLE, FALSE);
+ }
+}
+
+static void d3d_realloc_eosd_texture(d3d_priv *priv)
+{
+ int new_w = priv->eosd->surface.w;
+ int new_h = priv->eosd->surface.h;
+
+ d3d_fix_texture_size(priv, &new_w, &new_h);
+
+ if (new_w == priv->texture_eosd.tex_w && new_h == priv->texture_eosd.tex_h)
+ return;
+
+ mp_msg(MSGT_VO, MSGL_DBG2, "<vo_direct3d>reallocate EOSD surface.\n");
+
+ // fortunately, we don't need to keep the old image data
+ // we can always free it
+ d3dtex_release(priv, &priv->texture_eosd);
+
+ d3dtex_allocate(priv, &priv->texture_eosd, D3DFMT_A8, new_w, new_h);
+}
+
+static D3DCOLOR ass_to_d3d_color(uint32_t color)
+{
+ uint32_t r = (color >> 24) & 0xff;
+ uint32_t g = (color >> 16) & 0xff;
+ uint32_t b = (color >> 8) & 0xff;
+ uint32_t a = 0xff - (color & 0xff);
+ return D3DCOLOR_ARGB(a, r, g, b);
+}
+
+static void generate_eosd(d3d_priv *priv, mp_eosd_images_t *imgs)
+{
+ if (!priv->d3d_device)
+ return;
+
+ bool need_reposition, need_upload, need_resize;
+ eosd_packer_generate(priv->eosd, imgs, &need_reposition, &need_upload,
+ &need_resize);
+
+ if (!need_reposition)
+ return;
+ // even if the texture size is unchanged, the texture might have been free'd
+ d3d_realloc_eosd_texture(priv);
+ if (!priv->texture_eosd.system)
+ return; // failed to allocate
+
+ // reupload all EOSD images
+
+ // we need 2 primitives per quad which makes 6 vertices (we could reduce the
+ // number of vertices by using an indexed vertex array, but it's probably
+ // not worth doing)
+ priv->eosd_vb = talloc_realloc_size(priv->eosd, priv->eosd_vb,
+ priv->eosd->targets_count
+ * sizeof(vertex_eosd) * 6);
+
+ if (need_upload) {
+ struct eosd_rect rc;
+ eosd_packer_calculate_source_bb(priv->eosd, &rc);
+ RECT dirty_rc = { rc.x0, rc.y0, rc.x1, rc.y1 };
+
+ D3DLOCKED_RECT locked_rect;
+
+ if (FAILED(IDirect3DTexture9_LockRect(priv->texture_eosd.system, 0,
+ &locked_rect, &dirty_rc, 0)))
+ {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>EOSD texture lock failed.\n");
return;
}
+
+ //memset(locked_rect.pBits, 0, locked_rect.Pitch * priv->texture_eosd.tex_h);
+
+ for (int i = 0; i < priv->eosd->targets_count; i++) {
+ struct eosd_target *target = &priv->eosd->targets[i];
+ ASS_Image *img = target->ass_img;
+ char *src = img->bitmap;
+ char *dst = (char*)locked_rect.pBits + target->source.x0
+ + locked_rect.Pitch * target->source.y0;
+ for (int y = 0; y < img->h; y++) {
+ memcpy(dst, src, img->w);
+ src += img->stride;
+ dst += locked_rect.Pitch;
+ }
+ }
+
+ if (FAILED(IDirect3DTexture9_UnlockRect(priv->texture_eosd.system, 0))) {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>EOSD texture unlock failed.\n");
+ return;
+ }
+
+ d3dtex_update(priv, &priv->texture_eosd);
+ }
+
+ float eosd_w = priv->texture_eosd.tex_w;
+ float eosd_h = priv->texture_eosd.tex_h;
+
+ for (int i = 0; i < priv->eosd->targets_count; i++) {
+ struct eosd_target *target = &priv->eosd->targets[i];
+
+ D3DCOLOR color = ass_to_d3d_color(target->ass_img->color);
+
+ float x0 = target->dest.x0;
+ float y0 = target->dest.y0;
+ float x1 = target->dest.x1;
+ float y1 = target->dest.y1;
+ float tx0 = target->source.x0 / eosd_w;
+ float ty0 = target->source.y0 / eosd_h;
+ float tx1 = target->source.x1 / eosd_w;
+ float ty1 = target->source.y1 / eosd_h;
+
+ vertex_eosd *v = &priv->eosd_vb[i*6];
+ v[0] = (vertex_eosd) { x0, y0, 0, color, tx0, ty0 };
+ v[1] = (vertex_eosd) { x1, y0, 0, color, tx1, ty0 };
+ v[2] = (vertex_eosd) { x0, y1, 0, color, tx0, ty1 };
+ v[3] = (vertex_eosd) { x1, y1, 0, color, tx1, ty1 };
+ v[4] = v[2];
+ v[5] = v[1];
}
}
+
+static void draw_eosd(d3d_priv *priv)
+{
+ if (!priv->d3d_device)
+ return;
+
+ if (!priv->eosd->targets_count)
+ return;
+
+ d3d_begin_scene(priv);
+
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHABLENDENABLE, TRUE);
+
+ IDirect3DDevice9_SetTexture(priv->d3d_device, 0,
+ d3dtex_get_render_texture(priv, &priv->texture_eosd));
+
+ // do not use the color value from the A8 texture, because that is black
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,D3DRS_TEXTUREFACTOR,
+ 0xFFFFFFFF);
+ IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0,
+ D3DTSS_COLORARG1, D3DTA_TFACTOR);
+
+ IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0,
+ D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+
+ IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_EOSD_VERTEX);
+ IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLELIST,
+ priv->eosd->targets_count * 2,
+ priv->eosd_vb, sizeof(vertex_eosd));
+
+ IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0,
+ D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0,
+ D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+
+ IDirect3DDevice9_SetTexture(priv->d3d_device, 0, NULL);
+
+ IDirect3DDevice9_SetRenderState(priv->d3d_device,
+ D3DRS_ALPHABLENDENABLE, FALSE);
+}
+
+#define AUTHOR "Georgi Petrov (gogothebee) <gogothebee@gmail.com> and others"
+
+const struct vo_driver video_out_direct3d = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "Direct3D 9 Renderer",
+ "direct3d",
+ AUTHOR,
+ ""
+ },
+ .preinit = preinit_standard,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
+
+const struct vo_driver video_out_direct3d_shaders = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "Direct3D 9 Renderer (using shaders for YUV conversion)",
+ "direct3d_shaders",
+ AUTHOR,
+ ""
+ },
+ .preinit = preinit_shaders,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
diff --git a/libvo/vo_directfb2.c b/libvo/vo_directfb2.c
index d21c9e37c6..b50adba614 100644
--- a/libvo/vo_directfb2.c
+++ b/libvo/vo_directfb2.c
@@ -1279,7 +1279,7 @@ static uint32_t put_image(mp_image_t *mpi){
p = FFMIN(mpi->w, pitch);
- src = mpi->planes[0]+mpi->y*mpi->stride[0]+mpi->x;
+ src = mpi->planes[0];
for (i=0;i<mpi->h;i++) {
fast_memcpy(dst+i*pitch,src+i*mpi->stride[0],p);
@@ -1290,14 +1290,14 @@ static uint32_t put_image(mp_image_t *mpi){
dst += pitch*height;
p = p/2;
- src = mpi->planes[2]+mpi->y*mpi->stride[2]+mpi->x/2;
+ src = mpi->planes[2];
for (i=0;i<mpi->h/2;i++) {
fast_memcpy(dst+i*pitch/2,src+i*mpi->stride[2],p);
}
dst += pitch*height/4;
- src = mpi->planes[1]+mpi->y*mpi->stride[1]+mpi->x/2;
+ src = mpi->planes[1];
for (i=0;i<mpi->h/2;i++) {
fast_memcpy(dst+i*pitch/2,src+i*mpi->stride[1],p);
@@ -1307,14 +1307,14 @@ static uint32_t put_image(mp_image_t *mpi){
dst += pitch*height;
p = p/2;
- src = mpi->planes[1]+mpi->y*mpi->stride[1]+mpi->x/2;
+ src = mpi->planes[1];
for (i=0;i<mpi->h/2;i++) {
fast_memcpy(dst+i*pitch/2,src+i*mpi->stride[1],p);
}
dst += pitch*height/4;
- src = mpi->planes[2]+mpi->y*mpi->stride[2]+mpi->x/2;
+ src = mpi->planes[2];
for (i=0;i<mpi->h/2;i++) {
fast_memcpy(dst+i*pitch/2,src+i*mpi->stride[2],p);
@@ -1356,11 +1356,11 @@ static uint32_t put_image(mp_image_t *mpi){
if (frame) {
DFBCHECK (frame->Lock(frame,DSLF_WRITE,(void *)&dst,&pitch));
framelocked = 1;
- mem2agpcpy_pic(dst,mpi->planes[0] + mpi->y * mpi->stride[0] + mpi->x * (mpi->bpp >> 3) ,mpi->w * (mpi->bpp >> 3),mpi->h,pitch,mpi->stride[0]);
+ mem2agpcpy_pic(dst,mpi->planes[0] ,mpi->w * (mpi->bpp >> 3),mpi->h,pitch,mpi->stride[0]);
} else {
DFBCHECK (primary->Lock(primary,DSLF_WRITE,(void *)&dst,&pitch));
primarylocked = 1;
- mem2agpcpy_pic(dst + yoffset * pitch + xoffset * (mpi->bpp >> 3),mpi->planes[0] + mpi->y * mpi->stride[0] + mpi->x * (mpi->bpp >> 3) ,mpi->w * (mpi->bpp >> 3),mpi->h,pitch,mpi->stride[0]);
+ mem2agpcpy_pic(dst + yoffset * pitch + xoffset * (mpi->bpp >> 3),mpi->planes[0] ,mpi->w * (mpi->bpp >> 3),mpi->h,pitch,mpi->stride[0]);
};
unlock();
diff --git a/libvo/vo_directx.c b/libvo/vo_directx.c
index d504e23567..345ee6e8d4 100644
--- a/libvo/vo_directx.c
+++ b/libvo/vo_directx.c
@@ -36,31 +36,33 @@
#include "sub/sub.h"
#include "w32_common.h"
-static LPDIRECTDRAWCOLORCONTROL g_cc = NULL; //color control interface
-static LPDIRECTDRAW7 g_lpdd = NULL; //DirectDraw Object
-static LPDIRECTDRAWSURFACE7 g_lpddsPrimary = NULL; //Primary Surface: viewport through the Desktop
-static LPDIRECTDRAWSURFACE7 g_lpddsOverlay = NULL; //Overlay Surface
-static LPDIRECTDRAWSURFACE7 g_lpddsBack = NULL; //Back surface
-static LPDIRECTDRAWCLIPPER g_lpddclipper; //clipper object, can only be used without overlay
-static DDSURFACEDESC2 ddsdsf; //surface descripiton needed for locking
-static HINSTANCE hddraw_dll; //handle to ddraw.dll
-static RECT rd; //rect of our stretched image
-static RECT rs; //rect of our source image
-static HBRUSH colorbrush = NULL; // Handle to colorkey brush
-static HBRUSH blackbrush = NULL; // Handle to black brush
+#define vo_w32_window (global_vo->w32->window)
+
+static LPDIRECTDRAWCOLORCONTROL g_cc = NULL; //color control interface
+static LPDIRECTDRAW7 g_lpdd = NULL; //DirectDraw Object
+static LPDIRECTDRAWSURFACE7 g_lpddsPrimary = NULL; //Primary Surface: viewport through the Desktop
+static LPDIRECTDRAWSURFACE7 g_lpddsOverlay = NULL; //Overlay Surface
+static LPDIRECTDRAWSURFACE7 g_lpddsBack = NULL; //Back surface
+static LPDIRECTDRAWCLIPPER g_lpddclipper; //clipper object, can only be used without overlay
+static DDSURFACEDESC2 ddsdsf; //surface descripiton needed for locking
+static HINSTANCE hddraw_dll; //handle to ddraw.dll
+static RECT rd; //rect of our stretched image
+static RECT rs; //rect of our source image
+static HBRUSH colorbrush = NULL; // Handle to colorkey brush
+static HBRUSH blackbrush = NULL; // Handle to black brush
static uint32_t image_width, image_height; //image width and height
-static uint8_t *image=NULL; //image data
-static void* tmp_image = NULL;
-static uint32_t image_format=0; //image format
+static uint8_t *image = NULL; //image data
+static void *tmp_image = NULL;
+static uint32_t image_format = 0; //image format
static uint32_t primary_image_format;
-static uint32_t vm_height=0;
-static uint32_t vm_width=0;
-static uint32_t vm_bpp=0;
+static uint32_t vm_height = 0;
+static uint32_t vm_width = 0;
+static uint32_t vm_bpp = 0;
static uint32_t dstride; //surface stride
static uint32_t nooverlay = 0; //NonOverlay mode
-static DWORD destcolorkey; //colorkey for our surface
-static COLORREF windowcolor = RGB(0,0,16); //windowcolor == colorkey
-static int adapter_count=0;
+static DWORD destcolorkey; //colorkey for our surface
+static COLORREF windowcolor = RGB(0, 0, 16); //windowcolor == colorkey
+static int adapter_count = 0;
static GUID selected_guid;
static GUID *selected_guid_ptr = NULL;
@@ -70,296 +72,291 @@ static GUID *selected_guid_ptr = NULL;
* the linking stage.
*****************************************************************************/
#define IID_IDirectDraw7 MP_IID_IDirectDraw7
-static const GUID MP_IID_IDirectDraw7 =
-{
- 0x15e65ec0,0x3b9c,0x11d2,{0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b}
+static const GUID MP_IID_IDirectDraw7 = {
+ 0x15e65ec0, 0x3b9c, 0x11d2, { 0xb9, 0x2f, 0x00, 0x60, 0x97, 0x97, 0xea, 0x5b }
};
#define IID_IDirectDrawColorControl MP_IID_IDirectDrawColorControl
-static const GUID MP_IID_IDirectDrawColorControl =
-{
- 0x4b9f0ee0,0x0d7e,0x11d0,{0x9b,0x06,0x00,0xa0,0xc9,0x03,0xa3,0xb8}
+static const GUID MP_IID_IDirectDrawColorControl = {
+ 0x4b9f0ee0, 0x0d7e, 0x11d0, { 0x9b, 0x06, 0x00, 0xa0, 0xc9, 0x03, 0xa3, 0xb8 }
};
-
-typedef struct directx_fourcc_caps
-{
- char* img_format_name; //human readable name
- uint32_t img_format; //as MPlayer image format
- uint32_t drv_caps; //what hw supports with this format
- DDPIXELFORMAT g_ddpfOverlay; //as Directx Sourface description
-} directx_fourcc_caps;
-
-
-static directx_fourcc_caps g_ddpf[] =
-{
- {"YV12 ",IMGFMT_YV12 ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','V','1','2'),0,0,0,0,0}},
- {"I420 ",IMGFMT_I420 ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('I','4','2','0'),0,0,0,0,0}}, //yv12 with swapped uv
- {"IYUV ",IMGFMT_IYUV ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('I','Y','U','V'),0,0,0,0,0}}, //same as i420
- {"YVU9 ",IMGFMT_YVU9 ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','V','U','9'),0,0,0,0,0}},
- {"YUY2 ",IMGFMT_YUY2 ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','U','Y','2'),0,0,0,0,0}},
- {"UYVY ",IMGFMT_UYVY ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('U','Y','V','Y'),0,0,0,0,0}},
- {"BGR8 ",IMGFMT_BGR8 ,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 8, 0x00000000, 0x00000000, 0x00000000, 0}},
- {"RGB15",IMGFMT_RGB15,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000001F, 0x000003E0, 0x00007C00, 0}}, //RGB 5:5:5
- {"BGR15",IMGFMT_BGR15,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x00007C00, 0x000003E0, 0x0000001F, 0}},
- {"RGB16",IMGFMT_RGB16,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000001F, 0x000007E0, 0x0000F800, 0}}, //RGB 5:6:5
- {"BGR16",IMGFMT_BGR16,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000F800, 0x000007E0, 0x0000001F, 0}},
- {"RGB24",IMGFMT_RGB24,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0}},
- {"BGR24",IMGFMT_BGR24,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0}},
- {"RGB32",IMGFMT_RGB32,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0}},
- {"BGR32",IMGFMT_BGR32,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0}}
+struct directx_fourcc_caps {
+ char img_format_name[6]; //human readable name
+ uint32_t img_format; //as MPlayer image format
+ DDPIXELFORMAT g_ddpfOverlay; //as Directx Sourface description
+} static const g_ddpf[] = {
+ { "YV12 ", IMGFMT_YV12, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'V', '1', '2'), 0, 0, 0, 0, 0 } },
+ { "I420 ", IMGFMT_I420, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('I', '4', '2', '0'), 0, 0, 0, 0, 0 } }, //yv12 with swapped uv
+ { "IYUV ", IMGFMT_IYUV, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('I', 'Y', 'U', 'V'), 0, 0, 0, 0, 0 } }, //same as i420
+ { "YVU9 ", IMGFMT_YVU9, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'V', 'U', '9'), 0, 0, 0, 0, 0 } },
+ { "YUY2 ", IMGFMT_YUY2, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'U', 'Y', '2'), 0, 0, 0, 0, 0 } },
+ { "UYVY ", IMGFMT_UYVY, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('U', 'Y', 'V', 'Y'), 0, 0, 0, 0, 0 } },
+ { "BGR8 ", IMGFMT_BGR8, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 8, 0x00000000, 0x00000000, 0x00000000, 0 } },
+ { "RGB15", IMGFMT_RGB15, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000001F, 0x000003E0, 0x00007C00, 0 } }, //RGB 5:5:5
+ { "BGR15", IMGFMT_BGR15, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x00007C00, 0x000003E0, 0x0000001F, 0 } },
+ { "RGB16", IMGFMT_RGB16, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000001F, 0x000007E0, 0x0000F800, 0 } }, //RGB 5:6:5
+ { "BGR16", IMGFMT_BGR16, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000F800, 0x000007E0, 0x0000001F, 0 } },
+ { "RGB24", IMGFMT_RGB24, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0 } },
+ { "BGR24", IMGFMT_BGR24, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0 } },
+ { "RGB32", IMGFMT_RGB32, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0 } },
+ { "BGR32", IMGFMT_BGR32, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0 } }
};
#define NUM_FORMATS (sizeof(g_ddpf) / sizeof(g_ddpf[0]))
-static const vo_info_t info =
-{
- "Directx DDraw YUV/RGB/BGR renderer",
- "directx",
- "Sascha Sommer <saschasommer@freenet.de>",
- ""
+// what hw supports with corresponding format in g_ddpf
+static uint32_t drv_caps[NUM_FORMATS];
+
+static const vo_info_t info = {
+ "Directx DDraw YUV/RGB/BGR renderer",
+ "directx",
+ "Sascha Sommer <saschasommer@freenet.de>",
+ ""
};
const LIBVO_EXTERN(directx)
static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src,
- unsigned char *srca, int stride)
+ unsigned char *srca, int stride)
{
- switch(image_format) {
- case IMGFMT_YV12 :
- case IMGFMT_I420 :
- case IMGFMT_IYUV :
- case IMGFMT_YVU9 :
- vo_draw_alpha_yv12(w,h,src,srca,stride,((uint8_t *) image) + dstride*y0 + x0,dstride);
- break;
- case IMGFMT_YUY2 :
- vo_draw_alpha_yuy2(w,h,src,srca,stride,((uint8_t *) image)+ dstride*y0 + 2*x0 ,dstride);
- break;
- case IMGFMT_UYVY :
- vo_draw_alpha_yuy2(w,h,src,srca,stride,((uint8_t *) image) + dstride*y0 + 2*x0 + 1,dstride);
- break;
- case IMGFMT_RGB15:
+ switch (image_format) {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_YVU9:
+ vo_draw_alpha_yv12(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + x0, dstride);
+ break;
+ case IMGFMT_YUY2:
+ vo_draw_alpha_yuy2(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 2 * x0, dstride);
+ break;
+ case IMGFMT_UYVY:
+ vo_draw_alpha_yuy2(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 2 * x0 + 1, dstride);
+ break;
+ case IMGFMT_RGB15:
case IMGFMT_BGR15:
- vo_draw_alpha_rgb15(w,h,src,srca,stride,((uint8_t *) image)+dstride*y0+2*x0,dstride);
- break;
+ vo_draw_alpha_rgb15(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 2 * x0, dstride);
+ break;
case IMGFMT_RGB16:
- case IMGFMT_BGR16:
- vo_draw_alpha_rgb16(w,h,src,srca,stride,((uint8_t *) image)+dstride*y0+2*x0,dstride);
- break;
+ case IMGFMT_BGR16:
+ vo_draw_alpha_rgb16(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 2 * x0, dstride);
+ break;
case IMGFMT_RGB24:
- case IMGFMT_BGR24:
- vo_draw_alpha_rgb24(w,h,src,srca,stride,((uint8_t *) image)+dstride*y0+4*x0,dstride);
- break;
+ case IMGFMT_BGR24:
+ vo_draw_alpha_rgb24(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 4 * x0, dstride);
+ break;
case IMGFMT_RGB32:
- case IMGFMT_BGR32:
- vo_draw_alpha_rgb32(w,h,src,srca,stride,((uint8_t *) image)+dstride*y0+4*x0,dstride);
- break;
+ case IMGFMT_BGR32:
+ vo_draw_alpha_rgb32(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 4 * x0, dstride);
+ break;
}
}
static void draw_osd(void)
{
- vo_draw_text(image_width,image_height,draw_alpha);
+ vo_draw_text(image_width, image_height, draw_alpha);
}
-static int
-query_format(uint32_t format)
+static int query_format(uint32_t format)
{
- uint32_t i=0;
- while ( i < NUM_FORMATS )
- {
- if (g_ddpf[i].img_format == format)
- return g_ddpf[i].drv_caps;
- i++;
- }
+ int i;
+ for (i = 0; i < NUM_FORMATS; i++)
+ if (g_ddpf[i].img_format == format)
+ return drv_caps[i];
return 0;
}
+struct errmap {
+ HRESULT err;
+ const char *errstr;
+} static const dd_errmap[] = {
+ {DDERR_INCOMPATIBLEPRIMARY, "incompatible primary surface"},
+ {DDERR_INVALIDCAPS, "invalid caps"},
+ {DDERR_INVALIDOBJECT, "invalid object"},
+ {DDERR_INVALIDPARAMS, "invalid parameters"},
+ {DDERR_INVALIDRECT, "invalid rectangle"},
+ {DDERR_INVALIDSURFACETYPE, "invalid surfacetype"},
+ {DDERR_NODIRECTDRAWHW, "no directdraw hardware"},
+ {DDERR_NOEMULATION, "can't emulate"},
+ {DDERR_NOFLIPHW, "hardware can't do flip"},
+ {DDERR_NOOVERLAYHW, "hardware can't do overlay"},
+ {DDERR_NOSTRETCHHW, "hardware can't stretch: try to size the window back"},
+ {DDERR_OUTOFMEMORY, "not enough system memory"},
+ {DDERR_OUTOFVIDEOMEMORY, "not enough video memory"},
+ {DDERR_UNSUPPORTED, "unsupported"},
+ {DDERR_UNSUPPORTEDMODE, "unsupported mode"},
+ {DDERR_HEIGHTALIGN, "height align"},
+ {DDERR_XALIGN, "x align"},
+ {DDERR_SURFACELOST, "surfaces lost"},
+ {0, NULL}
+};
+
+static const char *dd_errstr(HRESULT res)
+{
+ int i;
+ for (i = 0; dd_errmap[i].errstr; i++)
+ if (dd_errmap[i].err == res)
+ return dd_errmap[i].errstr;
+ return "unknown error";
+}
+
static uint32_t Directx_CreatePrimarySurface(void)
{
- DDSURFACEDESC2 ddsd;
+ DDSURFACEDESC2 ddsd = { .dwSize = sizeof(ddsd) };
//cleanup
- if(g_lpddsPrimary)g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary);
- g_lpddsPrimary=NULL;
+ if (g_lpddsPrimary)
+ g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary);
+ g_lpddsPrimary = NULL;
- if(vidmode)g_lpdd->lpVtbl->SetDisplayMode(g_lpdd,vm_width,vm_height,vm_bpp,vo_refresh_rate,0);
- ZeroMemory(&ddsd, sizeof(ddsd));
- ddsd.dwSize = sizeof(ddsd);
+ if (vidmode)
+ g_lpdd->lpVtbl->SetDisplayMode(g_lpdd, vm_width, vm_height, vm_bpp, vo_refresh_rate, 0);
//set flags and create a primary surface.
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
- if(g_lpdd->lpVtbl->CreateSurface(g_lpdd,&ddsd, &g_lpddsPrimary, NULL )== DD_OK)
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>primary surface created\n");
- else
- {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>could not create primary surface\n");
- return 1;
- }
- return 0;
+ if (g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsd, &g_lpddsPrimary, NULL) == DD_OK)
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>primary surface created\n");
+ else {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>could not create primary surface\n");
+ return 1;
+ }
+ return 0;
}
static uint32_t Directx_CreateOverlay(uint32_t imgfmt)
{
HRESULT ddrval;
- DDSURFACEDESC2 ddsdOverlay;
- uint32_t i=0;
- while ( i < NUM_FORMATS && imgfmt != g_ddpf[i].img_format)
- {
- i++;
- }
- if (!g_lpdd || !g_lpddsPrimary || i == NUM_FORMATS)
+ DDSURFACEDESC2 ddsdOverlay = {
+ .dwSize = sizeof(ddsdOverlay),
+ .ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY,
+ .dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_BACKBUFFERCOUNT | DDSD_PIXELFORMAT,
+ .dwWidth = image_width,
+ .dwHeight = image_height,
+ .dwBackBufferCount = 2,
+ };
+ uint32_t i = 0;
+ while (i < NUM_FORMATS && imgfmt != g_ddpf[i].img_format)
+ i++;
+ if (!g_lpdd || !g_lpddsPrimary || i == NUM_FORMATS)
return 1;
//cleanup
- if (g_lpddsOverlay)g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);
- if (g_lpddsBack)g_lpddsBack->lpVtbl->Release(g_lpddsBack);
- g_lpddsOverlay= NULL;
- g_lpddsBack = NULL;
- //create our overlay
- ZeroMemory(&ddsdOverlay, sizeof(ddsdOverlay));
- ddsdOverlay.dwSize = sizeof(ddsdOverlay);
- ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;
- ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_BACKBUFFERCOUNT| DDSD_PIXELFORMAT;
- ddsdOverlay.dwWidth=image_width;
- ddsdOverlay.dwHeight=image_height;
- ddsdOverlay.dwBackBufferCount=2;
- ddsdOverlay.ddpfPixelFormat=g_ddpf[i].g_ddpfOverlay;
- if(vo_doublebuffering) //tribblebuffering
- {
- if (g_lpdd->lpVtbl->CreateSurface(g_lpdd,&ddsdOverlay, &g_lpddsOverlay, NULL)== DD_OK)
- {
- mp_msg(MSGT_VO, MSGL_V,"<vo_directx><INFO>overlay with format %s created\n",g_ddpf[i].img_format_name);
+ if (g_lpddsOverlay)
+ g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);
+ if (g_lpddsBack)
+ g_lpddsBack->lpVtbl->Release(g_lpddsBack);
+ g_lpddsOverlay = NULL;
+ g_lpddsBack = NULL;
+ //create our overlay
+ ddsdOverlay.ddpfPixelFormat = g_ddpf[i].g_ddpfOverlay;
+ if (vo_doublebuffering) { //tribblebuffering
+ if (g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsdOverlay, &g_lpddsOverlay, NULL) == DD_OK) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>overlay with format %s created\n", g_ddpf[i].img_format_name);
//get the surface directly attached to the primary (the back buffer)
ddsdOverlay.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
- if(g_lpddsOverlay->lpVtbl->GetAttachedSurface(g_lpddsOverlay,&ddsdOverlay.ddsCaps, &g_lpddsBack) != DD_OK)
- {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't get attached surface\n");
- return 1;
- }
- return 0;
- }
- vo_doublebuffering=0; //disable tribblebuffering
- mp_msg(MSGT_VO, MSGL_V,"<vo_directx><WARN>cannot create tribblebuffer overlay with format %s\n",g_ddpf[i].img_format_name);
- }
- //single buffer
- mp_msg(MSGT_VO, MSGL_V,"<vo_directx><INFO>using singlebuffer overlay\n");
- ddsdOverlay.dwBackBufferCount=0;
- ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
- ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT;
- ddsdOverlay.ddpfPixelFormat=g_ddpf[i].g_ddpfOverlay;
- // try to create the overlay surface
- ddrval = g_lpdd->lpVtbl->CreateSurface(g_lpdd,&ddsdOverlay, &g_lpddsOverlay, NULL);
- if(ddrval != DD_OK)
- {
- if(ddrval == DDERR_INVALIDPIXELFORMAT)mp_msg(MSGT_VO,MSGL_V,"<vo_directx><ERROR> invalid pixelformat: %s\n",g_ddpf[i].img_format_name);
- else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>");
- switch(ddrval)
- {
- case DDERR_INCOMPATIBLEPRIMARY:
- {mp_msg(MSGT_VO, MSGL_ERR,"incompatible primary surface\n");break;}
- case DDERR_INVALIDCAPS:
- {mp_msg(MSGT_VO, MSGL_ERR,"invalid caps\n");break;}
- case DDERR_INVALIDOBJECT:
- {mp_msg(MSGT_VO, MSGL_ERR,"invalid object\n");break;}
- case DDERR_INVALIDPARAMS:
- {mp_msg(MSGT_VO, MSGL_ERR,"invalid parameters\n");break;}
- case DDERR_NODIRECTDRAWHW:
- {mp_msg(MSGT_VO, MSGL_ERR,"no directdraw hardware\n");break;}
- case DDERR_NOEMULATION:
- {mp_msg(MSGT_VO, MSGL_ERR,"can't emulate\n");break;}
- case DDERR_NOFLIPHW:
- {mp_msg(MSGT_VO, MSGL_ERR,"hardware can't do flip\n");break;}
- case DDERR_NOOVERLAYHW:
- {mp_msg(MSGT_VO, MSGL_ERR,"hardware can't do overlay\n");break;}
- case DDERR_OUTOFMEMORY:
- {mp_msg(MSGT_VO, MSGL_ERR,"not enough system memory\n");break;}
- case DDERR_UNSUPPORTEDMODE:
- {mp_msg(MSGT_VO, MSGL_ERR,"unsupported mode\n");break;}
- case DDERR_OUTOFVIDEOMEMORY:
- {mp_msg(MSGT_VO, MSGL_ERR,"not enough video memory\n");break;}
- default:
- mp_msg(MSGT_VO, MSGL_ERR,"create surface failed with 0x%xu\n",(unsigned)ddrval);
- }
- return 1;
- }
+ if (g_lpddsOverlay->lpVtbl->GetAttachedSurface(g_lpddsOverlay, &ddsdOverlay.ddsCaps, &g_lpddsBack) != DD_OK) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't get attached surface\n");
+ return 1;
+ }
+ return 0;
+ }
+ vo_doublebuffering = 0; //disable tribblebuffering
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><WARN>cannot create tribblebuffer overlay with format %s\n", g_ddpf[i].img_format_name);
+ }
+ //single buffer
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>using singlebuffer overlay\n");
+ ddsdOverlay.dwBackBufferCount = 0;
+ ddsdOverlay.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
+ ddsdOverlay.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
+ ddsdOverlay.ddpfPixelFormat = g_ddpf[i].g_ddpfOverlay;
+ // try to create the overlay surface
+ ddrval = g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsdOverlay, &g_lpddsOverlay, NULL);
+ if (ddrval != DD_OK) {
+ if (ddrval == DDERR_INVALIDPIXELFORMAT)
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR> invalid pixelformat: %s\n", g_ddpf[i].img_format_name);
+ else
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>create surface failed: %s (0x%x)\n", dd_errstr(ddrval), (unsigned int)ddrval);
+ return 1;
+ }
g_lpddsBack = g_lpddsOverlay;
- return 0;
+ return 0;
}
static uint32_t Directx_CreateBackpuffer(void)
{
- DDSURFACEDESC2 ddsd;
- //cleanup
- if (g_lpddsBack)g_lpddsBack->lpVtbl->Release(g_lpddsBack);
- g_lpddsBack=NULL;
- ZeroMemory(&ddsd, sizeof(ddsd));
- ddsd.dwSize = sizeof(ddsd);
- ddsd.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
- ddsd.dwFlags= DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
- ddsd.dwWidth=image_width;
- ddsd.dwHeight=image_height;
- if(g_lpdd->lpVtbl->CreateSurface( g_lpdd, &ddsd, &g_lpddsBack, 0 ) != DD_OK )
- {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't create backpuffer\n");
- return 1;
- }
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>backbuffer created\n");
- return 0;
+ DDSURFACEDESC2 ddsd = {
+ .dwSize = sizeof(ddsd),
+ .ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY,
+ .dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT,
+ .dwWidth = image_width,
+ .dwHeight = image_height,
+ };
+ //cleanup
+ if (g_lpddsBack)
+ g_lpddsBack->lpVtbl->Release(g_lpddsBack);
+ g_lpddsBack = NULL;
+ if (g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsd, &g_lpddsBack, 0) != DD_OK) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't create backpuffer\n");
+ return 1;
+ }
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>backbuffer created\n");
+ return 0;
}
static void uninit(void)
{
- if (g_cc != NULL)
- {
- g_cc->lpVtbl->Release(g_cc);
- }
- g_cc=NULL;
- if (g_lpddclipper != NULL) g_lpddclipper->lpVtbl->Release(g_lpddclipper);
- g_lpddclipper = NULL;
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>clipper released\n");
- if (g_lpddsBack != NULL) g_lpddsBack->lpVtbl->Release(g_lpddsBack);
- g_lpddsBack = NULL;
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>back surface released\n");
- if(vo_doublebuffering && !nooverlay)
- {
- if (g_lpddsOverlay != NULL)g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);
+ if (g_cc)
+ g_cc->lpVtbl->Release(g_cc);
+ g_cc = NULL;
+ if (g_lpddclipper)
+ g_lpddclipper->lpVtbl->Release(g_lpddclipper);
+ g_lpddclipper = NULL;
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>clipper released\n");
+ if (g_lpddsBack)
+ g_lpddsBack->lpVtbl->Release(g_lpddsBack);
+ g_lpddsBack = NULL;
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>back surface released\n");
+ if (vo_doublebuffering && !nooverlay) {
+ if (g_lpddsOverlay)
+ g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);
g_lpddsOverlay = NULL;
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>overlay surface released\n");
- }
- if (g_lpddsPrimary != NULL) g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary);
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>overlay surface released\n");
+ }
+ if (g_lpddsPrimary)
+ g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary);
g_lpddsPrimary = NULL;
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>primary released\n");
- if (colorbrush) DeleteObject(colorbrush);
- colorbrush = NULL;
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>GDI resources deleted\n");
- if (g_lpdd != NULL){
- if(vidmode)g_lpdd->lpVtbl->RestoreDisplayMode(g_lpdd);
- g_lpdd->lpVtbl->Release(g_lpdd);
- }
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>directdrawobject released\n");
- FreeLibrary( hddraw_dll);
- hddraw_dll= NULL;
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>ddraw.dll freed\n");
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>uninitialized\n");
- vo_w32_uninit();
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>primary released\n");
+ if (colorbrush)
+ DeleteObject(colorbrush);
+ colorbrush = NULL;
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>GDI resources deleted\n");
+ if (g_lpdd) {
+ if (vidmode)
+ g_lpdd->lpVtbl->RestoreDisplayMode(g_lpdd);
+ g_lpdd->lpVtbl->Release(g_lpdd);
+ }
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>directdrawobject released\n");
+ FreeLibrary(hddraw_dll);
+ hddraw_dll = NULL;
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>ddraw.dll freed\n");
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>uninitialized\n");
+ vo_w32_uninit(global_vo);
}
-static BOOL WINAPI EnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm)
+static BOOL WINAPI EnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm)
{
if (!lpGUID)
lpDriverDescription = "Primary Display Adapter";
- mp_msg(MSGT_VO, MSGL_INFO ,"<vo_directx> adapter %d: %s", adapter_count, lpDriverDescription);
+ mp_msg(MSGT_VO, MSGL_INFO, "<vo_directx> adapter %d: %s", adapter_count, lpDriverDescription);
- if(adapter_count == vo_adapter_num){
+ if (adapter_count == vo_adapter_num) {
if (!lpGUID)
selected_guid_ptr = NULL;
- else
- {
- selected_guid = *lpGUID;
+ else {
+ selected_guid = *lpGUID;
selected_guid_ptr = &selected_guid;
}
- mp_msg(MSGT_VO, MSGL_INFO ,"\t\t<--");
+ mp_msg(MSGT_VO, MSGL_INFO, "\t\t<--");
}
- mp_msg(MSGT_VO, MSGL_INFO ,"\n");
+ mp_msg(MSGT_VO, MSGL_INFO, "\n");
adapter_count++;
@@ -368,692 +365,695 @@ static BOOL WINAPI EnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, L
static uint32_t Directx_InitDirectDraw(void)
{
- HRESULT (WINAPI *OurDirectDrawCreateEx)(GUID *,LPVOID *, REFIID,IUnknown FAR *);
- DDSURFACEDESC2 ddsd;
- LPDIRECTDRAWENUMERATEEX OurDirectDrawEnumerateEx;
+ HRESULT (WINAPI *OurDirectDrawCreateEx)(GUID *, LPVOID *, REFIID, IUnknown FAR *);
+ DDSURFACEDESC2 ddsd = {
+ .dwSize = sizeof(ddsd),
+ .dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT,
+ };
+ LPDIRECTDRAWENUMERATEEX OurDirectDrawEnumerateEx;
- adapter_count = 0;
+ adapter_count = 0;
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>Initing DirectDraw\n" );
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>Initing DirectDraw\n");
- //load direct draw DLL: based on videolans code
- hddraw_dll = LoadLibrary("DDRAW.DLL");
- if( hddraw_dll == NULL )
- {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>failed loading ddraw.dll\n" );
- return 1;
+ //load direct draw DLL: based on videolans code
+ hddraw_dll = LoadLibrary("DDRAW.DLL");
+ if (!hddraw_dll) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>failed loading ddraw.dll\n");
+ return 1;
}
- if(vo_adapter_num){ //display other than default
- OurDirectDrawEnumerateEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hddraw_dll,"DirectDrawEnumerateExA");
- if (!OurDirectDrawEnumerateEx){
- FreeLibrary( hddraw_dll );
+ if (vo_adapter_num) { //display other than default
+ OurDirectDrawEnumerateEx = (LPDIRECTDRAWENUMERATEEX)GetProcAddress(hddraw_dll, "DirectDrawEnumerateExA");
+ if (!OurDirectDrawEnumerateEx) {
+ FreeLibrary(hddraw_dll);
hddraw_dll = NULL;
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>failed geting proc address: DirectDrawEnumerateEx\n");
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>no directx 7 or higher installed\n");
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>failed geting proc address: DirectDrawEnumerateEx\n");
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>no directx 7 or higher installed\n");
return 1;
}
// enumerate all display devices attached to the desktop
- OurDirectDrawEnumerateEx(EnumCallbackEx, NULL, DDENUM_ATTACHEDSECONDARYDEVICES );
+ OurDirectDrawEnumerateEx(EnumCallbackEx, NULL, DDENUM_ATTACHEDSECONDARYDEVICES);
- if(vo_adapter_num >= adapter_count)
- mp_msg(MSGT_VO, MSGL_ERR,"Selected adapter (%d) doesn't exist: Default Display Adapter selected\n",vo_adapter_num);
+ if (vo_adapter_num >= adapter_count)
+ mp_msg(MSGT_VO, MSGL_ERR, "Selected adapter (%d) doesn't exist: Default Display Adapter selected\n", vo_adapter_num);
}
- OurDirectDrawCreateEx = (void *)GetProcAddress(hddraw_dll, "DirectDrawCreateEx");
- if ( OurDirectDrawCreateEx == NULL )
- {
- FreeLibrary( hddraw_dll );
- hddraw_dll = NULL;
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>failed geting proc address: DirectDrawCreateEx\n");
- return 1;
- }
-
- // initialize DirectDraw and create directx v7 object
- if (OurDirectDrawCreateEx(selected_guid_ptr, (VOID**)&g_lpdd, &IID_IDirectDraw7, NULL ) != DD_OK )
- {
- FreeLibrary( hddraw_dll );
+ OurDirectDrawCreateEx = (void *)GetProcAddress(hddraw_dll, "DirectDrawCreateEx");
+ if (!OurDirectDrawCreateEx) {
+ FreeLibrary(hddraw_dll);
hddraw_dll = NULL;
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't initialize ddraw\n");
- return 1;
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>failed geting proc address: DirectDrawCreateEx\n");
+ return 1;
}
- //get current screen siz for selected monitor ...
- ddsd.dwSize=sizeof(ddsd);
- ddsd.dwFlags=DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT;
- g_lpdd->lpVtbl->GetDisplayMode(g_lpdd, &ddsd);
- if(vo_screenwidth && vo_screenheight)
- {
- vm_height=vo_screenheight;
- vm_width=vo_screenwidth;
- }
- else
- {
- vm_height=ddsd.dwHeight;
- vm_width=ddsd.dwWidth;
- }
+ // initialize DirectDraw and create directx v7 object
+ if (OurDirectDrawCreateEx(selected_guid_ptr, (VOID **)&g_lpdd, &IID_IDirectDraw7, NULL) != DD_OK) {
+ FreeLibrary(hddraw_dll);
+ hddraw_dll = NULL;
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't initialize ddraw\n");
+ return 1;
+ }
+ //get current screen siz for selected monitor ...
+ g_lpdd->lpVtbl->GetDisplayMode(g_lpdd, &ddsd);
+ if (vo_screenwidth && vo_screenheight) {
+ vm_height = vo_screenheight;
+ vm_width = vo_screenwidth;
+ } else {
+ vm_height = ddsd.dwHeight;
+ vm_width = ddsd.dwWidth;
+ }
- if(vo_dbpp)vm_bpp=vo_dbpp;
- else vm_bpp=ddsd.ddpfPixelFormat.dwRGBBitCount;
+ if (vo_dbpp)
+ vm_bpp = vo_dbpp;
+ else
+ vm_bpp = ddsd.ddpfPixelFormat.dwRGBBitCount;
- if(vidmode){
- if (g_lpdd->lpVtbl->SetCooperativeLevel(g_lpdd, vo_w32_window, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN) != DD_OK)
- {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't set cooperativelevel for exclusive mode\n");
+ if (vidmode) {
+ if (g_lpdd->lpVtbl->SetCooperativeLevel(g_lpdd, vo_w32_window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN) != DD_OK) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't set cooperativelevel for exclusive mode\n");
return 1;
- }
- /*SetDisplayMode(ddobject,width,height,bpp,refreshrate,aditionalflags)*/
- if(g_lpdd->lpVtbl->SetDisplayMode(g_lpdd,vm_width, vm_height, vm_bpp,0,0) != DD_OK)
- {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't set displaymode\n");
- return 1;
- }
- mp_msg(MSGT_VO, MSGL_V,"<vo_directx><INFO>Initialized adapter %i for %i x %i @ %i \n",vo_adapter_num,vm_width,vm_height,vm_bpp);
- return 0;
- }
- if (g_lpdd->lpVtbl->SetCooperativeLevel(g_lpdd, vo_w32_window, DDSCL_NORMAL) != DD_OK) // or DDSCL_SETFOCUSWINDOW
- {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>could not set cooperativelevel for hardwarecheck\n");
- return 1;
+ }
+ /*SetDisplayMode(ddobject,width,height,bpp,refreshrate,aditionalflags)*/
+ if (g_lpdd->lpVtbl->SetDisplayMode(g_lpdd, vm_width, vm_height, vm_bpp, 0, 0) != DD_OK) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't set displaymode\n");
+ return 1;
+ }
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>Initialized adapter %i for %i x %i @ %i \n", vo_adapter_num, vm_width, vm_height, vm_bpp);
+ return 0;
+ }
+ if (g_lpdd->lpVtbl->SetCooperativeLevel(g_lpdd, vo_w32_window, DDSCL_NORMAL) != DD_OK) { // or DDSCL_SETFOCUSWINDOW
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>could not set cooperativelevel for hardwarecheck\n");
+ return 1;
}
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>DirectDraw Initialized\n");
- return 0;
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>DirectDraw Initialized\n");
+ return 0;
+}
+
+static void clear_border(HDC dc, int x1, int y1, int x2, int y2)
+{
+ if (x2 <= x1 || y2 <= y1)
+ return;
+ FillRect(dc, &(RECT) { x1, y1, x2, y2 }, blackbrush);
+}
+
+static void redraw_window(void)
+{
+ HDC dc = vo_w32_get_dc(global_vo, vo_w32_window);
+ RECT r;
+ GetClientRect(vo_w32_window, &r);
+ if (vo_fs || vidmode) {
+ FillRect(dc, &r, blackbrush);
+ } else {
+ FillRect(dc, &r, colorbrush);
+ // clear borders (not needed in fs; fs uses background = colorkey)
+ RECT rc = rd;
+ POINT origin = { 0, 0 };
+ ClientToScreen(vo_w32_window, &origin);
+ OffsetRect(&rc, -origin.x, -origin.y);
+ clear_border(dc, r.left, r.top, r.right, rc.top); // top
+ clear_border(dc, r.left, rc.bottom, r.right, r.bottom); // bottom
+ clear_border(dc, r.left, rc.top, rc.left, rc.bottom); // left
+ clear_border(dc, rc.right, rc.top, r.right, rc.bottom); // right
+ }
+ vo_w32_release_dc(global_vo, vo_w32_window, dc);
}
static uint32_t Directx_ManageDisplay(void)
{
- HRESULT ddrval;
- DDCAPS capsDrv;
- DDOVERLAYFX ovfx;
- DWORD dwUpdateFlags=0;
- int width,height;
-
- rd.left = vo_dx - xinerama_x;
- rd.top = vo_dy - xinerama_y;
- width = vo_dwidth;
- height = vo_dheight;
-
- aspect(&width, &height, A_WINZOOM);
- panscan_calc_windowed();
- width += vo_panscan_x;
- height += vo_panscan_y;
- width = FFMIN(width, vo_screenwidth);
- height = FFMIN(height, vo_screenheight);
+ HRESULT ddrval;
+ DDCAPS capsDrv = { .dwSize = sizeof(capsDrv) };
+ DDOVERLAYFX ovfx = { .dwSize = sizeof(ovfx) };
+ DWORD dwUpdateFlags = 0;
+ int width, height;
+
+ redraw_window();
+
+ POINT origin = { 0, 0 };
+ ClientToScreen(vo_w32_window, &origin);
+
+ rd.left = origin.x - xinerama_x;
+ rd.top = origin.y - xinerama_y;
+ width = vo_dwidth;
+ height = vo_dheight;
+
+ if (aspect_scaling()) {
+ aspect(&width, &height, A_WINZOOM);
+ panscan_calc_windowed();
+ width += vo_panscan_x;
+ height += vo_panscan_y;
+ }
rd.left += (vo_dwidth - width ) / 2;
rd.top += (vo_dheight - height) / 2;
- rd.right=rd.left+width;
- rd.bottom=rd.top+height;
-
- /*ok, let's workaround some overlay limitations*/
- if(!nooverlay)
- {
- uint32_t uStretchFactor1000; //minimum stretch
- uint32_t xstretch1000,ystretch1000;
- /*get driver capabilities*/
- ZeroMemory(&capsDrv, sizeof(capsDrv));
- capsDrv.dwSize = sizeof(capsDrv);
- if(g_lpdd->lpVtbl->GetCaps(g_lpdd,&capsDrv, NULL) != DD_OK)return 1;
- /*get minimum stretch, depends on display adaptor and mode (refresh rate!) */
- uStretchFactor1000 = capsDrv.dwMinOverlayStretch>1000 ? capsDrv.dwMinOverlayStretch : 1000;
- rd.right = ((width+rd.left)*uStretchFactor1000+999)/1000;
- rd.bottom = (height+rd.top)*uStretchFactor1000/1000;
+ rd.right = rd.left + width;
+ rd.bottom = rd.top + height;
+
+ /*ok, let's workaround some overlay limitations*/
+ if (!nooverlay) {
+ uint32_t uStretchFactor1000; //minimum stretch
+ uint32_t xstretch1000, ystretch1000;
+
+ if (!width || !height) {
+ // window is minimized, so we should hide the overlay in case
+ // colorkeying is not used or working.
+ // In addition trying to set width/height to 0 would crash
+ g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay, NULL, g_lpddsPrimary, NULL, DDOVER_HIDE, NULL);
+ return 0;
+ }
+
+ /*get driver capabilities*/
+ if (g_lpdd->lpVtbl->GetCaps(g_lpdd, &capsDrv, NULL) != DD_OK)
+ return 1;
+ /*get minimum stretch, depends on display adaptor and mode (refresh rate!) */
+ uStretchFactor1000 = capsDrv.dwMinOverlayStretch > 1000 ? capsDrv.dwMinOverlayStretch : 1000;
+ rd.right = ((width + rd.left) * uStretchFactor1000 + 999) / 1000;
+ rd.bottom = (height + rd.top) * uStretchFactor1000 / 1000;
/*calculate xstretch1000 and ystretch1000*/
- xstretch1000 = ((rd.right - rd.left)* 1000)/image_width ;
- ystretch1000 = ((rd.bottom - rd.top)* 1000)/image_height;
- rs.left=0;
- rs.right=image_width;
- rs.top=0;
- rs.bottom=image_height;
- if(rd.left < 0)rs.left=(-rd.left*1000)/xstretch1000;
- if(rd.top < 0)rs.top=(-rd.top*1000)/ystretch1000;
- if(rd.right > vo_screenwidth)rs.right=((vo_screenwidth-rd.left)*1000)/xstretch1000;
- if(rd.bottom > vo_screenheight)rs.bottom=((vo_screenheight-rd.top)*1000)/ystretch1000;
- /*do not allow to zoom or shrink if hardware isn't able to do so*/
- if((width < image_width)&& !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKX))
- {
- if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKXN)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can only shrinkN\n");
- else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't shrink x\n");
- rd.right=rd.left+image_width;
- }
- else if((width > image_width)&& !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHX))
- {
- if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHXN)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can only stretchN\n");
- else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't stretch x\n");
- rd.right = rd.left+image_width;
- }
- if((height < image_height) && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKY))
- {
- if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKYN)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can only shrinkN\n");
- else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't shrink y\n");
- rd.bottom = rd.top + image_height;
- }
- else if((height > image_height ) && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHY))
- {
- if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHYN)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can only stretchN\n");
- else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't stretch y\n");
- rd.bottom = rd.top + image_height;
- }
- /*the last thing to check are alignment restrictions
- these expressions (x & -y) just do alignment by dropping low order bits...
- so to round up, we add first, then truncate*/
- if((capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYSRC) && capsDrv.dwAlignBoundarySrc)
- rs.left = (rs.left + capsDrv.dwAlignBoundarySrc / 2) & -(signed)(capsDrv.dwAlignBoundarySrc);
- if((capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC) && capsDrv.dwAlignSizeSrc)
- rs.right = rs.left + ((rs.right - rs.left + capsDrv.dwAlignSizeSrc / 2) & -(signed) (capsDrv.dwAlignSizeSrc));
- if((capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYDEST) && capsDrv.dwAlignBoundaryDest)
- rd.left = (rd.left + capsDrv.dwAlignBoundaryDest / 2) & -(signed)(capsDrv.dwAlignBoundaryDest);
- if((capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST) && capsDrv.dwAlignSizeDest)
- rd.right = rd.left + ((rd.right - rd.left) & -(signed) (capsDrv.dwAlignSizeDest));
- /*create an overlay FX structure to specify a destination color key*/
- ZeroMemory(&ovfx, sizeof(ovfx));
- ovfx.dwSize = sizeof(ovfx);
- if(vo_fs||vidmode)
- {
- ovfx.dckDestColorkey.dwColorSpaceLowValue = 0;
+ xstretch1000 = ((rd.right - rd.left) * 1000) / image_width;
+ ystretch1000 = ((rd.bottom - rd.top) * 1000) / image_height;
+ rs.left = 0;
+ rs.right = image_width;
+ rs.top = 0;
+ rs.bottom = image_height;
+ if (rd.left < 0)
+ rs.left = (-rd.left * 1000) / xstretch1000;
+ if (rd.top < 0)
+ rs.top = (-rd.top * 1000) / ystretch1000;
+ if (rd.right > vo_screenwidth)
+ rs.right = ((vo_screenwidth - rd.left) * 1000) / xstretch1000;
+ if (rd.bottom > vo_screenheight)
+ rs.bottom = ((vo_screenheight - rd.top) * 1000) / ystretch1000;
+ /*do not allow to zoom or shrink if hardware isn't able to do so*/
+ if (width < image_width && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKX)) {
+ if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKXN)
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can only shrinkN\n");
+ else
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't shrink x\n");
+ rd.right = rd.left + image_width;
+ } else if (width > image_width && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHX)) {
+ if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHXN)
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can only stretchN\n");
+ else
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't stretch x\n");
+ rd.right = rd.left + image_width;
+ }
+ if (height < image_height && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKY)) {
+ if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKYN)
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can only shrinkN\n");
+ else
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't shrink y\n");
+ rd.bottom = rd.top + image_height;
+ } else if (height > image_height && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHY)) {
+ if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHYN)
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can only stretchN\n");
+ else
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't stretch y\n");
+ rd.bottom = rd.top + image_height;
+ }
+ /*the last thing to check are alignment restrictions
+ * these expressions (x & -y) just do alignment by dropping low order bits...
+ * so to round up, we add first, then truncate*/
+ if ((capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYSRC) && capsDrv.dwAlignBoundarySrc)
+ rs.left = (rs.left + capsDrv.dwAlignBoundarySrc / 2) & - (signed)(capsDrv.dwAlignBoundarySrc);
+ if ((capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC) && capsDrv.dwAlignSizeSrc)
+ rs.right = rs.left + ((rs.right - rs.left + capsDrv.dwAlignSizeSrc / 2) & - (signed)(capsDrv.dwAlignSizeSrc));
+ if ((capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYDEST) && capsDrv.dwAlignBoundaryDest)
+ rd.left = (rd.left + capsDrv.dwAlignBoundaryDest / 2) & - (signed)(capsDrv.dwAlignBoundaryDest);
+ if ((capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST) && capsDrv.dwAlignSizeDest)
+ rd.right = rd.left + ((rd.right - rd.left) & - (signed)(capsDrv.dwAlignSizeDest));
+ /*create an overlay FX structure to specify a destination color key*/
+ if (vo_fs || vidmode) {
+ ovfx.dckDestColorkey.dwColorSpaceLowValue = 0;
ovfx.dckDestColorkey.dwColorSpaceHighValue = 0;
- }
- else
- {
- ovfx.dckDestColorkey.dwColorSpaceLowValue = destcolorkey;
+ } else {
+ ovfx.dckDestColorkey.dwColorSpaceLowValue = destcolorkey;
ovfx.dckDestColorkey.dwColorSpaceHighValue = destcolorkey;
- }
+ }
// set the flags we'll send to UpdateOverlay //DDOVER_AUTOFLIP|DDOVERFX_MIRRORLEFTRIGHT|DDOVERFX_MIRRORUPDOWN could be useful?;
dwUpdateFlags = DDOVER_SHOW | DDOVER_DDFX;
/*if hardware can't do colorkeying set the window on top*/
- if(capsDrv.dwCKeyCaps & DDCKEYCAPS_DESTOVERLAY) dwUpdateFlags |= DDOVER_KEYDESTOVERRIDE;
- else if (!tmp_image) vo_ontop = 1;
- }
- else
- {
+ if (capsDrv.dwCKeyCaps & DDCKEYCAPS_DESTOVERLAY)
+ dwUpdateFlags |= DDOVER_KEYDESTOVERRIDE;
+ else if (!tmp_image)
+ vo_ontop = 1;
+ } else {
g_lpddclipper->lpVtbl->SetHWnd(g_lpddclipper, 0, vo_w32_window);
}
/*make sure the overlay is inside the screen*/
- if(rd.left<0)rd.left=0;
- if(rd.right>vo_screenwidth)rd.right=vo_screenwidth;
- if(rd.top<0)rd.top=0;
- if(rd.bottom>vo_screenheight)rd.bottom=vo_screenheight;
+ rd.top = FFMAX(rd.top, 0);
+ rd.left = FFMAX(rd.left, 0);
+ rd.bottom = FFMIN(rd.bottom, vo_screenheight);
+ rd.right = FFMIN(rd.right, vo_screenwidth);
- /*for nonoverlay mode we are finished, for overlay mode we have to display the overlay first*/
- if(nooverlay)return 0;
+ /*for nonoverlay mode we are finished, for overlay mode we have to display the overlay first*/
+ if (nooverlay)
+ return 0;
// printf("overlay: %i %i %ix%i\n",rd.left,rd.top,rd.right - rd.left,rd.bottom - rd.top);
- ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay,&rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx);
- if(FAILED(ddrval))
- {
+ ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay, &rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx);
+ if (FAILED(ddrval)) {
// one cause might be the driver lied about minimum stretch
// we should try upping the destination size a bit, or
// perhaps shrinking the source size
- mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>UpdateOverlay failed\n" );
- mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>Overlay:x1:%li,y1:%li,x2:%li,y2:%li,w:%li,h:%li\n",rd.left,rd.top,rd.right,rd.bottom,rd.right - rd.left,rd.bottom - rd.top );
- mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>");
- switch (ddrval)
- {
- case DDERR_NOSTRETCHHW:
- {mp_msg(MSGT_VO, MSGL_ERR ,"hardware can't stretch: try to size the window back\n");break;}
- case DDERR_INVALIDRECT:
- {mp_msg(MSGT_VO, MSGL_ERR ,"invalid rectangle\n");break;}
- case DDERR_INVALIDPARAMS:
- {mp_msg(MSGT_VO, MSGL_ERR ,"invalid parameters\n");break;}
- case DDERR_HEIGHTALIGN:
- {mp_msg(MSGT_VO, MSGL_ERR ,"height align\n");break;}
- case DDERR_XALIGN:
- {mp_msg(MSGT_VO, MSGL_ERR ,"x align\n");break;}
- case DDERR_UNSUPPORTED:
- {mp_msg(MSGT_VO, MSGL_ERR ,"unsupported\n");break;}
- case DDERR_INVALIDSURFACETYPE:
- {mp_msg(MSGT_VO, MSGL_ERR ,"invalid surfacetype\n");break;}
- case DDERR_INVALIDOBJECT:
- {mp_msg(MSGT_VO, MSGL_ERR ,"invalid object\n");break;}
- case DDERR_SURFACELOST:
- {
- mp_msg(MSGT_VO, MSGL_ERR ,"surfaces lost\n");
- g_lpddsOverlay->lpVtbl->Restore( g_lpddsOverlay ); //restore and try again
- g_lpddsPrimary->lpVtbl->Restore( g_lpddsPrimary );
- ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay,&rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx);
- if(ddrval !=DD_OK)mp_msg(MSGT_VO, MSGL_FATAL ,"<vo_directx><FATAL ERROR>UpdateOverlay failed again\n" );
- break;
- }
- default:
- mp_msg(MSGT_VO, MSGL_ERR ," 0x%xu\n",(unsigned)ddrval);
- }
- /*ok we can't do anything about it -> hide overlay*/
- if(ddrval != DD_OK)
- {
- ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay,NULL, g_lpddsPrimary, NULL, DDOVER_HIDE, NULL);
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>UpdateOverlay failed\n");
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>Overlay:x1:%li,y1:%li,x2:%li,y2:%li,w:%li,h:%li\n", rd.left, rd.top, rd.right, rd.bottom, rd.right - rd.left, rd.bottom - rd.top);
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>%s (0x%x)\n", dd_errstr(ddrval), (unsigned int)ddrval);
+ if (ddrval == DDERR_SURFACELOST) {
+ g_lpddsOverlay->lpVtbl->Restore(g_lpddsOverlay); //restore and try again
+ g_lpddsPrimary->lpVtbl->Restore(g_lpddsPrimary);
+ ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay, &rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx);
+ if (ddrval != DD_OK)
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>UpdateOverlay failed again\n");
+ }
+ /*ok we can't do anything about it -> hide overlay*/
+ if (ddrval != DD_OK) {
+ ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay, NULL, g_lpddsPrimary, NULL, DDOVER_HIDE, NULL);
return 1;
- }
- }
+ }
+ }
return 0;
}
static void check_events(void)
{
- int evt = vo_w32_check_events();
+ int evt = vo_w32_check_events(global_vo);
if (evt & (VO_EVENT_RESIZE | VO_EVENT_MOVE))
Directx_ManageDisplay();
if (evt & (VO_EVENT_RESIZE | VO_EVENT_MOVE | VO_EVENT_EXPOSE)) {
- HDC dc = vo_w32_get_dc(vo_w32_window);
- RECT r;
- GetClientRect(vo_w32_window, &r);
- FillRect(dc, &r, vo_fs || vidmode ? blackbrush : colorbrush);
- vo_w32_release_dc(vo_w32_window, dc);
+ redraw_window();
}
}
//find out supported overlay pixelformats
static uint32_t Directx_CheckOverlayPixelformats(void)
{
- DDCAPS capsDrv;
- HRESULT ddrval;
- DDSURFACEDESC2 ddsdOverlay;
- uint32_t i;
- uint32_t formatcount = 0;
- //get driver caps to determine overlay support
- ZeroMemory(&capsDrv, sizeof(capsDrv));
- capsDrv.dwSize = sizeof(capsDrv);
- ddrval = g_lpdd->lpVtbl->GetCaps(g_lpdd,&capsDrv, NULL);
- if (FAILED(ddrval))
- {
- mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>failed getting ddrawcaps\n");
- return 1;
- }
- if (!(capsDrv.dwCaps & DDCAPS_OVERLAY))
- {
- mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>Your card doesn't support overlay\n");
- return 1;
- }
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>testing supported overlay pixelformats\n");
+ DDCAPS capsDrv = { .dwSize = sizeof(capsDrv) };
+ HRESULT ddrval;
+ DDSURFACEDESC2 ddsdOverlay = {
+ .dwSize = sizeof(ddsdOverlay),
+ .ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY,
+ .dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT,
+ .dwWidth = 300,
+ .dwHeight = 280,
+ .dwBackBufferCount = 0,
+ };
+ uint32_t i;
+ uint32_t formatcount = 0;
+ //get driver caps to determine overlay support
+ ddrval = g_lpdd->lpVtbl->GetCaps(g_lpdd, &capsDrv, NULL);
+ if (FAILED(ddrval)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>failed getting ddrawcaps\n");
+ return 1;
+ }
+ if (!(capsDrv.dwCaps & DDCAPS_OVERLAY)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>Your card doesn't support overlay\n");
+ return 1;
+ }
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>testing supported overlay pixelformats\n");
//it is not possible to query for pixel formats supported by the
//overlay hardware: try out various formats till one works
- ZeroMemory(&ddsdOverlay, sizeof(ddsdOverlay));
- ddsdOverlay.dwSize = sizeof(ddsdOverlay);
- ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
- ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH| DDSD_PIXELFORMAT;
- ddsdOverlay.dwWidth=300;
- ddsdOverlay.dwHeight=280;
- ddsdOverlay.dwBackBufferCount=0;
//try to create an overlay surface using one of the pixel formats in our global list
- i=0;
- do
- {
- ddsdOverlay.ddpfPixelFormat=g_ddpf[i].g_ddpfOverlay;
- ddrval = g_lpdd->lpVtbl->CreateSurface(g_lpdd,&ddsdOverlay, &g_lpddsOverlay, NULL);
- if (ddrval == DD_OK)
- {
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><FORMAT OVERLAY>%i %s supported\n",i,g_ddpf[i].img_format_name);
- g_ddpf[i].drv_caps = VFCAP_CSP_SUPPORTED |VFCAP_OSD |VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP;
- formatcount++;}
- else mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><FORMAT OVERLAY>%i %s not supported\n",i,g_ddpf[i].img_format_name);
- if (g_lpddsOverlay != NULL) {g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);g_lpddsOverlay = NULL;}
- } while( ++i < NUM_FORMATS );
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>Your card supports %i of %i overlayformats\n",formatcount, NUM_FORMATS);
- if (formatcount == 0)
- {
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><WARN>Your card supports overlay, but we couldn't create one\n");
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>This can have the following reasons:\n");
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>- you are already using an overlay with another app\n");
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>- you don't have enough videomemory\n");
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>- vo_directx doesn't support the cards overlay pixelformat\n");
- return 1;
- }
- if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYMIRRORLEFTRIGHT)mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>can mirror left right\n"); //I don't have hardware which
- if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYMIRRORUPDOWN )mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>can mirror up down\n"); //supports those send me one and I'll implement ;)
- return 0;
+ for (i = 0; i < NUM_FORMATS; i++) {
+ ddsdOverlay.ddpfPixelFormat = g_ddpf[i].g_ddpfOverlay;
+ ddrval = g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsdOverlay, &g_lpddsOverlay, NULL);
+ if (ddrval == DD_OK) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><FORMAT OVERLAY>%i %s supported\n", i, g_ddpf[i].img_format_name);
+ drv_caps[i] = VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP;
+ formatcount++;
+ } else
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><FORMAT OVERLAY>%i %s not supported\n", i, g_ddpf[i].img_format_name);
+ if (g_lpddsOverlay) {
+ g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);
+ g_lpddsOverlay = NULL;
+ }
+ }
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>Your card supports %i of %i overlayformats\n", formatcount, NUM_FORMATS);
+ if (formatcount == 0) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><WARN>Your card supports overlay, but we couldn't create one\n");
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>This can have the following reasons:\n");
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>- you are already using an overlay with another app\n");
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>- you don't have enough videomemory\n");
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>- vo_directx doesn't support the cards overlay pixelformat\n");
+ return 1;
+ }
+ if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYMIRRORLEFTRIGHT)
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>can mirror left right\n"); //I don't have hardware which
+ if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYMIRRORUPDOWN)
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>can mirror up down\n"); //supports those send me one and I'll implement ;)
+ return 0;
}
//find out the Pixelformat of the Primary Surface
static uint32_t Directx_CheckPrimaryPixelformat(void)
{
- uint32_t i=0;
+ int i;
uint32_t formatcount = 0;
- DDPIXELFORMAT ddpf;
- DDSURFACEDESC2 ddsd;
- HDC hdc;
- HRESULT hres;
- COLORREF rgbT=RGB(0,0,0);
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>checking primary surface\n");
- memset( &ddpf, 0, sizeof( DDPIXELFORMAT ));
- ddpf.dwSize = sizeof( DDPIXELFORMAT );
+ DDPIXELFORMAT ddpf = { .dwSize = sizeof(ddpf) };
+ DDSURFACEDESC2 ddsd;
+ HDC hdc;
+ HRESULT hres;
+ COLORREF rgbT = RGB(0, 0, 0);
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>checking primary surface\n");
//we have to create a primary surface first
- if(Directx_CreatePrimarySurface()!=0)return 1;
- if(g_lpddsPrimary->lpVtbl->GetPixelFormat( g_lpddsPrimary, &ddpf ) != DD_OK )
- {
- mp_msg(MSGT_VO, MSGL_FATAL ,"<vo_directx><FATAL ERROR>can't get pixelformat\n");
- return 1;
- }
- while ( i < NUM_FORMATS )
- {
- if (g_ddpf[i].g_ddpfOverlay.dwRGBBitCount == ddpf.dwRGBBitCount)
- {
- if (g_ddpf[i].g_ddpfOverlay.dwRBitMask == ddpf.dwRBitMask)
- {
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><FORMAT PRIMARY>%i %s supported\n",i,g_ddpf[i].img_format_name);
- g_ddpf[i].drv_caps = VFCAP_CSP_SUPPORTED |VFCAP_OSD;
- formatcount++;
- primary_image_format=g_ddpf[i].img_format;
- }
- }
- i++;
+ if (Directx_CreatePrimarySurface() != 0)
+ return 1;
+ if (g_lpddsPrimary->lpVtbl->GetPixelFormat(g_lpddsPrimary, &ddpf) != DD_OK) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't get pixelformat\n");
+ return 1;
+ }
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (g_ddpf[i].g_ddpfOverlay.dwRGBBitCount == ddpf.dwRGBBitCount) {
+ if (g_ddpf[i].g_ddpfOverlay.dwRBitMask == ddpf.dwRBitMask) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><FORMAT PRIMARY>%i %s supported\n", i, g_ddpf[i].img_format_name);
+ drv_caps[i] = VFCAP_CSP_SUPPORTED | VFCAP_OSD;
+ formatcount++;
+ primary_image_format = g_ddpf[i].img_format;
+ }
+ }
}
//get the colorkey for overlay mode
- destcolorkey = CLR_INVALID;
- if (windowcolor != CLR_INVALID && g_lpddsPrimary->lpVtbl->GetDC(g_lpddsPrimary,&hdc) == DD_OK)
- {
+ destcolorkey = CLR_INVALID;
+ if (windowcolor != CLR_INVALID && g_lpddsPrimary->lpVtbl->GetDC(g_lpddsPrimary, &hdc) == DD_OK) {
rgbT = GetPixel(hdc, 0, 0);
SetPixel(hdc, 0, 0, windowcolor);
- g_lpddsPrimary->lpVtbl->ReleaseDC(g_lpddsPrimary,hdc);
+ g_lpddsPrimary->lpVtbl->ReleaseDC(g_lpddsPrimary, hdc);
}
// read back the converted color
ddsd.dwSize = sizeof(ddsd);
- while ((hres = g_lpddsPrimary->lpVtbl->Lock(g_lpddsPrimary,NULL, &ddsd, 0, NULL)) == DDERR_WASSTILLDRAWING)
+ while ((hres = g_lpddsPrimary->lpVtbl->Lock(g_lpddsPrimary, NULL, &ddsd, 0, NULL)) == DDERR_WASSTILLDRAWING)
;
- if (hres == DD_OK)
- {
- destcolorkey = *(DWORD *) ddsd.lpSurface;
+ if (hres == DD_OK) {
+ destcolorkey = *(DWORD *)ddsd.lpSurface;
if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32)
destcolorkey &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1;
- g_lpddsPrimary->lpVtbl->Unlock(g_lpddsPrimary,NULL);
+ g_lpddsPrimary->lpVtbl->Unlock(g_lpddsPrimary, NULL);
}
- if (windowcolor != CLR_INVALID && g_lpddsPrimary->lpVtbl->GetDC(g_lpddsPrimary,&hdc) == DD_OK)
- {
+ if (windowcolor != CLR_INVALID && g_lpddsPrimary->lpVtbl->GetDC(g_lpddsPrimary, &hdc) == DD_OK) {
SetPixel(hdc, 0, 0, rgbT);
- g_lpddsPrimary->lpVtbl->ReleaseDC(g_lpddsPrimary,hdc);
+ g_lpddsPrimary->lpVtbl->ReleaseDC(g_lpddsPrimary, hdc);
+ }
+ //release primary
+ g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary);
+ g_lpddsPrimary = NULL;
+ if (formatcount == 0) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>Unknown Pixelformat\n");
+ return 1;
}
- //release primary
- g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary);
- g_lpddsPrimary = NULL;
- if(formatcount==0)
- {
- mp_msg(MSGT_VO, MSGL_FATAL ,"<vo_directx><FATAL ERROR>Unknown Pixelformat\n");
- return 1;
- }
- return 0;
+ return 0;
}
static int preinit(const char *arg)
{
- if(arg)
- {
- if(strstr(arg,"noaccel"))
- {
- mp_msg(MSGT_VO,MSGL_V,"<vo_directx><INFO>disabled overlay\n");
- nooverlay = 1;
- }
- }
+ if (arg) {
+ if (strstr(arg, "noaccel")) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>disabled overlay\n");
+ nooverlay = 1;
+ }
+ }
windowcolor = vo_colorkey;
- colorbrush = CreateSolidBrush(windowcolor);
- blackbrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
- if (!vo_w32_init())
- return 1;
- if (!vo_w32_config(100, 100, VOFLAG_HIDDEN))
- return 1;
-
- if (Directx_InitDirectDraw()!= 0)return 1; //init DirectDraw
-
- if (Directx_CheckPrimaryPixelformat()!=0)return 1;
- if (!nooverlay && Directx_CheckOverlayPixelformats() == 0) //check for supported hardware
- {
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>hardware supports overlay\n");
- nooverlay = 0;
- }
- else //if we can't have overlay we create a backpuffer with the same imageformat as the primary surface
- {
- mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>using backpuffer\n");
- nooverlay = 1;
- }
- mp_msg(MSGT_VO, MSGL_DBG3 ,"<vo_directx><INFO>preinit succesfully finished\n");
- return 0;
+ colorbrush = CreateSolidBrush(windowcolor);
+ blackbrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ if (!vo_w32_init(global_vo))
+ return 1;
+ if (!vo_w32_config(global_vo, 100, 100, VOFLAG_HIDDEN))
+ return 1;
+
+ if (Directx_InitDirectDraw() != 0)
+ return 1; //init DirectDraw
+
+ if (Directx_CheckPrimaryPixelformat() != 0)
+ return 1;
+ if (!nooverlay && Directx_CheckOverlayPixelformats() == 0) { //check for supported hardware
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>hardware supports overlay\n");
+ nooverlay = 0;
+ } else { //if we can't have overlay we create a backpuffer with the same imageformat as the primary surface
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>using backpuffer\n");
+ nooverlay = 1;
+ }
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>preinit succesfully finished\n");
+ return 0;
}
-static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y )
+static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y)
{
- uint8_t *s;
+ uint8_t *s;
uint8_t *d;
- uint32_t uvstride=dstride/2;
- // copy Y
- d=image+dstride*y+x;
- s=src[0];
- mem2agpcpy_pic(d,s,w,h,dstride,stride[0]);
-
- w/=2;h/=2;x/=2;y/=2;
-
- // copy U
- d=image+dstride*image_height + uvstride*y+x;
- if(image_format == IMGFMT_YV12)s=src[2];
- else s=src[1];
- mem2agpcpy_pic(d,s,w,h,uvstride,stride[1]);
-
- // copy V
- d=image+dstride*image_height +uvstride*(image_height/2) + uvstride*y+x;
- if(image_format == IMGFMT_YV12)s=src[1];
- else s=src[2];
- mem2agpcpy_pic(d,s,w,h,uvstride,stride[2]);
+ uint32_t uvstride = dstride / 2;
+ // copy Y
+ d = image + dstride * y + x;
+ s = src[0];
+ mem2agpcpy_pic(d, s, w, h, dstride, stride[0]);
+
+ w /= 2;
+ h /= 2;
+ x /= 2;
+ y /= 2;
+
+ // copy U
+ d = image + dstride * image_height + uvstride * y + x;
+ if (image_format == IMGFMT_YV12)
+ s = src[2];
+ else
+ s = src[1];
+ mem2agpcpy_pic(d, s, w, h, uvstride, stride[1]);
+
+ // copy V
+ d = image + dstride * image_height + uvstride * (image_height / 2) + uvstride * y + x;
+ if (image_format == IMGFMT_YV12)
+ s = src[1];
+ else
+ s = src[2];
+ mem2agpcpy_pic(d, s, w, h, uvstride, stride[2]);
return 0;
}
static void flip_page(void)
{
- HRESULT dxresult;
- g_lpddsBack->lpVtbl->Unlock (g_lpddsBack,NULL);
- if (vo_doublebuffering)
- {
- // flip to the next image in the sequence
- dxresult = g_lpddsOverlay->lpVtbl->Flip( g_lpddsOverlay,NULL, DDFLIP_WAIT);
- if(dxresult == DDERR_SURFACELOST)
- {
- mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR><vo_directx><INFO>Restoring Surface\n");
- g_lpddsBack->lpVtbl->Restore( g_lpddsBack );
- // restore overlay and primary before calling
- // Directx_ManageDisplay() to avoid error messages
- g_lpddsOverlay->lpVtbl->Restore( g_lpddsOverlay );
- g_lpddsPrimary->lpVtbl->Restore( g_lpddsPrimary );
- // update overlay in case we return from screensaver
- Directx_ManageDisplay();
- dxresult = g_lpddsOverlay->lpVtbl->Flip( g_lpddsOverlay,NULL, DDFLIP_WAIT);
- }
- if(dxresult != DD_OK)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't flip page\n");
+ HRESULT dxresult;
+ g_lpddsBack->lpVtbl->Unlock(g_lpddsBack, NULL);
+ if (vo_doublebuffering) {
+ // flip to the next image in the sequence
+ dxresult = g_lpddsOverlay->lpVtbl->Flip(g_lpddsOverlay, NULL, DDFLIP_WAIT);
+ if (dxresult == DDERR_SURFACELOST) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR><vo_directx><INFO>Restoring Surface\n");
+ g_lpddsBack->lpVtbl->Restore(g_lpddsBack);
+ // restore overlay and primary before calling
+ // Directx_ManageDisplay() to avoid error messages
+ g_lpddsOverlay->lpVtbl->Restore(g_lpddsOverlay);
+ g_lpddsPrimary->lpVtbl->Restore(g_lpddsPrimary);
+ // update overlay in case we return from screensaver
+ Directx_ManageDisplay();
+ dxresult = g_lpddsOverlay->lpVtbl->Flip(g_lpddsOverlay, NULL, DDFLIP_WAIT);
+ }
+ if (dxresult != DD_OK)
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't flip page\n");
}
- if(nooverlay)
- {
- DDBLTFX ddbltfx;
+ if (nooverlay) {
// ask for the "NOTEARING" option
- memset( &ddbltfx, 0, sizeof(DDBLTFX) );
- ddbltfx.dwSize = sizeof(DDBLTFX);
- ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
+ DDBLTFX ddbltfx = {
+ .dwSize = sizeof(ddbltfx),
+ .dwDDFX = DDBLTFX_NOTEARING,
+ };
g_lpddsPrimary->lpVtbl->Blt(g_lpddsPrimary, &rd, g_lpddsBack, NULL, DDBLT_WAIT, &ddbltfx);
- }
- if (g_lpddsBack->lpVtbl->Lock(g_lpddsBack,NULL,&ddsdsf, DDLOCK_NOSYSLOCK | DDLOCK_WAIT , NULL) == DD_OK) {
- if(vo_directrendering && (dstride != ddsdsf.lPitch)){
- mp_msg(MSGT_VO,MSGL_WARN,"<vo_directx><WARN>stride changed !!!! disabling direct rendering\n");
- vo_directrendering=0;
- }
- free(tmp_image);
- tmp_image = NULL;
- dstride = ddsdsf.lPitch;
- image = ddsdsf.lpSurface;
- } else if (!tmp_image) {
- mp_msg(MSGT_VO, MSGL_WARN, "<vo_directx><WARN>Locking the surface failed, rendering to a hidden surface!\n");
- tmp_image = image = calloc(1, image_height * dstride * 2);
- }
+ }
+ if (g_lpddsBack->lpVtbl->Lock(g_lpddsBack, NULL, &ddsdsf, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL) == DD_OK) {
+ if (vo_directrendering && (dstride != ddsdsf.lPitch)) {
+ mp_msg(MSGT_VO, MSGL_WARN, "<vo_directx><WARN>stride changed !!!! disabling direct rendering\n");
+ vo_directrendering = 0;
+ }
+ free(tmp_image);
+ tmp_image = NULL;
+ dstride = ddsdsf.lPitch;
+ image = ddsdsf.lpSurface;
+ } else if (!tmp_image) {
+ mp_msg(MSGT_VO, MSGL_WARN, "<vo_directx><WARN>Locking the surface failed, rendering to a hidden surface!\n");
+ tmp_image = image = calloc(1, image_height * dstride * 2);
+ }
}
static int draw_frame(uint8_t *src[])
{
- fast_memcpy( image, *src, dstride * image_height );
- return 0;
+ fast_memcpy(image, *src, dstride * image_height);
+ return 0;
}
static uint32_t get_image(mp_image_t *mpi)
{
- if(mpi->flags&MP_IMGFLAG_READABLE) {mp_msg(MSGT_VO, MSGL_V,"<vo_directx><ERROR>slow video ram\n");return VO_FALSE;}
- if(mpi->type==MP_IMGTYPE_STATIC) {mp_msg(MSGT_VO, MSGL_V,"<vo_directx><ERROR>not static\n");return VO_FALSE;}
- if((mpi->width==dstride) || (mpi->flags&(MP_IMGFLAG_ACCEPT_STRIDE|MP_IMGFLAG_ACCEPT_WIDTH)))
- {
- if(mpi->flags&MP_IMGFLAG_PLANAR)
- {
- if(image_format == IMGFMT_YV12)
- {
- mpi->planes[2]= image + dstride*image_height;
- mpi->planes[1]= image + dstride*image_height+ dstride*image_height/4;
- mpi->stride[1]=mpi->stride[2]=dstride/2;
- }
- else if(image_format == IMGFMT_IYUV || image_format == IMGFMT_I420)
- {
- mpi->planes[1]= image + dstride*image_height;
- mpi->planes[2]= image + dstride*image_height+ dstride*image_height/4;
- mpi->stride[1]=mpi->stride[2]=dstride/2;
- }
- else if(image_format == IMGFMT_YVU9)
- {
- mpi->planes[2] = image + dstride*image_height;
- mpi->planes[1] = image + dstride*image_height+ dstride*image_height/16;
- mpi->stride[1]=mpi->stride[2]=dstride/4;
- }
- }
- mpi->planes[0]=image;
- mpi->stride[0]=dstride;
- mpi->width=image_width;
- mpi->height=image_height;
- mpi->flags|=MP_IMGFLAG_DIRECT;
+ if (mpi->flags & MP_IMGFLAG_READABLE) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR>slow video ram\n");
+ return VO_FALSE;
+ }
+ if (mpi->type == MP_IMGTYPE_STATIC) {
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR>not static\n");
+ return VO_FALSE;
+ }
+ if (mpi->width == dstride || (mpi->flags & (MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_ACCEPT_WIDTH))) {
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ if (image_format == IMGFMT_YV12) {
+ mpi->planes[2] = image + dstride * image_height;
+ mpi->planes[1] = image + dstride * image_height + dstride * image_height / 4;
+ mpi->stride[1] = mpi->stride[2] = dstride / 2;
+ } else if (image_format == IMGFMT_IYUV || image_format == IMGFMT_I420) {
+ mpi->planes[1] = image + dstride * image_height;
+ mpi->planes[2] = image + dstride * image_height + dstride * image_height / 4;
+ mpi->stride[1] = mpi->stride[2] = dstride / 2;
+ } else if (image_format == IMGFMT_YVU9) {
+ mpi->planes[2] = image + dstride * image_height;
+ mpi->planes[1] = image + dstride * image_height + dstride * image_height / 16;
+ mpi->stride[1] = mpi->stride[2] = dstride / 4;
+ }
+ }
+ mpi->planes[0] = image;
+ mpi->stride[0] = dstride;
+ mpi->width = image_width;
+ mpi->height = image_height;
+ mpi->flags |= MP_IMGFLAG_DIRECT;
mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>Direct Rendering ENABLED\n");
return VO_TRUE;
}
return VO_FALSE;
}
-static uint32_t put_image(mp_image_t *mpi){
-
- uint8_t *d;
- uint8_t *s;
- uint32_t x = mpi->x;
- uint32_t y = mpi->y;
- uint32_t w = mpi->w;
- uint32_t h = mpi->h;
-
- if((mpi->flags&MP_IMGFLAG_DIRECT)||(mpi->flags&MP_IMGFLAG_DRAW_CALLBACK))
- {
- mp_msg(MSGT_VO, MSGL_DBG3 ,"<vo_directx><INFO>put_image: nothing to do: drawslices\n");
- return VO_TRUE;
+static uint32_t put_image(mp_image_t *mpi)
+{
+ uint8_t *d;
+ uint8_t *s;
+ uint32_t x = 0;
+ uint32_t y = 0;
+ uint32_t w = mpi->w;
+ uint32_t h = mpi->h;
+
+ if ((mpi->flags & MP_IMGFLAG_DIRECT) || (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) {
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>put_image: nothing to do: drawslices\n");
+ return VO_TRUE;
}
- if (mpi->flags&MP_IMGFLAG_PLANAR)
- {
-
- if(image_format!=IMGFMT_YVU9)draw_slice(mpi->planes,mpi->stride,mpi->w,mpi->h,0,0);
- else
- {
- // copy Y
- d=image+dstride*y+x;
- s=mpi->planes[0];
- mem2agpcpy_pic(d,s,w,h,dstride,mpi->stride[0]);
- w/=4;h/=4;x/=4;y/=4;
- // copy V
- d=image+dstride*image_height + dstride*y/4+x;
- s=mpi->planes[2];
- mem2agpcpy_pic(d,s,w,h,dstride/4,mpi->stride[1]);
- // copy U
- d=image+dstride*image_height + dstride*image_height/16 + dstride/4*y+x;
- s=mpi->planes[1];
- mem2agpcpy_pic(d,s,w,h,dstride/4,mpi->stride[2]);
- }
- }
- else //packed
- {
- mem2agpcpy_pic(image, mpi->planes[0], w * (mpi->bpp / 8), h, dstride, mpi->stride[0]);
- }
- return VO_TRUE;
+ if (mpi->flags & MP_IMGFLAG_PLANAR) {
+ if (image_format != IMGFMT_YVU9)
+ draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
+ else {
+ // copy Y
+ d = image + dstride * y + x;
+ s = mpi->planes[0];
+ mem2agpcpy_pic(d, s, w, h, dstride, mpi->stride[0]);
+ w /= 4;
+ h /= 4;
+ x /= 4;
+ y /= 4;
+ // copy V
+ d = image + dstride * image_height + dstride * y / 4 + x;
+ s = mpi->planes[2];
+ mem2agpcpy_pic(d, s, w, h, dstride / 4, mpi->stride[1]);
+ // copy U
+ d = image + dstride * image_height + dstride * image_height / 16 + dstride / 4 * y + x;
+ s = mpi->planes[1];
+ mem2agpcpy_pic(d, s, w, h, dstride / 4, mpi->stride[2]);
+ }
+ } else { //packed
+ mem2agpcpy_pic(image, mpi->planes[0], w * (mpi->bpp / 8), h, dstride, mpi->stride[0]);
+ }
+ return VO_TRUE;
}
-static int
-config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t options, char *title, uint32_t format)
+static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t options, char *title, uint32_t format)
{
- image_format = format;
- image_width = width;
- image_height = height;
- if(format != primary_image_format)nooverlay = 0;
+ image_format = format;
+ image_width = width;
+ image_height = height;
+ if (format != primary_image_format)
+ nooverlay = 0;
/*release all directx objects*/
- if (g_cc != NULL)g_cc->lpVtbl->Release(g_cc);
- g_cc=NULL;
- if(g_lpddclipper)g_lpddclipper->lpVtbl->Release(g_lpddclipper);
- g_lpddclipper=NULL;
- if (g_lpddsBack != NULL) g_lpddsBack->lpVtbl->Release(g_lpddsBack);
+ if (g_cc)
+ g_cc->lpVtbl->Release(g_cc);
+ g_cc = NULL;
+ if (g_lpddclipper)
+ g_lpddclipper->lpVtbl->Release(g_lpddclipper);
+ g_lpddclipper = NULL;
+ if (g_lpddsBack)
+ g_lpddsBack->lpVtbl->Release(g_lpddsBack);
g_lpddsBack = NULL;
- if(vo_doublebuffering)
- if (g_lpddsOverlay != NULL)g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);
+ if (vo_doublebuffering)
+ if (g_lpddsOverlay)
+ g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);
g_lpddsOverlay = NULL;
- if (g_lpddsPrimary != NULL) g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary);
+ if (g_lpddsPrimary)
+ g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary);
g_lpddsPrimary = NULL;
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>overlay surfaces released\n");
-
- if (!vo_w32_config(d_width, d_height, options))
- return 1;
-
- if (WinID == -1)
- SetWindowText(vo_w32_window,title);
-
-
- /*create the surfaces*/
- if(Directx_CreatePrimarySurface())return 1;
-
- //create palette for 256 color mode
- if(image_format==IMGFMT_BGR8){
- LPDIRECTDRAWPALETTE ddpalette=NULL;
- LPPALETTEENTRY palette=calloc(256, sizeof(*palette));
- int i;
- for(i=0; i<256; i++){
- palette[i].peRed = ((i >> 5) & 0x07) * 255 / 7;
- palette[i].peGreen = ((i >> 2) & 0x07) * 255 / 7;
- palette[i].peBlue = ((i >> 0) & 0x03) * 255 / 3;
- palette[i].peFlags = PC_NOCOLLAPSE;
- }
- g_lpdd->lpVtbl->CreatePalette(g_lpdd,DDPCAPS_8BIT|DDPCAPS_INITIALIZE,palette,&ddpalette,NULL);
- g_lpddsPrimary->lpVtbl->SetPalette(g_lpddsPrimary,ddpalette);
- free(palette);
- ddpalette->lpVtbl->Release(ddpalette);
- }
-
- if (!nooverlay && Directx_CreateOverlay(image_format))
- {
- if(format == primary_image_format)nooverlay=1; /*overlay creation failed*/
- else {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't use overlay mode: please use -vo directx:noaccel\n");
- return 1;
- }
- }
- if(nooverlay)
- {
- if(Directx_CreateBackpuffer())
- {
- mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't get the driver to work on your system :(\n");
- return 1;
- }
- mp_msg(MSGT_VO, MSGL_V,"<vo_directx><INFO>back surface created\n");
- vo_doublebuffering = 0;
- /*create clipper for nonoverlay mode*/
- if(g_lpdd->lpVtbl->CreateClipper(g_lpdd, 0, &g_lpddclipper,NULL)!= DD_OK){mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't create clipper\n");return 1;}
- if(g_lpddclipper->lpVtbl->SetHWnd (g_lpddclipper, 0, vo_w32_window)!= DD_OK){mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't associate clipper with window\n");return 1;}
- if(g_lpddsPrimary->lpVtbl->SetClipper (g_lpddsPrimary,g_lpddclipper)!=DD_OK){mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't associate primary surface with clipper\n");return 1;}
- mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>clipper succesfully created\n");
- }else{
- if(DD_OK != g_lpddsOverlay->lpVtbl->QueryInterface(g_lpddsOverlay,&IID_IDirectDrawColorControl,(void**)&g_cc))
- mp_msg(MSGT_VO, MSGL_V,"<vo_directx><WARN>unable to get DirectDraw ColorControl interface\n");
- }
- Directx_ManageDisplay();
- memset(&ddsdsf, 0,sizeof(DDSURFACEDESC2));
- ddsdsf.dwSize = sizeof (DDSURFACEDESC2);
- if (g_lpddsBack->lpVtbl->Lock(g_lpddsBack,NULL,&ddsdsf, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL) == DD_OK) {
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>overlay surfaces released\n");
+
+ if (!vo_w32_config(global_vo, d_width, d_height, options))
+ return 1;
+
+ /*create the surfaces*/
+ if (Directx_CreatePrimarySurface())
+ return 1;
+
+ //create palette for 256 color mode
+ if (image_format == IMGFMT_BGR8) {
+ LPDIRECTDRAWPALETTE ddpalette = NULL;
+ LPPALETTEENTRY palette = calloc(256, sizeof(*palette));
+ int i;
+ for (i = 0; i < 256; i++) {
+ palette[i].peRed = ((i >> 5) & 0x07) * 255 / 7;
+ palette[i].peGreen = ((i >> 2) & 0x07) * 255 / 7;
+ palette[i].peBlue = ((i >> 0) & 0x03) * 255 / 3;
+ palette[i].peFlags = PC_NOCOLLAPSE;
+ }
+ g_lpdd->lpVtbl->CreatePalette(g_lpdd, DDPCAPS_8BIT | DDPCAPS_INITIALIZE, palette, &ddpalette, NULL);
+ g_lpddsPrimary->lpVtbl->SetPalette(g_lpddsPrimary, ddpalette);
+ free(palette);
+ ddpalette->lpVtbl->Release(ddpalette);
+ }
+
+ if (!nooverlay && Directx_CreateOverlay(image_format)) {
+ if (format == primary_image_format)
+ nooverlay = 1; /*overlay creation failed*/
+ else {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't use overlay mode: please use -vo directx:noaccel\n");
+ return 1;
+ }
+ }
+ if (nooverlay) {
+ if (Directx_CreateBackpuffer()) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't get the driver to work on your system :(\n");
+ return 1;
+ }
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>back surface created\n");
+ vo_doublebuffering = 0;
+ /*create clipper for nonoverlay mode*/
+ if (g_lpdd->lpVtbl->CreateClipper(g_lpdd, 0, &g_lpddclipper, NULL) != DD_OK) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't create clipper\n");
+ return 1;
+ }
+ if (g_lpddclipper->lpVtbl->SetHWnd(g_lpddclipper, 0, vo_w32_window) != DD_OK) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't associate clipper with window\n");
+ return 1;
+ }
+ if (g_lpddsPrimary->lpVtbl->SetClipper(g_lpddsPrimary, g_lpddclipper) != DD_OK) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't associate primary surface with clipper\n");
+ return 1;
+ }
+ mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>clipper succesfully created\n");
+ } else {
+ if (DD_OK != g_lpddsOverlay->lpVtbl->QueryInterface(g_lpddsOverlay, &IID_IDirectDrawColorControl, (void **)&g_cc))
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><WARN>unable to get DirectDraw ColorControl interface\n");
+ }
+ Directx_ManageDisplay();
+ memset(&ddsdsf, 0, sizeof(DDSURFACEDESC2));
+ ddsdsf.dwSize = sizeof(DDSURFACEDESC2);
+ if (g_lpddsBack->lpVtbl->Lock(g_lpddsBack, NULL, &ddsdsf, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL) == DD_OK) {
dstride = ddsdsf.lPitch;
- image = ddsdsf.lpSurface;
+ image = ddsdsf.lpSurface;
return 0;
- }
- mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR>Initial Lock on the Surface failed.\n");
- return 1;
+ }
+ mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR>Initial Lock on the Surface failed.\n");
+ return 1;
}
//function to set color controls
@@ -1063,121 +1063,114 @@ config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uin
// saturation [0, 20000]
static uint32_t color_ctrl_set(const char *what, int value)
{
- uint32_t r = VO_NOTIMPL;
- DDCOLORCONTROL dcc;
- //printf("\n*** %s = %d\n", what, value);
- if (!g_cc) {
- //printf("\n *** could not get color control interface!!!\n");
- return VO_NOTIMPL;
- }
- ZeroMemory(&dcc, sizeof(dcc));
- dcc.dwSize = sizeof(dcc);
-
- if (!strcmp(what, "brightness")) {
- dcc.dwFlags = DDCOLOR_BRIGHTNESS;
- dcc.lBrightness = (value + 100) * 10000 / 200;
- r = VO_TRUE;
- } else if (!strcmp(what, "contrast")) {
- dcc.dwFlags = DDCOLOR_CONTRAST;
- dcc.lContrast = (value + 100) * 20000 / 200;
- r = VO_TRUE;
- } else if (!strcmp(what, "hue")) {
- dcc.dwFlags = DDCOLOR_HUE;
- dcc.lHue = value * 180 / 100;
- r = VO_TRUE;
- } else if (!strcmp(what, "saturation")) {
- dcc.dwFlags = DDCOLOR_SATURATION;
- dcc.lSaturation = (value + 100) * 20000 / 200;
- r = VO_TRUE;
- }
-
- if (r == VO_TRUE) {
- g_cc->lpVtbl->SetColorControls(g_cc, &dcc);
- }
- return r;
+ uint32_t r = VO_NOTIMPL;
+ DDCOLORCONTROL dcc = { .dwSize = sizeof(dcc) };
+ //printf("\n*** %s = %d\n", what, value);
+ if (!g_cc) {
+ //printf("\n *** could not get color control interface!!!\n");
+ return VO_NOTIMPL;
+ }
+
+ if (!strcmp(what, "brightness")) {
+ dcc.dwFlags = DDCOLOR_BRIGHTNESS;
+ dcc.lBrightness = (value + 100) * 10000 / 200;
+ r = VO_TRUE;
+ } else if (!strcmp(what, "contrast")) {
+ dcc.dwFlags = DDCOLOR_CONTRAST;
+ dcc.lContrast = (value + 100) * 20000 / 200;
+ r = VO_TRUE;
+ } else if (!strcmp(what, "hue")) {
+ dcc.dwFlags = DDCOLOR_HUE;
+ dcc.lHue = value * 180 / 100;
+ r = VO_TRUE;
+ } else if (!strcmp(what, "saturation")) {
+ dcc.dwFlags = DDCOLOR_SATURATION;
+ dcc.lSaturation = (value + 100) * 20000 / 200;
+ r = VO_TRUE;
+ }
+
+ if (r == VO_TRUE) {
+ g_cc->lpVtbl->SetColorControls(g_cc, &dcc);
+ }
+ return r;
}
//analoguous to color_ctrl_set
static uint32_t color_ctrl_get(const char *what, int *value)
{
- uint32_t r = VO_NOTIMPL;
- DDCOLORCONTROL dcc;
- if (!g_cc) {
- //printf("\n *** could not get color control interface!!!\n");
- return VO_NOTIMPL;
- }
- ZeroMemory(&dcc, sizeof(dcc));
- dcc.dwSize = sizeof(dcc);
-
- if (g_cc->lpVtbl->GetColorControls(g_cc, &dcc) != DD_OK) {
- return r;
- }
-
- if (!strcmp(what, "brightness") && (dcc.dwFlags & DDCOLOR_BRIGHTNESS)) {
- *value = dcc.lBrightness * 200 / 10000 - 100;
- r = VO_TRUE;
- } else if (!strcmp(what, "contrast") && (dcc.dwFlags & DDCOLOR_CONTRAST)) {
- *value = dcc.lContrast * 200 / 20000 - 100;
- r = VO_TRUE;
- } else if (!strcmp(what, "hue") && (dcc.dwFlags & DDCOLOR_HUE)) {
- *value = dcc.lHue * 100 / 180;
- r = VO_TRUE;
- } else if (!strcmp(what, "saturation") && (dcc.dwFlags & DDCOLOR_SATURATION)) {
- *value = dcc.lSaturation * 200 / 20000 - 100;
- r = VO_TRUE;
- }
+ uint32_t r = VO_NOTIMPL;
+ DDCOLORCONTROL dcc = { .dwSize = sizeof(dcc) };
+ if (!g_cc) {
+ //printf("\n *** could not get color control interface!!!\n");
+ return VO_NOTIMPL;
+ }
+
+ if (g_cc->lpVtbl->GetColorControls(g_cc, &dcc) != DD_OK) {
+ return r;
+ }
+
+ if (!strcmp(what, "brightness") && (dcc.dwFlags & DDCOLOR_BRIGHTNESS)) {
+ *value = dcc.lBrightness * 200 / 10000 - 100;
+ r = VO_TRUE;
+ } else if (!strcmp(what, "contrast") && (dcc.dwFlags & DDCOLOR_CONTRAST)) {
+ *value = dcc.lContrast * 200 / 20000 - 100;
+ r = VO_TRUE;
+ } else if (!strcmp(what, "hue") && (dcc.dwFlags & DDCOLOR_HUE)) {
+ *value = dcc.lHue * 100 / 180;
+ r = VO_TRUE;
+ } else if (!strcmp(what, "saturation") && (dcc.dwFlags & DDCOLOR_SATURATION)) {
+ *value = dcc.lSaturation * 200 / 20000 - 100;
+ r = VO_TRUE;
+ }
// printf("\n*** %s = %d\n", what, *value);
- return r;
+ return r;
}
static int control(uint32_t request, void *data)
{
switch (request) {
-
- case VOCTRL_GET_IMAGE:
- return get_image(data);
+ case VOCTRL_GET_IMAGE:
+ return get_image(data);
case VOCTRL_QUERY_FORMAT:
- return query_format(*((uint32_t*)data));
- case VOCTRL_DRAW_IMAGE:
+ return query_format(*(uint32_t *)data);
+ case VOCTRL_DRAW_IMAGE:
return put_image(data);
case VOCTRL_BORDER:
- vo_w32_border();
+ vo_w32_border(global_vo);
Directx_ManageDisplay();
- return VO_TRUE;
+ return VO_TRUE;
case VOCTRL_ONTOP:
- vo_w32_ontop();
- return VO_TRUE;
+ vo_w32_ontop(global_vo);
+ return VO_TRUE;
case VOCTRL_ROOTWIN:
- if(WinID != -1) return VO_TRUE;
- if(vidmode)
- {
- mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>rootwin has no meaning in exclusive mode\n");
- }
- else
- {
- if(vo_rootwin) vo_rootwin = 0;
- else vo_rootwin = 1;
- Directx_ManageDisplay();
- }
- return VO_TRUE;
- case VOCTRL_FULLSCREEN:
- {
- vo_w32_fullscreen();
+ if (WinID != -1)
+ return VO_TRUE;
+ if (vidmode) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>rootwin has no meaning in exclusive mode\n");
+ } else {
+ if (vo_rootwin)
+ vo_rootwin = 0;
+ else
+ vo_rootwin = 1;
Directx_ManageDisplay();
- return VO_TRUE;
- }
- case VOCTRL_SET_EQUALIZER: {
- struct voctrl_set_equalizer_args *args = data;
- return color_ctrl_set(args->name, args->value);
- }
- case VOCTRL_GET_EQUALIZER: {
- struct voctrl_get_equalizer_args *args = data;
- return color_ctrl_get(args->name, args->valueptr);
- }
+ }
+ return VO_TRUE;
+ case VOCTRL_FULLSCREEN:
+ vo_w32_fullscreen(global_vo);
+ Directx_ManageDisplay();
+ return VO_TRUE;
+ case VOCTRL_SET_EQUALIZER: {
+ struct voctrl_set_equalizer_args *args = data;
+ return color_ctrl_set(args->name, args->value);
+ }
+ case VOCTRL_GET_EQUALIZER: {
+ struct voctrl_get_equalizer_args *args = data;
+ return color_ctrl_get(args->name, args->valueptr);
+ }
case VOCTRL_UPDATE_SCREENINFO:
- w32_update_xinerama_info();
+ w32_update_xinerama_info(global_vo);
return VO_TRUE;
- };
+ }
return VO_NOTIMPL;
}
diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c
index 12749426ab..f72f1ae70c 100644
--- a/libvo/vo_gl.c
+++ b/libvo/vo_gl.c
@@ -38,30 +38,27 @@
#include "osd.h"
#include "sub/font_load.h"
#include "sub/sub.h"
+#include "eosd_packer.h"
#include "gl_common.h"
#include "aspect.h"
#include "fastmemcpy.h"
#include "sub/ass_mp.h"
-static int preinit_nosw(struct vo *vo, const char *arg);
-
//! How many parts the OSD may consist of at most
#define MAX_OSD_PARTS 20
-#define LARGE_EOSD_TEX_SIZE 512
-#define TINYTEX_SIZE 16
-#define TINYTEX_COLS (LARGE_EOSD_TEX_SIZE / TINYTEX_SIZE)
-#define TINYTEX_MAX (TINYTEX_COLS * TINYTEX_COLS)
-#define SMALLTEX_SIZE 32
-#define SMALLTEX_COLS (LARGE_EOSD_TEX_SIZE / SMALLTEX_SIZE)
-#define SMALLTEX_MAX (SMALLTEX_COLS * SMALLTEX_COLS)
-
//for gl_priv.use_yuv
#define MASK_ALL_YUV (~(1 << YUV_CONVERSION_NONE))
#define MASK_NOT_COMBINERS (~((1 << YUV_CONVERSION_NONE) | (1 << YUV_CONVERSION_COMBINERS)))
#define MASK_GAMMA_SUPPORT (MASK_NOT_COMBINERS & ~(1 << YUV_CONVERSION_FRAGMENT))
+struct vertex_eosd {
+ float x, y;
+ uint8_t color[4];
+ float u, v;
+};
+
struct gl_priv {
MPGLContext *glctx;
GL *gl;
@@ -74,17 +71,17 @@ struct gl_priv {
//! Alpha textures for OSD
GLuint osdatex[MAX_OSD_PARTS];
#endif
- GLuint *eosdtex;
- GLuint largeeosdtex[2];
+ GLuint eosd_texture;
+ int eosd_texture_width, eosd_texture_height;
+ struct eosd_packer *eosd;
+ struct vertex_eosd *eosd_va;
//! Display lists that draw the OSD parts
GLuint osdDispList[MAX_OSD_PARTS];
#ifndef FAST_OSD
GLuint osdaDispList[MAX_OSD_PARTS];
#endif
- GLuint eosdDispList;
//! How many parts the OSD currently consists of
int osdtexCnt;
- int eosdtexCnt;
int osd_color;
int use_ycbcr;
@@ -94,6 +91,7 @@ struct gl_priv {
int lscale;
int cscale;
float filter_strength;
+ float noise_strength;
int yuvconvtype;
int use_rectangle;
int err_shown;
@@ -215,7 +213,8 @@ static void update_yuvconv(struct vo *vo)
mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
gl_conversion_params_t params = {
p->target, p->yuvconvtype, cparams,
- p->texture_width, p->texture_height, 0, 0, p->filter_strength
+ p->texture_width, p->texture_height, 0, 0, p->filter_strength,
+ p->noise_strength
};
mp_get_chroma_shift(p->image_format, &xs, &ys, &depth);
params.chrom_texw = params.texw >> xs;
@@ -286,150 +285,111 @@ static void clearOSD(struct vo *vo)
}
/**
- * \brief remove textures, display list and free memory used by EOSD
+ * \brief construct display list from ass image list
+ * \param img image list to create OSD from.
*/
-static void clearEOSD(struct vo *vo)
+static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
- if (p->eosdDispList)
- gl->DeleteLists(p->eosdDispList, 1);
- p->eosdDispList = 0;
- if (p->eosdtexCnt)
- gl->DeleteTextures(p->eosdtexCnt, p->eosdtex);
- p->eosdtexCnt = 0;
- free(p->eosdtex);
- p->eosdtex = NULL;
-}
+ bool need_repos, need_upload, need_allocate;
+ eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload,
+ &need_allocate);
-static inline int is_tinytex(ASS_Image *i, int tinytexcur)
-{
- return i->w < TINYTEX_SIZE && i->h < TINYTEX_SIZE
- && tinytexcur < TINYTEX_MAX;
-}
+ if (!need_repos)
+ return;
-static inline int is_smalltex(ASS_Image *i, int smalltexcur)
-{
- return i->w < SMALLTEX_SIZE && i->h < SMALLTEX_SIZE
- && smalltexcur < SMALLTEX_MAX;
-}
+ if (!p->eosd_texture)
+ gl->GenTextures(1, &p->eosd_texture);
-static inline void tinytex_pos(int tinytexcur, int *x, int *y)
-{
- *x = (tinytexcur % TINYTEX_COLS) * TINYTEX_SIZE;
- *y = (tinytexcur / TINYTEX_COLS) * TINYTEX_SIZE;
-}
+ gl->BindTexture(p->target, p->eosd_texture);
-static inline void smalltex_pos(int smalltexcur, int *x, int *y)
-{
- *x = (smalltexcur % SMALLTEX_COLS) * SMALLTEX_SIZE;
- *y = (smalltexcur / SMALLTEX_COLS) * SMALLTEX_SIZE;
+ if (need_allocate) {
+ texSize(vo, p->eosd->surface.w, p->eosd->surface.h,
+ &p->eosd_texture_width, &p->eosd_texture_height);
+ // xxx it doesn't need to be cleared, that's a waste of time
+ glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE,
+ GL_NEAREST, p->eosd_texture_width,
+ p->eosd_texture_height, 0);
+ }
+
+ // 2 triangles primitives per quad = 6 vertices per quad
+ // not using GL_QUADS, as it is deprecated in OpenGL 3.x and later
+ p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va,
+ p->eosd->targets_count
+ * sizeof(struct vertex_eosd) * 6);
+
+ float eosd_w = p->eosd_texture_width;
+ float eosd_h = p->eosd_texture_height;
+
+ if (p->use_rectangle == 1)
+ eosd_w = eosd_h = 1.0f;
+
+ for (int n = 0; n < p->eosd->targets_count; n++) {
+ struct eosd_target *target = &p->eosd->targets[n];
+ ASS_Image *i = target->ass_img;
+
+ if (need_upload) {
+ glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap,
+ i->stride, target->source.x0, target->source.y0,
+ i->w, i->h, 0);
+ }
+
+ uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff,
+ (i->color >> 8) & 0xff, 255 - (i->color & 0xff) };
+
+ float x0 = target->dest.x0;
+ float y0 = target->dest.y0;
+ float x1 = target->dest.x1;
+ float y1 = target->dest.y1;
+ float tx0 = target->source.x0 / eosd_w;
+ float ty0 = target->source.y0 / eosd_h;
+ float tx1 = target->source.x1 / eosd_w;
+ float ty1 = target->source.y1 / eosd_h;
+
+#define COLOR_INIT {color[0], color[1], color[2], color[3]}
+ struct vertex_eosd *va = &p->eosd_va[n * 6];
+ va[0] = (struct vertex_eosd) { x0, y0, COLOR_INIT, tx0, ty0 };
+ va[1] = (struct vertex_eosd) { x0, y1, COLOR_INIT, tx0, ty1 };
+ va[2] = (struct vertex_eosd) { x1, y0, COLOR_INIT, tx1, ty0 };
+ va[3] = (struct vertex_eosd) { x1, y1, COLOR_INIT, tx1, ty1 };
+ va[4] = va[2];
+ va[5] = va[1];
+#undef COLOR_INIT
+ }
+
+ gl->BindTexture(p->target, 0);
}
-/**
- * \brief construct display list from ass image list
- * \param img image list to create OSD from.
- * A value of NULL has the same effect as clearEOSD()
- */
-static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs)
+// Note: relies on state being setup, like projection matrix and blending
+static void drawEOSD(struct vo *vo)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
- int sx, sy;
- int tinytexcur = 0;
- int smalltexcur = 0;
- GLuint *curtex;
- GLint scale_type = p->scaled_osd ? GL_LINEAR : GL_NEAREST;
- ASS_Image *img = imgs->imgs;
- ASS_Image *i;
-
- if (imgs->changed == 0) // there are elements, but they are unchanged
+ if (p->eosd->targets_count == 0)
return;
- if (img && imgs->changed == 1) // there are elements, but they just moved
- goto skip_upload;
- clearEOSD(vo);
- if (!img)
- return;
- if (!p->largeeosdtex[0]) {
- gl->GenTextures(2, p->largeeosdtex);
- for (int n = 0; n < 2; n++) {
- gl->BindTexture(p->target, p->largeeosdtex[n]);
- glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA,
- GL_UNSIGNED_BYTE, scale_type,
- LARGE_EOSD_TEX_SIZE, LARGE_EOSD_TEX_SIZE, 0);
- }
- }
- for (i = img; i; i = i->next) {
- if (i->w <= 0 || i->h <= 0 || i->stride < i->w)
- continue;
- if (is_tinytex(i, tinytexcur))
- tinytexcur++;
- else if (is_smalltex(i, smalltexcur))
- smalltexcur++;
- else
- p->eosdtexCnt++;
- }
- mp_msg(MSGT_VO, MSGL_DBG2, "EOSD counts (tiny, small, all): %i, %i, %i\n",
- tinytexcur, smalltexcur, p->eosdtexCnt);
- if (p->eosdtexCnt) {
- p->eosdtex = calloc(p->eosdtexCnt, sizeof(GLuint));
- gl->GenTextures(p->eosdtexCnt, p->eosdtex);
- }
- tinytexcur = smalltexcur = 0;
- for (i = img, curtex = p->eosdtex; i; i = i->next) {
- int x = 0, y = 0;
- if (i->w <= 0 || i->h <= 0 || i->stride < i->w) {
- mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n");
- continue;
- }
- if (is_tinytex(i, tinytexcur)) {
- tinytex_pos(tinytexcur, &x, &y);
- gl->BindTexture(p->target, p->largeeosdtex[0]);
- tinytexcur++;
- } else if (is_smalltex(i, smalltexcur)) {
- smalltex_pos(smalltexcur, &x, &y);
- gl->BindTexture(p->target, p->largeeosdtex[1]);
- smalltexcur++;
- } else {
- texSize(vo, i->w, i->h, &sx, &sy);
- gl->BindTexture(p->target, *curtex++);
- glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA,
- GL_UNSIGNED_BYTE, scale_type, sx, sy, 0);
- }
- glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap,
- i->stride, x, y, i->w, i->h, 0);
- }
- p->eosdDispList = gl->GenLists(1);
-skip_upload:
- gl->NewList(p->eosdDispList, GL_COMPILE);
- tinytexcur = smalltexcur = 0;
- for (i = img, curtex = p->eosdtex; i; i = i->next) {
- int x = 0, y = 0;
- if (i->w <= 0 || i->h <= 0 || i->stride < i->w)
- continue;
- gl->Color4ub(i->color >> 24, (i->color >> 16) & 0xff,
- (i->color >> 8) & 0xff, 255 - (i->color & 0xff));
- if (is_tinytex(i, tinytexcur)) {
- tinytex_pos(tinytexcur, &x, &y);
- sx = sy = LARGE_EOSD_TEX_SIZE;
- gl->BindTexture(p->target, p->largeeosdtex[0]);
- tinytexcur++;
- } else if (is_smalltex(i, smalltexcur)) {
- smalltex_pos(smalltexcur, &x, &y);
- sx = sy = LARGE_EOSD_TEX_SIZE;
- gl->BindTexture(p->target, p->largeeosdtex[1]);
- smalltexcur++;
- } else {
- texSize(vo, i->w, i->h, &sx, &sy);
- gl->BindTexture(p->target, *curtex++);
- }
- glDrawTex(gl, i->dst_x, i->dst_y, i->w, i->h, x, y, i->w, i->h, sx, sy,
- p->use_rectangle == 1, 0, 0);
- }
- gl->EndList();
+ gl->BindTexture(p->target, p->eosd_texture);
+
+ struct vertex_eosd *va = p->eosd_va;
+ size_t stride = sizeof(struct vertex_eosd);
+
+ gl->VertexPointer(2, GL_FLOAT, stride, &va[0].x);
+ gl->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &va[0].color[0]);
+ gl->TexCoordPointer(2, GL_FLOAT, stride, &va[0].u);
+
+ gl->EnableClientState(GL_VERTEX_ARRAY);
+ gl->EnableClientState(GL_TEXTURE_COORD_ARRAY);
+ gl->EnableClientState(GL_COLOR_ARRAY);
+
+ gl->DrawArrays(GL_TRIANGLES, 0, p->eosd->targets_count * 6);
+
+ gl->DisableClientState(GL_VERTEX_ARRAY);
+ gl->DisableClientState(GL_TEXTURE_COORD_ARRAY);
+ gl->DisableClientState(GL_COLOR_ARRAY);
+
gl->BindTexture(p->target, 0);
}
@@ -451,10 +411,10 @@ static void uninitGl(struct vo *vo)
gl->DeleteTextures(i, p->default_texs);
p->default_texs[0] = 0;
clearOSD(vo);
- clearEOSD(vo);
- if (p->largeeosdtex[0])
- gl->DeleteTextures(2, p->largeeosdtex);
- p->largeeosdtex[0] = 0;
+ if (p->eosd_texture)
+ gl->DeleteTextures(1, &p->eosd_texture);
+ eosd_packer_reinit(p->eosd, 0, 0);
+ p->eosd_texture = 0;
if (gl->DeleteBuffers && p->buffer)
gl->DeleteBuffers(1, &p->buffer);
p->buffer = 0;
@@ -472,8 +432,10 @@ static int isSoftwareGl(struct vo *vo)
{
struct gl_priv *p = vo->priv;
const char *renderer = p->gl->GetString(GL_RENDERER);
+ const char *vendor = p->gl->GetString(GL_VENDOR);
return !renderer || strcmp(renderer, "Software Rasterizer") == 0 ||
- strstr(renderer, "llvmpipe");
+ strstr(renderer, "llvmpipe") ||
+ strcmp(vendor, "Microsoft Corporation") == 0;
}
static void autodetectGlExtensions(struct vo *vo)
@@ -634,6 +596,10 @@ static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height)
update_yuvconv(vo);
}
+ GLint max_texture_size;
+ gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+ eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size);
+
resize(vo, d_width, d_height);
gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -787,7 +753,7 @@ static void do_render_osd(struct vo *vo, int type)
GL *gl = p->gl;
int draw_osd = (type & RENDER_OSD) && p->osdtexCnt > 0;
- int draw_eosd = (type & RENDER_EOSD) && p->eosdDispList;
+ int draw_eosd = (type & RENDER_EOSD);
if (!draw_osd && !draw_eosd)
return;
// set special rendering parameters
@@ -800,7 +766,7 @@ static void do_render_osd(struct vo *vo, int type)
gl->Enable(GL_BLEND);
if (draw_eosd) {
gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- gl->CallList(p->eosdDispList);
+ drawEOSD(vo);
}
if (draw_osd) {
gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff,
@@ -848,7 +814,7 @@ static void do_render(struct vo *vo)
// Enable(GL_TEXTURE_2D);
// BindTexture(GL_TEXTURE_2D, texture_id);
- gl->Color3f(1, 1, 1);
+ gl->Color4f(1, 1, 1, 1);
if (p->is_yuv || p->custom_prog)
glEnableYUVConversion(gl, p->target, p->yuvconvtype);
if (p->stereo_mode) {
@@ -1092,7 +1058,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
slice = 0; // always "upload" full texture
}
glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[0],
- stride[0], mpi->x, mpi->y, w, h, slice);
+ stride[0], 0, 0, w, h, slice);
if (p->is_yuv) {
int xs, ys;
mp_get_chroma_shift(p->image_format, &xs, &ys, NULL);
@@ -1103,8 +1069,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
}
gl->ActiveTexture(GL_TEXTURE1);
glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[1],
- stride[1], mpi->x >> xs, mpi->y >> ys, w >> xs, h >> ys,
- slice);
+ stride[1], 0, 0, w >> xs, h >> ys, slice);
if ((mpi->flags & MP_IMGFLAG_DIRECT) && !(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) {
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]);
gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
@@ -1112,8 +1077,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
}
gl->ActiveTexture(GL_TEXTURE2);
glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[2],
- stride[2], mpi->x >> xs, mpi->y >> ys, w >> xs, h >> ys,
- slice);
+ stride[2], 0, 0, w >> xs, h >> ys, slice);
gl->ActiveTexture(GL_TEXTURE0);
}
if (mpi->flags & MP_IMGFLAG_DIRECT) {
@@ -1218,8 +1182,12 @@ static void uninit(struct vo *vo)
p->gl = NULL;
}
-static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
- enum MPGLType gltype)
+static int backend_valid(void *arg)
+{
+ return mpgl_find_backend(*(const char **)arg) >= 0;
+}
+
+static int preinit(struct vo *vo, const char *arg)
{
struct gl_priv *p = talloc_zero(vo, struct gl_priv);
vo->priv = p;
@@ -1240,6 +1208,11 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
.osd_color = 0xffffff,
};
+ p->eosd = eosd_packer_create(vo);
+
+ int allow_sw = 0;
+ char *backend_arg = NULL;
+
//essentially unused; for legacy warnings only
int user_colorspace = 0;
int levelconv = -1;
@@ -1256,6 +1229,7 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
{"lscale", OPT_ARG_INT, &p->lscale, int_non_neg},
{"cscale", OPT_ARG_INT, &p->cscale, int_non_neg},
{"filter-strength", OPT_ARG_FLOAT, &p->filter_strength, NULL},
+ {"noise-strength", OPT_ARG_FLOAT, &p->noise_strength, NULL},
{"ati-hack", OPT_ARG_BOOL, &p->ati_hack, NULL},
{"force-pbo", OPT_ARG_BOOL, &p->force_pbo, NULL},
{"glfinish", OPT_ARG_BOOL, &p->use_glFinish, NULL},
@@ -1267,6 +1241,8 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
{"mipmapgen", OPT_ARG_BOOL, &p->mipmap_gen, NULL},
{"osdcolor", OPT_ARG_INT, &p->osd_color, NULL},
{"stereo", OPT_ARG_INT, &p->stereo_mode, NULL},
+ {"sw", OPT_ARG_BOOL, &allow_sw, NULL},
+ {"backend", OPT_ARG_MSTRZ,&backend_arg, backend_valid},
// Removed options.
// They are only parsed to notify the user about the replacements.
{"aspect", OPT_ARG_BOOL, &aspect, NULL},
@@ -1323,6 +1299,8 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
" as lscale but for chroma (2x slower with little visible effect).\n"
" filter-strength=<value>\n"
" set the effect strength for some lscale/cscale filters\n"
+ " noise-strength=<value>\n"
+ " set how much noise to add. 1.0 is suitable for dithering to 6 bit.\n"
" customprog=<filename>\n"
" use a custom YUV conversion program\n"
" customtex=<filename>\n"
@@ -1340,6 +1318,14 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
" 1: side-by-side to red-cyan stereo\n"
" 2: side-by-side to green-magenta stereo\n"
" 3: side-by-side to quadbuffer stereo\n"
+ " sw\n"
+ " allow using a software renderer, if such is detected\n"
+ " backend=<sys>\n"
+ " auto: auto-select (default)\n"
+ " cocoa: Cocoa/OSX\n"
+ " win: Win32/WGL\n"
+ " x11: X11/GLX\n"
+ " sdl: SDL\n"
"\n");
return -1;
}
@@ -1359,7 +1345,11 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
" been removed, using yuv=2 instead.\n");
p->use_yuv = 2;
}
- p->glctx = init_mpglcontext(gltype, vo);
+
+ int backend = backend_arg ? mpgl_find_backend(backend_arg) : GLTYPE_AUTO;
+ free(backend_arg);
+
+ p->glctx = init_mpglcontext(backend, vo);
if (!p->glctx)
goto err_out;
p->gl = p->glctx->gl;
@@ -1383,7 +1373,7 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
// acceleration and so on. Destroy that window to make sure all state
// associated with it is lost.
uninit(vo);
- p->glctx = init_mpglcontext(gltype, vo);
+ p->glctx = init_mpglcontext(backend, vo);
if (!p->glctx)
goto err_out;
p->gl = p->glctx->gl;
@@ -1401,11 +1391,6 @@ err_out:
return -1;
}
-static int preinit(struct vo *vo, const char *arg)
-{
- return preinit_internal(vo, arg, 1, GLTYPE_AUTO);
-}
-
static int control(struct vo *vo, uint32_t request, void *data)
{
struct gl_priv *p = vo->priv;
@@ -1526,11 +1511,9 @@ const struct vo_driver video_out_gl = {
.uninit = uninit,
};
-static int preinit_nosw(struct vo *vo, const char *arg)
-{
- return preinit_internal(vo, arg, 0, GLTYPE_AUTO);
-}
-
+// "-vo gl" used to accept software renderers by default. This is not the case
+// anymore: you have to use "-vo gl:sw" to get this. This means gl and gl_nosw
+// are exactly the same thing now. Keep gl_nosw to not break user configs.
const struct vo_driver video_out_gl_nosw =
{
.is_new = true,
@@ -1540,31 +1523,7 @@ const struct vo_driver video_out_gl_nosw =
"Reimar Doeffinger <Reimar.Doeffinger@gmx.de>",
""
},
- .preinit = preinit_nosw,
- .config = config,
- .control = control,
- .draw_slice = draw_slice,
- .draw_osd = draw_osd,
- .flip_page = flip_page,
- .check_events = check_events,
- .uninit = uninit,
-};
-
-#ifdef CONFIG_GL_SDL
-static int preinit_sdl(struct vo *vo, const char *arg)
-{
- return preinit_internal(vo, arg, 1, GLTYPE_SDL);
-}
-
-const struct vo_driver video_out_gl_sdl = {
- .is_new = true,
- .info = &(const vo_info_t) {
- "OpenGL with SDL",
- "gl_sdl",
- "Reimar Doeffinger <Reimar.Doeffinger@gmx.de>",
- ""
- },
- .preinit = preinit_sdl,
+ .preinit = preinit,
.config = config,
.control = control,
.draw_slice = draw_slice,
@@ -1573,4 +1532,3 @@ const struct vo_driver video_out_gl_sdl = {
.check_events = check_events,
.uninit = uninit,
};
-#endif
diff --git a/libvo/vo_gl3.c b/libvo/vo_gl3.c
new file mode 100644
index 0000000000..adfe636909
--- /dev/null
+++ b/libvo/vo_gl3.c
@@ -0,0 +1,2418 @@
+/*
+ * 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.
+ *
+ * You can alternatively redistribute this file 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdbool.h>
+#include <assert.h>
+#include "config.h"
+
+#include <libavutil/avutil.h>
+
+#ifdef CONFIG_LCMS2
+#include <lcms2.h>
+#include "stream/stream.h"
+#endif
+
+#include "talloc.h"
+#include "bstr.h"
+#include "mp_msg.h"
+#include "subopt-helper.h"
+#include "video_out.h"
+#include "libmpcodecs/vfcap.h"
+#include "libmpcodecs/mp_image.h"
+#include "geometry.h"
+#include "osd.h"
+#include "sub/font_load.h"
+#include "sub/sub.h"
+#include "eosd_packer.h"
+
+#include "gl_common.h"
+#include "filter_kernels.h"
+#include "aspect.h"
+#include "fastmemcpy.h"
+#include "sub/ass_mp.h"
+
+// Generated from libvo/vo_gl3_shaders.glsl
+#include "libvo/vo_gl3_shaders.h"
+
+// How many parts the OSD may consist of at most.
+#define MAX_OSD_PARTS 20
+
+// Pixel width of 1D lookup textures.
+#define LOOKUP_TEXTURE_SIZE 256
+
+// Texture units 0-2 are used by the video, with unit 0 for free use.
+// Units 3-4 are used for scaler LUTs.
+#define TEXUNIT_SCALERS 3
+#define TEXUNIT_3DLUT 5
+#define TEXUNIT_DITHER 6
+
+// lscale/cscale arguments that map directly to shader filter routines.
+// Note that the convolution filters are not included in this list.
+static const char *fixed_scale_filters[] = {
+ "bilinear",
+ "bicubic_fast",
+ "sharpen3",
+ "sharpen5",
+ NULL
+};
+
+struct lut_tex_format {
+ int pixels;
+ GLint internal_format;
+ GLenum format;
+};
+
+// Indexed with filter_kernel->size.
+// This must match the weightsN functions in the shader.
+// Each entry uses (size+3)/4 pixels per LUT entry, and size/pixels components
+// per pixel.
+struct lut_tex_format lut_tex_formats[] = {
+ [2] = {1, GL_RG16F, GL_RG},
+ [4] = {1, GL_RGBA16F, GL_RGBA},
+ [6] = {2, GL_RGB16F, GL_RGB},
+ [8] = {2, GL_RGBA16F, GL_RGBA},
+ [12] = {3, GL_RGBA16F, GL_RGBA},
+ [16] = {4, GL_RGBA16F, GL_RGBA},
+};
+
+// must be sorted, and terminated with 0
+static const int filter_sizes[] = {2, 4, 6, 8, 12, 16, 0};
+
+struct vertex {
+ float position[2];
+ uint8_t color[4];
+ float texcoord[2];
+};
+
+#define VERTEX_ATTRIB_POSITION 0
+#define VERTEX_ATTRIB_COLOR 1
+#define VERTEX_ATTRIB_TEXCOORD 2
+
+// 2 triangles primitives per quad = 6 vertices per quad
+// (GL_QUAD is deprecated, strips can't be used with EOSD image lists)
+#define VERTICES_PER_QUAD 6
+
+struct texplane {
+ int shift_x, shift_y;
+ GLuint gl_texture;
+ int gl_buffer;
+ int buffer_size;
+ void *buffer_ptr;
+};
+
+struct scaler {
+ int index;
+ const char *name;
+ float params[2];
+ struct filter_kernel *kernel;
+ GLuint gl_lut;
+ const char *lut_name;
+
+ // kernel points here
+ struct filter_kernel kernel_storage;
+};
+
+struct fbotex {
+ GLuint fbo;
+ GLuint texture;
+ int tex_w, tex_h; // size of .texture
+ int vp_w, vp_h; // viewport of fbo / used part of the texture
+};
+
+struct gl_priv {
+ struct vo *vo;
+ MPGLContext *glctx;
+ GL *gl;
+ const char *shader_version;
+
+ int use_indirect;
+ int use_gamma;
+ int use_srgb;
+ int use_scale_sep;
+ int use_fancy_downscaling;
+ int use_lut_3d;
+ int use_npot;
+ int use_pbo;
+ int use_glFinish;
+ int use_gl_debug;
+ int use_gl2;
+
+ int dither_depth;
+ int swap_interval;
+ GLint fbo_format;
+ int stereo_mode;
+ int osd_color;
+
+ GLuint vertex_buffer;
+ GLuint vao;
+
+ GLuint osd_program, eosd_program;
+ GLuint indirect_program, scale_sep_program, final_program;
+
+ GLuint osd_textures[MAX_OSD_PARTS];
+ int osd_textures_count;
+ struct vertex osd_va[MAX_OSD_PARTS * VERTICES_PER_QUAD];
+
+ GLuint eosd_texture;
+ int eosd_texture_width, eosd_texture_height;
+ GLuint eosd_buffer;
+ struct vertex *eosd_va;
+ struct eosd_packer *eosd;
+
+ GLuint lut_3d_texture;
+ int lut_3d_w, lut_3d_h, lut_3d_d;
+ void *lut_3d_data;
+
+ GLuint dither_texture;
+ float dither_quantization;
+ float dither_multiply;
+
+ uint32_t image_width;
+ uint32_t image_height;
+ uint32_t image_format;
+ int texture_width;
+ int texture_height;
+
+ bool is_yuv;
+ bool is_linear_rgb;
+
+ // per pixel (full pixel when packed, each component when planar)
+ int plane_bytes;
+ int plane_bits;
+ int component_bits;
+
+ GLint gl_internal_format;
+ GLenum gl_format;
+ GLenum gl_type;
+
+ int plane_count;
+ struct texplane planes[3];
+
+ struct fbotex indirect_fbo; // RGB target
+ struct fbotex scale_sep_fbo; // first pass when doing 2 pass scaling
+
+ // state for luma (0) and chroma (1) scalers
+ struct scaler scalers[2];
+ // luma scaler parameters (the same are used for chroma)
+ float scaler_params[2];
+
+ struct mp_csp_details colorspace;
+ struct mp_csp_equalizer video_eq;
+
+ int mpi_flipped;
+ int vo_flipped;
+
+ struct vo_rect src_rect; // displayed part of the source video
+ struct vo_rect dst_rect; // video rectangle on output window
+ int border_x, border_y; // OSD borders
+ int vp_x, vp_y, vp_w, vp_h; // GL viewport
+};
+
+struct fmt_entry {
+ int mp_format;
+ GLint internal_format;
+ GLenum format;
+ int component_bits;
+ GLenum type;
+};
+
+static const struct fmt_entry mp_to_gl_formats[] = {
+ {IMGFMT_RGB48NE, GL_RGB16, GL_RGB, 16, GL_UNSIGNED_SHORT},
+ {IMGFMT_RGB24, GL_RGB, GL_RGB, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_RGBA, GL_RGBA, GL_RGBA, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_RGB15, GL_RGBA, GL_RGBA, 5, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ {IMGFMT_RGB16, GL_RGB, GL_RGB, 6, GL_UNSIGNED_SHORT_5_6_5_REV},
+ {IMGFMT_BGR15, GL_RGBA, GL_BGRA, 5, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ {IMGFMT_BGR16, GL_RGB, GL_RGB, 6, GL_UNSIGNED_SHORT_5_6_5},
+ {IMGFMT_BGR24, GL_RGB, GL_BGR, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_BGRA, GL_RGBA, GL_BGRA, 8, GL_UNSIGNED_BYTE},
+ {0},
+};
+
+
+static const char help_text[];
+
+static void uninit_rendering(struct gl_priv *p);
+static void delete_shaders(struct gl_priv *p);
+
+
+static void default_tex_params(struct GL *gl, GLenum target, GLint filter)
+{
+ gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+static void debug_check_gl(struct gl_priv *p, const char *msg)
+{
+ if (p->use_gl_debug)
+ glCheckError(p->gl, msg);
+}
+
+static void tex_size(struct gl_priv *p, int w, int h, int *texw, int *texh)
+{
+ if (p->use_npot) {
+ *texw = w;
+ *texh = h;
+ } else {
+ *texw = 32;
+ while (*texw < w)
+ *texw *= 2;
+ *texh = 32;
+ while (*texh < h)
+ *texh *= 2;
+ }
+}
+
+static void draw_triangles(struct gl_priv *p, struct vertex *vb, int vert_count)
+{
+ GL *gl = p->gl;
+
+ assert(vert_count % 3 == 0);
+
+ gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer);
+ gl->BufferData(GL_ARRAY_BUFFER, vert_count * sizeof(struct vertex), vb,
+ GL_DYNAMIC_DRAW);
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
+
+ gl->BindVertexArray(p->vao);
+ gl->DrawArrays(GL_TRIANGLES, 0, vert_count);
+ gl->BindVertexArray(0);
+
+ debug_check_gl(p, "after rendering");
+}
+
+// Write a textured quad to a vertex array.
+// va = destination vertex array, VERTICES_PER_QUAD entries will be overwritten
+// x0, y0, x1, y1 = destination coordinates of the quad
+// tx0, ty0, tx1, ty1 = source texture coordinates (usually in pixels)
+// texture_w, texture_h = size of the texture, or an inverse factor
+// color = optional color for all vertices, NULL for opaque white
+// flip = flip vertically
+static void write_quad(struct vertex *va,
+ float x0, float y0, float x1, float y1,
+ float tx0, float ty0, float tx1, float ty1,
+ float texture_w, float texture_h,
+ const uint8_t color[4], bool flip)
+{
+ static const uint8_t white[4] = { 255, 255, 255, 255 };
+
+ if (!color)
+ color = white;
+
+ tx0 /= texture_w;
+ ty0 /= texture_h;
+ tx1 /= texture_w;
+ ty1 /= texture_h;
+
+ if (flip) {
+ float tmp = ty0;
+ ty0 = ty1;
+ ty1 = tmp;
+ }
+
+#define COLOR_INIT {color[0], color[1], color[2], color[3]}
+ va[0] = (struct vertex) { {x0, y0}, COLOR_INIT, {tx0, ty0} };
+ va[1] = (struct vertex) { {x0, y1}, COLOR_INIT, {tx0, ty1} };
+ va[2] = (struct vertex) { {x1, y0}, COLOR_INIT, {tx1, ty0} };
+ va[3] = (struct vertex) { {x1, y1}, COLOR_INIT, {tx1, ty1} };
+ va[4] = va[2];
+ va[5] = va[1];
+#undef COLOR_INIT
+}
+
+static void fbotex_init(struct gl_priv *p, struct fbotex *fbo, int w, int h)
+{
+ GL *gl = p->gl;
+
+ assert(!fbo->fbo);
+ assert(!fbo->texture);
+
+ tex_size(p, w, h, &fbo->tex_w, &fbo->tex_h);
+
+ fbo->vp_w = w;
+ fbo->vp_h = h;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
+
+ gl->GenFramebuffers(1, &fbo->fbo);
+ gl->GenTextures(1, &fbo->texture);
+ gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, p->fbo_format, fbo->tex_w, fbo->tex_h, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, fbo->texture, 0);
+
+ if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER)
+ != GL_FRAMEBUFFER_COMPLETE)
+ {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Error: framebuffer completeness "
+ "check failed!\n");
+ }
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ debug_check_gl(p, "after creating framebuffer & associated texture");
+}
+
+static void fbotex_uninit(struct gl_priv *p, struct fbotex *fbo)
+{
+ GL *gl = p->gl;
+
+ gl->DeleteFramebuffers(1, &fbo->fbo);
+ gl->DeleteTextures(1, &fbo->texture);
+ *fbo = (struct fbotex) {0};
+}
+
+static void matrix_ortho2d(float m[3][3], float x0, float x1,
+ float y0, float y1)
+{
+ memset(m, 0, 9 * sizeof(float));
+ m[0][0] = 2.0f / (x1 - x0);
+ m[1][1] = 2.0f / (y1 - y0);
+ m[2][0] = -(x1 + x0) / (x1 - x0);
+ m[2][1] = -(y1 + y0) / (y1 - y0);
+ m[2][2] = 1.0f;
+}
+
+static void update_uniforms(struct gl_priv *p, GLuint program)
+{
+ GL *gl = p->gl;
+ GLint loc;
+
+ if (program == 0)
+ return;
+
+ gl->UseProgram(program);
+
+ struct mp_csp_params cparams = {
+ .colorspace = p->colorspace,
+ .input_bits = p->plane_bits,
+ .texture_bits = (p->plane_bits + 7) & ~7,
+ };
+ mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
+
+ loc = gl->GetUniformLocation(program, "transform");
+ if (loc >= 0) {
+ float matrix[3][3];
+ matrix_ortho2d(matrix, 0, p->vp_w, p->vp_h, 0);
+ gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]);
+ }
+
+ loc = gl->GetUniformLocation(program, "colormatrix");
+ if (loc >= 0) {
+ float yuv2rgb[3][4] = {{0}};
+ if (p->is_yuv)
+ mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb);
+ gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &yuv2rgb[0][0]);
+ }
+
+ gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"),
+ 1.0 / cparams.rgamma,
+ 1.0 / cparams.ggamma,
+ 1.0 / cparams.bgamma);
+
+ gl->Uniform1i(gl->GetUniformLocation(program, "texture1"), 0);
+ gl->Uniform1i(gl->GetUniformLocation(program, "texture2"), 1);
+ gl->Uniform1i(gl->GetUniformLocation(program, "texture3"), 2);
+
+ gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT);
+
+ for (int n = 0; n < 2; n++) {
+ const char *lut = p->scalers[n].lut_name;
+ if (lut)
+ gl->Uniform1i(gl->GetUniformLocation(program, lut),
+ TEXUNIT_SCALERS + n);
+ }
+
+ gl->Uniform1i(gl->GetUniformLocation(program, "dither"), TEXUNIT_DITHER);
+ gl->Uniform1f(gl->GetUniformLocation(program, "dither_quantization"),
+ p->dither_quantization);
+ gl->Uniform1f(gl->GetUniformLocation(program, "dither_multiply"),
+ p->dither_multiply);
+
+ float sparam1 = p->scaler_params[0];
+ gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1"),
+ isnan(sparam1) ? 0.5f : sparam1);
+
+ gl->UseProgram(0);
+
+ debug_check_gl(p, "update_uniforms()");
+}
+
+static void update_all_uniforms(struct gl_priv *p)
+{
+ update_uniforms(p, p->osd_program);
+ update_uniforms(p, p->eosd_program);
+ update_uniforms(p, p->indirect_program);
+ update_uniforms(p, p->scale_sep_program);
+ update_uniforms(p, p->final_program);
+}
+
+#define SECTION_HEADER "#!section "
+
+static char *get_section(void *talloc_ctx, struct bstr source,
+ const char *section)
+{
+ char *res = talloc_strdup(talloc_ctx, "");
+ bool copy = false;
+ while (source.len) {
+ struct bstr line = bstr_strip_linebreaks(bstr_getline(source, &source));
+ if (bstr_eatstart(&line, bstr(SECTION_HEADER))) {
+ copy = bstrcmp0(line, section) == 0;
+ } else if (copy) {
+ res = talloc_asprintf_append_buffer(res, "%.*s\n", BSTR_P(line));
+ }
+ }
+ return res;
+}
+
+static char *t_concat(void *talloc_ctx, const char *s1, const char *s2)
+{
+ return talloc_asprintf(talloc_ctx, "%s%s", s1, s2);
+}
+
+static GLuint create_shader(GL *gl, GLenum type, const char *header,
+ const char *source)
+{
+ void *tmp = talloc_new(NULL);
+ const char *full_source = t_concat(tmp, header, source);
+
+ GLuint shader = gl->CreateShader(type);
+ gl->ShaderSource(shader, 1, &full_source, NULL);
+ gl->CompileShader(shader);
+ GLint status;
+ gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ GLint log_length;
+ gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+
+ int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DBG2) : MSGL_ERR;
+ const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
+ if (mp_msg_test(MSGT_VO, pri)) {
+ mp_msg(MSGT_VO, pri, "[gl] %s shader source:\n", typestr);
+ mp_log_source(MSGT_VO, pri, full_source);
+ }
+ if (log_length > 1) {
+ GLchar *log = talloc_zero_size(tmp, log_length + 1);
+ gl->GetShaderInfoLog(shader, log_length, NULL, log);
+ mp_msg(MSGT_VO, pri, "[gl] %s shader compile log (status=%d):\n%s\n",
+ typestr, status, log);
+ }
+
+ talloc_free(tmp);
+
+ return shader;
+}
+
+static void prog_create_shader(GL *gl, GLuint program, GLenum type,
+ const char *header, const char *source)
+{
+ GLuint shader = create_shader(gl, type, header, source);
+ gl->AttachShader(program, shader);
+ gl->DeleteShader(shader);
+}
+
+static void link_shader(GL *gl, GLuint program)
+{
+ gl->LinkProgram(program);
+ GLint status;
+ gl->GetProgramiv(program, GL_LINK_STATUS, &status);
+ GLint log_length;
+ gl->GetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+
+ int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DBG2) : MSGL_ERR;
+ if (mp_msg_test(MSGT_VO, pri)) {
+ GLchar *log = talloc_zero_size(NULL, log_length + 1);
+ gl->GetProgramInfoLog(program, log_length, NULL, log);
+ mp_msg(MSGT_VO, pri, "[gl] shader link log (status=%d): %s\n",
+ status, log);
+ talloc_free(log);
+ }
+}
+
+static void bind_attrib_locs(GL *gl, GLuint program)
+{
+ gl->BindAttribLocation(program, VERTEX_ATTRIB_POSITION, "vertex_position");
+ gl->BindAttribLocation(program, VERTEX_ATTRIB_COLOR, "vertex_color");
+ gl->BindAttribLocation(program, VERTEX_ATTRIB_TEXCOORD, "vertex_texcoord");
+}
+
+static GLuint create_program(GL *gl, const char *name, const char *header,
+ const char *vertex, const char *frag)
+{
+ mp_msg(MSGT_VO, MSGL_V, "[gl] compiling shader program '%s'\n", name);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] header:\n");
+ mp_log_source(MSGT_VO, MSGL_V, header);
+ GLuint prog = gl->CreateProgram();
+ prog_create_shader(gl, prog, GL_VERTEX_SHADER, header, vertex);
+ prog_create_shader(gl, prog, GL_FRAGMENT_SHADER, header, frag);
+ bind_attrib_locs(gl, prog);
+ link_shader(gl, prog);
+ return prog;
+}
+
+static void shader_def(char **shader, const char *name,
+ const char *value)
+{
+ *shader = talloc_asprintf_append(*shader, "#define %s %s\n", name, value);
+}
+
+static void shader_def_opt(char **shader, const char *name, bool b)
+{
+ if (b)
+ shader_def(shader, name, "1");
+}
+
+static void shader_setup_scaler(char **shader, struct scaler *scaler, int pass)
+{
+ const char *target = scaler->index == 0 ? "SAMPLE_L" : "SAMPLE_C";
+ if (!scaler->kernel) {
+ *shader = talloc_asprintf_append(*shader, "#define %s sample_%s\n",
+ target, scaler->name);
+ } else {
+ int size = scaler->kernel->size;
+ if (pass != -1) {
+ // The direction/pass assignment is rather arbitrary, but fixed in
+ // other parts of the code (like FBO setup).
+ const char *direction = pass == 0 ? "0, 1" : "1, 0";
+ *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1) "
+ "sample_convolution_sep%d(vec2(%s), %s, p0, p1)\n",
+ target, size, direction, scaler->lut_name);
+ } else {
+ *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1) "
+ "sample_convolution%d(%s, p0, p1)\n",
+ target, size, scaler->lut_name);
+ }
+ }
+}
+
+// return false if RGB or 4:4:4 YUV
+static bool input_is_subsampled(struct gl_priv *p)
+{
+ for (int i = 0; i < p->plane_count; i++)
+ if (p->planes[i].shift_x || p->planes[i].shift_y)
+ return true;
+ return false;
+}
+
+static void compile_shaders(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ delete_shaders(p);
+
+ void *tmp = talloc_new(NULL);
+
+ struct bstr src = { (char*)vo_gl3_shaders, sizeof(vo_gl3_shaders) };
+ char *vertex_shader = get_section(tmp, src, "vertex_all");
+ char *shader_prelude = get_section(tmp, src, "prelude");
+ char *s_video = get_section(tmp, src, "frag_video");
+ char *s_eosd = get_section(tmp, src, "frag_eosd");
+ char *s_osd = get_section(tmp, src, "frag_osd");
+
+ char *header = talloc_asprintf(tmp, "#version %s\n%s", p->shader_version,
+ shader_prelude);
+
+ char *header_eosd = talloc_strdup(tmp, header);
+ shader_def_opt(&header_eosd, "USE_3DLUT", p->use_lut_3d);
+
+ p->eosd_program =
+ create_program(gl, "eosd", header_eosd, vertex_shader, s_eosd);
+
+ p->osd_program =
+ create_program(gl, "osd", header, vertex_shader, s_osd);
+
+ char *header_conv = talloc_strdup(tmp, "");
+ char *header_final = talloc_strdup(tmp, "");
+ char *header_sep = NULL;
+
+ bool convert_input_to_linear = !p->is_linear_rgb
+ && (p->use_srgb || p->use_lut_3d);
+
+ shader_def_opt(&header_conv, "USE_PLANAR", p->plane_count > 1);
+ shader_def_opt(&header_conv, "USE_GBRP", p->image_format == IMGFMT_GBRP);
+ shader_def_opt(&header_conv, "USE_YGRAY", p->is_yuv && p->plane_count == 1);
+ shader_def_opt(&header_conv, "USE_COLORMATRIX", p->is_yuv);
+ shader_def_opt(&header_conv, "USE_LINEAR_CONV", convert_input_to_linear);
+
+ shader_def_opt(&header_final, "USE_LINEAR_CONV_INV", p->use_lut_3d);
+ shader_def_opt(&header_final, "USE_GAMMA_POW", p->use_gamma);
+ shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d);
+ shader_def_opt(&header_final, "USE_DITHER", p->dither_texture != 0);
+
+ if (p->use_scale_sep && p->scalers[0].kernel) {
+ header_sep = talloc_strdup(tmp, "");
+ shader_def_opt(&header_sep, "FIXED_SCALE", true);
+ shader_setup_scaler(&header_sep, &p->scalers[0], 0);
+ shader_setup_scaler(&header_final, &p->scalers[0], 1);
+ } else {
+ shader_setup_scaler(&header_final, &p->scalers[0], -1);
+ }
+
+ // We want to do scaling in linear light. Scaling is closely connected to
+ // texture sampling due to how the shader is structured (or if GL bilinear
+ // scaling is used). The purpose of the "indirect" pass is to convert the
+ // input video to linear RGB.
+ // Another purpose is reducing input to a single texture for scaling.
+ bool use_indirect = p->use_indirect;
+
+ // Don't sample from input video textures before converting the input to
+ // linear light. (Unneeded when sRGB textures are used.)
+ if (convert_input_to_linear)
+ use_indirect = true;
+
+ // It doesn't make sense to scale the chroma with cscale in the 1. scale
+ // step and with lscale in the 2. step. If the chroma is subsampled, a
+ // convolution filter wouldn't even work entirely correctly, because the
+ // luma scaler would sample two texels instead of one per tap for chroma.
+ // Also, even with 4:4:4 YUV or planar RGB, the indirection might be faster,
+ // because the shader can't use one scaler for sampling from 3 textures. It
+ // has to fetch the coefficients for each texture separately, even though
+ // they're the same (this is not an inherent restriction, but would require
+ // to restructure the shader).
+ if (header_sep && p->plane_count > 1)
+ use_indirect = true;
+
+ if (input_is_subsampled(p)) {
+ shader_setup_scaler(&header_conv, &p->scalers[1], -1);
+ } else {
+ // Force using the luma scaler on chroma. If the "indirect" stage is
+ // used, the actual scaling will happen in the next stage.
+ shader_def(&header_conv, "SAMPLE_C",
+ use_indirect ? "sample_bilinear" : "SAMPLE_L");
+ }
+
+ if (use_indirect) {
+ // We don't use filtering for the Y-plane (luma), because it's never
+ // scaled in this scenario.
+ shader_def(&header_conv, "SAMPLE_L", "sample_bilinear");
+ shader_def_opt(&header_conv, "FIXED_SCALE", true);
+ header_conv = t_concat(tmp, header, header_conv);
+ p->indirect_program =
+ create_program(gl, "indirect", header_conv, vertex_shader, s_video);
+ } else if (header_sep) {
+ header_sep = t_concat(tmp, header_sep, header_conv);
+ } else {
+ header_final = t_concat(tmp, header_final, header_conv);
+ }
+
+ if (header_sep) {
+ header_sep = t_concat(tmp, header, header_sep);
+ p->scale_sep_program =
+ create_program(gl, "scale_sep", header_sep, vertex_shader, s_video);
+ }
+
+ header_final = t_concat(tmp, header, header_final);
+ p->final_program =
+ create_program(gl, "final", header_final, vertex_shader, s_video);
+
+ debug_check_gl(p, "shader compilation");
+
+ talloc_free(tmp);
+}
+
+static void delete_program(GL *gl, GLuint *prog)
+{
+ gl->DeleteProgram(*prog);
+ *prog = 0;
+}
+
+static void delete_shaders(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ delete_program(gl, &p->osd_program);
+ delete_program(gl, &p->eosd_program);
+ delete_program(gl, &p->indirect_program);
+ delete_program(gl, &p->scale_sep_program);
+ delete_program(gl, &p->final_program);
+}
+
+static double get_scale_factor(struct gl_priv *p)
+{
+ double sx = p->dst_rect.width / (double)p->src_rect.width;
+ double sy = p->dst_rect.height / (double)p->src_rect.height;
+ // xxx: actually we should use different scalers in X/Y directions if the
+ // scale factors are different due to anamorphic content
+ return FFMIN(sx, sy);
+}
+
+static bool update_scale_factor(struct gl_priv *p, struct filter_kernel *kernel)
+{
+ double scale = get_scale_factor(p);
+ if (!p->use_fancy_downscaling && scale < 1.0)
+ scale = 1.0;
+ return mp_init_filter(kernel, filter_sizes, FFMAX(1.0, 1.0 / scale));
+}
+
+static void init_scaler(struct gl_priv *p, struct scaler *scaler)
+{
+ GL *gl = p->gl;
+
+ assert(scaler->name);
+
+ scaler->kernel = NULL;
+
+ const struct filter_kernel *t_kernel = mp_find_filter_kernel(scaler->name);
+ if (!t_kernel)
+ return;
+
+ scaler->kernel_storage = *t_kernel;
+ scaler->kernel = &scaler->kernel_storage;
+
+ for (int n = 0; n < 2; n++) {
+ if (!isnan(p->scaler_params[n]))
+ scaler->kernel->params[n] = p->scaler_params[n];
+ }
+
+ update_scale_factor(p, scaler->kernel);
+
+ int size = scaler->kernel->size;
+ assert(size < FF_ARRAY_ELEMS(lut_tex_formats));
+ struct lut_tex_format *fmt = &lut_tex_formats[size];
+ bool use_2d = fmt->pixels > 1;
+ bool is_luma = scaler->index == 0;
+ scaler->lut_name = use_2d
+ ? (is_luma ? "lut_l_2d" : "lut_c_2d")
+ : (is_luma ? "lut_l_1d" : "lut_c_1d");
+
+ gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_SCALERS + scaler->index);
+ GLenum target = use_2d ? GL_TEXTURE_2D : GL_TEXTURE_1D;
+
+ if (!scaler->gl_lut)
+ gl->GenTextures(1, &scaler->gl_lut);
+
+ gl->BindTexture(target, scaler->gl_lut);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
+ float *weights = talloc_array(NULL, float, LOOKUP_TEXTURE_SIZE * size);
+ mp_compute_lut(scaler->kernel, LOOKUP_TEXTURE_SIZE, weights);
+ if (use_2d) {
+ gl->TexImage2D(GL_TEXTURE_2D, 0, fmt->internal_format, fmt->pixels,
+ LOOKUP_TEXTURE_SIZE, 0, fmt->format, GL_FLOAT,
+ weights);
+ } else {
+ gl->TexImage1D(GL_TEXTURE_1D, 0, fmt->internal_format,
+ LOOKUP_TEXTURE_SIZE, 0, fmt->format, GL_FLOAT,
+ weights);
+ }
+ talloc_free(weights);
+
+ gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ debug_check_gl(p, "after initializing scaler");
+}
+
+static void make_dither_matrix(unsigned char *m, int size)
+{
+ m[0] = 0;
+ for (int sz = 1; sz < size; sz *= 2) {
+ int offset[] = {sz*size, sz, sz * (size+1), 0};
+ for (int i = 0; i < 4; i++)
+ for (int y = 0; y < sz * size; y += size)
+ for (int x = 0; x < sz; x++)
+ m[x+y+offset[i]] = m[x+y] * 4 + (3-i) * 256/size/size;
+ }
+}
+
+static void init_dither(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ // Assume 8 bits per component if unknown.
+ int dst_depth = p->glctx->depth_g ? p->glctx->depth_g : 8;
+ if (p->dither_depth > 0)
+ dst_depth = p->dither_depth;
+
+ int src_depth = p->component_bits;
+ if (p->use_lut_3d)
+ src_depth = 16;
+
+ if (dst_depth >= src_depth || p->dither_depth < 0 || src_depth < 0)
+ return;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Dither %d->%d.\n", src_depth, dst_depth);
+
+ // This defines how many bits are considered significant for output on
+ // screen. The superfluous bits will be used for rounded according to the
+ // dither matrix. The precision of the source implicitly decides how many
+ // dither patterns can be visible.
+ p->dither_quantization = (1 << dst_depth) - 1;
+ int size = 8;
+ p->dither_multiply = p->dither_quantization + 1.0 / (size*size);
+ unsigned char dither[256];
+ make_dither_matrix(dither, size);
+
+ gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_DITHER);
+ gl->GenTextures(1, &p->dither_texture);
+ gl->BindTexture(GL_TEXTURE_2D, p->dither_texture);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED, size, size, 0, GL_RED,
+ GL_UNSIGNED_BYTE, dither);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ gl->ActiveTexture(GL_TEXTURE0);
+}
+
+static void reinit_rendering(struct gl_priv *p)
+{
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Reinit rendering.\n");
+
+ debug_check_gl(p, "before scaler initialization");
+
+ uninit_rendering(p);
+
+ init_dither(p);
+
+ init_scaler(p, &p->scalers[0]);
+ init_scaler(p, &p->scalers[1]);
+
+ compile_shaders(p);
+
+ if (p->indirect_program && !p->indirect_fbo.fbo)
+ fbotex_init(p, &p->indirect_fbo, p->texture_width, p->texture_height);
+}
+
+static void uninit_rendering(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ delete_shaders(p);
+
+ for (int n = 0; n < 2; n++) {
+ gl->DeleteTextures(1, &p->scalers->gl_lut);
+ p->scalers->gl_lut = 0;
+ p->scalers->lut_name = NULL;
+ p->scalers->kernel = NULL;
+ }
+
+ gl->DeleteTextures(1, &p->dither_texture);
+ p->dither_texture = 0;
+}
+
+static void init_lut_3d(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ gl->GenTextures(1, &p->lut_3d_texture);
+ gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_3DLUT);
+ gl->BindTexture(GL_TEXTURE_3D, p->lut_3d_texture);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->TexImage3D(GL_TEXTURE_3D, 0, GL_RGB16, p->lut_3d_w, p->lut_3d_h,
+ p->lut_3d_d, 0, GL_RGB, GL_UNSIGNED_SHORT, p->lut_3d_data);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ debug_check_gl(p, "after 3d lut creation");
+}
+
+static void init_video(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ if (p->use_lut_3d && !p->lut_3d_texture)
+ init_lut_3d(p);
+
+ if (!p->is_yuv && (p->use_srgb || p->use_lut_3d)) {
+ p->is_linear_rgb = true;
+ p->gl_internal_format = GL_SRGB;
+ }
+
+ int eq_caps = MP_CSP_EQ_CAPS_GAMMA;
+ if (p->is_yuv)
+ eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX;
+ p->video_eq.capabilities = eq_caps;
+
+ debug_check_gl(p, "before video texture creation");
+
+ tex_size(p, p->image_width, p->image_height,
+ &p->texture_width, &p->texture_height);
+
+ for (int n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &p->planes[n];
+
+ int w = p->texture_width >> plane->shift_x;
+ int h = p->texture_height >> plane->shift_y;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Texture for plane %d: %dx%d\n", n, w, h);
+
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->GenTextures(1, &plane->gl_texture);
+ gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture);
+
+ gl->TexImage2D(GL_TEXTURE_2D, 0, p->gl_internal_format, w, h, 0,
+ p->gl_format, p->gl_type, NULL);
+ default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ debug_check_gl(p, "after video texture creation");
+
+ reinit_rendering(p);
+}
+
+static void uninit_video(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ uninit_rendering(p);
+
+ for (int n = 0; n < 3; n++) {
+ struct texplane *plane = &p->planes[n];
+
+ gl->DeleteTextures(1, &plane->gl_texture);
+ plane->gl_texture = 0;
+ gl->DeleteBuffers(1, &plane->gl_buffer);
+ plane->gl_buffer = 0;
+ plane->buffer_ptr = NULL;
+ plane->buffer_size = 0;
+ }
+
+ fbotex_uninit(p, &p->indirect_fbo);
+ fbotex_uninit(p, &p->scale_sep_fbo);
+}
+
+static void render_to_fbo(struct gl_priv *p, struct fbotex *fbo, int w, int h,
+ int tex_w, int tex_h)
+{
+ GL *gl = p->gl;
+
+ gl->Viewport(0, 0, fbo->vp_w, fbo->vp_h);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
+
+ struct vertex vb[VERTICES_PER_QUAD];
+ write_quad(vb, -1, -1, 1, 1,
+ 0, 0, w, h,
+ tex_w, tex_h,
+ NULL, false);
+ draw_triangles(p, vb, VERTICES_PER_QUAD);
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+ gl->Viewport(p->vp_x, p->vp_y, p->vp_w, p->vp_h);
+
+}
+
+static void handle_pass(struct gl_priv *p, struct fbotex **source,
+ struct fbotex *fbo, GLuint program)
+{
+ GL *gl = p->gl;
+
+ if (!program)
+ return;
+
+ gl->BindTexture(GL_TEXTURE_2D, (*source)->texture);
+ gl->UseProgram(program);
+ render_to_fbo(p, fbo, (*source)->vp_w, (*source)->vp_h,
+ (*source)->tex_w, (*source)->tex_h);
+ *source = fbo;
+}
+
+static void do_render(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+ struct vertex vb[VERTICES_PER_QUAD];
+ bool is_flipped = p->mpi_flipped ^ p->vo_flipped;
+
+ // Order of processing:
+ // [indirect -> [scale_sep ->]] final
+
+ struct fbotex dummy = {
+ .vp_w = p->image_width, .vp_h = p->image_height,
+ .tex_w = p->texture_width, .tex_h = p->texture_height,
+ .texture = p->planes[0].gl_texture,
+ };
+ struct fbotex *source = &dummy;
+
+ handle_pass(p, &source, &p->indirect_fbo, p->indirect_program);
+ handle_pass(p, &source, &p->scale_sep_fbo, p->scale_sep_program);
+
+ gl->BindTexture(GL_TEXTURE_2D, source->texture);
+ gl->UseProgram(p->final_program);
+
+ float final_texw = p->image_width * source->tex_w / (float)source->vp_w;
+ float final_texh = p->image_height * source->tex_h / (float)source->vp_h;
+
+ if (p->use_srgb && !p->use_lut_3d)
+ gl->Enable(GL_FRAMEBUFFER_SRGB);
+
+ if (p->stereo_mode) {
+ int w = p->src_rect.width;
+ int imgw = p->image_width;
+
+ glEnable3DLeft(gl, p->stereo_mode);
+
+ write_quad(vb,
+ p->dst_rect.left, p->dst_rect.top,
+ p->dst_rect.right, p->dst_rect.bottom,
+ p->src_rect.left / 2, p->src_rect.top,
+ p->src_rect.left / 2 + w / 2, p->src_rect.bottom,
+ final_texw, final_texh,
+ NULL, is_flipped);
+ draw_triangles(p, vb, VERTICES_PER_QUAD);
+
+ glEnable3DRight(gl, p->stereo_mode);
+
+ write_quad(vb,
+ p->dst_rect.left, p->dst_rect.top,
+ p->dst_rect.right, p->dst_rect.bottom,
+ p->src_rect.left / 2 + imgw / 2, p->src_rect.top,
+ p->src_rect.left / 2 + imgw / 2 + w / 2, p->src_rect.bottom,
+ final_texw, final_texh,
+ NULL, is_flipped);
+ draw_triangles(p, vb, VERTICES_PER_QUAD);
+
+ glDisable3D(gl, p->stereo_mode);
+ } else {
+ write_quad(vb,
+ p->dst_rect.left, p->dst_rect.top,
+ p->dst_rect.right, p->dst_rect.bottom,
+ p->src_rect.left, p->src_rect.top,
+ p->src_rect.right, p->src_rect.bottom,
+ final_texw, final_texh,
+ NULL, is_flipped);
+ draw_triangles(p, vb, VERTICES_PER_QUAD);
+ }
+
+ gl->Disable(GL_FRAMEBUFFER_SRGB);
+
+ gl->UseProgram(0);
+
+ debug_check_gl(p, "after video rendering");
+}
+
+static void update_window_sized_objects(struct gl_priv *p)
+{
+ if (p->scale_sep_program) {
+ if (p->dst_rect.height > p->scale_sep_fbo.tex_h) {
+ fbotex_uninit(p, &p->scale_sep_fbo);
+ // Round up to an arbitrary alignment to make window resizing or
+ // panscan controls smoother (less texture reallocations).
+ int height = FFALIGN(p->dst_rect.height, 256);
+ fbotex_init(p, &p->scale_sep_fbo, p->image_width, height);
+ }
+ p->scale_sep_fbo.vp_w = p->image_width;
+ p->scale_sep_fbo.vp_h = p->dst_rect.height;
+ }
+}
+
+static void resize(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+ struct vo *vo = p->vo;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n", vo->dwidth, vo->dheight);
+ p->vp_x = 0, p->vp_y = 0;
+ if (WinID >= 0) {
+ int w = vo->dwidth, h = vo->dheight;
+ int old_y = vo->dheight;
+ geometry(&p->vp_x, &p->vp_y, &w, &h, vo->dwidth, vo->dheight);
+ p->vp_y = old_y - h - p->vp_y;
+ }
+ p->vp_w = vo->dwidth, p->vp_h = vo->dheight;
+ gl->Viewport(p->vp_x, p->vp_y, p->vp_w, p->vp_h);
+
+ struct vo_rect borders;
+ calc_src_dst_rects(vo, p->image_width, p->image_height, &p->src_rect,
+ &p->dst_rect, &borders, NULL);
+ p->border_x = borders.left;
+ p->border_y = borders.top;
+
+ bool need_scaler_reinit = false; // filter size change needed
+ bool need_scaler_update = false; // filter LUT change needed
+ bool too_small = false;
+ for (int n = 0; n < 2; n++) {
+ if (p->scalers[n].kernel) {
+ struct filter_kernel tkernel = *p->scalers[n].kernel;
+ struct filter_kernel old = tkernel;
+ bool ok = update_scale_factor(p, &tkernel);
+ too_small |= !ok;
+ need_scaler_reinit |= (tkernel.size != old.size);
+ need_scaler_update |= (tkernel.inv_scale != old.inv_scale);
+ }
+ }
+ if (need_scaler_reinit) {
+ reinit_rendering(p);
+ } else if (need_scaler_update) {
+ init_scaler(p, &p->scalers[0]);
+ init_scaler(p, &p->scalers[1]);
+ }
+ if (too_small)
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] Can't downscale that much, window "
+ "output may look suboptimal.\n");
+
+ update_window_sized_objects(p);
+ update_all_uniforms(p);
+
+#ifdef CONFIG_FREETYPE
+ // adjust font size to display size
+ force_load_font = 1;
+#endif
+ vo_osd_changed(OSDTYPE_OSD);
+
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ vo->want_redraw = true;
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ if (p->use_glFinish)
+ gl->Finish();
+
+ p->glctx->swapGlBuffers(p->glctx);
+
+ if (p->dst_rect.left > p->vp_x || p->dst_rect.top > p->vp_y
+ || p->dst_rect.right < p->vp_x + p->vp_w
+ || p->dst_rect.bottom < p->vp_y + p->vp_h)
+ {
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ }
+}
+
+static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
+ int x, int y)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ p->mpi_flipped = stride[0] < 0;
+
+ for (int n = 0; n < p->plane_count; n++) {
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->BindTexture(GL_TEXTURE_2D, p->planes[n].gl_texture);
+ int xs = p->planes[n].shift_x, ys = p->planes[n].shift_y;
+ glUploadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type, src[n],
+ stride[n], x >> xs, y >> ys, w >> xs, h >> ys, 0);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ return 0;
+}
+
+static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ if (!p->use_pbo)
+ return VO_FALSE;
+
+ // We don't support alpha planes. (Disabling PBOs with normal draw calls is
+ // an undesired, but harmless side-effect.)
+ if (mpi->num_planes != p->plane_count)
+ return VO_FALSE;
+
+ if (mpi->flags & MP_IMGFLAG_READABLE)
+ return VO_FALSE;
+ if (mpi->type != MP_IMGTYPE_STATIC && mpi->type != MP_IMGTYPE_TEMP &&
+ (mpi->type != MP_IMGTYPE_NUMBERED || mpi->number))
+ return VO_FALSE;
+ mpi->flags &= ~MP_IMGFLAG_COMMON_PLANE;
+ for (int n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &p->planes[n];
+ mpi->stride[n] = (mpi->width >> plane->shift_x) * p->plane_bytes;
+ int needed_size = (mpi->height >> plane->shift_y) * mpi->stride[n];
+ if (!plane->gl_buffer)
+ gl->GenBuffers(1, &plane->gl_buffer);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
+ if (needed_size > plane->buffer_size) {
+ plane->buffer_size = needed_size;
+ gl->BufferData(GL_PIXEL_UNPACK_BUFFER, plane->buffer_size,
+ NULL, GL_DYNAMIC_DRAW);
+ }
+ if (!plane->buffer_ptr)
+ plane->buffer_ptr = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER,
+ GL_WRITE_ONLY);
+ mpi->planes[n] = plane->buffer_ptr;
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+ mpi->flags |= MP_IMGFLAG_DIRECT;
+ return VO_TRUE;
+}
+
+static uint32_t draw_image(struct gl_priv *p, mp_image_t *mpi)
+{
+ GL *gl = p->gl;
+ int n;
+
+ assert(mpi->num_planes >= p->plane_count);
+
+ mp_image_t mpi2 = *mpi;
+ int w = mpi->w, h = mpi->h;
+ if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
+ goto skip_upload;
+ mpi2.flags = 0;
+ mpi2.type = MP_IMGTYPE_TEMP;
+ mpi2.width = mpi2.w;
+ mpi2.height = mpi2.h;
+ if (!(mpi->flags & MP_IMGFLAG_DIRECT)
+ && !p->planes[0].buffer_ptr
+ && get_image(p->vo, &mpi2) == VO_TRUE)
+ {
+ for (n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &p->planes[n];
+ int xs = plane->shift_x, ys = plane->shift_y;
+ int line_bytes = (mpi->w >> xs) * p->plane_bytes;
+ memcpy_pic(mpi2.planes[n], mpi->planes[n], line_bytes, mpi->h >> ys,
+ mpi2.stride[n], mpi->stride[n]);
+ }
+ mpi = &mpi2;
+ }
+ p->mpi_flipped = mpi->stride[0] < 0;
+ for (n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &p->planes[n];
+ int xs = plane->shift_x, ys = plane->shift_y;
+ void *plane_ptr = mpi->planes[n];
+ if (mpi->flags & MP_IMGFLAG_DIRECT) {
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
+ if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Video PBO upload failed. "
+ "Remove the 'pbo' suboption.\n");
+ plane->buffer_ptr = NULL;
+ plane_ptr = NULL; // PBO offset 0
+ }
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture);
+ glUploadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type, plane_ptr,
+ mpi->stride[n], 0, 0, w >> xs, h >> ys, 0);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+skip_upload:
+ do_render(p);
+ return VO_TRUE;
+}
+
+static mp_image_t *get_screenshot(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ mp_image_t *image = alloc_mpi(p->texture_width, p->texture_height,
+ p->image_format);
+
+ // NOTE about image formats with alpha plane: we don't even have the alpha
+ // anymore. We never upload it to any texture, as it would be a waste of
+ // time. On the other hand, we can't find a "similar", non-alpha image
+ // format easily. So we just leave the alpha plane of the newly allocated
+ // image as-is, and hope that the alpha is ignored by the receiver of the
+ // screenshot. (If not, code should be added to make it fully opaque.)
+
+ for (int n = 0; n < p->plane_count; n++) {
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->BindTexture(GL_TEXTURE_2D, p->planes[n].gl_texture);
+ glDownloadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type,
+ image->planes[n], image->stride[n]);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+
+ image->width = p->image_width;
+ image->height = p->image_height;
+
+ image->w = p->vo->aspdat.prew;
+ image->h = p->vo->aspdat.preh;
+
+ return image;
+}
+
+static mp_image_t *get_window_screenshot(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ mp_image_t *image = alloc_mpi(p->vp_w, p->vp_h, IMGFMT_RGB24);
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
+ gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
+ gl->ReadBuffer(GL_FRONT);
+ // flip image while reading
+ for (int y = 0; y < p->vp_h; y++) {
+ gl->ReadPixels(p->vp_x, p->vp_y + p->vp_h - y - 1, p->vp_w, 1,
+ GL_RGB, GL_UNSIGNED_BYTE,
+ image->planes[0] + y * image->stride[0]);
+ }
+ return image;
+}
+
+static void clear_osd(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ if (!p->osd_textures_count)
+ return;
+ gl->DeleteTextures(p->osd_textures_count, p->osd_textures);
+ p->osd_textures_count = 0;
+}
+
+static void create_osd_texture(void *ctx, int x0, int y0, int w, int h,
+ unsigned char *src, unsigned char *srca,
+ int stride)
+{
+ struct gl_priv *p = ctx;
+ GL *gl = p->gl;
+
+ if (w <= 0 || h <= 0 || stride < w) {
+ mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n");
+ return;
+ }
+
+ if (p->osd_textures_count >= MAX_OSD_PARTS) {
+ mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the developers!\n");
+ return;
+ }
+
+ int sx, sy;
+ tex_size(p, w, h, &sx, &sy);
+
+ gl->GenTextures(1, &p->osd_textures[p->osd_textures_count]);
+ gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[p->osd_textures_count]);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RG, sx, sy, 0, GL_RG, GL_UNSIGNED_BYTE,
+ NULL);
+ default_tex_params(gl, GL_TEXTURE_2D, GL_NEAREST);
+ unsigned char *tmp = malloc(stride * h * 2);
+ // Convert alpha from weird MPlayer scale.
+ for (int i = 0; i < h * stride; i++) {
+ tmp[i*2+0] = src[i];
+ tmp[i*2+1] = -srca[i];
+ }
+ glUploadTex(gl, GL_TEXTURE_2D, GL_RG, GL_UNSIGNED_BYTE, tmp, stride * 2,
+ 0, 0, w, h, 0);
+ free(tmp);
+
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+
+ uint8_t color[4] = {(p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff,
+ p->osd_color & 0xff, 0xff - (p->osd_color >> 24)};
+
+ write_quad(&p->osd_va[p->osd_textures_count * VERTICES_PER_QUAD],
+ x0, y0, x0 + w, y0 + h, 0, 0, w, h,
+ sx, sy, color, false);
+
+ p->osd_textures_count++;
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct gl_priv *p = vo->priv;
+ GL *gl = p->gl;
+
+ if (vo_osd_changed(0)) {
+ clear_osd(p);
+ osd_draw_text_ext(osd, vo->dwidth, vo->dheight, p->border_x,
+ p->border_y, p->border_x,
+ p->border_y, p->image_width,
+ p->image_height, create_osd_texture, p);
+ }
+
+ if (p->osd_textures_count > 0) {
+ gl->Enable(GL_BLEND);
+ // OSD bitmaps use premultiplied alpha.
+ gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ gl->UseProgram(p->osd_program);
+
+ for (int n = 0; n < p->osd_textures_count; n++) {
+ gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[n]);
+ draw_triangles(p, &p->osd_va[n * VERTICES_PER_QUAD],
+ VERTICES_PER_QUAD);
+ }
+
+ gl->UseProgram(0);
+
+ gl->Disable(GL_BLEND);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+ }
+}
+
+static void gen_eosd(struct gl_priv *p, mp_eosd_images_t *imgs)
+{
+ GL *gl = p->gl;
+
+ bool need_repos, need_upload, need_allocate;
+ eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload,
+ &need_allocate);
+
+ if (!need_repos)
+ return;
+
+ if (!p->eosd_texture) {
+ gl->GenTextures(1, &p->eosd_texture);
+ gl->GenBuffers(1, &p->eosd_buffer);
+ }
+
+ gl->BindTexture(GL_TEXTURE_2D, p->eosd_texture);
+
+ if (need_allocate) {
+ tex_size(p, p->eosd->surface.w, p->eosd->surface.h,
+ &p->eosd_texture_width, &p->eosd_texture_height);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED,
+ p->eosd_texture_width, p->eosd_texture_height, 0,
+ GL_RED, GL_UNSIGNED_BYTE, NULL);
+ default_tex_params(gl, GL_TEXTURE_2D, GL_NEAREST);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->eosd_buffer);
+ gl->BufferData(GL_PIXEL_UNPACK_BUFFER,
+ p->eosd->surface.w * p->eosd->surface.h,
+ NULL,
+ GL_DYNAMIC_COPY);
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+
+ p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va,
+ p->eosd->targets_count
+ * sizeof(struct vertex)
+ * VERTICES_PER_QUAD);
+
+ if (need_upload && p->use_pbo) {
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->eosd_buffer);
+ char *data = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
+ if (!data) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error: can't upload subtitles! "
+ "Subtitles will look corrupted.\n");
+ } else {
+ for (int n = 0; n < p->eosd->targets_count; n++) {
+ struct eosd_target *target = &p->eosd->targets[n];
+ ASS_Image *i = target->ass_img;
+
+ void *pdata = data + target->source.y0 * p->eosd->surface.w
+ + target->source.x0;
+
+ memcpy_pic(pdata, i->bitmap, i->w, i->h,
+ p->eosd->surface.w, i->stride);
+ }
+ if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] EOSD PBO upload failed. "
+ "Remove the 'pbo' suboption.\n");
+ struct eosd_rect rc;
+ eosd_packer_calculate_source_bb(p->eosd, &rc);
+ glUploadTex(gl, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, NULL,
+ p->eosd->surface.w, rc.x0, rc.y0,
+ rc.x1 - rc.x0, rc.y1 - rc.y0, 0);
+ }
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ } else if (need_upload) {
+ // non-PBO upload
+ for (int n = 0; n < p->eosd->targets_count; n++) {
+ struct eosd_target *target = &p->eosd->targets[n];
+ ASS_Image *i = target->ass_img;
+
+ glUploadTex(gl, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, i->bitmap,
+ i->stride, target->source.x0, target->source.y0,
+ i->w, i->h, 0);
+ }
+ }
+
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+
+ debug_check_gl(p, "EOSD upload");
+
+ for (int n = 0; n < p->eosd->targets_count; n++) {
+ struct eosd_target *target = &p->eosd->targets[n];
+ ASS_Image *i = target->ass_img;
+ uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff,
+ (i->color >> 8) & 0xff, 255 - (i->color & 0xff) };
+
+ write_quad(&p->eosd_va[n * VERTICES_PER_QUAD],
+ target->dest.x0, target->dest.y0,
+ target->dest.x1, target->dest.y1,
+ target->source.x0, target->source.y0,
+ target->source.x1, target->source.y1,
+ p->eosd_texture_width, p->eosd_texture_height,
+ color, false);
+ }
+}
+
+static void draw_eosd(struct gl_priv *p, mp_eosd_images_t *imgs)
+{
+ GL *gl = p->gl;
+
+ gen_eosd(p, imgs);
+
+ if (p->eosd->targets_count == 0)
+ return;
+
+ gl->Enable(GL_BLEND);
+ gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ gl->BindTexture(GL_TEXTURE_2D, p->eosd_texture);
+ gl->UseProgram(p->eosd_program);
+ draw_triangles(p, p->eosd_va, p->eosd->targets_count * VERTICES_PER_QUAD);
+ gl->UseProgram(0);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+ gl->Disable(GL_BLEND);
+}
+
+static void setup_vertex_array(GL *gl)
+{
+ size_t stride = sizeof(struct vertex);
+
+ gl->EnableVertexAttribArray(VERTEX_ATTRIB_POSITION);
+ gl->VertexAttribPointer(VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE,
+ stride, (void*)offsetof(struct vertex, position));
+
+ gl->EnableVertexAttribArray(VERTEX_ATTRIB_COLOR);
+ gl->VertexAttribPointer(VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE,
+ stride, (void*)offsetof(struct vertex, color));
+
+ gl->EnableVertexAttribArray(VERTEX_ATTRIB_TEXCOORD);
+ gl->VertexAttribPointer(VERTEX_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE,
+ stride, (void*)offsetof(struct vertex, texcoord));
+}
+
+static int init_gl(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ debug_check_gl(p, "before init_gl");
+
+ const char *vendor = gl->GetString(GL_VENDOR);
+ const char *version = gl->GetString(GL_VERSION);
+ const char *renderer = gl->GetString(GL_RENDERER);
+ const char *glsl = gl->GetString(GL_SHADING_LANGUAGE_VERSION);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] GL_RENDERER='%s', GL_VENDOR='%s', "
+ "GL_VERSION='%s', GL_SHADING_LANGUAGE_VERSION='%s'"
+ "\n", renderer, vendor, version, glsl);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Display depth: R=%d, G=%d, B=%d\n",
+ p->glctx->depth_r, p->glctx->depth_g, p->glctx->depth_b);
+
+ GLint major, minor;
+ gl->GetIntegerv(GL_MAJOR_VERSION, &major);
+ gl->GetIntegerv(GL_MINOR_VERSION, &minor);
+
+ p->shader_version = "130";
+
+ // Hack for OSX: it only creates 3.2 contexts.
+ if (MPGL_VER(major, minor) >= MPGL_VER(3, 2))
+ p->shader_version = "150";
+
+ gl->Disable(GL_DITHER);
+ gl->Disable(GL_BLEND);
+ gl->Disable(GL_DEPTH_TEST);
+ gl->DepthMask(GL_FALSE);
+ gl->Disable(GL_CULL_FACE);
+ gl->DrawBuffer(GL_BACK);
+
+ gl->GenBuffers(1, &p->vertex_buffer);
+ gl->GenVertexArrays(1, &p->vao);
+
+ gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer);
+ gl->BindVertexArray(p->vao);
+ setup_vertex_array(gl);
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
+ gl->BindVertexArray(0);
+
+ GLint max_texture_size;
+ gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+ eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size);
+
+ gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ if (gl->SwapInterval && p->swap_interval >= 0)
+ gl->SwapInterval(p->swap_interval);
+
+ debug_check_gl(p, "after init_gl");
+
+ return 1;
+}
+
+static void uninit_gl(struct gl_priv *p)
+{
+ GL *gl = p->gl;
+
+ // NOTE: GL functions might not be loaded yet
+ if (!(p->glctx && p->gl->DeleteTextures))
+ return;
+
+ uninit_video(p);
+
+ gl->DeleteVertexArrays(1, &p->vao);
+ p->vao = 0;
+ gl->DeleteBuffers(1, &p->vertex_buffer);
+ p->vertex_buffer = 0;
+
+ clear_osd(p);
+ gl->DeleteTextures(1, &p->eosd_texture);
+ p->eosd_texture = 0;
+ gl->DeleteBuffers(1, &p->eosd_buffer);
+ p->eosd_buffer = 0;
+ eosd_packer_reinit(p->eosd, 0, 0);
+
+ gl->DeleteTextures(1, &p->lut_3d_texture);
+ p->lut_3d_texture = 0;
+}
+
+static bool init_format(int fmt, struct gl_priv *init)
+{
+ bool supported = false;
+ struct gl_priv dummy;
+ if (!init)
+ init = &dummy;
+
+ mp_image_t dummy_img = {0};
+ mp_image_setfmt(&dummy_img, fmt);
+
+ init->image_format = fmt;
+ init->component_bits = -1;
+
+ // RGB/packed formats
+ for (const struct fmt_entry *e = mp_to_gl_formats; e->mp_format; e++) {
+ if (e->mp_format == fmt) {
+ supported = true;
+ init->plane_bits = dummy_img.bpp;
+ init->gl_format = e->format;
+ init->gl_internal_format = e->internal_format;
+ init->component_bits = e->component_bits;
+ init->gl_type = e->type;
+ break;
+ }
+ }
+
+ // YUV/planar formats
+ if (!supported && mp_get_chroma_shift(fmt, NULL, NULL, &init->plane_bits)) {
+ init->gl_format = GL_RED;
+ init->component_bits = init->plane_bits;
+ if (init->plane_bits == 8) {
+ supported = true;
+ init->gl_internal_format = GL_RED;
+ init->gl_type = GL_UNSIGNED_BYTE;
+ } else if (IMGFMT_IS_YUVP16_NE(fmt)) {
+ supported = true;
+ init->gl_internal_format = GL_R16;
+ init->gl_type = GL_UNSIGNED_SHORT;
+ }
+ }
+
+ // RGB/planar
+ if (!supported && fmt == IMGFMT_GBRP) {
+ supported = true;
+ init->plane_bits = init->component_bits = 8;
+ init->gl_format = GL_RED;
+ init->gl_internal_format = GL_RED;
+ init->gl_type = GL_UNSIGNED_BYTE;
+ }
+
+ if (!supported)
+ return false;
+
+ init->plane_bytes = (init->plane_bits + 7) / 8;
+ init->is_yuv = dummy_img.flags & MP_IMGFLAG_YUV;
+ init->is_linear_rgb = false;
+
+ // NOTE: we throw away the additional alpha plane, if one exists.
+ init->plane_count = dummy_img.num_planes > 2 ? 3 : 1;
+ assert(dummy_img.num_planes >= init->plane_count);
+ assert(dummy_img.num_planes <= init->plane_count + 1);
+
+ for (int n = 0; n < init->plane_count; n++) {
+ struct texplane *plane = &init->planes[n];
+
+ plane->shift_x = n > 0 ? dummy_img.chroma_x_shift : 0;
+ plane->shift_y = n > 0 ? dummy_img.chroma_y_shift : 0;
+ }
+
+ return true;
+}
+
+static int query_format(uint32_t format)
+{
+ int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP |
+ VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE |
+ VFCAP_OSD | VFCAP_EOSD | VFCAP_EOSD_UNSCALED;
+ if (!init_format(format, NULL))
+ return 0;
+ return caps;
+}
+
+static bool config_window(struct gl_priv *p, uint32_t d_width,
+ uint32_t d_height, uint32_t flags)
+{
+ if (p->stereo_mode == GL_3D_QUADBUFFER)
+ flags |= VOFLAG_STEREO;
+
+ int mpgl_version = MPGL_VER(3, 0);
+ int mpgl_flags = p->use_gl_debug ? MPGLFLAG_DEBUG : 0;
+
+ if (p->use_gl2)
+ mpgl_version = MPGL_VER(2, 1);
+
+ if (create_mpglcontext(p->glctx, mpgl_flags, mpgl_version, d_width,
+ d_height, flags) == SET_WINDOW_FAILED)
+ return false;
+
+ if (!p->vertex_buffer)
+ init_gl(p);
+
+ return true;
+}
+
+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 gl_priv *p = vo->priv;
+
+ if (!config_window(p, d_width, d_height, flags))
+ return -1;
+
+ p->vo_flipped = !!(flags & VOFLAG_FLIPPING);
+
+ if (p->image_format != format || p->image_width != width
+ || p->image_height != height)
+ {
+ uninit_video(p);
+ p->image_height = height;
+ p->image_width = width;
+ init_format(format, p);
+ init_video(p);
+ }
+
+ resize(p);
+
+ return 0;
+}
+
+static void check_events(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+
+ int e = p->glctx->check_events(vo);
+ if (e & VO_EVENT_REINIT) {
+ uninit_gl(p);
+ init_gl(p);
+ init_video(p);
+ resize(p);
+ }
+ if (e & VO_EVENT_RESIZE)
+ resize(p);
+ if (e & VO_EVENT_EXPOSE)
+ vo->want_redraw = true;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct gl_priv *p = vo->priv;
+
+ switch (request) {
+ case VOCTRL_QUERY_FORMAT:
+ return query_format(*(uint32_t *)data);
+ case VOCTRL_GET_IMAGE:
+ return get_image(vo, data);
+ case VOCTRL_DRAW_IMAGE:
+ return draw_image(p, data);
+ case VOCTRL_DRAW_EOSD:
+ if (!data)
+ return VO_FALSE;
+ draw_eosd(p, data);
+ return VO_TRUE;
+ case VOCTRL_GET_EOSD_RES: {
+ mp_eosd_res_t *r = data;
+ r->w = vo->dwidth;
+ r->h = vo->dheight;
+ r->ml = r->mr = p->border_x;
+ r->mt = r->mb = p->border_y;
+ return VO_TRUE;
+ }
+ case VOCTRL_ONTOP:
+ if (!p->glctx->ontop)
+ break;
+ p->glctx->ontop(vo);
+ return VO_TRUE;
+ case VOCTRL_FULLSCREEN:
+ p->glctx->fullscreen(vo);
+ resize(p);
+ return VO_TRUE;
+ case VOCTRL_BORDER:
+ if (!p->glctx->border)
+ break;
+ p->glctx->border(vo);
+ resize(p);
+ return VO_TRUE;
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ resize(p);
+ return VO_TRUE;
+ case VOCTRL_GET_EQUALIZER: {
+ struct voctrl_get_equalizer_args *args = data;
+ return mp_csp_equalizer_get(&p->video_eq, args->name, args->valueptr)
+ >= 0 ? VO_TRUE : VO_NOTIMPL;
+ }
+ case VOCTRL_SET_EQUALIZER: {
+ struct voctrl_set_equalizer_args *args = data;
+ if (mp_csp_equalizer_set(&p->video_eq, args->name, args->value) < 0)
+ return VO_NOTIMPL;
+ if (!p->use_gamma && p->video_eq.values[MP_CSP_EQ_GAMMA] != 0) {
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Auto-enabling gamma.\n");
+ p->use_gamma = true;
+ compile_shaders(p);
+ }
+ update_all_uniforms(p);
+ vo->want_redraw = true;
+ return VO_TRUE;
+ }
+ case VOCTRL_SET_YUV_COLORSPACE: {
+ if (p->is_yuv) {
+ p->colorspace = *(struct mp_csp_details *)data;
+ update_all_uniforms(p);
+ vo->want_redraw = true;
+ }
+ return VO_TRUE;
+ }
+ case VOCTRL_GET_YUV_COLORSPACE:
+ *(struct mp_csp_details *)data = p->colorspace;
+ return VO_TRUE;
+ case VOCTRL_UPDATE_SCREENINFO:
+ if (!p->glctx->update_xinerama_info)
+ break;
+ p->glctx->update_xinerama_info(vo);
+ return VO_TRUE;
+ case VOCTRL_SCREENSHOT: {
+ struct voctrl_screenshot_args *args = data;
+ if (args->full_window)
+ args->out_image = get_window_screenshot(p);
+ else
+ args->out_image = get_screenshot(p);
+ return true;
+ }
+ case VOCTRL_REDRAW_FRAME:
+ do_render(p);
+ return true;
+ }
+ return VO_NOTIMPL;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct gl_priv *p = vo->priv;
+
+ uninit_gl(p);
+ uninit_mpglcontext(p->glctx);
+ p->glctx = NULL;
+ p->gl = NULL;
+}
+
+#ifdef CONFIG_LCMS2
+
+static void lcms2_error_handler(cmsContext ctx, cmsUInt32Number code,
+ const char *msg)
+{
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] lcms2: %s\n", msg);
+}
+
+static struct bstr load_file(struct gl_priv *p, void *talloc_ctx,
+ const char *filename)
+{
+ struct bstr res = {0};
+ stream_t *s = open_stream(filename, p->vo->opts, NULL);
+ if (s) {
+ res = stream_read_complete(s, talloc_ctx, 1000000000, 0);
+ free_stream(s);
+ }
+ return res;
+}
+
+#define LUT3D_CACHE_HEADER "mplayer2 3dlut cache 1.0\n"
+
+static bool load_icc(struct gl_priv *p, const char *icc_file,
+ const char *icc_cache, int icc_intent,
+ int s_r, int s_g, int s_b)
+{
+ void *tmp = talloc_new(p);
+ uint16_t *output = talloc_array(tmp, uint16_t, s_r * s_g * s_b * 3);
+
+ if (icc_intent == -1)
+ icc_intent = INTENT_ABSOLUTE_COLORIMETRIC;
+
+ mp_msg(MSGT_VO, MSGL_INFO, "[gl] Opening ICC profile '%s'\n", icc_file);
+ struct bstr iccdata = load_file(p, tmp, icc_file);
+ if (!iccdata.len)
+ goto error_exit;
+
+ char *cache_info = talloc_asprintf(tmp, "intent=%d, size=%dx%dx%d\n",
+ icc_intent, s_r, s_g, s_b);
+
+ // check cache
+ if (icc_cache) {
+ mp_msg(MSGT_VO, MSGL_INFO, "[gl] Opening 3D LUT cache in file '%s'.\n",
+ icc_cache);
+ struct bstr cachedata = load_file(p, tmp, icc_cache);
+ if (bstr_eatstart(&cachedata, bstr(LUT3D_CACHE_HEADER))
+ && bstr_eatstart(&cachedata, bstr(cache_info))
+ && bstr_eatstart(&cachedata, iccdata)
+ && cachedata.len == talloc_get_size(output))
+ {
+ memcpy(output, cachedata.start, cachedata.len);
+ goto done;
+ } else {
+ mp_msg(MSGT_VO, MSGL_WARN, "[gl] 3D LUT cache invalid!\n");
+ }
+ }
+
+ cmsSetLogErrorHandler(lcms2_error_handler);
+
+ cmsHPROFILE profile = cmsOpenProfileFromMem(iccdata.start, iccdata.len);
+ if (!profile)
+ goto error_exit;
+
+ cmsCIExyY d65;
+ cmsWhitePointFromTemp(&d65, 6504);
+ static const cmsCIExyYTRIPLE bt709prim = {
+ .Red = {0.64, 0.33, 1.0},
+ .Green = {0.30, 0.60, 1.0},
+ .Blue = {0.15, 0.06, 1.0},
+ };
+ cmsToneCurve *tonecurve = cmsBuildGamma(NULL, 2.2);
+ cmsHPROFILE vid_profile = cmsCreateRGBProfile(&d65, &bt709prim,
+ (cmsToneCurve*[3]){tonecurve, tonecurve, tonecurve});
+ cmsFreeToneCurve(tonecurve);
+ cmsHTRANSFORM trafo = cmsCreateTransform(vid_profile, TYPE_RGB_16,
+ profile, TYPE_RGB_16,
+ icc_intent,
+ cmsFLAGS_HIGHRESPRECALC);
+ cmsCloseProfile(profile);
+ cmsCloseProfile(vid_profile);
+
+ if (!trafo)
+ goto error_exit;
+
+ // transform a (s_r)x(s_g)x(s_b) cube, with 3 components per channel
+ uint16_t *input = talloc_array(tmp, uint16_t, s_r * 3);
+ for (int b = 0; b < s_b; b++) {
+ for (int g = 0; g < s_g; g++) {
+ for (int r = 0; r < s_r; r++) {
+ input[r * 3 + 0] = r * 65535 / (s_r - 1);
+ input[r * 3 + 1] = g * 65535 / (s_g - 1);
+ input[r * 3 + 2] = b * 65535 / (s_b - 1);
+ }
+ size_t base = (b * s_r * s_g + g * s_r) * 3;
+ cmsDoTransform(trafo, input, output + base, s_r);
+ }
+ }
+
+ cmsDeleteTransform(trafo);
+
+ if (icc_cache) {
+ FILE *out = fopen(icc_cache, "wb");
+ if (out) {
+ fprintf(out, "%s%s", LUT3D_CACHE_HEADER, cache_info);
+ fwrite(iccdata.start, iccdata.len, 1, out);
+ fwrite(output, talloc_get_size(output), 1, out);
+ fclose(out);
+ }
+ }
+
+done:
+
+ p->lut_3d_data = talloc_steal(p, output);
+ p->lut_3d_w = s_r, p->lut_3d_h = s_g, p->lut_3d_d = s_b;
+ p->use_lut_3d = true;
+
+ talloc_free(tmp);
+ return true;
+
+error_exit:
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error loading ICC profile.\n");
+ talloc_free(tmp);
+ return false;
+}
+
+#else /* CONFIG_LCMS2 */
+
+static bool load_icc(struct gl_priv *p, ...)
+{
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] LCMS2 support not compiled.\n");
+ return false;
+}
+
+#endif /* CONFIG_LCMS2 */
+
+static bool parse_3dlut_size(const char *s, int *p1, int *p2, int *p3)
+{
+ if (sscanf(s, "%dx%dx%d", p1, p2, p3) != 3)
+ return false;
+ for (int n = 0; n < 3; n++) {
+ int s = ((int[]) { *p1, *p2, *p3 })[n];
+ if (s < 2 || s > 256 || ((s - 1) & s))
+ return false;
+ }
+ return true;
+}
+
+static int lut3d_size_valid(void *arg)
+{
+ char *s = *(char **)arg;
+ int p1, p2, p3;
+ return parse_3dlut_size(s, &p1, &p2, &p3);
+}
+
+static int backend_valid(void *arg)
+{
+ return mpgl_find_backend(*(const char **)arg) >= 0;
+}
+
+struct fbo_format {
+ const char *name;
+ GLint format;
+};
+
+const struct fbo_format fbo_formats[] = {
+ {"rgb", GL_RGB},
+ {"rgba", GL_RGBA},
+ {"rgb8", GL_RGB8},
+ {"rgb16", GL_RGB16},
+ {"rgb16f", GL_RGB16F},
+ {"rgb32f", GL_RGB32F},
+ {0}
+};
+
+static GLint find_fbo_format(const char *name)
+{
+ for (const struct fbo_format *fmt = fbo_formats; fmt->name; fmt++) {
+ if (strcmp(fmt->name, name) == 0)
+ return fmt->format;
+ }
+ return -1;
+}
+
+static int fbo_format_valid(void *arg)
+{
+ return find_fbo_format(*(const char **)arg) >= 0;
+}
+
+static bool can_use_filter_kernel(const struct filter_kernel *kernel)
+{
+ if (!kernel)
+ return false;
+ struct filter_kernel k = *kernel;
+ return mp_init_filter(&k, filter_sizes, 1);
+}
+
+static const char* handle_scaler_opt(const char *name)
+{
+ const struct filter_kernel *kernel = mp_find_filter_kernel(name);
+ if (can_use_filter_kernel(kernel))
+ return kernel->name;
+
+ for (const char **filter = fixed_scale_filters; *filter; filter++) {
+ if (strcmp(*filter, name) == 0)
+ return *filter;
+ }
+
+ return NULL;
+}
+
+static int scaler_valid(void *arg)
+{
+ return handle_scaler_opt(*(const char **)arg) != NULL;
+}
+
+#if 0
+static void print_scalers(void)
+{
+ mp_msg(MSGT_VO, MSGL_INFO, "Available scalers:\n");
+ for (const char **e = fixed_scale_filters; *e; e++) {
+ mp_msg(MSGT_VO, MSGL_INFO, " %s\n", *e);
+ }
+ for (const struct filter_kernel *e = mp_filter_kernels; e->name; e++) {
+ if (can_use_filter_kernel(e))
+ mp_msg(MSGT_VO, MSGL_INFO, " %s\n", e->name);
+ }
+}
+#endif
+
+static int preinit(struct vo *vo, const char *arg)
+{
+ struct gl_priv *p = talloc_zero(vo, struct gl_priv);
+ vo->priv = p;
+
+ *p = (struct gl_priv) {
+ .vo = vo,
+ .colorspace = MP_CSP_DETAILS_DEFAULTS,
+ .use_npot = 1,
+ .use_pbo = 0,
+ .swap_interval = 1,
+ .osd_color = 0xffffff,
+ .fbo_format = GL_RGB16,
+ .use_scale_sep = 1,
+ .use_fancy_downscaling = 1,
+ .scalers = {
+ { .index = 0, .name = "lanczos2" },
+ { .index = 1, .name = "bilinear" },
+ },
+ .scaler_params = {NAN, NAN},
+ };
+
+
+ char *scalers[2] = {0};
+ char *backend_arg = NULL;
+ char *fbo_format = NULL;
+ char *icc_profile = NULL;
+ char *icc_cache = NULL;
+ int icc_intent = -1;
+ char *icc_size_str = NULL;
+
+ const opt_t subopts[] = {
+ {"gamma", OPT_ARG_BOOL, &p->use_gamma},
+ {"srgb", OPT_ARG_BOOL, &p->use_srgb},
+ {"npot", OPT_ARG_BOOL, &p->use_npot},
+ {"pbo", OPT_ARG_BOOL, &p->use_pbo},
+ {"glfinish", OPT_ARG_BOOL, &p->use_glFinish},
+ {"swapinterval", OPT_ARG_INT, &p->swap_interval},
+ {"osdcolor", OPT_ARG_INT, &p->osd_color},
+ {"stereo", OPT_ARG_INT, &p->stereo_mode},
+ {"lscale", OPT_ARG_MSTRZ, &scalers[0], scaler_valid},
+ {"cscale", OPT_ARG_MSTRZ, &scalers[1], scaler_valid},
+ {"lparam1", OPT_ARG_FLOAT, &p->scaler_params[0]},
+ {"lparam2", OPT_ARG_FLOAT, &p->scaler_params[1]},
+ {"fancy-downscaling", OPT_ARG_BOOL, &p->use_fancy_downscaling},
+ {"debug", OPT_ARG_BOOL, &p->use_gl_debug},
+ {"force-gl2", OPT_ARG_BOOL, &p->use_gl2},
+ {"indirect", OPT_ARG_BOOL, &p->use_indirect},
+ {"scale-sep", OPT_ARG_BOOL, &p->use_scale_sep},
+ {"fbo-format", OPT_ARG_MSTRZ, &fbo_format, fbo_format_valid},
+ {"backend", OPT_ARG_MSTRZ, &backend_arg, backend_valid},
+ {"icc-profile", OPT_ARG_MSTRZ, &icc_profile},
+ {"icc-cache", OPT_ARG_MSTRZ, &icc_cache},
+ {"icc-intent", OPT_ARG_INT, &icc_intent},
+ {"3dlut-size", OPT_ARG_MSTRZ, &icc_size_str,
+ lut3d_size_valid},
+ {"dither-depth", OPT_ARG_INT, &p->dither_depth},
+ {NULL}
+ };
+
+ if (subopt_parse(arg, subopts) != 0) {
+ mp_msg(MSGT_VO, MSGL_FATAL, help_text);
+ goto err_out;
+ }
+
+ int backend = backend_arg ? mpgl_find_backend(backend_arg) : GLTYPE_AUTO;
+ free(backend_arg);
+
+ if (fbo_format)
+ p->fbo_format = find_fbo_format(fbo_format);
+ free(fbo_format);
+
+ for (int n = 0; n < 2; n++) {
+ if (scalers[n])
+ p->scalers[n].name = handle_scaler_opt(scalers[n]);
+ free(scalers[n]);
+ }
+
+ int s_r = 128, s_g = 256, s_b = 64;
+ if (icc_size_str)
+ parse_3dlut_size(icc_size_str, &s_r, &s_g, &s_b);
+ free(icc_size_str);
+
+ bool success = true;
+ if (icc_profile) {
+ success = load_icc(p, icc_profile, icc_cache, icc_intent,
+ s_r, s_g, s_b);
+ }
+ free(icc_profile);
+ free(icc_cache);
+
+ if (!success)
+ goto err_out;
+
+ p->eosd = eosd_packer_create(vo);
+
+ p->glctx = init_mpglcontext(backend, vo);
+ if (!p->glctx)
+ goto err_out;
+ p->gl = p->glctx->gl;
+
+ if (true) {
+ if (!config_window(p, 320, 200, VOFLAG_HIDDEN))
+ goto err_out;
+ // We created a window to test whether the GL context could be
+ // created and so on. Destroy that window to make sure all state
+ // associated with it is lost.
+ uninit(vo);
+ p->glctx = init_mpglcontext(backend, vo);
+ if (!p->glctx)
+ goto err_out;
+ p->gl = p->glctx->gl;
+ }
+
+ return 0;
+
+err_out:
+ uninit(vo);
+ return -1;
+}
+
+const struct vo_driver video_out_gl3 = {
+ .is_new = true,
+ .info = &(const vo_info_t) {
+ "OpenGL 3.x",
+ "gl3",
+ "Based on vo_gl.c by Reimar Doeffinger",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page = flip_page,
+ .check_events = check_events,
+ .uninit = uninit,
+};
+
+static const char help_text[] =
+"\n--vo=gl3 command line help:\n"
+"Example: mplayer --vo=gl3:scale-sep:lscale=lanczos2\n"
+"\nOptions:\n"
+" lscale=<filter>\n"
+" Set the scaling filter. Possible choices:\n"
+" bilinear: bilinear texture filtering (fastest).\n"
+" bicubic_fast: bicubic filter (without lookup texture).\n"
+" sharpen3: unsharp masking (sharpening) with radius=3.\n"
+" sharpen5: unsharp masking (sharpening) with radius=5.\n"
+" lanczos2: Lanczos with radius=2 (default, recommended).\n"
+" lanczos3: Lanczos with radius=3 (not recommended).\n"
+" mitchell: Mitchell-Netravali.\n"
+" Default: lanczos2\n"
+" lparam1=<value> / lparam2=<value>\n"
+" Set parameters for configurable filters. Affects chroma scaler\n"
+" as well.\n"
+" Filters which use this:\n"
+" mitchell: b and c params (defaults: b=1/3 c=1/3)\n"
+" kaiser: (defaults: 6.33 6.33)\n"
+" sharpen3: lparam1 sets sharpening strength (default: 0.5)\n"
+" sharpen5: as with sharpen3\n"
+" osdcolor=<0xAARRGGBB>\n"
+" Use the given color for the OSD.\n"
+" stereo=<n>\n"
+" 0: normal display\n"
+" 1: side-by-side to red-cyan stereo\n"
+" 2: side-by-side to green-magenta stereo\n"
+" 3: side-by-side to quadbuffer stereo\n"
+" srgb\n"
+" Enable gamma-correct scaling by working in linear light. This\n"
+" makes use of sRGB textures and framebuffers.\n"
+" This option forces the options 'indirect' and 'gamma'.\n"
+" NOTE: for BT.709 colorspaces, a gamma of 2.35 is assumed. For\n"
+" other YUV colorspaces, 2.2 is assumed. RGB input is always\n"
+" assumed to be in sRGB.\n"
+" pbo\n"
+" Enable use of PBOs. This is faster, but can sometimes lead to\n"
+" sparodic and temporary image corruption.\n"
+" dither-depth=<n>\n"
+" Positive non-zero values select the target bit depth.\n"
+" -1: Disable any dithering done by mplayer.\n"
+" 0: Automatic selection. If output bit depth can't be detected,\n"
+" 8 bits per component are assumed.\n"
+" 8: Dither to 8 bit output.\n"
+" Default: 0.\n"
+" Note that dithering will always be disabled if the bit depth\n"
+" of the video is lower or qual to the detected dither-depth.\n"
+" If color management is enabled, input depth is assumed to be\n"
+" 16 bits, because the 3D LUT output is 16 bit wide.\n"
+" debug\n"
+" Check for OpenGL errors, i.e. call glGetError(). Also request a\n"
+" debug OpenGL context.\n"
+"Less useful options:\n"
+" swapinterval=<n>\n"
+" Interval in displayed frames between to buffer swaps.\n"
+" 1 is equivalent to enable VSYNC, 0 to disable VSYNC.\n"
+" no-scale-sep\n"
+" When using a separable scale filter for luma, usually two filter\n"
+" passes are done. This is often faster. However, it forces\n"
+" conversion to RGB in an extra pass, so it can actually be slower\n"
+" if used with fast filters on small screen resolutions. Using\n"
+" this options will make rendering a single operation.\n"
+" Note that chroma scalers are always done as 1-pass filters.\n"
+" cscale=<n>\n"
+" As lscale but for chroma (2x slower with little visible effect).\n"
+" Note that with some scaling filters, upscaling is always done in\n"
+" RGB. If chroma is not subsampled, this option is ignored, and the\n"
+" luma scaler is used instead. Setting this option is often useless.\n"
+" no-fancy-downscaling\n"
+" When using convolution based filters, don't extend the filter\n"
+" size when downscaling. Trades downscaling performance for\n"
+" reduced quality.\n"
+" no-npot\n"
+" Force use of power-of-2 texture sizes. For debugging only.\n"
+" Borders will look discolored due to filtering.\n"
+" glfinish\n"
+" Call glFinish() before swapping buffers\n"
+" backend=<sys>\n"
+" auto: auto-select (default)\n"
+" cocoa: Cocoa/OSX\n"
+" win: Win32/WGL\n"
+" x11: X11/GLX\n"
+" indirect\n"
+" Do YUV conversion and scaling as separate passes. This will\n"
+" first render the video into a video-sized RGB texture, and\n"
+" draw the result on screen. The luma scaler is used to scale\n"
+" the RGB image when rendering to screen. The chroma scaler\n"
+" is used only on YUV conversion, and only if the video uses\n"
+" chroma-subsampling.\n"
+" This mechanism is disabled on RGB input.\n"
+" fbo-format=<fmt>\n"
+" Selects the internal format of any FBO textures used.\n"
+" fmt can be one of: rgb, rgba, rgb8, rgb16, rgb16f, rgb32f\n"
+" Default: rgb16.\n"
+" gamma\n"
+" Always enable gamma control. (Disables delayed enabling.)\n"
+" force-gl2\n"
+" Create a legacy GL context. This will randomly malfunction\n"
+" if the proper extensions are not supported.\n"
+"Color management:\n"
+" icc-profile=<file>\n"
+" Load an ICC profile and use it to transform linear RGB to\n"
+" screen output. Needs LittleCMS2 support compiled in.\n"
+" icc-cache=<file>\n"
+" Store and load the 3D LUT created from the ICC profile in\n"
+" this file. This can be used to speed up loading, since\n"
+" LittleCMS2 can take a while to create the 3D LUT.\n"
+" Note that this file will be at most about 100 MB big.\n"
+" icc-intent=<value>\n"
+" 0: perceptual\n"
+" 1: relative colorimetric\n"
+" 2: saturation\n"
+" 3: absolute colorimetric (default)\n"
+" 3dlut-size=<r>x<g>x<b>\n"
+" Size of the 3D LUT generated from the ICC profile in each\n"
+" dimension. Default is 128x256x64.\n"
+" Sizes must be a power of two, and 256 at most.\n"
+"\n";
diff --git a/libvo/vo_gl3_shaders.glsl b/libvo/vo_gl3_shaders.glsl
new file mode 100644
index 0000000000..f67e55e6f5
--- /dev/null
+++ b/libvo/vo_gl3_shaders.glsl
@@ -0,0 +1,316 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * mplayer2 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.
+ *
+ * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// Note that this file is not directly passed as shader, but run through some
+// text processing functions, and in fact contains multiple vertex and fragment
+// shaders.
+
+// inserted at the beginning of all shaders
+#!section prelude
+#!section vertex_all
+uniform mat3 transform;
+uniform sampler3D lut_3d;
+
+in vec2 vertex_position;
+in vec4 vertex_color;
+out vec4 color;
+in vec2 vertex_texcoord;
+out vec2 texcoord;
+
+void main() {
+ vec3 position = vec3(vertex_position, 1);
+#ifndef FIXED_SCALE
+ position = transform * position;
+#endif
+ gl_Position = vec4(position, 1);
+ color = vertex_color;
+#ifdef USE_3DLUT
+ color = vec4(texture(lut_3d, color.rgb).rgb, color.a);
+#endif
+ texcoord = vertex_texcoord;
+}
+
+#!section frag_eosd
+uniform sampler2D texture1;
+
+in vec2 texcoord;
+in vec4 color;
+out vec4 out_color;
+
+void main() {
+ out_color = vec4(color.rgb, color.a * texture(texture1, texcoord).r);
+}
+
+#!section frag_osd
+uniform sampler2D texture1;
+
+in vec2 texcoord;
+in vec4 color;
+out vec4 out_color;
+
+void main() {
+ out_color = texture(texture1, texcoord).rrrg * color;
+}
+
+#!section frag_video
+uniform sampler2D texture1;
+uniform sampler2D texture2;
+uniform sampler2D texture3;
+uniform sampler1D lut_c_1d;
+uniform sampler1D lut_l_1d;
+uniform sampler2D lut_c_2d;
+uniform sampler2D lut_l_2d;
+uniform sampler3D lut_3d;
+uniform sampler2D dither;
+uniform mat4x3 colormatrix;
+uniform vec3 inv_gamma;
+uniform float conv_gamma;
+uniform float dither_quantization;
+uniform float dither_multiply;
+uniform float filter_param1;
+
+in vec2 texcoord;
+out vec4 out_color;
+
+vec4 sample_bilinear(sampler2D tex, vec2 texcoord) {
+ return texture(tex, texcoord);
+}
+
+// Explanation how bicubic scaling with only 4 texel fetches is done:
+// http://www.mate.tue.nl/mate/pdfs/10318.pdf
+// 'Efficient GPU-Based Texture Interpolation using Uniform B-Splines'
+// Explanation why this algorithm normally always blurs, even with unit scaling:
+// http://bigwww.epfl.ch/preprints/ruijters1001p.pdf
+// 'GPU Prefilter for Accurate Cubic B-spline Interpolation'
+vec4 calcweights(float s) {
+ vec4 t = vec4(-0.5, 0.1666, 0.3333, -0.3333) * s + vec4(1, 0, -0.5, 0.5);
+ t = t * s + vec4(0, 0, -0.5, 0.5);
+ t = t * s + vec4(-0.6666, 0, 0.8333, 0.1666);
+ vec2 a = vec2(1 / t.z, 1 / t.w);
+ t.xy = t.xy * a + vec2(1, 1);
+ t.x = t.x + s;
+ t.y = t.y - s;
+ return t;
+}
+
+vec4 sample_bicubic_fast(sampler2D tex, vec2 texcoord) {
+ vec2 texsize = textureSize(tex, 0);
+ vec2 pt = 1 / texsize;
+ vec2 fcoord = fract(texcoord * texsize + vec2(0.5, 0.5));
+ vec4 parmx = calcweights(fcoord.x);
+ vec4 parmy = calcweights(fcoord.y);
+ vec4 cdelta;
+ cdelta.xz = parmx.rg * vec2(-pt.x, pt.x);
+ cdelta.yw = parmy.rg * vec2(-pt.y, pt.y);
+ // first y-interpolation
+ vec4 ar = texture(tex, texcoord + cdelta.xy);
+ vec4 ag = texture(tex, texcoord + cdelta.xw);
+ vec4 ab = mix(ag, ar, parmy.b);
+ // second y-interpolation
+ vec4 br = texture(tex, texcoord + cdelta.zy);
+ vec4 bg = texture(tex, texcoord + cdelta.zw);
+ vec4 aa = mix(bg, br, parmy.b);
+ // x-interpolation
+ return mix(aa, ab, parmx.b);
+}
+
+float[2] weights2(sampler1D lookup, float f) {
+ vec4 c = texture(lookup, f);
+ return float[2](c.r, c.g);
+}
+
+float[4] weights4(sampler1D lookup, float f) {
+ vec4 c = texture(lookup, f);
+ return float[4](c.r, c.g, c.b, c.a);
+}
+
+float[6] weights6(sampler2D lookup, float f) {
+ vec4 c1 = texture(lookup, vec2(0.25, f));
+ vec4 c2 = texture(lookup, vec2(0.75, f));
+ return float[6](c1.r, c1.g, c1.b, c2.r, c2.g, c2.b);
+}
+
+float[8] weights8(sampler2D lookup, float f) {
+ vec4 c1 = texture(lookup, vec2(0.25, f));
+ vec4 c2 = texture(lookup, vec2(0.75, f));
+ return float[8](c1.r, c1.g, c1.b, c1.a, c2.r, c2.g, c2.b, c2.a);
+}
+
+float[12] weights12(sampler2D lookup, float f) {
+ vec4 c1 = texture(lookup, vec2(1.0/6.0, f));
+ vec4 c2 = texture(lookup, vec2(0.5, f));
+ vec4 c3 = texture(lookup, vec2(5.0/6.0, f));
+ return float[12](c1.r, c1.g, c1.b, c1.a,
+ c2.r, c2.g, c2.b, c2.a,
+ c3.r, c3.g, c3.b, c3.a);
+}
+
+float[16] weights16(sampler2D lookup, float f) {
+ vec4 c1 = texture(lookup, vec2(0.125, f));
+ vec4 c2 = texture(lookup, vec2(0.375, f));
+ vec4 c3 = texture(lookup, vec2(0.625, f));
+ vec4 c4 = texture(lookup, vec2(0.875, f));
+ return float[16](c1.r, c1.g, c1.b, c1.a, c2.r, c2.g, c2.b, c2.a,
+ c3.r, c3.g, c3.b, c3.a, c4.r, c4.g, c4.b, c4.a);
+}
+
+#define CONVOLUTION_SEP_N(NAME, N) \
+ vec4 NAME(sampler2D tex, vec2 texcoord, vec2 pt, float weights[N]) { \
+ vec4 res = vec4(0); \
+ for (int n = 0; n < N; n++) { \
+ res += weights[n] * texture(tex, texcoord + pt * n); \
+ } \
+ return res; \
+ }
+
+CONVOLUTION_SEP_N(convolution_sep2, 2)
+CONVOLUTION_SEP_N(convolution_sep4, 4)
+CONVOLUTION_SEP_N(convolution_sep6, 6)
+CONVOLUTION_SEP_N(convolution_sep8, 8)
+CONVOLUTION_SEP_N(convolution_sep12, 12)
+CONVOLUTION_SEP_N(convolution_sep16, 16)
+
+// The dir parameter is (0, 1) or (1, 0), and we expect the shader compiler to
+// remove all the redundant multiplications and additions.
+#define SAMPLE_CONVOLUTION_SEP_N(NAME, N, SAMPLERT, CONV_FUNC, WEIGHTS_FUNC)\
+ vec4 NAME(vec2 dir, SAMPLERT lookup, sampler2D tex, vec2 texcoord) { \
+ vec2 texsize = textureSize(tex, 0); \
+ vec2 pt = (1 / texsize) * dir; \
+ float fcoord = dot(fract(texcoord * texsize - 0.5), dir); \
+ vec2 base = texcoord - fcoord * pt; \
+ return CONV_FUNC(tex, base - pt * (N / 2 - 1), pt, \
+ WEIGHTS_FUNC(lookup, fcoord)); \
+ }
+
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep2, 2, sampler1D, convolution_sep2, weights2)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep4, 4, sampler1D, convolution_sep4, weights4)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep6, 6, sampler2D, convolution_sep6, weights6)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep8, 8, sampler2D, convolution_sep8, weights8)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep12, 12, sampler2D, convolution_sep12, weights12)
+SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep16, 16, sampler2D, convolution_sep16, weights16)
+
+
+#define CONVOLUTION_N(NAME, N) \
+ vec4 NAME(sampler2D tex, vec2 texcoord, vec2 pt, float taps_x[N], \
+ float taps_y[N]) { \
+ vec4 res = vec4(0); \
+ for (int y = 0; y < N; y++) { \
+ vec4 line = vec4(0); \
+ for (int x = 0; x < N; x++) \
+ line += taps_x[x] * texture(tex, texcoord + pt * vec2(x, y));\
+ res += taps_y[y] * line; \
+ } \
+ return res; \
+ }
+
+CONVOLUTION_N(convolution2, 2)
+CONVOLUTION_N(convolution4, 4)
+CONVOLUTION_N(convolution6, 6)
+CONVOLUTION_N(convolution8, 8)
+CONVOLUTION_N(convolution12, 12)
+CONVOLUTION_N(convolution16, 16)
+
+#define SAMPLE_CONVOLUTION_N(NAME, N, SAMPLERT, CONV_FUNC, WEIGHTS_FUNC) \
+ vec4 NAME(SAMPLERT lookup, sampler2D tex, vec2 texcoord) { \
+ vec2 texsize = textureSize(tex, 0); \
+ vec2 pt = 1 / texsize; \
+ vec2 fcoord = fract(texcoord * texsize - 0.5); \
+ vec2 base = texcoord - fcoord * pt; \
+ return CONV_FUNC(tex, base - pt * (N / 2 - 1), pt, \
+ WEIGHTS_FUNC(lookup, fcoord.x), \
+ WEIGHTS_FUNC(lookup, fcoord.y)); \
+ }
+
+SAMPLE_CONVOLUTION_N(sample_convolution2, 2, sampler1D, convolution2, weights2)
+SAMPLE_CONVOLUTION_N(sample_convolution4, 4, sampler1D, convolution4, weights4)
+SAMPLE_CONVOLUTION_N(sample_convolution6, 6, sampler2D, convolution6, weights6)
+SAMPLE_CONVOLUTION_N(sample_convolution8, 8, sampler2D, convolution8, weights8)
+SAMPLE_CONVOLUTION_N(sample_convolution12, 12, sampler2D, convolution12, weights12)
+SAMPLE_CONVOLUTION_N(sample_convolution16, 16, sampler2D, convolution16, weights16)
+
+
+// Unsharp masking
+vec4 sample_sharpen3(sampler2D tex, vec2 texcoord) {
+ vec2 texsize = textureSize(tex, 0);
+ vec2 pt = 1 / texsize;
+ vec2 st = pt * 0.5;
+ vec4 p = texture(tex, texcoord);
+ vec4 sum = texture(tex, texcoord + st * vec2(+1, +1))
+ + texture(tex, texcoord + st * vec2(+1, -1))
+ + texture(tex, texcoord + st * vec2(-1, +1))
+ + texture(tex, texcoord + st * vec2(-1, -1));
+ return p + (p - 0.25 * sum) * filter_param1;
+}
+
+vec4 sample_sharpen5(sampler2D tex, vec2 texcoord) {
+ vec2 texsize = textureSize(tex, 0);
+ vec2 pt = 1 / texsize;
+ vec2 st1 = pt * 1.2;
+ vec4 p = texture(tex, texcoord);
+ vec4 sum1 = texture(tex, texcoord + st1 * vec2(+1, +1))
+ + texture(tex, texcoord + st1 * vec2(+1, -1))
+ + texture(tex, texcoord + st1 * vec2(-1, +1))
+ + texture(tex, texcoord + st1 * vec2(-1, -1));
+ vec2 st2 = pt * 1.5;
+ vec4 sum2 = texture(tex, texcoord + st2 * vec2(+1, 0))
+ + texture(tex, texcoord + st2 * vec2( 0, +1))
+ + texture(tex, texcoord + st2 * vec2(-1, 0))
+ + texture(tex, texcoord + st2 * vec2( 0, -1));
+ vec4 t = p * 0.859375 + sum2 * -0.1171875 + sum1 * -0.09765625;
+ return p + t * filter_param1;
+}
+
+void main() {
+#ifdef USE_PLANAR
+ vec3 color = vec3(SAMPLE_L(texture1, texcoord).r,
+ SAMPLE_C(texture2, texcoord).r,
+ SAMPLE_C(texture3, texcoord).r);
+#else
+ vec3 color = SAMPLE_L(texture1, texcoord).rgb;
+#endif
+#ifdef USE_GBRP
+ color.gbr = color;
+#endif
+#ifdef USE_YGRAY
+ // NOTE: actually slightly wrong for 16 bit input video, and completely
+ // wrong for 9/10 bit input
+ color.gb = vec2(128.0/255.0);
+#endif
+#ifdef USE_COLORMATRIX
+ color = mat3(colormatrix) * color + colormatrix[3];
+#endif
+#ifdef USE_LINEAR_CONV
+ color = pow(color, vec3(2.2));
+#endif
+#ifdef USE_LINEAR_CONV_INV
+ // Convert from linear RGB to gamma RGB before putting it through the 3D-LUT
+ // in the final stage.
+ color = pow(color, vec3(1.0/2.2));
+#endif
+#ifdef USE_GAMMA_POW
+ color = pow(color, inv_gamma);
+#endif
+#ifdef USE_3DLUT
+ color = texture(lut_3d, color).rgb;
+#endif
+#ifdef USE_DITHER
+ float dither = texture(dither, gl_FragCoord.xy / textureSize(dither, 0)).r;
+ color = floor(color * dither_multiply + dither ) / dither_quantization;
+#endif
+ out_color = vec4(color, 1);
+}
diff --git a/libvo/vo_png.c b/libvo/vo_png.c
index 6e23bafd5f..d0d56d3e85 100644
--- a/libvo/vo_png.c
+++ b/libvo/vo_png.c
@@ -50,6 +50,7 @@ static int z_compression;
static int framenum;
static int use_alpha;
static AVCodecContext *avctx;
+static AVFrame *pic;
static uint8_t *outbuffer;
int outbuffer_size;
@@ -70,6 +71,9 @@ config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uin
avctx = avcodec_alloc_context3(png_codec);
if (!avctx)
goto error;
+ pic = avcodec_alloc_frame();
+ if (!pic)
+ goto error;
avctx->width = width;
avctx->height = height;
avctx->pix_fmt = imgfmt2pixfmt(format);
@@ -85,7 +89,6 @@ config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uin
static uint32_t draw_image(mp_image_t* mpi){
- AVFrame pic;
int buffersize;
int res;
char buf[100];
@@ -101,15 +104,17 @@ static uint32_t draw_image(mp_image_t* mpi){
return 1;
}
- pic.data[0] = mpi->planes[0];
- pic.linesize[0] = mpi->stride[0];
+ avcodec_get_frame_defaults(pic);
+
+ pic->data[0] = mpi->planes[0];
+ pic->linesize[0] = mpi->stride[0];
buffersize = mpi->w * mpi->h * 8;
if (outbuffer_size < buffersize) {
av_freep(&outbuffer);
outbuffer = av_malloc(buffersize);
outbuffer_size = buffersize;
}
- res = avcodec_encode_video(avctx, outbuffer, outbuffer_size, &pic);
+ res = avcodec_encode_video(avctx, outbuffer, outbuffer_size, pic);
if(res < 0){
mp_msg(MSGT_VO,MSGL_WARN, "[VO_PNG] Error in create_png.\n");
@@ -156,6 +161,8 @@ static void uninit(void)
avcodec_close(avctx);
av_freep(&avctx);
av_freep(&outbuffer);
+ av_free(pic);
+ pic = NULL;
outbuffer_size = 0;
}
diff --git a/libvo/vo_svga.c b/libvo/vo_svga.c
index 3b8a107c7d..19a454e354 100644
--- a/libvo/vo_svga.c
+++ b/libvo/vo_svga.c
@@ -237,8 +237,8 @@ static uint32_t svga_draw_image(mp_image_t *mpi){
PageStore[cpage].locks=PAGE_BUSY;
// these variables are used in loops
- x = mpi->x;
- y = mpi->y;
+ x = 0;
+ y = 0;
w = mpi->w;
h = mpi->h;
stride = mpi->stride[0];
diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c
index 684776dabc..9343dea3e1 100644
--- a/libvo/vo_vdpau.c
+++ b/libvo/vo_vdpau.c
@@ -56,6 +56,7 @@
#include "libavutil/mathematics.h"
#include "sub/ass_mp.h"
+#include "eosd_packer.h"
#define WRAP_ADD(x, a, m) ((a) < 0 \
? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \
@@ -85,9 +86,6 @@
/* number of palette entries */
#define PALETTE_SIZE 256
-/* Initial size of EOSD surface in pixels (x*x) */
-#define EOSD_SURFACE_INITIAL_SIZE 256
-
/* Pixelformat used for output surfaces */
#define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8
@@ -174,24 +172,8 @@ struct vdpctx {
// EOSD
// Pool of surfaces
- struct eosd_bitmap_surface {
- VdpBitmapSurface surface;
- int w;
- int h;
- uint32_t max_width;
- uint32_t max_height;
- } eosd_surface;
-
- // List of surfaces to be rendered
- struct eosd_target {
- VdpRect source;
- VdpRect dest;
- VdpColor color;
- } *eosd_targets;
- int eosd_targets_size;
- int *eosd_scratch;
-
- int eosd_render_count;
+ VdpBitmapSurface eosd_surface;
+ struct eosd_packer *eosd_packer;
// Video equalizer
struct mp_csp_equalizer video_eq;
@@ -803,13 +785,16 @@ static int initialize_vdpau_objects(struct vo *vo)
if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0)
return -1;
+ int max_width = 0, max_height = 0;
vdp_st = vdp->
bitmap_surface_query_capabilities(vc->vdp_device,
VDP_RGBA_FORMAT_A8,
&(VdpBool){0},
- &vc->eosd_surface.max_width,
- &vc->eosd_surface.max_height);
+ &max_width,
+ &max_height);
CHECK_ST_WARNING("Query to get max EOSD surface size failed");
+ eosd_packer_reinit(vc->eosd_packer, max_width, max_height);
+
forget_frames(vo);
resize(vo);
return 0;
@@ -829,11 +814,9 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo)
for (int i = 0; i <= MAX_OUTPUT_SURFACES; i++)
vc->output_surfaces[i] = VDP_INVALID_HANDLE;
vc->vdp_device = VDP_INVALID_HANDLE;
- vc->eosd_surface = (struct eosd_bitmap_surface){
- .surface = VDP_INVALID_HANDLE,
- };
+ vc->eosd_surface = VDP_INVALID_HANDLE;
+ eosd_packer_reinit(vc->eosd_packer, 0, 0);
vc->output_surface_width = vc->output_surface_height = -1;
- vc->eosd_render_count = 0;
}
static int handle_preemption(struct vo *vo)
@@ -1029,6 +1012,8 @@ static void draw_osd_I8A8(void *ctx, int x0, int y0, int w, int h,
"vdp_output_surface_render_output_surface");
}
+#define EOSD_VDP_RC(r) &(VdpRect){r.x0, r.y0, r.x1, r.y1}
+
static void draw_eosd(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
@@ -1051,200 +1036,66 @@ static void draw_eosd(struct vo *vo)
.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
};
- for (i = 0; i < vc->eosd_render_count; i++) {
+ for (i = 0; i < vc->eosd_packer->targets_count; i++) {
+ struct eosd_target *target = &vc->eosd_packer->targets[i];
+ VdpColor color = {
+ .alpha = 1.0 - ((target->color >> 0) & 0xff) / 255.0,
+ .blue = ((target->color >> 8) & 0xff) / 255.0,
+ .green = ((target->color >> 16) & 0xff) / 255.0,
+ .red = ((target->color >> 24) & 0xff) / 255.0,
+ };
vdp_st = vdp->
output_surface_render_bitmap_surface(output_surface,
- &vc->eosd_targets[i].dest,
- vc->eosd_surface.surface,
- &vc->eosd_targets[i].source,
- &vc->eosd_targets[i].color,
+ EOSD_VDP_RC(target->dest),
+ vc->eosd_surface,
+ EOSD_VDP_RC(target->source),
+ &color,
&blend_state,
VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
CHECK_ST_WARNING("EOSD: Error when rendering");
}
}
-#define HEIGHT_SORT_BITS 4
-static int size_index(struct eosd_target *r)
-{
- unsigned int h = r->source.y1;
- int n = av_log2_16bit(h);
- return (n << HEIGHT_SORT_BITS)
- + (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
-}
-
-/* Pack the given rectangles into an area of size w * h.
- * The size of each rectangle is read from .source.x1/.source.y1.
- * The height of each rectangle must be at least 1 and less than 65536.
- * The .source rectangle is then set corresponding to the packed position.
- * 'scratch' must point to work memory for num_rects+16 ints.
- * Return 0 on success, -1 if the rectangles did not fit in w*h.
- *
- * The rectangles are placed in rows in order approximately sorted by
- * height (the approximate sorting is simpler than a full one would be,
- * and allows the algorithm to work in linear time). Additionally, to
- * reduce wasted space when there are a few tall rectangles, empty
- * lower-right parts of rows are filled recursively when the size of
- * rectangles in the row drops past a power-of-two threshold. So if a
- * row starts with rectangles of size 3x50, 10x40 and 5x20 then the
- * free rectangle with corners (13, 20)-(w, 50) is filled recursively.
- */
-static int pack_rectangles(struct eosd_target *rects, int num_rects,
- int w, int h, int *scratch)
-{
- int bins[16 << HEIGHT_SORT_BITS];
- int sizes[16 << HEIGHT_SORT_BITS] = {};
- for (int i = 0; i < num_rects; i++)
- sizes[size_index(rects + i)]++;
- int idx = 0;
- for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
- for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
- bins[i + j] = idx;
- idx += sizes[i + j];
- }
- scratch[idx++] = -1;
- }
- for (int i = 0; i < num_rects; i++)
- scratch[bins[size_index(rects + i)]++] = i;
- for (int i = 0; i < 16; i++)
- bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
- struct {
- int size, x, bottom;
- } stack[16] = {{15, 0, h}}, s = {};
- int stackpos = 1;
- int y;
- while (stackpos) {
- y = s.bottom;
- s = stack[--stackpos];
- s.size++;
- while (s.size--) {
- int maxy = -1;
- int obj;
- while ((obj = scratch[bins[s.size]]) >= 0) {
- int bottom = y + rects[obj].source.y1;
- if (bottom > s.bottom)
- break;
- int right = s.x + rects[obj].source.x1;
- if (right > w)
- break;
- bins[s.size]++;
- rects[obj].source.x0 = s.x;
- rects[obj].source.x1 += s.x;
- rects[obj].source.y0 = y;
- rects[obj].source.y1 += y;
- num_rects--;
- if (maxy <= 0)
- stack[stackpos++] = s;
- s.x = right;
- maxy = FFMAX(maxy, bottom);
- }
- if (maxy > 0)
- s.bottom = maxy;
- }
- }
- return num_rects ? -1 : 0;
-}
-
static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
{
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
+ struct eosd_packer *packer = vc->eosd_packer;
int i;
- ASS_Image *img = imgs->imgs;
- ASS_Image *p;
- struct eosd_bitmap_surface *sfc = &vc->eosd_surface;
- bool need_upload = false;
-
- if (imgs->changed == 0)
- return; // Nothing changed, no need to redraw
-
- vc->eosd_render_count = 0;
-
- if (!img)
- return; // There's nothing to render!
-
- if (imgs->changed == 1)
- goto eosd_skip_upload;
-
- need_upload = true;
- bool reallocate = false;
- while (1) {
- for (p = img, i = 0; p; p = p->next) {
- if (p->w <= 0 || p->h <= 0)
- continue;
- // Allocate new space for surface/target arrays
- if (i >= vc->eosd_targets_size) {
- vc->eosd_targets_size = FFMAX(vc->eosd_targets_size * 2, 512);
- vc->eosd_targets =
- talloc_realloc_size(vc, vc->eosd_targets,
- vc->eosd_targets_size
- * sizeof(*vc->eosd_targets));
- vc->eosd_scratch =
- talloc_realloc_size(vc, vc->eosd_scratch,
- (vc->eosd_targets_size + 16)
- * sizeof(*vc->eosd_scratch));
- }
- vc->eosd_targets[i].source.x1 = p->w;
- vc->eosd_targets[i].source.y1 = p->h;
- i++;
- }
- if (pack_rectangles(vc->eosd_targets, i, sfc->w, sfc->h,
- vc->eosd_scratch) >= 0)
- break;
- int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE),
- sfc->max_width);
- int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE),
- sfc->max_height);
- if (w == sfc->w && h == sfc->h) {
- mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on "
- "a surface with the maximum supported size\n");
- return;
- } else {
- sfc->w = w;
- sfc->h = h;
- }
- reallocate = true;
- }
- if (reallocate) {
- if (sfc->surface != VDP_INVALID_HANDLE) {
- vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
+
+ bool need_repos, need_upload, need_resize;
+ eosd_packer_generate(packer, imgs, &need_repos, &need_upload, &need_resize);
+
+ if (!need_upload)
+ return;
+
+ if (need_resize) {
+ if (vc->eosd_surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface);
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
- mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
- "EOSD bitmaps.\n", sfc->w, sfc->h);
vdp_st = vdp->bitmap_surface_create(vc->vdp_device, VDP_RGBA_FORMAT_A8,
- sfc->w, sfc->h, true,
- &sfc->surface);
+ packer->surface.w, packer->surface.h,
+ true, &vc->eosd_surface);
if (vdp_st != VDP_STATUS_OK)
- sfc->surface = VDP_INVALID_HANDLE;
+ vc->eosd_surface = VDP_INVALID_HANDLE;
CHECK_ST_WARNING("EOSD: error when creating surface");
}
-eosd_skip_upload:
- if (sfc->surface == VDP_INVALID_HANDLE)
+ if (vc->eosd_surface == VDP_INVALID_HANDLE)
return;
- for (p = img; p; p = p->next) {
- if (p->w <= 0 || p->h <= 0)
- continue;
- struct eosd_target *target = &vc->eosd_targets[vc->eosd_render_count];
- if (need_upload) {
- vdp_st = vdp->
- bitmap_surface_put_bits_native(sfc->surface,
- (const void *) &p->bitmap,
- &p->stride, &target->source);
- CHECK_ST_WARNING("EOSD: putbits failed");
- }
- // Render dest, color, etc.
- target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0;
- target->color.blue = ((p->color >> 8) & 0xff) / 255.0;
- target->color.green = ((p->color >> 16) & 0xff) / 255.0;
- target->color.red = ((p->color >> 24) & 0xff) / 255.0;
- target->dest.x0 = p->dst_x;
- target->dest.y0 = p->dst_y;
- target->dest.x1 = p->w + p->dst_x;
- target->dest.y1 = p->h + p->dst_y;
- vc->eosd_render_count++;
+
+ for (i = 0; i < vc->eosd_packer->targets_count; i++) {
+ struct eosd_target *target = &vc->eosd_packer->targets[i];
+ ASS_Image *p = target->ass_img;
+ vdp_st = vdp->
+ bitmap_surface_put_bits_native(vc->eosd_surface,
+ (const void *) &p->bitmap,
+ &p->stride,
+ EOSD_VDP_RC(target->source));
+ CHECK_ST_WARNING("EOSD: putbits failed");
+ target->ass_img = NULL;
}
}
@@ -1610,8 +1461,8 @@ static void destroy_vdpau_objects(struct vo *vo)
CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
}
- if (vc->eosd_surface.surface != VDP_INVALID_HANDLE) {
- vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface.surface);
+ if (vc->eosd_surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface);
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
@@ -1641,6 +1492,8 @@ static int preinit(struct vo *vo, const char *arg)
{
struct vdpctx *vc = vo->priv;
+ vc->eosd_packer = eosd_packer_create(vo);
+
// Mark everything as invalid first so uninit() can tell what has been
// allocated
mark_vdpau_objects_uninitialized(vo);
diff --git a/libvo/w32_common.c b/libvo/w32_common.c
index 475327ce5c..ebe17bd154 100644
--- a/libvo/w32_common.c
+++ b/libvo/w32_common.c
@@ -18,11 +18,11 @@
#include <stdio.h>
#include <limits.h>
+#include <assert.h>
#include <windows.h>
#include <windowsx.h>
-// To get "#define vo_ontop global_vo->opts->vo_ontop" etc
-#include "old_vo_defines.h"
+#include "options.h"
#include "input/keycodes.h"
#include "input/input.h"
#include "mp_msg.h"
@@ -30,47 +30,17 @@
#include "aspect.h"
#include "w32_common.h"
#include "mp_fifo.h"
-
-#ifndef WM_XBUTTONDOWN
-# define WM_XBUTTONDOWN 0x020B
-# define WM_XBUTTONUP 0x020C
-# define WM_XBUTTONDBLCLK 0x020D
-#endif
-
-#ifndef MONITOR_DEFAULTTOPRIMARY
-#define MONITOR_DEFAULTTOPRIMARY 1
-#endif
+#include "osdep/io.h"
+#include "talloc.h"
#define WIN_ID_TO_HWND(x) ((HWND)(uint32_t)(x))
-static const char classname[] = "MPlayer - The Movie Player";
-int vo_vm = 0;
-
-static int depthonscreen;
-// last non-fullscreen extends
-static int prev_width;
-static int prev_height;
-static int prev_x;
-static int prev_y;
-
-static uint32_t o_dwidth;
-static uint32_t o_dheight;
-
-static HINSTANCE hInstance;
-#define vo_window vo_w32_window
-HWND vo_window = 0;
-/** HDC used when rendering to a device instead of window */
-static HDC dev_hdc;
-static int event_flags;
-static int mon_cnt;
-
-static HMONITOR (WINAPI* myMonitorFromWindow)(HWND, DWORD);
-static BOOL (WINAPI* myGetMonitorInfo)(HMONITOR, LPMONITORINFO);
-static BOOL (WINAPI* myEnumDisplayMonitors)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
+static const wchar_t classname[] = L"mplayer2";
static const struct mp_keymap vk_map[] = {
// special keys
- {VK_ESCAPE, KEY_ESC}, {VK_BACK, KEY_BS}, {VK_TAB, KEY_TAB}, {VK_CONTROL, KEY_CTRL},
+ {VK_ESCAPE, KEY_ESC}, {VK_BACK, KEY_BS}, {VK_TAB, KEY_TAB},
+ {VK_RETURN, KEY_ENTER}, {VK_PAUSE, KEY_PAUSE}, {VK_SNAPSHOT, KEY_PRINT},
// cursor keys
{VK_LEFT, KEY_LEFT}, {VK_UP, KEY_UP}, {VK_RIGHT, KEY_RIGHT}, {VK_DOWN, KEY_DOWN},
@@ -82,7 +52,7 @@ static const struct mp_keymap vk_map[] = {
// F-keys
{VK_F1, KEY_F+1}, {VK_F2, KEY_F+2}, {VK_F3, KEY_F+3}, {VK_F4, KEY_F+4},
{VK_F5, KEY_F+5}, {VK_F6, KEY_F+6}, {VK_F7, KEY_F+7}, {VK_F8, KEY_F+8},
- {VK_F9, KEY_F+9}, {VK_F10, KEY_F+10}, {VK_F11, KEY_F+11}, {VK_F1, KEY_F+12},
+ {VK_F9, KEY_F+9}, {VK_F10, KEY_F+10}, {VK_F11, KEY_F+11}, {VK_F12, KEY_F+12},
// numpad
{VK_NUMPAD0, KEY_KP0}, {VK_NUMPAD1, KEY_KP1}, {VK_NUMPAD2, KEY_KP2},
{VK_NUMPAD3, KEY_KP3}, {VK_NUMPAD4, KEY_KP4}, {VK_NUMPAD5, KEY_KP5},
@@ -92,47 +62,116 @@ static const struct mp_keymap vk_map[] = {
{0, 0}
};
-static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
- RECT r;
- POINT p;
- int mpkey;
+static void add_window_borders(HWND hwnd, RECT *rc)
+{
+ AdjustWindowRect(rc, GetWindowLong(hwnd, GWL_STYLE), 0);
+}
+
+// basically a reverse AdjustWindowRect (win32 doesn't appear to have this)
+static void subtract_window_borders(HWND hwnd, RECT *rc)
+{
+ RECT b = { 0, 0, 0, 0 };
+ add_window_borders(hwnd, &b);
+ rc->left -= b.left;
+ rc->top -= b.top;
+ rc->right -= b.right;
+ rc->bottom -= b.bottom;
+}
+
+// turn a WMSZ_* input value in v into the border that should be resized
+// returns: 0=left, 1=top, 2=right, 3=bottom, -1=undefined
+static int get_resize_border(int v) {
+ switch (v) {
+ case WMSZ_LEFT: return 3;
+ case WMSZ_TOP: return 2;
+ case WMSZ_RIGHT: return 3;
+ case WMSZ_BOTTOM: return 2;
+ case WMSZ_TOPLEFT: return 1;
+ case WMSZ_TOPRIGHT: return 1;
+ case WMSZ_BOTTOMLEFT: return 3;
+ case WMSZ_BOTTOMRIGHT: return 3;
+ default: return -1;
+ }
+}
+
+static bool key_state(struct vo *vo, int vk)
+{
+ return GetKeyState(vk) & 0x8000;
+}
+
+static int mod_state(struct vo *vo)
+{
+ int res = 0;
+ if (key_state(vo, VK_CONTROL))
+ res |= KEY_MODIFIER_CTRL;
+ if (key_state(vo, VK_SHIFT))
+ res |= KEY_MODIFIER_SHIFT;
+ if (key_state(vo, VK_MENU))
+ res |= KEY_MODIFIER_ALT;
+ return res;
+}
+
+static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
+ LPARAM lParam)
+{
+ if (message == WM_NCCREATE) {
+ CREATESTRUCT *cs = (void*)lParam;
+ SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams);
+ }
+ struct vo *vo = (void*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
+ // message before WM_NCCREATE, pray to Raymond Chen that it's not important
+ if (!vo)
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ struct vo_w32_state *w32 = vo->w32;
+
switch (message) {
case WM_ERASEBKGND: // no need to erase background seperately
return 1;
case WM_PAINT:
- event_flags |= VO_EVENT_EXPOSE;
+ w32->event_flags |= VO_EVENT_EXPOSE;
break;
- case WM_MOVE:
- event_flags |= VO_EVENT_MOVE;
- p.x = 0;
- p.y = 0;
- ClientToScreen(vo_window, &p);
- vo_dx = p.x;
- vo_dy = p.y;
+ case WM_MOVE: {
+ w32->event_flags |= VO_EVENT_MOVE;
+ POINT p = {0};
+ ClientToScreen(w32->window, &p);
+ w32->window_x = p.x;
+ w32->window_y = p.y;
+ mp_msg(MSGT_VO, MSGL_V, "[vo] move window: %d:%d\n",
+ w32->window_x, w32->window_y);
break;
- case WM_SIZE:
- event_flags |= VO_EVENT_RESIZE;
- GetClientRect(vo_window, &r);
- vo_dwidth = r.right;
- vo_dheight = r.bottom;
+ }
+ case WM_SIZE: {
+ w32->event_flags |= VO_EVENT_RESIZE;
+ RECT r;
+ GetClientRect(w32->window, &r);
+ vo->dwidth = r.right;
+ vo->dheight = r.bottom;
+ mp_msg(MSGT_VO, MSGL_V, "[vo] resize window: %d:%d\n",
+ vo->dwidth, vo->dheight);
break;
- case WM_WINDOWPOSCHANGING:
+ }
+ case WM_SIZING:
if (vo_keepaspect && !vo_fs && WinID < 0) {
- WINDOWPOS *wpos = (WINDOWPOS*)lParam;
- int xborder, yborder;
- r.left = r.top = 0;
- r.right = wpos->cx;
- r.bottom = wpos->cy;
- AdjustWindowRect(&r, GetWindowLong(vo_window, GWL_STYLE), 0);
- xborder = (r.right - r.left) - wpos->cx;
- yborder = (r.bottom - r.top) - wpos->cy;
- wpos->cx -= xborder; wpos->cy -= yborder;
- aspect_fit(global_vo, &wpos->cx, &wpos->cy, wpos->cx, wpos->cy);
- wpos->cx += xborder; wpos->cy += yborder;
+ RECT *rc = (RECT*)lParam;
+ // get client area of the windows if it had the rect rc
+ // (subtracting the window borders)
+ RECT r = *rc;
+ subtract_window_borders(w32->window, &r);
+ int c_w = r.right - r.left, c_h = r.bottom - r.top;
+ float aspect = vo->aspdat.asp;
+ int d_w = c_h * aspect - c_w;
+ int d_h = c_w / aspect - c_h;
+ int d_corners[4] = { d_w, d_h, -d_w, -d_h };
+ int corners[4] = { rc->left, rc->top, rc->right, rc->bottom };
+ int corner = get_resize_border(wParam);
+ if (corner >= 0)
+ corners[corner] -= d_corners[corner];
+ *rc = (RECT) { corners[0], corners[1], corners[2], corners[3] };
+ return TRUE;
}
- return 0;
+ break;
case WM_CLOSE:
- mplayer_put_key(KEY_CLOSE_WIN);
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
break;
case WM_SYSCOMMAND:
switch (wParam) {
@@ -143,16 +182,42 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l
}
break;
case WM_KEYDOWN:
- mpkey = lookup_keymap_table(vk_map, wParam);
+ case WM_SYSKEYDOWN: {
+ int mpkey = lookup_keymap_table(vk_map, wParam);
if (mpkey)
- mplayer_put_key(mpkey);
+ mplayer_put_key(vo->key_fifo, mpkey | mod_state(vo));
+ if (wParam == VK_F10)
+ return 0;
break;
+ }
case WM_CHAR:
- mplayer_put_key(wParam);
+ case WM_SYSCHAR: {
+ int mods = mod_state(vo);
+ int code = wParam;
+ // Windows enables Ctrl+Alt when AltGr (VK_RMENU) is pressed.
+ // E.g. AltGr+9 on a German keyboard would yield Ctrl+Alt+[
+ // Warning: wine handles this differently. Don't test this on wine!
+ if (key_state(vo, VK_RMENU))
+ mods &= ~(KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT);
+ // Apparently Ctrl+A to Ctrl+Z is special cased, and produces
+ // character codes from 1-26. Work it around.
+ // Also, enter/return (including the keypad variant) and CTRL+J both
+ // map to wParam==10. As a workaround, check VK_RETURN to
+ // distinguish these two key combinations.
+ if ((mods & KEY_MODIFIER_CTRL) && code >= 1 && code <= 26
+ && !key_state(vo, VK_RETURN))
+ code = code - 1 + (mods & KEY_MODIFIER_SHIFT ? 'A' : 'a');
+ if (code >= 32 && code < (1<<21)) {
+ mplayer_put_key(vo->key_fifo, code | mods);
+ // At least with Alt+char, not calling DefWindowProcW stops
+ // Windows from emitting a beep.
+ return 0;
+ }
break;
+ }
case WM_LBUTTONDOWN:
if (!vo_nomouse_input && (vo_fs || (wParam & MK_CONTROL))) {
- mplayer_put_key(MOUSE_BTN0);
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN0 | mod_state(vo));
break;
}
if (!vo_fs) {
@@ -163,36 +228,36 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l
break;
case WM_MBUTTONDOWN:
if (!vo_nomouse_input)
- mplayer_put_key(MOUSE_BTN1);
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN1 | mod_state(vo));
break;
case WM_RBUTTONDOWN:
if (!vo_nomouse_input)
- mplayer_put_key(MOUSE_BTN2);
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN2 | mod_state(vo));
break;
case WM_MOUSEMOVE:
- vo_mouse_movement(global_vo, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ vo_mouse_movement(vo, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
case WM_MOUSEWHEEL:
if (!vo_nomouse_input) {
int x = GET_WHEEL_DELTA_WPARAM(wParam);
if (x > 0)
- mplayer_put_key(MOUSE_BTN3);
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN3 | mod_state(vo));
else
- mplayer_put_key(MOUSE_BTN4);
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN4 | mod_state(vo));
}
break;
case WM_XBUTTONDOWN:
if (!vo_nomouse_input) {
int x = HIWORD(wParam);
if (x == 1)
- mplayer_put_key(MOUSE_BTN5);
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN5 | mod_state(vo));
else // if (x == 2)
- mplayer_put_key(MOUSE_BTN6);
+ mplayer_put_key(vo->key_fifo, MOUSE_BTN6 | mod_state(vo));
}
break;
}
- return DefWindowProc(hWnd, message, wParam, lParam);
+ return DefWindowProcW(hWnd, message, wParam, lParam);
}
/**
@@ -200,10 +265,6 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l
*
* This function should be placed inside libvo's function "check_events".
*
- * Global libvo variables changed:
- * vo_dwidth: new window client area width
- * vo_dheight: new window client area height
- *
* \return int with these flags possibly set, take care to handle in the right order
* if it matters in your driver:
*
@@ -212,48 +273,53 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l
* VO_EVENT_EXPOSE = The window was exposed. Call e.g. flip_frame() to redraw
* the window if the movie is paused.
*/
-int vo_w32_check_events(void) {
+int vo_w32_check_events(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
MSG msg;
- event_flags = 0;
- while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+ w32->event_flags = 0;
+ while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
- DispatchMessage(&msg);
+ DispatchMessageW(&msg);
}
if (WinID >= 0) {
BOOL res;
RECT r;
POINT p;
- res = GetClientRect(vo_window, &r);
- if (res && (r.right != vo_dwidth || r.bottom != vo_dheight)) {
- vo_dwidth = r.right; vo_dheight = r.bottom;
- event_flags |= VO_EVENT_RESIZE;
+ res = GetClientRect(w32->window, &r);
+ if (res && (r.right != vo->dwidth || r.bottom != vo->dheight)) {
+ vo->dwidth = r.right; vo->dheight = r.bottom;
+ w32->event_flags |= VO_EVENT_RESIZE;
}
p.x = 0; p.y = 0;
- ClientToScreen(vo_window, &p);
- if (p.x != vo_dx || p.y != vo_dy) {
- vo_dx = p.x; vo_dy = p.y;
- event_flags |= VO_EVENT_MOVE;
+ ClientToScreen(w32->window, &p);
+ if (p.x != w32->window_x || p.y != w32->window_y) {
+ w32->window_x = p.x; w32->window_y = p.y;
+ w32->event_flags |= VO_EVENT_MOVE;
}
res = GetClientRect(WIN_ID_TO_HWND(WinID), &r);
- if (res && (r.right != vo_dwidth || r.bottom != vo_dheight))
- MoveWindow(vo_window, 0, 0, r.right, r.bottom, FALSE);
+ if (res && (r.right != vo->dwidth || r.bottom != vo->dheight))
+ MoveWindow(w32->window, 0, 0, r.right, r.bottom, FALSE);
if (!IsWindow(WIN_ID_TO_HWND(WinID)))
// Window has probably been closed, e.g. due to program crash
- mplayer_put_key(KEY_CLOSE_WIN);
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
}
- return event_flags;
+ return w32->event_flags;
}
-static BOOL CALLBACK mon_enum(HMONITOR hmon, HDC hdc, LPRECT r, LPARAM p) {
+static BOOL CALLBACK mon_enum(HMONITOR hmon, HDC hdc, LPRECT r, LPARAM p)
+{
+ struct vo *vo = (void*)p;
+ struct vo_w32_state *w32 = vo->w32;
// this defaults to the last screen if specified number does not exist
xinerama_x = r->left;
xinerama_y = r->top;
- vo_screenwidth = r->right - r->left;
- vo_screenheight = r->bottom - r->top;
- if (mon_cnt == xinerama_screen)
+ vo->opts->vo_screenwidth = r->right - r->left;
+ vo->opts->vo_screenheight = r->bottom - r->top;
+ if (w32->mon_cnt == xinerama_screen)
return FALSE;
- mon_cnt++;
+ w32->mon_cnt++;
return TRUE;
}
@@ -271,67 +337,77 @@ static BOOL CALLBACK mon_enum(HMONITOR hmon, HDC hdc, LPRECT r, LPARAM p) {
* vo_screenwidth
* vo_screenheight
*/
-void w32_update_xinerama_info(void) {
+void w32_update_xinerama_info(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
xinerama_x = xinerama_y = 0;
if (xinerama_screen < -1) {
int tmp;
xinerama_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
xinerama_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
tmp = GetSystemMetrics(SM_CXVIRTUALSCREEN);
- if (tmp) vo_screenwidth = tmp;
+ if (tmp) vo->opts->vo_screenwidth = tmp;
tmp = GetSystemMetrics(SM_CYVIRTUALSCREEN);
- if (tmp) vo_screenheight = tmp;
- } else if (xinerama_screen == -1 && myMonitorFromWindow && myGetMonitorInfo) {
+ if (tmp) vo->opts->vo_screenheight = tmp;
+ } else if (xinerama_screen == -1) {
MONITORINFO mi;
- HMONITOR m = myMonitorFromWindow(vo_window, MONITOR_DEFAULTTOPRIMARY);
+ HMONITOR m = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
mi.cbSize = sizeof(mi);
- myGetMonitorInfo(m, &mi);
+ GetMonitorInfoW(m, &mi);
xinerama_x = mi.rcMonitor.left;
xinerama_y = mi.rcMonitor.top;
- vo_screenwidth = mi.rcMonitor.right - mi.rcMonitor.left;
- vo_screenheight = mi.rcMonitor.bottom - mi.rcMonitor.top;
- } else if (xinerama_screen > 0 && myEnumDisplayMonitors) {
- mon_cnt = 0;
- myEnumDisplayMonitors(NULL, NULL, mon_enum, 0);
+ vo->opts->vo_screenwidth = mi.rcMonitor.right - mi.rcMonitor.left;
+ vo->opts->vo_screenheight = mi.rcMonitor.bottom - mi.rcMonitor.top;
+ } else if (xinerama_screen > 0) {
+ w32->mon_cnt = 0;
+ EnumDisplayMonitors(NULL, NULL, mon_enum, (LONG_PTR)vo);
}
- aspect_save_screenres(vo_screenwidth, vo_screenheight);
+ aspect_save_screenres(vo, vo->opts->vo_screenwidth,
+ vo->opts->vo_screenheight);
}
-static void updateScreenProperties(void) {
+static void updateScreenProperties(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
DEVMODE dm;
dm.dmSize = sizeof dm;
dm.dmDriverExtra = 0;
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if (!EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm)) {
- mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to enumerate display settings!\n");
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vo: win32: unable to enumerate display settings!\n");
return;
}
- vo_screenwidth = dm.dmPelsWidth;
- vo_screenheight = dm.dmPelsHeight;
- depthonscreen = dm.dmBitsPerPel;
- w32_update_xinerama_info();
+ vo->opts->vo_screenwidth = dm.dmPelsWidth;
+ vo->opts->vo_screenheight = dm.dmPelsHeight;
+ w32->depthonscreen = dm.dmBitsPerPel;
+ w32_update_xinerama_info(vo);
}
-static void changeMode(void) {
+static void changeMode(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
DEVMODE dm;
dm.dmSize = sizeof dm;
dm.dmDriverExtra = 0;
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
- dm.dmBitsPerPel = depthonscreen;
- dm.dmPelsWidth = vo_screenwidth;
- dm.dmPelsHeight = vo_screenheight;
+ dm.dmBitsPerPel = w32->depthonscreen;
+ dm.dmPelsWidth = vo->opts->vo_screenwidth;
+ dm.dmPelsHeight = vo->opts->vo_screenheight;
- if (vo_vm) {
+ if (w32->vm) {
int bestMode = -1;
int bestScore = INT_MAX;
int i;
for (i = 0; EnumDisplaySettings(0, i, &dm); ++i) {
- int score = (dm.dmPelsWidth - o_dwidth) * (dm.dmPelsHeight - o_dheight);
- if (dm.dmBitsPerPel != depthonscreen) continue;
- if (dm.dmPelsWidth < o_dwidth) continue;
- if (dm.dmPelsHeight < o_dheight) continue;
+ int score = (dm.dmPelsWidth - w32->o_dwidth)
+ * (dm.dmPelsHeight - w32->o_dheight);
+ if (dm.dmBitsPerPel != w32->depthonscreen
+ || dm.dmPelsWidth < w32->o_dwidth
+ || dm.dmPelsHeight < w32->o_dheight)
+ continue;
if (score < bestScore) {
bestScore = score;
@@ -346,60 +422,101 @@ static void changeMode(void) {
}
}
-static void resetMode(void) {
- if (vo_vm)
- ChangeDisplaySettings(0, 0);
+static void resetMode(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ if (w32->vm)
+ ChangeDisplaySettings(0, 0);
}
-static int createRenderingContext(void) {
+static DWORD update_style(struct vo *vo, DWORD style)
+{
+ const DWORD NO_FRAME = WS_POPUP;
+ const DWORD FRAME = WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
+ style &= ~(NO_FRAME | FRAME);
+ style |= (vo_border && !vo_fs) ? FRAME : NO_FRAME;
+ return style;
+}
+
+// Update the window title, position, size, and border style from vo_* values.
+static int reinit_window_state(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
HWND layer = HWND_NOTOPMOST;
RECT r;
- int style = (vo_border && !vo_fs) ?
- (WS_OVERLAPPEDWINDOW | WS_SIZEBOX) : WS_POPUP;
if (WinID >= 0)
return 1;
- if (vo_fs || vo_ontop) layer = HWND_TOPMOST;
+ wchar_t *title = mp_from_utf8(NULL, vo_get_window_title(vo));
+ SetWindowTextW(w32->window, title);
+ talloc_free(title);
+
+ bool toggle_fs = w32->current_fs != vo_fs;
+ w32->current_fs = vo_fs;
+
+ DWORD style = update_style(vo, GetWindowLong(w32->window, GWL_STYLE));
+
+ if (vo_fs || vo->opts->vo_ontop)
+ layer = HWND_TOPMOST;
+
+ // xxx not sure if this can trigger any unwanted messages (WM_MOVE/WM_SIZE)
if (vo_fs) {
- changeMode();
+ changeMode(vo);
while (ShowCursor(0) >= 0) /**/ ;
} else {
- resetMode();
+ resetMode(vo);
while (ShowCursor(1) < 0) /**/ ;
}
- updateScreenProperties();
- ShowWindow(vo_window, SW_HIDE);
- SetWindowLong(vo_window, GWL_STYLE, style);
+ updateScreenProperties(vo);
+
if (vo_fs) {
- prev_width = vo_dwidth;
- prev_height = vo_dheight;
- prev_x = vo_dx;
- prev_y = vo_dy;
- vo_dwidth = vo_screenwidth;
- vo_dheight = vo_screenheight;
- vo_dx = xinerama_x;
- vo_dy = xinerama_y;
+ // Save window position and size when switching to fullscreen.
+ if (toggle_fs) {
+ w32->prev_width = vo->dwidth;
+ w32->prev_height = vo->dheight;
+ w32->prev_x = w32->window_x;
+ w32->prev_y = w32->window_y;
+ mp_msg(MSGT_VO, MSGL_V, "[vo] save window bounds: %d:%d:%d:%d\n",
+ w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
+ }
+ vo->dwidth = vo->opts->vo_screenwidth;
+ vo->dheight = vo->opts->vo_screenheight;
+ w32->window_x = xinerama_x;
+ w32->window_y = xinerama_y;
} else {
- // make sure there are no "stale" resize events
- // that would set vo_d* to wrong values
- vo_w32_check_events();
- vo_dwidth = prev_width;
- vo_dheight = prev_height;
- vo_dx = prev_x;
- vo_dy = prev_y;
- // HACK around what probably is a windows focus bug:
- // when pressing 'f' on the console, then 'f' again to
- // return to windowed mode, any input into the video
- // window is lost forever.
- SetFocus(vo_window);
+ if (toggle_fs) {
+ // Restore window position and size when switching from fullscreen.
+ mp_msg(MSGT_VO, MSGL_V, "[vo] restore window bounds: %d:%d:%d:%d\n",
+ w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
+ vo->dwidth = w32->prev_width;
+ vo->dheight = w32->prev_height;
+ w32->window_x = w32->prev_x;
+ w32->window_y = w32->prev_y;
+ }
}
- r.left = vo_dx;
- r.right = r.left + vo_dwidth;
- r.top = vo_dy;
- r.bottom = r.top + vo_dheight;
- AdjustWindowRect(&r, style, 0);
- SetWindowPos(vo_window, layer, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_SHOWWINDOW);
+
+ r.left = w32->window_x;
+ r.right = r.left + vo->dwidth;
+ r.top = w32->window_y;
+ r.bottom = r.top + vo->dheight;
+
+ SetWindowLong(w32->window, GWL_STYLE, style);
+ add_window_borders(w32->window, &r);
+
+ mp_msg(MSGT_VO, MSGL_V, "[vo] reset window bounds: %ld:%ld:%ld:%ld\n",
+ r.left, r.top, r.right - r.left, r.bottom - r.top);
+
+ SetWindowPos(w32->window, layer, r.left, r.top, r.right - r.left,
+ r.bottom - r.top, SWP_FRAMECHANGED);
+ // For some reason, moving SWP_SHOWWINDOW to a second call works better
+ // with wine: returning from fullscreen doesn't cause a bogus resize to
+ // screen size.
+ // It's not needed on Windows XP or wine with a virtual desktop.
+ // It doesn't seem to have any negative effects.
+ SetWindowPos(w32->window, NULL, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
+
return 1;
}
@@ -409,16 +526,15 @@ static int createRenderingContext(void) {
* This function should be called in libvo's "config" callback.
* It configures a window and shows it on the screen.
*
- * Global libvo variables changed:
- * vo_fs
- * vo_vm
- *
* \return 1 - Success, 0 - Failure
*/
-int vo_w32_config(uint32_t width, uint32_t height, uint32_t flags) {
+int vo_w32_config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t flags)
+{
+ struct vo_w32_state *w32 = vo->w32;
PIXELFORMATDESCRIPTOR pfd;
int pf;
- HDC vo_hdc = vo_w32_get_dc(vo_window);
+ HDC vo_hdc = vo_w32_get_dc(vo, w32->window);
memset(&pfd, 0, sizeof pfd);
pfd.nSize = sizeof pfd;
@@ -432,44 +548,65 @@ int vo_w32_config(uint32_t width, uint32_t height, uint32_t flags) {
pf = ChoosePixelFormat(vo_hdc, &pfd);
if (!pf) {
mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to select a valid pixel format!\n");
- vo_w32_release_dc(vo_window, vo_hdc);
+ vo_w32_release_dc(vo, w32->window, vo_hdc);
return 0;
}
SetPixelFormat(vo_hdc, pf, &pfd);
- vo_w32_release_dc(vo_window, vo_hdc);
+ vo_w32_release_dc(vo, w32->window, vo_hdc);
// we already have a fully initialized window, so nothing needs to be done
if (flags & VOFLAG_HIDDEN)
return 1;
- // store original size for videomode switching
- o_dwidth = width;
- o_dheight = height;
+ bool reset_size = !(w32->o_dwidth == width && w32->o_dheight == height);
+
+ w32->o_dwidth = width;
+ w32->o_dheight = height;
+
+ // the desired size is ignored in wid mode, it always matches the window size.
if (WinID < 0) {
- // the desired size is ignored in wid mode, it always matches the window size.
- prev_width = vo_dwidth = width;
- prev_height = vo_dheight = height;
- prev_x = vo_dx;
- prev_y = vo_dy;
+ if (w32->window_bounds_initialized) {
+ // restore vo_dwidth/vo_dheight, which are reset against our will
+ // in vo_config()
+ RECT r;
+ GetClientRect(w32->window, &r);
+ vo->dwidth = r.right;
+ vo->dheight = r.bottom;
+ } else {
+ // first vo_config call; vo_config() will always set vo_dx/dy so
+ // that the window is centered on the screen, and this is the only
+ // time we actually want to use vo_dy/dy (this is not sane, and
+ // video_out.h should provide a function to query the initial
+ // window position instead)
+ w32->window_bounds_initialized = true;
+ reset_size = true;
+ w32->window_x = w32->prev_x = vo->dx;
+ w32->window_y = w32->prev_y = vo->dy;
+ }
+ if (reset_size) {
+ w32->prev_width = vo->dwidth = width;
+ w32->prev_height = vo->dheight = height;
+ }
}
vo_fs = flags & VOFLAG_FULLSCREEN;
- vo_vm = flags & VOFLAG_MODESWITCHING;
- return createRenderingContext();
+ w32->vm = flags & VOFLAG_MODESWITCHING;
+ return reinit_window_state(vo);
}
/**
* \brief return the name of the selected device if it is indepedant
* \return pointer to string, must be freed.
*/
-static char *get_display_name(void) {
- DISPLAY_DEVICE disp;
+static wchar_t *get_display_name(void)
+{
+ DISPLAY_DEVICEW disp;
disp.cb = sizeof(disp);
- EnumDisplayDevices(NULL, vo_adapter_num, &disp, 0);
+ EnumDisplayDevicesW(NULL, vo_adapter_num, &disp, 0);
if (disp.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
return NULL;
- return strdup(disp.DeviceName);
+ return wcsdup(disp.DeviceName);
}
/**
@@ -488,65 +625,70 @@ static char *get_display_name(void) {
*
* \return 1 = Success, 0 = Failure
*/
-int vo_w32_init(void) {
- HICON mplayerIcon = 0;
- char exedir[MAX_PATH];
- HINSTANCE user32;
- char *dev;
-
- if (vo_window)
+int vo_w32_init(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ if (w32 && w32->window)
return 1;
- hInstance = GetModuleHandle(0);
+ if (!w32)
+ w32 = vo->w32 = talloc_zero(vo, struct vo_w32_state);
- if (GetModuleFileName(0, exedir, MAX_PATH))
- mplayerIcon = ExtractIcon(hInstance, exedir, 0);
- if (!mplayerIcon)
- mplayerIcon = LoadIcon(0, IDI_APPLICATION);
+ HINSTANCE hInstance = GetModuleHandleW(NULL);
- {
- WNDCLASSEX wcex = { sizeof wcex, CS_OWNDC | CS_DBLCLKS, WndProc, 0, 0, hInstance, mplayerIcon, LoadCursor(0, IDC_ARROW), NULL, 0, classname, mplayerIcon };
+ HICON mplayerIcon = LoadIconW(hInstance, L"IDI_ICON1");
- if (!RegisterClassEx(&wcex)) {
- mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to register window class!\n");
+ WNDCLASSEXW wcex = {
+ .cbSize = sizeof wcex,
+ .style = CS_OWNDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
+ .lpfnWndProc = WndProc,
+ .hInstance = hInstance,
+ .hIcon = mplayerIcon,
+ .hCursor = LoadCursor(0, IDC_ARROW),
+ .lpszClassName = classname,
+ .hIconSm = mplayerIcon,
+ };
+
+ if (!RegisterClassExW(&wcex)) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vo: win32: unable to register window class!\n");
return 0;
}
- }
- if (WinID >= 0)
- {
+ if (WinID >= 0) {
RECT r;
GetClientRect(WIN_ID_TO_HWND(WinID), &r);
- vo_dwidth = r.right; vo_dheight = r.bottom;
- vo_window = CreateWindowEx(WS_EX_NOPARENTNOTIFY, classname, classname,
- WS_CHILD | WS_VISIBLE, 0, 0, vo_dwidth, vo_dheight,
- WIN_ID_TO_HWND(WinID), 0, hInstance, 0);
- EnableWindow(vo_window, 0);
- } else
- vo_window = CreateWindowEx(0, classname, classname,
- vo_border ? (WS_OVERLAPPEDWINDOW | WS_SIZEBOX) : WS_POPUP,
- CW_USEDEFAULT, 0, 100, 100, 0, 0, hInstance, 0);
- if (!vo_window) {
+ vo->dwidth = r.right; vo->dheight = r.bottom;
+ w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY, classname,
+ classname,
+ WS_CHILD | WS_VISIBLE,
+ 0, 0, vo->dwidth, vo->dheight,
+ WIN_ID_TO_HWND(WinID), 0, hInstance, vo);
+ } else {
+ w32->window = CreateWindowExW(0, classname,
+ classname,
+ update_style(vo, 0),
+ CW_USEDEFAULT, 0, 100, 100,
+ 0, 0, hInstance, vo);
+ }
+
+ if (!w32->window) {
mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to create window!\n");
return 0;
}
- myMonitorFromWindow = NULL;
- myGetMonitorInfo = NULL;
- myEnumDisplayMonitors = NULL;
- user32 = GetModuleHandle("user32.dll");
- if (user32) {
- myMonitorFromWindow = (void *)GetProcAddress(user32, "MonitorFromWindow");
- myGetMonitorInfo = GetProcAddress(user32, "GetMonitorInfoA");
- myEnumDisplayMonitors = GetProcAddress(user32, "EnumDisplayMonitors");
- }
- dev_hdc = 0;
- dev = get_display_name();
- if (dev) dev_hdc = CreateDC(dev, NULL, NULL, NULL);
+ if (WinID >= 0)
+ EnableWindow(w32->window, 0);
+
+ w32->dev_hdc = 0;
+ wchar_t *dev = get_display_name();
+ if (dev) w32->dev_hdc = CreateDCW(dev, NULL, NULL, NULL);
free(dev);
- updateScreenProperties();
+ updateScreenProperties(vo);
- mp_msg(MSGT_VO, MSGL_V, "vo: win32: running at %dx%d with depth %d\n", vo_screenwidth, vo_screenheight, depthonscreen);
+ mp_msg(MSGT_VO, MSGL_V, "vo: win32: running at %dx%d with depth %d\n",
+ vo->opts->vo_screenwidth, vo->opts->vo_screenheight,
+ w32->depthonscreen);
return 1;
}
@@ -555,50 +697,38 @@ int vo_w32_init(void) {
* \brief Toogle fullscreen / windowed mode.
*
* Should be called on VOCTRL_FULLSCREEN event. The window is
- * always resized after this call, so the rendering context
+ * always resized during this call, so the rendering context
* should be reinitialized with the new dimensions.
* It is unspecified if vo_check_events will create a resize
* event in addition or not.
- *
- * Global libvo variables changed:
- * vo_dwidth
- * vo_dheight
- * vo_fs
*/
-void vo_w32_fullscreen(void) {
+void vo_w32_fullscreen(struct vo *vo)
+{
vo_fs = !vo_fs;
-
- createRenderingContext();
+ reinit_window_state(vo);
}
/**
* \brief Toogle window border attribute.
*
* Should be called on VOCTRL_BORDER event.
- *
- * Global libvo variables changed:
- * vo_border
*/
-void vo_w32_border(void) {
+void vo_w32_border(struct vo *vo)
+{
vo_border = !vo_border;
- createRenderingContext();
+ reinit_window_state(vo);
}
/**
* \brief Toogle window ontop attribute.
*
* Should be called on VOCTRL_ONTOP event.
- *
- * Global libvo variables changed:
- * vo_ontop
*/
-void vo_w32_ontop( void )
+void vo_w32_ontop(struct vo *vo)
{
- vo_ontop = !vo_ontop;
- if (!vo_fs) {
- createRenderingContext();
- }
+ vo->opts->vo_ontop = !vo->opts->vo_ontop;
+ reinit_window_state(vo);
}
/**
@@ -608,16 +738,19 @@ void vo_w32_ontop( void )
* anything built on top of the created window e.g. rendering context inside
* and call vo_w32_uninit at the end.
*/
-void vo_w32_uninit(void) {
+void vo_w32_uninit(struct vo *vo)
+{
+ struct vo_w32_state *w32 = vo->w32;
mp_msg(MSGT_VO, MSGL_V, "vo: win32: uninit\n");
- resetMode();
+ if (!w32)
+ return;
+ resetMode(vo);
ShowCursor(1);
- depthonscreen = 0;
- if (dev_hdc) DeleteDC(dev_hdc);
- dev_hdc = 0;
- DestroyWindow(vo_window);
- vo_window = 0;
- UnregisterClass(classname, 0);
+ if (w32->dev_hdc) DeleteDC(w32->dev_hdc);
+ DestroyWindow(w32->window);
+ UnregisterClassW(classname, 0);
+ talloc_free(w32);
+ vo->w32 = NULL;
}
/**
@@ -625,8 +758,11 @@ void vo_w32_uninit(void) {
*
* \param wnd window the DC should belong to if it makes sense
*/
-HDC vo_w32_get_dc(HWND wnd) {
- if (dev_hdc) return dev_hdc;
+HDC vo_w32_get_dc(struct vo *vo, HWND wnd)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ if (w32->dev_hdc)
+ return w32->dev_hdc;
return GetDC(wnd);
}
@@ -635,7 +771,10 @@ HDC vo_w32_get_dc(HWND wnd) {
*
* \param wnd window the DC probably belongs to
*/
-void vo_w32_release_dc(HWND wnd, HDC dc) {
- if (dev_hdc) return;
+void vo_w32_release_dc(struct vo *vo, HWND wnd, HDC dc)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ if (w32->dev_hdc)
+ return;
ReleaseDC(wnd, dc);
}
diff --git a/libvo/w32_common.h b/libvo/w32_common.h
index cb3bbf3504..1e4bd604a0 100644
--- a/libvo/w32_common.h
+++ b/libvo/w32_common.h
@@ -20,21 +20,50 @@
#define MPLAYER_W32_COMMON_H
#include <stdint.h>
+#include <stdbool.h>
#include <windows.h>
-extern HWND vo_w32_window;
-extern int vo_vm;
-
-int vo_w32_init(void);
-void vo_w32_uninit(void);
-void vo_w32_ontop(void);
-void vo_w32_border(void);
-void vo_w32_fullscreen(void);
-int vo_w32_check_events(void);
-int vo_w32_config(uint32_t, uint32_t, uint32_t);
-void destroyRenderingContext(void);
-void w32_update_xinerama_info(void);
-HDC vo_w32_get_dc(HWND wnd);
-void vo_w32_release_dc(HWND wnd, HDC dc);
+struct vo_w32_state {
+ HWND window;
+
+ // HDC used when rendering to a device instead of window
+ HDC dev_hdc;
+
+ bool vm;
+
+ int depthonscreen;
+
+ // last non-fullscreen extends (updated only on fullscreen or on initialization)
+ int prev_width;
+ int prev_height;
+ int prev_x;
+ int prev_y;
+
+ // whether the window position and size were intialized
+ bool window_bounds_initialized;
+
+ bool current_fs;
+
+ int window_x;
+ int window_y;
+
+ // video size
+ uint32_t o_dwidth;
+ uint32_t o_dheight;
+
+ int event_flags;
+ int mon_cnt;
+};
+
+int vo_w32_init(struct vo *vo);
+void vo_w32_uninit(struct vo *vo);
+void vo_w32_ontop(struct vo *vo);
+void vo_w32_border(struct vo *vo);
+void vo_w32_fullscreen(struct vo *vo);
+int vo_w32_check_events(struct vo *vo);
+int vo_w32_config(struct vo *vo, uint32_t, uint32_t, uint32_t);
+void w32_update_xinerama_info(struct vo *vo);
+HDC vo_w32_get_dc(struct vo *vo, HWND wnd);
+void vo_w32_release_dc(struct vo *vo, HWND wnd, HDC dc);
#endif /* MPLAYER_W32_COMMON_H */
diff --git a/libvo/x11_common.c b/libvo/x11_common.c
index cf8bb2dc4f..074e97bfed 100644
--- a/libvo/x11_common.c
+++ b/libvo/x11_common.c
@@ -1023,7 +1023,8 @@ static void vo_x11_set_property_string(struct vo *vo, Atom name, const char *t)
XTextProperty prop = {0};
if (Xutf8TextListToTextProperty(x11->display, (char **)&t, 1,
- XStdICCTextStyle, &prop) == Success) {
+ XStdICCTextStyle, &prop) == Success)
+ {
XSetTextProperty(x11->display, x11->window, &prop, name);
} else {
// Strictly speaking this violates the ICCCM, but there's no way we
diff --git a/m_option.c b/m_option.c
index 18f94dc7a9..086f2c8842 100644
--- a/m_option.c
+++ b/m_option.c
@@ -272,13 +272,20 @@ static int parse_choice(const struct m_option *opt, struct bstr name,
struct bstr param, bool ambiguous_param, void *dst,
void *talloc_ctx)
{
- if (param.len == 0)
- return M_OPT_MISSING_PARAM;
-
- struct m_opt_choice_alternatives *alt;
- for (alt = opt->priv; alt->name; alt++)
- if (!bstrcasecmp0(param, alt->name))
- break;
+ bool allow_empty = opt->flags & M_OPT_IMPLICIT_DEFAULT;
+ int ret;
+
+ struct m_opt_choice_alternatives *alt = opt->priv;
+ if (param.len == 0 || (ambiguous_param && allow_empty)) {
+ if (!allow_empty)
+ return M_OPT_MISSING_PARAM;
+ ret = 0;
+ } else {
+ for ( ; alt->name; alt++)
+ if (!bstrcasecmp0(param, alt->name))
+ break;
+ ret = 1;
+ }
if (!alt->name) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Invalid value for option %.*s: %.*s\n",
@@ -292,7 +299,7 @@ static int parse_choice(const struct m_option *opt, struct bstr name,
if (dst)
*(int *)dst = alt->value;
- return 1;
+ return ret;
}
static char *print_choice(const m_option_t *opt, const void *val)
@@ -988,6 +995,7 @@ static struct {
{"argb", IMGFMT_ARGB},
{"bgra", IMGFMT_BGRA},
{"abgr", IMGFMT_ABGR},
+ {"gbrp", IMGFMT_GBRP},
{"mjpeg", IMGFMT_MJPEG},
{"mjpg", IMGFMT_MJPEG},
{ NULL, 0 }
diff --git a/m_option.h b/m_option.h
index 648c560305..7acdb067f0 100644
--- a/m_option.h
+++ b/m_option.h
@@ -297,6 +297,11 @@ struct m_option {
// The option should be set during command line pre-parsing
#define M_OPT_PRE_PARSE (1 << 6)
+// Accept an option without parameter, even if the option type normally requires
+// a parameter. The option value will be set to a default value.
+// For m_option_type_choice, the first listed choice will be used.
+#define M_OPT_IMPLICIT_DEFAULT (1 << 7)
+
// These are kept for compatibility with older code.
#define CONF_MIN M_OPT_MIN
#define CONF_MAX M_OPT_MAX
diff --git a/mp_core.h b/mp_core.h
index 4481e61469..190b81883f 100644
--- a/mp_core.h
+++ b/mp_core.h
@@ -90,6 +90,7 @@ typedef struct MPContext {
struct mp_fifo *key_fifo;
struct input_ctx *input;
struct osd_state *osd;
+ char *terminal_osd_text;
struct sub_data *subdata; // current sub_data style subtitles if any
// last sub_data style sub line if any, used by log_sub() only
struct subtitle *vo_sub_last;
@@ -260,5 +261,7 @@ void update_subtitles(struct MPContext *mpctx, double refpts, bool reset);
void build_ordered_chapter_timeline(struct MPContext *mpctx);
// timeline/tl_edl.c
void build_edl_timeline(struct MPContext *mpctx);
+// timeline/tl_cue.c
+void build_cue_timeline(struct MPContext *mpctx);
#endif /* MPLAYER_MP_CORE_H */
diff --git a/mp_msg.c b/mp_msg.c
index 7b97d286e4..6ad39a0416 100644
--- a/mp_msg.c
+++ b/mp_msg.c
@@ -38,7 +38,7 @@
#include "mp_msg.h"
/* maximum message length of mp_msg */
-#define MSGSIZE_MAX 3072
+#define MSGSIZE_MAX 6144
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
diff --git a/mpcommon.c b/mpcommon.c
index 47c056a08e..95c21ead65 100644
--- a/mpcommon.c
+++ b/mpcommon.c
@@ -18,4 +18,4 @@
#include "version.h"
-const char *mplayer_version = "MPlayer " VERSION;
+const char *mplayer_version = "mplayer2 " VERSION;
diff --git a/mplayer.c b/mplayer.c
index fe2e509fb4..5a15c55b25 100644
--- a/mplayer.c
+++ b/mplayer.c
@@ -90,7 +90,6 @@
#include "sub/av_sub.h"
#include "libmpcodecs/dec_teletext.h"
#include "cpudetect.h"
-#include "version.h"
#ifdef CONFIG_X11
#include "libvo/x11_common.h"
@@ -543,9 +542,10 @@ static void print_file_properties(struct MPContext *mpctx, const char *filename)
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTERS=%d\n", chapter_count);
for (int i = 0; i < chapter_count; i++) {
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_ID=%d\n", i);
- // in milliseconds
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_%d_START=%"PRIu64"\n",
- i, (int64_t)(chapter_start_time(mpctx, i) * 1000.0));
+ // print in milliseconds
+ double time = chapter_start_time(mpctx, i) * 1000.0;
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_%d_START=%"PRId64"\n",
+ i, (int64_t)(time < 0 ? -1 : time));
char *name = chapter_name(mpctx, i);
if (name) {
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_%d_NAME=%s\n", i,
@@ -690,6 +690,7 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
ao_uninit(mpctx->ao, mpctx->stop_play != AT_END_OF_FILE);
}
mpctx->ao = NULL;
+ mpctx->mixer.ao = NULL;
}
current_module = NULL;
@@ -1615,7 +1616,7 @@ void set_osd_bar(struct MPContext *mpctx, int type, const char *name,
if (opts->osd_level < 1)
return;
- if (mpctx->sh_video) {
+ if (mpctx->sh_video && opts->term_osd != 1) {
mpctx->osd_visible = (GetTimerMS() + 1000) | 1;
vo_osd_progbar_type = type;
vo_osd_progbar_value = 256 * (val - min) / (max - min);
@@ -1670,25 +1671,30 @@ static void update_osd_msg(struct MPContext *mpctx)
if (mpctx->add_osd_seek_info) {
double percentage = get_percent_pos(mpctx);
set_osd_bar(mpctx, 0, "Position", 0, 100, percentage);
- if (mpctx->sh_video)
+ if (mpctx->sh_video && opts->term_osd != 1)
mpctx->osd_show_percentage_until = (GetTimerMS() + 1000) | 1;
mpctx->add_osd_seek_info = false;
}
// Look if we have a msg
if ((msg = get_osd_msg(mpctx))) {
- if (strcmp(osd->osd_text, msg->msg)) {
- osd_set_text(osd, msg->msg);
- if (mpctx->sh_video)
+ if (mpctx->sh_video && opts->term_osd != 1) {
+ if (strcmp(osd->osd_text, msg->msg)) {
+ osd_set_text(osd, msg->msg);
vo_osd_changed(OSDTYPE_OSD);
- else if (opts->term_osd)
+ }
+ } else if (opts->term_osd) {
+ if (strcmp(mpctx->terminal_osd_text, msg->msg)) {
+ talloc_free(mpctx->terminal_osd_text);
+ mpctx->terminal_osd_text = talloc_strdup(mpctx, msg->msg);
mp_msg(MSGT_CPLAYER, MSGL_STATUS, "%s%s\n", opts->term_osd_esc,
- msg->msg);
+ mpctx->terminal_osd_text);
+ }
}
return;
}
- if (mpctx->sh_video) {
+ if (mpctx->sh_video && opts->term_osd != 1) {
// fallback on the timer
if (opts->osd_level >= 2) {
int len = get_time_length(mpctx);
@@ -1753,8 +1759,8 @@ static void update_osd_msg(struct MPContext *mpctx)
}
// Clear the term osd line
- if (opts->term_osd && osd->osd_text[0]) {
- osd->osd_text[0] = 0;
+ if (opts->term_osd && mpctx->terminal_osd_text[0]) {
+ mpctx->terminal_osd_text[0] = '\0';
mp_msg(MSGT_CPLAYER, MSGL_STATUS, "%s\n", opts->term_osd_esc);
}
}
@@ -3839,9 +3845,10 @@ static int select_audio(demuxer_t *demuxer, int audio_id, char **audio_lang)
return demuxer->audio->id;
}
-static void print_version(const char *name)
+static void print_version(void)
{
- mp_msg(MSGT_CPLAYER, MSGL_INFO, MP_TITLE, name);
+ mp_msg(MSGT_CPLAYER, MSGL_INFO,
+ "%s (C) 2000-2012 MPlayer & mplayer2 teams\n", mplayer_version);
/* Test for CPU capabilities (and corresponding OS support) for optimizing */
GetCpuCaps(&gCpuCaps);
@@ -3921,6 +3928,7 @@ int main(int argc, char *argv[])
.file_format = DEMUXER_TYPE_UNKNOWN,
.last_dvb_step = 1,
.paused_cache_fill = -1,
+ .terminal_osd_text = talloc_strdup(mpctx, ""),
};
InitTimer();
@@ -3928,6 +3936,7 @@ int main(int argc, char *argv[])
mp_msg_init();
init_libav();
+ screenshot_init(mpctx);
#ifdef CONFIG_X11
mpctx->x11_state = vo_x11_init_state();
@@ -3974,7 +3983,7 @@ int main(int argc, char *argv[])
}
}
- print_version("MPlayer2");
+ print_version();
#if defined(__MINGW32__) || defined(__CYGWIN__)
{
@@ -4570,6 +4579,9 @@ goto_enable_cache:
if (mpctx->demuxer->type == DEMUXER_TYPE_EDL)
build_edl_timeline(mpctx);
+ if (mpctx->demuxer->type == DEMUXER_TYPE_CUE)
+ build_cue_timeline(mpctx);
+
if (mpctx->timeline) {
mpctx->timeline_part = 0;
mpctx->demuxer = mpctx->timeline[0].source->demuxer;
@@ -4941,6 +4953,9 @@ goto_enable_cache:
vo_control(mpctx->video_out,
mpctx->paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL);
+ if (mpctx->opts.start_paused)
+ pause_player(mpctx);
+
while (!mpctx->stop_play)
run_playloop(mpctx);
diff --git a/options.h b/options.h
index 48f5c539a5..e77384b7f4 100644
--- a/options.h
+++ b/options.h
@@ -72,6 +72,7 @@ typedef struct MPOpts {
int doubleclick_time;
int list_properties;
double seek_to_sec;
+ int start_paused;
int audio_id;
int video_id;
int sub_id;
@@ -87,6 +88,11 @@ typedef struct MPOpts {
char *sub_demuxer_name;
int extension_parsing;
+ int screenshot_jpeg_quality;
+ int screenshot_png_compression;
+ char *screenshot_filetype;
+ char *screenshot_template;
+
int audio_output_channels;
int audio_output_format;
float playback_speed;
diff --git a/osdep/getch2.c b/osdep/getch2.c
index 0cbaa82a25..f0aa19a2cc 100644
--- a/osdep/getch2.c
+++ b/osdep/getch2.c
@@ -34,6 +34,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef CONFIG_IOCTL
@@ -288,27 +289,41 @@ bool getch2(struct mp_fifo *fifo)
return true;
}
-static int getch2_status=0;
+static volatile int getch2_status=0;
-void getch2_enable(void){
+static void do_enable_getch2(void)
+{
#ifdef HAVE_TERMIOS
-struct termios tio_new;
- tcgetattr(0,&tio_orig);
- tio_new=tio_orig;
+ struct termios tio_new;
+ tcgetattr(0,&tio_new);
tio_new.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tio_new.c_cc[VMIN] = 0;
tio_new.c_cc[VTIME] = 0;
tcsetattr(0,TCSANOW,&tio_new);
#endif
+}
+
+static void continue_sighandler(int signum)
+{
+ if (getch2_status)
+ do_enable_getch2();
+}
+
+void getch2_enable(void){
+#ifdef HAVE_TERMIOS
+ tcgetattr(0,&tio_orig);
+ do_enable_getch2();
+#endif
getch2_status=1;
+ signal(SIGCONT,continue_sighandler);
}
void getch2_disable(void){
if(!getch2_status) return; // already disabled / never enabled
+ getch2_status=0;
#ifdef HAVE_TERMIOS
tcsetattr(0,TCSANOW,&tio_orig);
#endif
- getch2_status=0;
}
#ifdef CONFIG_ICONV
diff --git a/osdep/io.c b/osdep/io.c
index e3e750e30b..5531e3ce7c 100644
--- a/osdep/io.c
+++ b/osdep/io.c
@@ -92,13 +92,10 @@ int mp_stat(const char *path, struct stat *buf)
return res;
}
-int mp_fprintf(FILE *stream, const char *format, ...)
+static int mp_vfprintf(FILE *stream, const char *format, va_list args)
{
- va_list args;
int done = 0;
- va_start(args, format);
-
if (stream == stdout || stream == stderr)
{
HANDLE *wstream = GetStdHandle(stream == stdout ?
@@ -146,9 +143,27 @@ int mp_fprintf(FILE *stream, const char *format, ...)
else
done = vfprintf(stream, format, args);
+ return done;
+}
+
+int mp_fprintf(FILE *stream, const char *format, ...)
+{
+ int res;
+ va_list args;
+ va_start(args, format);
+ res = mp_vfprintf(stream, format, args);
va_end(args);
+ return res;
+}
- return done;
+int mp_printf(const char *format, ...)
+{
+ int res;
+ va_list args;
+ va_start(args, format);
+ res = mp_vfprintf(stdout, format, args);
+ va_end(args);
+ return res;
}
int mp_open(const char *filename, int oflag, ...)
diff --git a/osdep/io.h b/osdep/io.h
index 462a84917b..4383d61143 100644
--- a/osdep/io.h
+++ b/osdep/io.h
@@ -46,6 +46,7 @@ char *mp_to_utf8(void *talloc_ctx, const wchar_t *s);
void mp_get_converted_argv(int *argc, char ***argv);
int mp_stat(const char *path, struct stat *buf);
+int mp_printf(const char *format, ...);
int mp_fprintf(FILE *stream, const char *format, ...);
int mp_open(const char *filename, int oflag, ...);
int mp_creat(const char *filename, int mode);
@@ -58,6 +59,7 @@ int mp_mkdir(const char *path, int mode);
// NOTE: Stat is not overridden with mp_stat, because MinGW-w64 defines it as
// macro.
+#define printf(...) mp_printf(__VA_ARGS__)
#define fprintf(...) mp_fprintf(__VA_ARGS__)
#define open(...) mp_open(__VA_ARGS__)
#define creat(...) mp_creat(__VA_ARGS__)
diff --git a/osdep/mplayer.rc b/osdep/mplayer.rc
index fe68aa2175..fe70aa871f 100644
--- a/osdep/mplayer.rc
+++ b/osdep/mplayer.rc
@@ -29,9 +29,9 @@ FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
{
- BLOCK "000004b0" // LANG_NEUTRAL,UNICODE_CP
+ BLOCK "000004b0" // LANG_NEUTRAL,UNICODE_CP
{
- VALUE "Comments","mplayer2 is distributed under the terms of the GNU General Public License Version 3. Source code is available at http://www.mplayer2.org\000"
+ VALUE "Comments","mplayer2 is distributed under the terms of the GNU General Public License Version 3. Source code is available at http://www.mplayer2.org\000"
VALUE "CompanyName", "\000"
VALUE "FileDescription", "mplayer2 - Movie Player\000"
VALUE "FileVersion",VERSION
@@ -45,7 +45,7 @@ FILETYPE VFT_APP
}
BLOCK "VarFileInfo"
{
- VALUE "Translation",0,0x04b0
+ VALUE "Translation",0,0x04b0
}
}
diff --git a/playtree.c b/playtree.c
index 22205d3bf5..0d6358406f 100644
--- a/playtree.c
+++ b/playtree.c
@@ -411,6 +411,58 @@ play_tree_iter_push_params(play_tree_iter_t* iter) {
iter->entry_pushed = 1;
}
+// Shuffle the tree if the PLAY_TREE_RND flag is set, and unset it.
+// This is done recursively, but only the siblings with the same parent are
+// shuffled with each other.
+static void shuffle_tree(play_tree_t *pt) {
+ if (!pt)
+ return;
+
+ int count = 0;
+ play_tree_t *child = pt->child;
+ while (child) {
+ // possibly shuffle children
+ shuffle_tree(child);
+ child = child->next;
+ count++;
+ }
+
+ if (pt->flags & PLAY_TREE_RND) {
+ // Move a random element to the front and go to the next, until no
+ // elements are left.
+ // prev = pointer to next-link to the first yet-unshuffled entry
+ play_tree_t** prev = &pt->child;
+ while (count > 1) {
+ int n = (int)((double)(count) * rand() / (RAND_MAX + 1.0));
+ // move = element that is moved to front (inserted after prev)
+ play_tree_t **before_move = prev;
+ play_tree_t *move = *before_move;
+ while (n > 0) {
+ before_move = &move->next;
+ move = *before_move;
+ n--;
+ }
+ // unlink from old list
+ *before_move = move->next;
+ // insert between prev and the following element
+ // note that move could be the first unshuffled element
+ move->next = (*prev == move) ? move->next : *prev;
+ *prev = move;
+ prev = &move->next;
+ count--;
+ }
+ // reconstruct prev links
+ child = pt->child;
+ play_tree_t *prev_child = NULL;
+ while (child) {
+ child->prev = prev_child;
+ prev_child = child;
+ child = child->next;
+ }
+ pt->flags = pt->flags & ~PLAY_TREE_RND;
+ }
+}
+
play_tree_iter_t*
play_tree_iter_new(play_tree_t* pt,m_config_t* config) {
play_tree_iter_t* iter;
@@ -430,6 +482,8 @@ play_tree_iter_new(play_tree_t* pt,m_config_t* config) {
iter->tree = NULL;
iter->config = config;
+ shuffle_tree(pt);
+
if(pt->parent)
iter->loop = pt->parent->loop;
diff --git a/screenshot.c b/screenshot.c
index 4a9a6c435a..df92f60d89 100644
--- a/screenshot.c
+++ b/screenshot.c
@@ -20,16 +20,27 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
-#include <assert.h>
+#include <setjmp.h>
+#include <time.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
#include "config.h"
+
+#ifdef CONFIG_JPEG
+#include <jpeglib.h>
+#endif
+
+#include "osdep/io.h"
+
#include "talloc.h"
#include "screenshot.h"
#include "mp_core.h"
+#include "m_property.h"
+#include "bstr.h"
#include "mp_msg.h"
+#include "metadata.h"
#include "path.h"
#include "libmpcodecs/img_format.h"
#include "libmpcodecs/mp_image.h"
@@ -39,45 +50,39 @@
#include "fmt-conversion.h"
-//for sws_getContextFromCmdLine and mp_sws_set_colorspace
+//for sws_getContextFromCmdLine_hq and mp_sws_set_colorspace
#include "libmpcodecs/vf_scale.h"
#include "libvo/csputils.h"
typedef struct screenshot_ctx {
- AVFrame *pic;
+ struct MPContext *mpctx;
+
int full_window;
int each_frame;
int using_vf_screenshot;
int frameno;
- char fname[102];
} screenshot_ctx;
-static int destroy_ctx(void *ptr)
-{
- struct screenshot_ctx *ctx = ptr;
- av_free(ctx->pic);
- return 0;
-}
+struct img_writer {
+ const char *file_ext;
+ int (*write)(screenshot_ctx *ctx, mp_image_t *image, FILE *fp);
+};
-static screenshot_ctx *screenshot_get_ctx(MPContext *mpctx)
+void screenshot_init(struct MPContext *mpctx)
{
- if (!mpctx->screenshot_ctx) {
- struct screenshot_ctx *ctx = talloc_zero(mpctx, screenshot_ctx);
- talloc_set_destructor(ctx, destroy_ctx);
- ctx->pic = avcodec_alloc_frame();
- assert(ctx->pic);
- mpctx->screenshot_ctx = ctx;
- }
- return mpctx->screenshot_ctx;
+ mpctx->screenshot_ctx = talloc(mpctx, screenshot_ctx);
+ *mpctx->screenshot_ctx = (screenshot_ctx) {
+ .mpctx = mpctx,
+ .frameno = 1,
+ };
}
-static int write_png(screenshot_ctx *ctx, struct mp_image *image)
+static int write_png(screenshot_ctx *ctx, struct mp_image *image, FILE *fp)
{
- char *fname = ctx->fname;
- FILE *fp = NULL;
void *outbuffer = NULL;
int success = 0;
+ AVFrame *pic = NULL;
struct AVCodec *png_codec = avcodec_find_encoder(CODEC_ID_PNG);
AVCodecContext *avctx = NULL;
@@ -91,7 +96,7 @@ static int write_png(screenshot_ctx *ctx, struct mp_image *image)
avctx->width = image->width;
avctx->height = image->height;
avctx->pix_fmt = PIX_FMT_RGB24;
- avctx->compression_level = 0;
+ avctx->compression_level = ctx->mpctx->opts.screenshot_png_compression;
if (avcodec_open2(avctx, png_codec, NULL) < 0) {
print_open_fail:
@@ -105,7 +110,9 @@ static int write_png(screenshot_ctx *ctx, struct mp_image *image)
if (!outbuffer)
goto error_exit;
- AVFrame *pic = ctx->pic;
+ pic = avcodec_alloc_frame();
+ if (!pic)
+ goto error_exit;
avcodec_get_frame_defaults(pic);
for (int n = 0; n < 4; n++) {
pic->data[n] = image->planes[n];
@@ -115,91 +122,375 @@ static int write_png(screenshot_ctx *ctx, struct mp_image *image)
if (size < 1)
goto error_exit;
- fp = fopen(fname, "wb");
- if (fp == NULL) {
- mp_msg(MSGT_CPLAYER, MSGL_ERR, "\nPNG Error opening %s for writing!\n",
- fname);
- goto error_exit;
- }
-
fwrite(outbuffer, size, 1, fp);
- fflush(fp);
-
- if (ferror(fp))
- goto error_exit;
success = 1;
error_exit:
if (avctx)
avcodec_close(avctx);
av_free(avctx);
- if (fp)
- fclose(fp);
+ av_free(pic);
free(outbuffer);
return success;
}
+#ifdef CONFIG_JPEG
+
+static void write_jpeg_error_exit(j_common_ptr cinfo)
+{
+ // NOTE: do not write error message, too much effort to connect the libjpeg
+ // log callbacks with mplayer's log function mp_msp()
+
+ // Return control to the setjmp point
+ longjmp(*(jmp_buf*)cinfo->client_data, 1);
+}
+
+static int write_jpeg(screenshot_ctx *ctx, mp_image_t *image, FILE *fp)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jerr.error_exit = write_jpeg_error_exit;
+
+ jmp_buf error_return_jmpbuf;
+ cinfo.client_data = &error_return_jmpbuf;
+ if (setjmp(cinfo.client_data)) {
+ jpeg_destroy_compress(&cinfo);
+ return 0;
+ }
+
+ jpeg_create_compress(&cinfo);
+ jpeg_stdio_dest(&cinfo, fp);
+
+ cinfo.image_width = image->width;
+ cinfo.image_height = image->height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, ctx->mpctx->opts.screenshot_jpeg_quality, 1);
+
+ jpeg_start_compress(&cinfo, TRUE);
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ JSAMPROW row_pointer[1];
+ row_pointer[0] = image->planes[0] +
+ cinfo.next_scanline * image->stride[0];
+ jpeg_write_scanlines(&cinfo, row_pointer,1);
+ }
+
+ jpeg_finish_compress(&cinfo);
+
+ jpeg_destroy_compress(&cinfo);
+
+ return 1;
+}
+
+#endif
+
+static const struct img_writer img_writers[] = {
+ { "png", write_png },
+#ifdef CONFIG_JPEG
+ { "jpg", write_jpeg },
+ { "jpeg", write_jpeg },
+#endif
+};
+
+static const struct img_writer *get_writer(screenshot_ctx *ctx)
+{
+ const char *type = ctx->mpctx->opts.screenshot_filetype;
+
+ for (size_t n = 0; n < sizeof(img_writers) / sizeof(img_writers[0]); n++) {
+ const struct img_writer *writer = &img_writers[n];
+ if (type && strcmp(type, writer->file_ext) == 0)
+ return writer;
+ }
+
+ return &img_writers[0];
+}
+
static int fexists(char *fname)
{
return mp_path_exists(fname);
}
-static void gen_fname(screenshot_ctx *ctx)
+static char *stripext(void *talloc_ctx, const char *s)
{
- do {
- snprintf(ctx->fname, 100, "shot%04d.png", ++ctx->frameno);
- } while (fexists(ctx->fname) && ctx->frameno < 100000);
- if (fexists(ctx->fname)) {
- ctx->fname[0] = '\0';
- return;
+ const char *end = strrchr(s, '.');
+ if (!end)
+ end = s + strlen(s);
+ return talloc_asprintf(talloc_ctx, "%.*s", end - s, s);
+}
+
+static char *format_time(void *talloc_ctx, double time, bool sub_seconds)
+{
+ int h, m, s = time;
+ h = s / 3600;
+ s -= h * 3600;
+ m = s / 60;
+ s -= m * 60;
+ char *res = talloc_asprintf(talloc_ctx, "%02d:%02d:%02d", h, m, s);
+ if (sub_seconds)
+ res = talloc_asprintf_append(res, ".%03d",
+ (int)((time - (int)time) * 1000));
+ return res;
+}
+
+static char *do_format_property(struct MPContext *mpctx, struct bstr s) {
+ struct bstr prop_name = s;
+ int fallbackpos = bstrchr(s, ':');
+ if (fallbackpos >= 0)
+ prop_name = bstr_splice(prop_name, 0, fallbackpos);
+ char *pn = bstrdup0(NULL, prop_name);
+ char *res = mp_property_print(pn, mpctx);
+ talloc_free(pn);
+ if (!res && fallbackpos >= 0)
+ res = bstrdup0(NULL, bstr_cut(s, fallbackpos + 1));
+ return res;
+}
+
+#ifdef _WIN32
+#define ILLEGAL_FILENAME_CHARS "?\"/\\<>*|:"
+#else
+#define ILLEGAL_FILENAME_CHARS "/"
+#endif
+
+// Replace all characters disallowed in filenames with '_' and return the newly
+// allocated result string.
+static char *sanitize_filename(void *talloc_ctx, const char *s)
+{
+ char *res = talloc_strdup(talloc_ctx, s);
+ char *cur = res;
+ while (*cur) {
+ if (strchr(ILLEGAL_FILENAME_CHARS, *cur) || ((unsigned char)*cur) < 32)
+ *cur = '_';
+ cur++;
+ }
+ return res;
+}
+
+static void append_filename(char **s, const char *f)
+{
+ char *append = sanitize_filename(NULL, f);
+ *s = talloc_strdup_append(*s, append);
+ talloc_free(append);
+}
+
+static char *create_fname(struct MPContext *mpctx, char *template,
+ const char *file_ext, int *sequence, int *frameno)
+{
+ char *res = talloc_strdup(NULL, ""); //empty string, non-NULL context
+
+ time_t raw_time = time(NULL);
+ struct tm *local_time = localtime(&raw_time);
+
+ if (!template || *template == '\0')
+ template = "shot%n";
+
+ for (;;) {
+ char *next = strchr(template, '%');
+ if (!next)
+ break;
+ res = talloc_strndup_append(res, template, next - template);
+ template = next + 1;
+ char fmt = *template++;
+ switch (fmt) {
+ case '#':
+ case '0':
+ case 'n': {
+ int digits = '4';
+ if (fmt == '#') {
+ if (!*sequence) {
+ *frameno = 1;
+ }
+ fmt = *template++;
+ }
+ if (fmt == '0') {
+ digits = *template++;
+ if (digits < '0' || digits > '9')
+ goto error_exit;
+ fmt = *template++;
+ }
+ if (fmt != 'n')
+ goto error_exit;
+ char fmtstr[] = {'%', '0', digits, 'd', '\0'};
+ res = talloc_asprintf_append(res, fmtstr, *frameno);
+ if (*frameno < 100000 - 1) {
+ (*frameno) += 1;
+ (*sequence) += 1;
+ }
+ break;
+ }
+ case 'f':
+ case 'F': {
+ char *video_file = get_metadata(mpctx, META_NAME);
+ if (video_file) {
+ char *name = video_file;
+ if (fmt == 'F')
+ name = stripext(res, video_file);
+ append_filename(&res, name);
+ }
+ talloc_free(video_file);
+ break;
+ }
+ case 'p':
+ case 'P':
+ append_filename(&res,
+ format_time(res, get_current_time(mpctx), fmt == 'P'));
+ break;
+ case 't': {
+ char fmt = *template;
+ if (!fmt)
+ goto error_exit;
+ template++;
+ char fmtstr[] = {'%', fmt, '\0'};
+ char buffer[80];
+ if (strftime(buffer, sizeof(buffer), fmtstr, local_time) == 0)
+ buffer[0] = '\0';
+ append_filename(&res, buffer);
+ break;
+ }
+ case '{': {
+ char *end = strchr(template, '}');
+ if (!end)
+ goto error_exit;
+ struct bstr prop = bstr_splice(bstr(template), 0, end - template);
+ template = end + 1;
+ char *s = do_format_property(mpctx, prop);
+ if (s)
+ append_filename(&res, s);
+ talloc_free(s);
+ break;
+ }
+ case '%':
+ res = talloc_strdup_append(res, "%");
+ break;
+ default:
+ goto error_exit;
+ }
}
- mp_msg(MSGT_CPLAYER, MSGL_INFO, "*** screenshot '%s' ***\n", ctx->fname);
+ res = talloc_strdup_append(res, template);
+ return talloc_asprintf_append(res, ".%s", file_ext);
+error_exit:
+ talloc_free(res);
+ return NULL;
}
-void screenshot_save(struct MPContext *mpctx, struct mp_image *image)
+static char *gen_fname(screenshot_ctx *ctx)
{
- screenshot_ctx *ctx = screenshot_get_ctx(mpctx);
- struct mp_image *dst = alloc_mpi(image->w, image->h, IMGFMT_RGB24);
+ int sequence = 0;
+ for (;;) {
+ int prev_sequence = sequence;
+ char *fname = create_fname(ctx->mpctx,
+ ctx->mpctx->opts.screenshot_template,
+ get_writer(ctx)->file_ext,
+ &sequence,
+ &ctx->frameno);
+
+ if (!fname) {
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "Invalid screenshot filename "
+ "template! Fix or remove the --screenshot-template option."
+ "\n");
+ return NULL;
+ }
- struct SwsContext *sws = sws_getContextFromCmdLine(image->width,
- image->height,
- image->imgfmt,
- dst->width,
- dst->height,
- dst->imgfmt);
+ if (!fexists(fname))
+ return fname;
- struct mp_csp_details colorspace;
- get_detected_video_colorspace(mpctx->sh_video, &colorspace);
- // this is a property of the output device; images always use full-range RGB
- colorspace.levels_out = MP_CSP_LEVELS_PC;
- mp_sws_set_colorspace(sws, &colorspace);
+ if (sequence == prev_sequence) {
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "Can't save screenshot, file '%s' "
+ "already exists!\n", fname);
+ talloc_free(fname);
+ return NULL;
+ }
- sws_scale(sws, (const uint8_t **)image->planes, image->stride, 0,
- image->height, dst->planes, dst->stride);
+ talloc_free(fname);
+ }
+}
+
+void screenshot_save(struct MPContext *mpctx, struct mp_image *image)
+{
+ screenshot_ctx *ctx = mpctx->screenshot_ctx;
+ const struct img_writer *writer = get_writer(ctx);
+ struct mp_image *allocated_image = NULL;
+ const int destfmt = IMGFMT_RGB24;
+
+ if (image->imgfmt != destfmt) {
+ struct mp_image *dst = alloc_mpi(image->w, image->h, destfmt);
+
+ struct SwsContext *sws = sws_getContextFromCmdLine_hq(image->width,
+ image->height,
+ image->imgfmt,
+ dst->width,
+ dst->height,
+ dst->imgfmt);
+
+ struct mp_csp_details colorspace;
+ get_detected_video_colorspace(mpctx->sh_video, &colorspace);
+ // this is a property of the output device; images always use
+ // full-range RGB
+ colorspace.levels_out = MP_CSP_LEVELS_PC;
+ mp_sws_set_colorspace(sws, &colorspace);
+
+ sws_scale(sws, (const uint8_t **)image->planes, image->stride, 0,
+ image->height, dst->planes, dst->stride);
+
+ sws_freeContext(sws);
+
+ allocated_image = dst;
+ image = dst;
+ }
- gen_fname(ctx);
- write_png(ctx, dst);
+ char *filename = gen_fname(ctx);
+ if (filename) {
+ FILE *fp = fopen(filename, "wb");
+ if (fp == NULL) {
+ mp_msg(MSGT_CPLAYER, MSGL_ERR,
+ "\nError opening '%s' for writing!\n", filename);
+ } else {
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, "*** screenshot '%s' ***\n",
+ filename);
+ int success = writer->write(ctx, image, fp);
+ success = !fclose(fp) && success;
+ if (!success)
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "Error writing screenshot!\n");
+ }
+ talloc_free(filename);
+ }
- sws_freeContext(sws);
- free_mp_image(dst);
+ free_mp_image(allocated_image);
}
static void vf_screenshot_callback(void *pctx, struct mp_image *image)
{
struct MPContext *mpctx = (struct MPContext *)pctx;
- screenshot_ctx *ctx = screenshot_get_ctx(mpctx);
+ screenshot_ctx *ctx = mpctx->screenshot_ctx;
screenshot_save(mpctx, image);
if (ctx->each_frame)
screenshot_request(mpctx, 0, ctx->full_window);
}
+static bool force_vf(struct MPContext *mpctx)
+{
+ if (mpctx->sh_video) {
+ struct vf_instance *vf = mpctx->sh_video->vfilter;
+ while (vf) {
+ if (strcmp(vf->info->name, "screenshot_force") == 0)
+ return true;
+ vf = vf->next;
+ }
+ }
+ return false;
+}
+
void screenshot_request(struct MPContext *mpctx, bool each_frame,
bool full_window)
{
if (mpctx->video_out && mpctx->video_out->config_ok) {
- screenshot_ctx *ctx = screenshot_get_ctx(mpctx);
+ screenshot_ctx *ctx = mpctx->screenshot_ctx;
ctx->using_vf_screenshot = 0;
@@ -211,7 +502,9 @@ void screenshot_request(struct MPContext *mpctx, bool each_frame,
}
struct voctrl_screenshot_args args = { .full_window = full_window };
- if (vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args) == true) {
+ if (!force_vf(mpctx)
+ && vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args) == true)
+ {
screenshot_save(mpctx, args.out_image);
free_mp_image(args.out_image);
} else {
@@ -233,7 +526,7 @@ void screenshot_request(struct MPContext *mpctx, bool each_frame,
void screenshot_flip(struct MPContext *mpctx)
{
- screenshot_ctx *ctx = screenshot_get_ctx(mpctx);
+ screenshot_ctx *ctx = mpctx->screenshot_ctx;
if (!ctx->each_frame)
return;
diff --git a/screenshot.h b/screenshot.h
index c57778c0b3..6d205990f8 100644
--- a/screenshot.h
+++ b/screenshot.h
@@ -24,6 +24,9 @@
struct MPContext;
struct mp_image;
+// One time initialization at program start.
+void screenshot_init(struct MPContext *mpctx);
+
// Request a taking & saving a screenshot of the currently displayed frame.
// each_frame: If set, this toggles per-frame screenshots, exactly like the
// screenshot slave command (MP_CMD_SCREENSHOT).
diff --git a/stream/stream_file.c b/stream/stream_file.c
index e9bb3eb03a..28f8f975ce 100644
--- a/stream/stream_file.c
+++ b/stream/stream_file.c
@@ -173,6 +173,15 @@ static int open_f(stream_t *stream,int mode, void* opts, int* file_format) {
m_struct_free(&stream_opts,opts);
return STREAM_ERROR;
}
+#ifndef __MINGW32__
+ struct stat st;
+ if (fstat(f, &st) == 0 && S_ISDIR(st.st_mode)) {
+ mp_tmsg(MSGT_OPEN,MSGL_ERR,"File is a directory: '%s'\n",filename);
+ close(f);
+ m_struct_free(&stream_opts,opts);
+ return STREAM_ERROR;
+ }
+#endif
}
len=lseek(f,0,SEEK_END); lseek(f,0,SEEK_SET);
diff --git a/sub/ass_mp.c b/sub/ass_mp.c
index 0713248db0..88c55862f2 100644
--- a/sub/ass_mp.c
+++ b/sub/ass_mp.c
@@ -248,8 +248,7 @@ void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts, int w, int h,
int hinting;
ass_set_frame_size(priv, w, h);
ass_set_margins(priv, opts->ass_top_margin, opts->ass_bottom_margin, 0, 0);
- ass_set_use_margins(priv, opts->ass_use_margins);
- ass_set_font_scale(priv, opts->ass_font_scale);
+ mp_ass_reload_options(priv, opts);
if (!unscaled && (opts->ass_hinting & 4))
hinting = 0;
else
diff --git a/sub/subreader.c b/sub/subreader.c
index f694f57a7c..ad4142d50a 100644
--- a/sub/subreader.c
+++ b/sub/subreader.c
@@ -1505,7 +1505,11 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
utf16--;
mpsub_multiplier = (uses_time ? 100.0 : 1.0);
- if (sub_format==SUB_INVALID) {mp_msg(MSGT_SUBREADER,MSGL_WARN,"SUB: Could not determine file format\n");return NULL;}
+ if (sub_format==SUB_INVALID) {
+ mp_msg(MSGT_SUBREADER,MSGL_WARN,"SUB: Could not determine file format\n");
+ free_stream(fd);
+ return NULL;
+ }
srp=sr+sub_format;
mp_msg(MSGT_SUBREADER, MSGL_V, "SUB: Detected subtitle file format: %s\n", srp->name);
@@ -1533,7 +1537,8 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
subcp_close();
sub_utf8=sub_utf8_prev;
#endif
- return NULL;
+ free_stream(fd);
+ return NULL;
}
#ifdef CONFIG_SORTSUB
@@ -1570,6 +1575,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
#endif
free(first);
free(alloced_sub);
+ free_stream(fd);
return NULL;
}
// Apply any post processing that needs recoding first
diff --git a/timeline/tl_cue.c b/timeline/tl_cue.c
new file mode 100644
index 0000000000..c4958a823a
--- /dev/null
+++ b/timeline/tl_cue.c
@@ -0,0 +1,436 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * mplayer2 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.
+ *
+ * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <dirent.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#include "talloc.h"
+
+#include "mp_core.h"
+#include "mp_msg.h"
+#include "libmpdemux/demuxer.h"
+#include "path.h"
+#include "bstr.h"
+#include "mpcommon.h"
+#include "stream/stream.h"
+
+// used by demuxer_cue.c
+bool mp_probe_cue(struct bstr data);
+
+#define SECS_PER_CUE_FRAME (1.0/75.0)
+
+enum cue_command {
+ CUE_ERROR = -1, // not a valid CUE command, or an unknown extension
+ CUE_EMPTY, // line with whitespace only
+ CUE_UNUSED, // valid CUE command, but ignored by this code
+ CUE_FILE,
+ CUE_TRACK,
+ CUE_INDEX,
+ CUE_TITLE,
+};
+
+static const struct {
+ enum cue_command command;
+ const char *text;
+} cue_command_strings[] = {
+ { CUE_FILE, "FILE" },
+ { CUE_TRACK, "TRACK" },
+ { CUE_INDEX, "INDEX" },
+ { CUE_TITLE, "TITLE" },
+ { CUE_UNUSED, "CATALOG" },
+ { CUE_UNUSED, "CDTEXTFILE" },
+ { CUE_UNUSED, "FLAGS" },
+ { CUE_UNUSED, "ISRC" },
+ { CUE_UNUSED, "PERFORMER" },
+ { CUE_UNUSED, "POSTGAP" },
+ { CUE_UNUSED, "PREGAP" },
+ { CUE_UNUSED, "REM" },
+ { CUE_UNUSED, "SONGWRITER" },
+ { CUE_UNUSED, "MESSAGE" },
+ { -1 },
+};
+
+struct cue_track {
+ double pregap_start; // corresponds to INDEX 00
+ double start; // corresponds to INDEX 01
+ struct bstr filename;
+ int source;
+ struct bstr title;
+};
+
+static enum cue_command read_cmd(struct bstr *data, struct bstr *out_params)
+{
+ struct bstr line = bstr_strip_linebreaks(bstr_getline(*data, data));
+ line = bstr_lstrip(line);
+ if (line.len == 0)
+ return CUE_EMPTY;
+ for (int n = 0; cue_command_strings[n].command != -1; n++) {
+ struct bstr name = bstr(cue_command_strings[n].text);
+ if (bstr_startswith(line, name)) {
+ struct bstr rest = bstr_cut(line, name.len);
+ if (rest.len && !strchr(WHITESPACE, rest.start[0]))
+ continue;
+ if (out_params)
+ *out_params = rest;
+ return cue_command_strings[n].command;
+ }
+ }
+ return CUE_ERROR;
+}
+
+static bool eat_char(struct bstr *data, char ch)
+{
+ if (data->len && data->start[0] == ch) {
+ *data = bstr_cut(*data, 1);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static struct bstr read_quoted(struct bstr *data)
+{
+ *data = bstr_lstrip(*data);
+ if (!eat_char(data, '"'))
+ return (struct bstr) {0};
+ int end = bstrchr(*data, '"');
+ if (end < 0)
+ return (struct bstr) {0};
+ struct bstr res = bstr_splice(*data, 0, end);
+ *data = bstr_cut(*data, end + 1);
+ return res;
+}
+
+// Read a 2 digit unsigned decimal integer.
+// Return -1 on failure.
+static int read_int_2(struct bstr *data)
+{
+ *data = bstr_lstrip(*data);
+ if (data->len && data->start[0] == '-')
+ return -1;
+ struct bstr s = *data;
+ int res = (int)bstrtoll(s, &s, 10);
+ if (data->len == s.len || data->len - s.len > 2)
+ return -1;
+ *data = s;
+ return res;
+}
+
+static double read_time(struct bstr *data)
+{
+ struct bstr s = *data;
+ bool ok = true;
+ double t1 = read_int_2(&s);
+ ok = eat_char(&s, ':') && ok;
+ double t2 = read_int_2(&s);
+ ok = eat_char(&s, ':') && ok;
+ double t3 = read_int_2(&s);
+ ok = ok && t1 >= 0 && t2 >= 0 && t3 >= 0;
+ return ok ? t1 * 60.0 + t2 + t3 * SECS_PER_CUE_FRAME : 0;
+}
+
+static struct bstr skip_utf8_bom(struct bstr data)
+{
+ return bstr_startswith0(data, "\xEF\xBB\xBF") ? bstr_cut(data, 3) : data;
+}
+
+// Check if the text in data is most likely CUE data. This is used by the
+// demuxer code to check the file type.
+// data is the start of the probed file, possibly cut off at a random point.
+bool mp_probe_cue(struct bstr data)
+{
+ bool valid = false;
+ data = skip_utf8_bom(data);
+ for (;;) {
+ enum cue_command cmd = read_cmd(&data, NULL);
+ // End reached. Since the line was most likely cut off, don't use the
+ // result of the last parsing call.
+ if (data.len == 0)
+ break;
+ if (cmd == CUE_ERROR)
+ return false;
+ if (cmd != CUE_EMPTY)
+ valid = true;
+ }
+ return valid;
+}
+
+static void add_source(struct MPContext *mpctx, struct stream *s,
+ struct demuxer *d)
+{
+ mpctx->num_sources++;
+ mpctx->sources = talloc_realloc(NULL, mpctx->sources, struct content_source,
+ mpctx->num_sources);
+ mpctx->sources[mpctx->num_sources - 1] = (struct content_source) {
+ .stream = s,
+ .demuxer = d,
+ };
+}
+
+static bool try_open(struct MPContext *mpctx, char *filename)
+{
+ struct bstr bfilename = bstr(filename);
+ // Avoid trying to open itself or another .cue file. Best would be
+ // to check the result of demuxer auto-detection, but the demuxer
+ // API doesn't allow this without opening a full demuxer.
+ if (bstr_case_endswith(bfilename, bstr(".cue"))
+ || bstrcasecmp(bstr(mpctx->demuxer->filename), bfilename) == 0)
+ return false;
+
+ int format = 0;
+ struct stream *s = open_stream(filename, &mpctx->opts, &format);
+ if (!s)
+ return false;
+ struct demuxer *d = demux_open(&mpctx->opts, s, format,
+ mpctx->opts.audio_id,
+ mpctx->opts.video_id,
+ mpctx->opts.sub_id,
+ filename);
+ // Since .bin files are raw PCM data with no headers, we have to explicitly
+ // open them. Also, try to avoid to open files that are most likely not .bin
+ // files, as that would only play noise. Checking the file extension is
+ // fragile, but it's about the only way we have.
+ // TODO: maybe also could check if the .bin file is a multiple of the Audio
+ // CD sector size (2352 bytes)
+ if (!d && bstr_case_endswith(bfilename, bstr(".bin"))) {
+ mp_msg(MSGT_CPLAYER, MSGL_WARN, "CUE: Opening as BIN file!\n");
+ d = demux_open(&mpctx->opts, s, DEMUXER_TYPE_RAWAUDIO,
+ mpctx->opts.audio_id,
+ mpctx->opts.video_id,
+ mpctx->opts.sub_id,
+ filename);
+ }
+ if (d) {
+ add_source(mpctx, s, d);
+ return true;
+ }
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "Could not open source '%s'!\n", filename);
+ free_stream(s);
+ return false;
+}
+
+static bool open_source(struct MPContext *mpctx, struct bstr filename)
+{
+ void *ctx = talloc_new(NULL);
+ bool res = false;
+
+ struct bstr dirname = mp_dirname(mpctx->demuxer->filename);
+
+ struct bstr base_filename = bstr(mp_basename(bstrdup0(ctx, filename)));
+ if (!base_filename.len) {
+ mp_msg(MSGT_CPLAYER, MSGL_WARN,
+ "CUE: Invalid audio filename in .cue file!\n");
+ } else {
+ char *fullname = mp_path_join(ctx, dirname, base_filename);
+ if (try_open(mpctx, fullname)) {
+ res = true;
+ goto out;
+ }
+ }
+
+ // Try an audio file with the same name as the .cue file (but different
+ // extension).
+ // Rationale: this situation happens easily if the audio file or both files
+ // are renamed.
+
+ struct bstr cuefile =
+ bstr_strip_ext(bstr(mp_basename(mpctx->demuxer->filename)));
+
+ DIR *d = opendir(bstrdup0(ctx, dirname));
+ if (!d)
+ goto out;
+ struct dirent *de;
+ while ((de = readdir(d))) {
+ char *dename0 = de->d_name;
+ struct bstr dename = bstr(dename0);
+ if (bstr_case_startswith(dename, cuefile)) {
+ mp_msg(MSGT_CPLAYER, MSGL_WARN, "CUE: No useful audio filename "
+ "in .cue file found, trying with '%s' instead!\n",
+ dename0);
+ if (try_open(mpctx, mp_path_join(ctx, dirname, dename))) {
+ res = true;
+ break;
+ }
+ }
+ }
+ closedir(d);
+
+out:
+ talloc_free(ctx);
+ if (!res)
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "CUE: Could not open audio file!\n");
+ return res;
+}
+
+// return length of the source in seconds, or -1 if unknown
+static double source_get_length(struct content_source *source)
+{
+ struct demuxer *demuxer = source->demuxer;
+ double get_time_ans;
+ // <= 0 means DEMUXER_CTRL_NOTIMPL or DEMUXER_CTRL_DONTKNOW
+ if (demuxer && demux_control(demuxer, DEMUXER_CTRL_GET_TIME_LENGTH,
+ (void *) &get_time_ans) > 0)
+ {
+ return get_time_ans;
+ } else {
+ return -1;
+ }
+}
+
+void build_cue_timeline(struct MPContext *mpctx)
+{
+ void *ctx = talloc_new(NULL);
+
+ struct bstr data = mpctx->demuxer->file_contents;
+ data = skip_utf8_bom(data);
+
+ struct cue_track *tracks = NULL;
+ size_t track_count = 0;
+
+ struct bstr filename = {0};
+ // Global metadata, and copied into new tracks.
+ struct cue_track proto_track = {0};
+ struct cue_track *cur_track = &proto_track;
+
+ while (data.len) {
+ struct bstr param;
+ switch (read_cmd(&data, &param)) {
+ case CUE_ERROR:
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "CUE: error parsing input file!\n");
+ goto out;
+ case CUE_TRACK: {
+ track_count++;
+ tracks = talloc_realloc(ctx, tracks, struct cue_track, track_count);
+ cur_track = &tracks[track_count - 1];
+ *cur_track = proto_track;
+ break;
+ }
+ case CUE_TITLE:
+ cur_track->title = read_quoted(&param);
+ break;
+ case CUE_INDEX: {
+ int type = read_int_2(&param);
+ double time = read_time(&param);
+ if (type == 1) {
+ cur_track->start = time;
+ cur_track->filename = filename;
+ } else if (type == 0) {
+ cur_track->pregap_start = time;
+ }
+ break;
+ }
+ case CUE_FILE:
+ // NOTE: FILE comes before TRACK, so don't use cur_track->filename
+ filename = read_quoted(&param);
+ break;
+ }
+ }
+
+ if (track_count == 0) {
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "CUE: no tracks found!\n");
+ goto out;
+ }
+
+ // Remove duplicate file entries. This might be too sophisticated, since
+ // CUE files usually use either separate files for every single track, or
+ // only one file for all tracks.
+
+ struct bstr *files = 0;
+ size_t file_count = 0;
+
+ for (size_t n = 0; n < track_count; n++) {
+ struct cue_track *track = &tracks[n];
+ track->source = -1;
+ for (size_t file = 0; file < file_count; file++) {
+ if (bstrcmp(files[file], track->filename) == 0) {
+ track->source = file;
+ break;
+ }
+ }
+ if (track->source == -1) {
+ file_count++;
+ files = talloc_realloc(ctx, files, struct bstr, file_count);
+ files[file_count - 1] = track->filename;
+ track->source = file_count - 1;
+ }
+ }
+
+ add_source(mpctx, mpctx->stream, mpctx->demuxer);
+
+ for (size_t i = 0; i < file_count; i++) {
+ if (!open_source(mpctx, files[i]))
+ goto out;
+ }
+
+ struct timeline_part *timeline = talloc_array_ptrtype(NULL, timeline,
+ track_count + 1);
+ struct chapter *chapters = talloc_array_ptrtype(NULL, chapters,
+ track_count);
+ double starttime = 0;
+ for (int i = 0; i < track_count; i++) {
+ struct content_source *source = mpctx->sources + 1 + tracks[i].source;
+ double duration;
+ if (i + 1 < track_count && tracks[i].source == tracks[i + 1].source) {
+ duration = tracks[i + 1].start - tracks[i].start;
+ } else {
+ duration = source_get_length(source);
+ // Two cases: 1) last track of a single-file cue, or 2) any track of
+ // a multi-file cue. We need to do this for 1) only because the
+ // timeline needs to be terminated with the length of the last
+ // track.
+ duration -= tracks[i].start;
+ }
+ if (duration < 0) {
+ mp_msg(MSGT_CPLAYER, MSGL_WARN,
+ "CUE: Can't get duration of source file!\n");
+ // xxx: do something more reasonable
+ duration = 0.0;
+ }
+ timeline[i] = (struct timeline_part) {
+ .start = starttime,
+ .source_start = tracks[i].start,
+ .source = source,
+ };
+ chapters[i] = (struct chapter) {
+ .start = timeline[i].start,
+ // might want to include other metadata here
+ .name = bstrdup0(chapters, tracks[i].title),
+ };
+ starttime += duration;
+ }
+
+ // apparently we need this to give the last part a non-zero length
+ timeline[track_count] = (struct timeline_part) {
+ .start = starttime,
+ // perhaps unused by the timeline code
+ .source_start = 0,
+ .source = timeline[0].source,
+ };
+
+ mpctx->timeline = timeline;
+ // the last part is not included it in the count
+ mpctx->num_timeline_parts = track_count + 1 - 1;
+ mpctx->chapters = chapters;
+ mpctx->num_chapters = track_count;
+
+out:
+ talloc_free(ctx);
+}
diff --git a/timeline/tl_matroska.c b/timeline/tl_matroska.c
index 00fd6b7088..09f86b8284 100644
--- a/timeline/tl_matroska.c
+++ b/timeline/tl_matroska.c
@@ -21,6 +21,9 @@
#include <inttypes.h>
#include <assert.h>
#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <libavutil/common.h>
#include "osdep/io.h"
@@ -35,6 +38,25 @@
#include "mpcommon.h"
#include "stream/stream.h"
+struct find_entry {
+ char *name;
+ int matchlen;
+ off_t size;
+};
+
+static int cmp_entry(const void *pa, const void *pb)
+{
+ const struct find_entry *a = pa, *b = pb;
+ // check "similar" filenames first
+ int matchdiff = b->matchlen - a->matchlen;
+ if (matchdiff)
+ return FFSIGN(matchdiff);
+ // check small files first
+ off_t sizediff = a->size - b->size;
+ if (sizediff)
+ return FFSIGN(sizediff);
+ return 0;
+}
static char **find_files(const char *original_file, const char *suffix)
{
@@ -48,9 +70,8 @@ static char **find_files(const char *original_file, const char *suffix)
talloc_free(tmpmem);
return results;
}
+ struct find_entry *entries = NULL;
struct dirent *ep;
- char ***names_by_matchlen = talloc_zero_array(tmpmem, char **,
- strlen(basename) + 1);
int num_results = 0;
while ((ep = readdir(dp))) {
int suffix_offset = strlen(ep->d_name) - strlen(suffix);
@@ -67,22 +88,26 @@ static char **find_files(const char *original_file, const char *suffix)
int matchlen = 0;
while (*s1 && *s1++ == *s2++)
matchlen++;
- int oldcount = MP_TALLOC_ELEMS(names_by_matchlen[matchlen]);
- names_by_matchlen[matchlen] = talloc_realloc(names_by_matchlen,
- names_by_matchlen[matchlen],
- char *, oldcount + 1);
- names_by_matchlen[matchlen][oldcount] = name;
+ // be a bit more fuzzy about matching the filename
+ matchlen = (matchlen + 3) / 5;
+
+ struct stat statbuf;
+ if (stat(name, &statbuf) != 0)
+ continue;
+ off_t size = statbuf.st_size;
+
+ entries = talloc_realloc(entries, entries, struct find_entry,
+ num_results + 1);
+ entries[num_results] = (struct find_entry) { name, matchlen, size };
num_results++;
}
closedir(dp);
+ // NOTE: maybe should make it compare pointers instead
+ qsort(entries, num_results, sizeof(struct find_entry), cmp_entry);
results = talloc_realloc(NULL, results, char *, num_results);
- char **resptr = results;
- for (int i = strlen(basename); i >= 0; i--) {
- char **p = names_by_matchlen[i];
- for (int j = 0; j < talloc_get_size(p) / sizeof(char *); j++)
- *resptr++ = p[j];
+ for (int i = 0; i < num_results; i++) {
+ results[i] = entries[i].name;
}
- assert(resptr == results + num_results);
talloc_free(tmpmem);
return results;
}
diff --git a/version.sh b/version.sh
index 6f3be92639..73905f05fc 100755
--- a/version.sh
+++ b/version.sh
@@ -15,12 +15,10 @@ test $version || version=$git_revision
NEW_REVISION="#define VERSION \"${version}${extra}\""
OLD_REVISION=$(head -n 1 version.h 2> /dev/null)
-TITLE='#define MP_TITLE "%s "VERSION" (C) 2000-2012 MPlayer Team\n"'
# Update version.h only on revision changes to avoid spurious rebuilds
if test "$NEW_REVISION" != "$OLD_REVISION"; then
cat <<EOF > version.h
$NEW_REVISION
-$TITLE
EOF
fi