summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml12
-rw-r--r--DOCS/interface-changes.rst3
-rw-r--r--DOCS/man/ao.rst5
-rw-r--r--DOCS/man/input.rst7
-rw-r--r--DOCS/man/libmpv.rst4
-rw-r--r--DOCS/man/options.rst65
-rw-r--r--DOCS/man/vf.rst2
-rw-r--r--README.md6
-rwxr-xr-xTOOLS/appveyor-install.sh10
-rwxr-xr-xTOOLS/umpv4
-rw-r--r--appveyor.yml16
-rw-r--r--audio/aframe.c18
-rw-r--r--audio/filter/af_scaletempo2_internals.c22
-rw-r--r--audio/out/ao.c4
-rw-r--r--audio/out/ao_oss.c410
-rw-r--r--audio/out/ao_pulse.c24
-rw-r--r--common/msg.c4
-rw-r--r--demux/demux.c8
-rw-r--r--demux/demux.h2
-rw-r--r--demux/demux_mf.c41
-rw-r--r--filters/f_auto_filters.c4
-rw-r--r--filters/f_auto_filters.h2
-rw-r--r--filters/f_decoder_wrapper.c19
-rw-r--r--options/m_option.c32
-rw-r--r--options/m_option.h40
-rw-r--r--options/options.c7
-rw-r--r--osdep/macosx_menubar.m7
-rw-r--r--osdep/macosx_touchbar.h1
-rw-r--r--osdep/macosx_touchbar.m248
-rw-r--r--player/audio.c3
-rw-r--r--player/command.c40
-rw-r--r--player/core.h3
-rw-r--r--player/loadfile.c18
-rw-r--r--player/lua/stats.lua4
-rw-r--r--player/lua/ytdl_hook.lua2
-rw-r--r--player/main.c8
-rw-r--r--player/scripting.c5
-rw-r--r--stream/stream.h2
-rw-r--r--stream/stream_concat.c2
-rw-r--r--stream/stream_file.c2
-rw-r--r--stream/stream_lavf.c8
-rw-r--r--stream/stream_memory.c2
-rw-r--r--stream/stream_mf.c1
-rw-r--r--stream/stream_slice.c7
-rw-r--r--video/decode/vd_lavc.c46
-rw-r--r--video/out/cocoa_cb_common.swift4
-rw-r--r--video/out/d3d11/context.c7
-rw-r--r--video/out/drm_common.c49
-rw-r--r--video/out/gpu/context.c40
-rw-r--r--video/out/gpu/context.h8
-rw-r--r--video/out/gpu/hwdec.c3
-rw-r--r--video/out/gpu/hwdec.h2
-rw-r--r--video/out/gpu/lcms.c3
-rw-r--r--video/out/gpu/video.c24
-rw-r--r--video/out/gpu/video_shaders.c2
-rw-r--r--video/out/hwdec/hwdec_vaapi_vk.c4
-rw-r--r--video/out/mac/common.swift5
-rw-r--r--video/out/mac/window.swift29
-rw-r--r--video/out/placebo/ra_pl.c56
-rw-r--r--video/out/vo_gpu.c8
-rw-r--r--video/out/vo_wlshm.c3
-rw-r--r--video/out/vulkan/context.c3
-rw-r--r--video/out/wayland_common.c105
-rw-r--r--wscript7
-rw-r--r--wscript_build.py1
65 files changed, 1096 insertions, 447 deletions
diff --git a/.travis.yml b/.travis.yml
index 18946c05e1..0ab0e75f5e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -118,13 +118,21 @@ before_install:
fi
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
- remove=$(brew list --formula)
+ if [[ "$TRAVIS_OSX_IMAGE" == "xcode12.2" ]]; then
+ remove=$(brew list --formula)
+ else
+ remove=$(brew list)
+ fi
keep="gettext pcre2 git"
install="autoconf automake pkg-config libtool python freetype fribidi little-cms2 luajit libass ffmpeg"
for formula in ${keep[@]}; do remove=("${remove[@]/$formula}"); done
for formula in ${install[@]}; do remove=("${remove[@]/$formula}"); done
brew remove --force $remove --ignore-dependencies
- brew remove --cask $(brew list --cask)
+ if [[ "$TRAVIS_OSX_IMAGE" == "xcode12.2" ]]; then
+ brew remove $(brew list --cask)
+ else
+ brew cask remove $(brew cask list)
+ fi
brew untap homebrew/cask
brew update
if [[ -n "$CI_HOMEBREW_HASH" ]]; then
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst
index 1f5fda5b02..4a36041595 100644
--- a/DOCS/interface-changes.rst
+++ b/DOCS/interface-changes.rst
@@ -34,6 +34,9 @@ Interface changes
which didn't take the menu bar and Dock into account. The new default
behaviour includes both. To revert to the old behavior set this to
`whole`.
+ - add an additional optional `albumart` argument to the `video-add` command,
+ which tells mpv to load the given video as album art.
+ - undeprecate `--cache-secs` option
--- mpv 0.33.0 ---
- add `--d3d11-exclusive-fs` flag to enable D3D11 exclusive fullscreen mode
when the player enters fullscreen.
diff --git a/DOCS/man/ao.rst b/DOCS/man/ao.rst
index d70286647f..1c0b9e146b 100644
--- a/DOCS/man/ao.rst
+++ b/DOCS/man/ao.rst
@@ -14,7 +14,7 @@ in the list.
See ``--ao=help`` for a list of compiled-in audio output drivers. The
driver ``--ao=alsa`` is preferred. ``--ao=pulse`` is preferred on systems
- where PulseAudio is used.
+ where PulseAudio is used. On BSD systems, ``--ao=oss`` is preferred.
Available audio output drivers are:
@@ -35,6 +35,9 @@ Available audio output drivers are:
with automatic upmixing with shared access, so playing stereo
and multichannel audio at the same time will work as expected.
+``oss``
+ OSS audio output driver
+
``jack``
JACK (Jack Audio Connection Kit) audio output driver.
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index 82b570c228..9f77123a6c 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -801,8 +801,11 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``audio-reload [<id>]``
Reload the given audio tracks. See ``sub-reload`` command.
-``video-add <url> [<flags> [<title> [<lang>]]]``
- Load the given video file. See ``sub-add`` command.
+``video-add <url> [<flags> [<title> [<lang> [<albumart>]]]]``
+ Load the given video file. See ``sub-add`` command for common options.
+
+ ``albumart`` (``MPV_FORMAT_FLAG``)
+ If enabled, mpv will load the given video as album art.
``video-remove [<id>]``
Remove the given video track. See ``sub-remove`` command.
diff --git a/DOCS/man/libmpv.rst b/DOCS/man/libmpv.rst
index 909a2bb447..7f5d2f5c39 100644
--- a/DOCS/man/libmpv.rst
+++ b/DOCS/man/libmpv.rst
@@ -21,8 +21,8 @@ C PLUGINS
You can write C plugins for mpv. These use the libmpv API, although they do not
use the libmpv library itself.
-Currently, they must be explicitly enabled at build time with
-``--enable-cplugins``. They are available on Linux/BSD platforms only.
+They are available on Linux/BSD platforms only and enabled by default if the
+compiler supports linking with the ``-rdynamic`` flag.
C plugins location
------------------
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 16625fe0c4..14e58b28e5 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -56,7 +56,7 @@ Track Selection
behavior tends to change around with each mpv release.
The track selection properties will return the option value outside of
- playback (as expected), but during playbac, the affective track
+ playback (as expected), but during playback, the affective track
selection is returned. For example, with ``--aid=auto``, the ``aid``
property will suddenly return ``2`` after playback initialization
(assuming the file has at least 2 audio tracks, and the second is the
@@ -213,7 +213,7 @@ Playback Control
Slow down or speed up playback by the factor given as parameter.
If ``--audio-pitch-correction`` (on by default) is used, playing with a
- speed higher than normal automatically inserts the ``scaletempo`` audio
+ speed higher than normal automatically inserts the ``scaletempo2`` audio
filter.
``--pause``
@@ -392,10 +392,12 @@ Playback Control
difference between the two option is that this option performs a seek on
loop, instead of reloading the file.
- Note that ``--loop-file`` counts the number of times it causes the player to
- seek to the beginning of the file, not the number of full playthroughs. This
- means ``--loop-file=1`` will end up playing the file twice. Contrast with
- ``--loop-playlist``, which counts the number of full playthroughs.
+ .. note::
+
+ ``--loop-file`` counts the number of times it causes the player to
+ seek to the beginning of the file, not the number of full playthroughs. This
+ means ``--loop-file=1`` will end up playing the file twice. Contrast with
+ ``--loop-playlist``, which counts the number of full playthroughs.
``--loop`` is an alias for this option.
@@ -1482,11 +1484,14 @@ Video
This option is disabled if the ``--no-keepaspect`` option is used.
``--video-rotate=<0-359|no>``
- Rotate the video clockwise, in degrees. Currently supports 90° steps only.
- If ``no`` is given, the video is never rotated, even if the file has
- rotation metadata. (The rotation value is added to the rotation metadata,
- which means the value ``0`` would rotate the video according to the
- rotation metadata.)
+ Rotate the video clockwise, in degrees. If ``no`` is given, the video is
+ never rotated, even if the file has rotation metadata. (The rotation value
+ is added to the rotation metadata, which means the value ``0`` would rotate
+ the video according to the rotation metadata.)
+
+ When using hardware decoding without copy-back, only 90° steps work, while
+ software decoding and hardware decoding methods that copy the video back to
+ system memory support all values between 0 and 359.
``--video-zoom=<value>``
Adjust the video display scale factor by the given value. The parameter is
@@ -1744,7 +1749,7 @@ Audio
``--audio-pitch-correction=<yes|no>``
If this is enabled (default), playing with a speed different from normal
- automatically inserts the ``scaletempo`` audio filter. For details, see
+ automatically inserts the ``scaletempo2`` audio filter. For details, see
audio filter section.
``--audio-device=<name>``
@@ -2997,7 +3002,7 @@ Window
applied to window/video with size exceeding size of the screen.
``--on-all-workspaces``
- (X11 only)
+ (X11 and macOS only)
Show the video window on all virtual desktops.
``--geometry=<[W[xH]][+-x+-y][/WS]>``, ``--geometry=<x:y>``
@@ -4574,12 +4579,6 @@ Cache
Turn off input stream caching. See ``--cache``.
``--cache-secs=<seconds>``
- Deprecated. Once this option is removed, there will be no way to limit the
- cache size by time (only by size with ``--demuxer-max-bytes``). This option
- is considered useless, since there is no good reason to limit the cache by
- time, and the default value of this option is already something very high.
- The interaction with the other cache options is also confusing.
-
How many seconds of audio/video to prefetch if the cache is active. This
overrides the ``--demuxer-readahead-secs`` option if and only if the cache
is enabled and the value is larger. The default value is set to something
@@ -5178,18 +5177,29 @@ The following video options are currently all specific to ``--vo=gpu`` and
``--interpolation-threshold=<0..1,-1>``
Threshold below which frame ratio interpolation gets disabled (default:
- ``0.0001``). This is calculated as ``abs(disphz/vfps - 1) < threshold``,
+ ``0.01``). This is calculated as ``abs(disphz/vfps - 1) < threshold``,
where ``vfps`` is the speed-adjusted video FPS, and ``disphz`` the
display refresh rate. (The speed-adjusted video FPS is roughly equal to
the normal video FPS, but with slowdown and speedup applied. This matters
if you use ``--video-sync=display-resample`` to make video run synchronously
to the display FPS, or if you change the ``speed`` property.)
- The default is intended to almost always enable interpolation if the
- playback rate is even slightly different from the display refresh rate. But
- note that if you use e.g. ``--video-sync=display-vdrop``, small deviations
- in the rate can disable interpolation and introduce a discontinuity every
- other minute.
+ The default is intended to enable interpolation in scenarios where
+ retiming with the ``--video-sync=display-*`` cannot adjust the speed of
+ the video sufficiently for smooth playback. For example if a video is
+ 60.00 FPS and your display refresh rate is 59.94 Hz, interpolation will
+ never be activated, since the mismatch is within 1% of the refresh
+ rate. The default also handles the scenario when mpv cannot determine the
+ container FPS, such as during certain live streams, and may dynamically
+ toggle interpolation on and off. In this scenario, the default would be to
+ not use interpolation but rather to allow ``--video-sync=display-*`` to
+ retime the video to match display refresh rate. See
+ ``--video-sync-max-video-change`` for more information about how mpv
+ will retime video.
+
+ Also note that if you use e.g. ``--video-sync=display-vdrop``, small
+ deviations in the rate can disable interpolation and introduce a
+ discontinuity every other minute.
Set this to ``-1`` to disable this logic.
@@ -5703,7 +5713,7 @@ The following video options are currently all specific to ``--vo=gpu`` and
``--deband-threshold=<0..4096>``
The debanding filter's cut-off threshold. Higher numbers increase the
debanding strength dramatically but progressively diminish image details.
- (Default 64)
+ (Default 32)
``--deband-range=<1..64>``
The debanding filter's initial radius. The radius increases linearly for
@@ -6576,6 +6586,9 @@ Miscellaneous
multiple of the display FPS, as long as the speed change does not exceed
the value set by ``--video-sync-max-video-change``.
+ See ``--interpolation-threshold`` for how this option affects
+ interpolation.
+
This is mostly for testing, and the option may be randomly changed in the
future without notice.
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index 3797ad26bd..e07360b24d 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -310,7 +310,7 @@ Available mpv-only filters are:
``<stereo-in>``
Set the stereo mode the video is assumed to be encoded in. Use
- ``--vf format:stereo-in=help`` to list all available modes. Check with
+ ``--vf=format:stereo-in=help`` to list all available modes. Check with
the ``stereo3d`` filter documentation to see what the names mean.
``<stereo-out>``
diff --git a/README.md b/README.md
index 8cc3dc3bcc..3f6eee54ae 100644
--- a/README.md
+++ b/README.md
@@ -121,9 +121,9 @@ Libass dependencies (when building libass):
- gcc or clang, yasm on x86 and x86_64
- fribidi, freetype, fontconfig development headers (for libass)
-- harfbuzz (optional, required for correct rendering of combining characters,
- particularly for correct rendering of non-English text on OSX, and
- Arabic/Indic scripts on any platform)
+- harfbuzz (required for correct rendering of combining characters, particularly
+ for correct rendering of non-English text on OSX, and Arabic/Indic scripts on
+ any platform)
FFmpeg dependencies (when building FFmpeg):
diff --git a/TOOLS/appveyor-install.sh b/TOOLS/appveyor-install.sh
index 835850abf1..95e4f7b4ed 100755
--- a/TOOLS/appveyor-install.sh
+++ b/TOOLS/appveyor-install.sh
@@ -24,6 +24,7 @@ pacman -S --noconfirm --needed \
$MINGW_PACKAGE_PREFIX-ninja \
$MINGW_PACKAGE_PREFIX-rubberband \
$MINGW_PACKAGE_PREFIX-shaderc \
+ $MINGW_PACKAGE_PREFIX-spirv-cross \
$MINGW_PACKAGE_PREFIX-uchardet \
$MINGW_PACKAGE_PREFIX-vulkan
@@ -51,12 +52,3 @@ pacman -Sc --noconfirm
--enable-schannel
make -j4 install
)
-
-# Compile SPIRV-Cross
-(
- git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Cross && cd SPIRV-Cross
-
- mkdir build && cd build
- cmake -GNinja -DSPIRV_CROSS_SHARED=ON -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX ..
- ninja install
-)
diff --git a/TOOLS/umpv b/TOOLS/umpv
index 762e73a622..2044e3e926 100755
--- a/TOOLS/umpv
+++ b/TOOLS/umpv
@@ -31,8 +31,6 @@ import os
import socket
import errno
import subprocess
-import fcntl
-import stat
import string
files = sys.argv[1:]
@@ -52,7 +50,7 @@ def make_abs(filename):
if not is_url(filename):
return os.path.abspath(filename)
return filename
-files = [make_abs(f) for f in files]
+files = (make_abs(f) for f in files)
SOCK = os.path.join(os.getenv("HOME"), ".umpv_socket")
diff --git a/appveyor.yml b/appveyor.yml
index 92f229aa3a..a385ac0364 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,3 +1,5 @@
+image: Visual Studio 2019
+
branches:
only:
- master
@@ -15,18 +17,8 @@ shallow_clone: true
test: off
install:
- # Work around age/brokenness of Appveyor's msys2 by adding the new
- # maintainer's keyring.
- - bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
- - bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- - bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- - bash -lc "pacman -U --noconfirm msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
- # Support for Ada and Objective-C was removed from MSYS2. GCC won't update if
- # these packages are installed.
- - >-
- C:\msys64\usr\bin\pacman -R --noconfirm --noprogressbar
- mingw-w64-i686-gcc-ada mingw-w64-i686-gcc-objc mingw-w64-x86_64-gcc-ada
- mingw-w64-x86_64-gcc-objc
+ # Disable checking disk space to speed up install time
+ - C:\msys64\usr\bin\sed -i 's/^CheckSpace/#CheckSpace/g' /etc/pacman.conf
# Update core packages
- C:\msys64\usr\bin\pacman -Syyuu --noconfirm --noprogressbar --ask=20
# Explicitly kill any remaining msys2 processes after core update
diff --git a/audio/aframe.c b/audio/aframe.c
index fc5d73be2d..c2c0df7c9b 100644
--- a/audio/aframe.c
+++ b/audio/aframe.c
@@ -635,18 +635,24 @@ int mp_aframe_pool_allocate(struct mp_aframe_pool *pool, struct mp_aframe *frame
AVFrame *av_frame = frame->av_frame;
if (av_frame->extended_data != av_frame->data)
av_freep(&av_frame->extended_data); // sigh
- av_frame->extended_data =
- av_mallocz_array(planes, sizeof(av_frame->extended_data[0]));
- if (!av_frame->extended_data)
- abort();
+ if (planes > AV_NUM_DATA_POINTERS) {
+ av_frame->extended_data =
+ av_mallocz_array(planes, sizeof(av_frame->extended_data[0]));
+ if (!av_frame->extended_data)
+ abort();
+ } else {
+ av_frame->extended_data = av_frame->data;
+ }
av_frame->buf[0] = av_buffer_pool_get(pool->avpool);
if (!av_frame->buf[0])
return -1;
av_frame->linesize[0] = samples * sstride;
for (int n = 0; n < planes; n++)
av_frame->extended_data[n] = av_frame->buf[0]->data + n * plane_size;
- for (int n = 0; n < MPMIN(planes, AV_NUM_DATA_POINTERS); n++)
- av_frame->data[n] = av_frame->extended_data[n];
+ if (planes > AV_NUM_DATA_POINTERS) {
+ for (int n = 0; n < AV_NUM_DATA_POINTERS; n++)
+ av_frame->data[n] = av_frame->extended_data[n];
+ }
av_frame->nb_samples = samples;
return 0;
diff --git a/audio/filter/af_scaletempo2_internals.c b/audio/filter/af_scaletempo2_internals.c
index e348cb37a2..5eb0e6b8d9 100644
--- a/audio/filter/af_scaletempo2_internals.c
+++ b/audio/filter/af_scaletempo2_internals.c
@@ -389,10 +389,8 @@ static int write_completed_frames_to(struct mp_scaletempo2 *p,
static bool can_perform_wsola(struct mp_scaletempo2 *p)
{
- const int search_block_size = p->num_candidate_blocks
- + (p->ola_window_size - 1);
return p->target_block_index + p->ola_window_size <= p->input_buffer_frames
- && p->search_block_index + search_block_size <= p->input_buffer_frames;
+ && p->search_block_index + p->search_block_size <= p->input_buffer_frames;
}
// number of frames needed until a wsola iteration can be performed
@@ -403,6 +401,14 @@ static int frames_needed(struct mp_scaletempo2 *p)
p->search_block_index + p->search_block_size - p->input_buffer_frames));
}
+static void resize_input_buffer(struct mp_scaletempo2 *p, int size)
+{
+ if (size > p->input_buffer_size) {
+ p->input_buffer_size = size;
+ p->input_buffer = realloc_2d(p->input_buffer, p->channels, size);
+ }
+}
+
int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
uint8_t **planes, int frame_size, bool final)
{
@@ -411,7 +417,8 @@ int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
int total_fill = final ? needed : read;
if (total_fill == 0) return 0;
- assert(total_fill + p->input_buffer_frames <= p->input_buffer_size);
+ int required_size = total_fill + p->input_buffer_frames;
+ resize_input_buffer(p, required_size);
for (int i = 0; i < p->channels; ++i) {
memcpy(p->input_buffer[i] + p->input_buffer_frames,
@@ -427,11 +434,9 @@ int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
static bool target_is_within_search_region(struct mp_scaletempo2 *p)
{
- const int search_block_size = p->num_candidate_blocks + (p->ola_window_size - 1);
-
return p->target_block_index >= p->search_block_index
&& p->target_block_index + p->ola_window_size
- <= p->search_block_index + search_block_size;
+ <= p->search_block_index + p->search_block_size;
}
@@ -715,8 +720,7 @@ void mp_scaletempo2_init(struct mp_scaletempo2 *p, int channels, int rate)
p->search_block = realloc_2d(p->search_block, p->channels, p->search_block_size);
p->target_block = realloc_2d(p->target_block, p->channels, p->ola_window_size);
- p->input_buffer_size = 4 * MPMAX(p->ola_window_size, p->search_block_size);
- p->input_buffer = realloc_2d(p->input_buffer, p->channels, p->input_buffer_size);
+ resize_input_buffer(p, 4 * MPMAX(p->ola_window_size, p->search_block_size));
p->input_buffer_frames = 0;
p->energy_candidate_blocks = realloc(p->energy_candidate_blocks,
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 52a38b63be..7c347cb138 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -35,6 +35,7 @@
#include "common/common.h"
#include "common/global.h"
+extern const struct ao_driver audio_out_oss;
extern const struct ao_driver audio_out_audiotrack;
extern const struct ao_driver audio_out_audiounit;
extern const struct ao_driver audio_out_coreaudio;
@@ -71,6 +72,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_WASAPI
&audio_out_wasapi,
#endif
+#if HAVE_OSS_AUDIO
+ &audio_out_oss,
+#endif
// wrappers:
#if HAVE_JACK
&audio_out_jack,
diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c
new file mode 100644
index 0000000000..11b182e52d
--- /dev/null
+++ b/audio/out/ao_oss.c
@@ -0,0 +1,410 @@
+/*
+ * OSS audio output driver
+ *
+ * Original author: A'rpi
+ * Support for >2 output channels added 2001-11-25
+ * - Steve Davies <steve@daviesfam.org>
+ * Rozhuk Ivan <rozhuk.im@gmail.com> 2020
+ *
+ * This file is part of mpv.
+ *
+ * mpv 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.
+ *
+ * mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <sys/stat.h>
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#endif
+#include <sys/types.h>
+
+#include "config.h"
+#include "audio/format.h"
+#include "common/msg.h"
+#include "options/options.h"
+#include "osdep/endian.h"
+#include "osdep/io.h"
+#include "ao.h"
+#include "internal.h"
+
+#ifndef AFMT_AC3
+#define AFMT_AC3 -1
+#endif
+
+#define PATH_DEV_DSP "/dev/dsp"
+
+struct priv {
+ int dsp_fd;
+ bool playing;
+ double bps; /* Bytes per second. */
+};
+
+/* like alsa except for 6.1 and 7.1, from pcm/matrix_map.h */
+static const struct mp_chmap oss_layouts[MP_NUM_CHANNELS + 1] = {
+ {0}, /* empty */
+ MP_CHMAP_INIT_MONO, /* mono */
+ MP_CHMAP2(FL, FR), /* stereo */
+ MP_CHMAP3(FL, FR, LFE), /* 2.1 */
+ MP_CHMAP4(FL, FR, BL, BR), /* 4.0 */
+ MP_CHMAP5(FL, FR, BL, BR, FC), /* 5.0 */
+ MP_CHMAP6(FL, FR, BL, BR, FC, LFE), /* 5.1 */
+ MP_CHMAP7(FL, FR, BL, BR, FC, LFE, BC), /* 6.1 */
+ MP_CHMAP8(FL, FR, BL, BR, FC, LFE, SL, SR), /* 7.1 */
+};
+
+#if !defined(AFMT_S32_NE) && defined(AFMT_S32_LE) && defined(AFMT_S32_BE)
+#define AFMT_S32_NE AFMT_S32MP_SELECT_LE_BE(AFMT_S32_LE, AFMT_S32_BE)
+#endif
+
+static const int format_table[][2] = {
+ {AFMT_U8, AF_FORMAT_U8},
+ {AFMT_S16_NE, AF_FORMAT_S16},
+#ifdef AFMT_S32_NE
+ {AFMT_S32_NE, AF_FORMAT_S32},
+#endif
+#ifdef AFMT_FLOAT
+ {AFMT_FLOAT, AF_FORMAT_FLOAT},
+#endif
+#ifdef AFMT_MPEG
+ {AFMT_MPEG, AF_FORMAT_S_MP3},
+#endif
+ {-1, -1}
+};
+
+#define MP_WARN_IOCTL_ERR(__ao) \
+ MP_WARN((__ao), "%s: ioctl() fail, err = %i: %s\n", \
+ __FUNCTION__, errno, strerror(errno))
+
+
+static void uninit(struct ao *ao);
+
+
+static void device_descr_get(size_t dev_idx, char *buf, size_t buf_size)
+{
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+ char dev_path[32];
+ size_t tmp = (buf_size - 1);
+
+ snprintf(dev_path, sizeof(dev_path), "dev.pcm.%zu.%%desc", dev_idx);
+ if (sysctlbyname(dev_path, buf, &tmp, NULL, 0) != 0) {
+ tmp = 0;
+ }
+ buf[tmp] = 0x00;
+#elif defined(SOUND_MIXER_INFO)
+ size_t tmp = 0;
+ char dev_path[32];
+ mixer_info mi;
+
+ snprintf(dev_path, sizeof(dev_path), PATH_DEV_MIXER"%zu", dev_idx);
+ int fd = open(dev_path, O_RDONLY);
+ if (ioctl(fd, SOUND_MIXER_INFO, &mi) == 0) {
+ strncpy(buf, mi.name, buf_size);
+ tmp = (buf_size - 1);
+ }
+ close(fd);
+ buf[tmp] = 0x00;
+#else
+ buf[0] = 0x00;
+#endif
+}
+
+static int format2oss(int format)
+{
+ for (size_t i = 0; format_table[i][0] != -1; i++) {
+ if (format_table[i][1] == format)
+ return format_table[i][0];
+ }
+ return -1;
+}
+
+static bool try_format(struct ao *ao, int *format)
+{
+ struct priv *p = ao->priv;
+ int oss_format = format2oss(*format);
+
+ if (oss_format == -1 && af_fmt_is_spdif(*format))
+ oss_format = AFMT_AC3;
+
+ if (oss_format == -1) {
+ MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n",
+ af_fmt_to_str(*format));
+ *format = 0;
+ return false;
+ }
+
+ return (ioctl(p->dsp_fd, SNDCTL_DSP_SETFMT, &oss_format) != -1);
+}
+
+static int init(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ struct mp_chmap channels = ao->channels;
+ audio_buf_info info;
+ size_t i;
+ int format, samplerate, nchannels, reqchannels, trig = 0;
+ int best_sample_formats[AF_FORMAT_COUNT + 1];
+ const char *device = ((ao->device) ? ao->device : PATH_DEV_DSP);
+
+ /* Opening device. */
+ MP_VERBOSE(ao, "Using '%s' audio device.\n", device);
+ p->dsp_fd = open(device, (O_WRONLY | O_CLOEXEC));
+ if (p->dsp_fd < 0) {
+ MP_ERR(ao, "Can't open audio device %s: %s.\n",
+ device, mp_strerror(errno));
+ goto err_out;
+ }
+
+ /* Selecting sound format. */
+ format = af_fmt_from_planar(ao->format);
+ af_get_best_sample_formats(format, best_sample_formats);
+ for (i = 0; best_sample_formats[i]; i++) {
+ format = best_sample_formats[i];
+ if (try_format(ao, &format))
+ break;
+ }
+ if (!format) {
+ MP_ERR(ao, "Can't set sample format.\n");
+ goto err_out;
+ }
+ MP_VERBOSE(ao, "Sample format: %s\n", af_fmt_to_str(format));
+
+ /* Channels count. */
+ if (af_fmt_is_spdif(format)) {
+ /* Probably could be fixed by setting number of channels;
+ * needs testing. */
+ if (channels.num != 2) {
+ MP_ERR(ao, "Format %s not implemented.\n", af_fmt_to_str(format));
+ goto err_out;
+ }
+ } else {
+ struct mp_chmap_sel sel = {0};
+ for (i = 0; i < MP_ARRAY_SIZE(oss_layouts); i++) {
+ mp_chmap_sel_add_map(&sel, &oss_layouts[i]);
+ }
+ if (!ao_chmap_sel_adjust(ao, &sel, &channels))
+ goto err_out;
+ nchannels = reqchannels = channels.num;
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1) {
+ MP_ERR(ao, "Failed to set audio device to %d channels.\n",
+ reqchannels);
+ goto err_out_ioctl;
+ }
+ if (nchannels != reqchannels) {
+ /* Update number of channels to OSS suggested value. */
+ if (!ao_chmap_sel_get_def(ao, &sel, &channels, nchannels))
+ goto err_out;
+ }
+ MP_VERBOSE(ao, "Using %d channels (requested: %d).\n",
+ channels.num, reqchannels);
+ }
+
+ /* Sample rate. */
+ samplerate = ao->samplerate;
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_SPEED, &samplerate) == -1)
+ goto err_out_ioctl;
+ MP_VERBOSE(ao, "Using %d Hz samplerate.\n", samplerate);
+
+ /* Get buffer size. */
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
+ goto err_out_ioctl;
+ /* See ao.c ao->sstride initializations and get_state(). */
+ ao->device_buffer = ((info.fragstotal * info.fragsize) /
+ af_fmt_to_bytes(format));
+ if (!af_fmt_is_planar(format)) {
+ ao->device_buffer /= channels.num;
+ }
+
+ /* Do not start playback after data written. */
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1)
+ goto err_out_ioctl;
+
+ /* Update sound params. */
+ ao->format = format;
+ ao->samplerate = samplerate;
+ ao->channels = channels;
+ p->bps = (channels.num * samplerate * af_fmt_to_bytes(format));
+ p->playing = false;
+
+ return 0;
+
+err_out_ioctl:
+ MP_WARN_IOCTL_ERR(ao);
+err_out:
+ uninit(ao);
+ return -1;
+}
+
+static void uninit(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+
+ if (p->dsp_fd == -1)
+ return;
+ ioctl(p->dsp_fd, SNDCTL_DSP_HALT, NULL);
+ close(p->dsp_fd);
+ p->dsp_fd = -1;
+ p->playing = false;
+}
+
+static int control(struct ao *ao, enum aocontrol cmd, void *arg)
+{
+ struct priv *p = ao->priv;
+ ao_control_vol_t *vol = (ao_control_vol_t *)arg;
+ int v;
+
+ if (p->dsp_fd < 0)
+ return CONTROL_ERROR;
+
+ switch (cmd) {
+ case AOCONTROL_GET_VOLUME:
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_GETPLAYVOL, &v) == -1) {
+ MP_WARN_IOCTL_ERR(ao);
+ return CONTROL_ERROR;
+ }
+ vol->right = ((v & 0xff00) >> 8);
+ vol->left = (v & 0x00ff);
+ return CONTROL_OK;
+ case AOCONTROL_SET_VOLUME:
+ v = ((int)vol->right << 8) | (int)vol->left;
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_SETPLAYVOL, &v) == -1) {
+ MP_WARN_IOCTL_ERR(ao);
+ return CONTROL_ERROR;
+ }
+ return CONTROL_OK;
+ }
+
+ return CONTROL_UNKNOWN;
+}
+
+static void reset(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ int trig = 0;
+
+ /* Clear buf and do not start playback after data written. */
+ p->playing = false;
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_HALT, NULL) == -1 ||
+ ioctl(p->dsp_fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1)
+ {
+ MP_WARN_IOCTL_ERR(ao);
+ MP_WARN(ao, "Force reinitialize audio device.\n");
+ uninit(ao);
+ init(ao);
+ }
+}
+
+static void start(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ int trig = PCM_ENABLE_OUTPUT;
+
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) {
+ MP_WARN_IOCTL_ERR(ao);
+ return;
+ }
+ p->playing = true;
+}
+
+static bool audio_write(struct ao *ao, void **data, int samples)
+{
+ struct priv *p = ao->priv;
+ ssize_t rc;
+ const size_t size = (samples * ao->sstride);
+
+ if (size == 0)
+ return true;
+
+ while ((rc = write(p->dsp_fd, data[0], size)) == -1) {
+ if (errno == EINTR)
+ continue;
+ MP_WARN(ao, "audio_write: write() fail, err = %i: %s.\n",
+ errno, strerror(errno));
+ p->playing = false;
+ return false;
+ }
+ if ((size_t)rc != size) {
+ MP_WARN(ao, "audio_write: unexpected partial write: required: %zu, written: %zu.\n",
+ size, (size_t)rc);
+ p->playing = false;
+ return false;
+ }
+
+ return true;
+}
+
+static void get_state(struct ao *ao, struct mp_pcm_state *state)
+{
+ struct priv *p = ao->priv;
+ audio_buf_info info;
+ int odelay;
+
+ if (ioctl(p->dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1 ||
+ ioctl(p->dsp_fd, SNDCTL_DSP_GETODELAY, &odelay) == -1)
+ {
+ MP_WARN_IOCTL_ERR(ao);
+ p->playing = false;
+ memset(state, 0x00, sizeof(struct mp_pcm_state));
+ state->delay = 0.0;
+ return;
+ }
+ state->free_samples = (info.bytes / ao->sstride);
+ state->queued_samples = (ao->device_buffer - state->free_samples);
+ state->delay = (odelay / p->bps);
+ state->playing = p->playing;
+}
+
+static void list_devs(struct ao *ao, struct ao_device_list *list)
+{
+ struct stat st;
+ char dev_path[32] = PATH_DEV_DSP, dev_descr[256] = "Default";
+ struct ao_device_desc dev = {.name = dev_path, .desc = dev_descr};
+
+ if (stat(PATH_DEV_DSP, &st) == 0) {
+ ao_device_list_add(list, ao, &dev);
+ }
+
+ /* Auto detect. */
+ for (size_t i = 0, fail_cnt = 0; fail_cnt < 8; i ++, fail_cnt ++) {
+ snprintf(dev_path, sizeof(dev_path), PATH_DEV_DSP"%zu", i);
+ if (stat(dev_path, &st) != 0)
+ continue;
+ device_descr_get(i, dev_descr, sizeof(dev_descr));
+ ao_device_list_add(list, ao, &dev);
+ fail_cnt = 0; /* Reset fail counter. */
+ }
+}
+
+const struct ao_driver audio_out_oss = {
+ .name = "oss",
+ .description = "OSS/ioctl audio output",
+ .init = init,
+ .uninit = uninit,
+ .control = control,
+ .reset = reset,
+ .start = start,
+ .write = audio_write,
+ .get_state = get_state,
+ .list_devs = list_devs,
+ .priv_size = sizeof(struct priv),
+ .priv_defaults = &(const struct priv) {
+ .dsp_fd = -1,
+ .playing = false,
+ },
+};
diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c
index b09d26241e..aa802e77a5 100644
--- a/audio/out/ao_pulse.c
+++ b/audio/out/ao_pulse.c
@@ -696,9 +696,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
case AOCONTROL_SET_MUTE:
case AOCONTROL_SET_VOLUME: {
- pa_operation *o;
-
pa_threaded_mainloop_lock(priv->mainloop);
+ priv->retval = 0;
uint32_t stream_index = pa_stream_get_index(priv->stream);
if (cmd == AOCONTROL_SET_VOLUME) {
const ao_control_vol_t *vol = arg;
@@ -711,27 +710,26 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
volume.values[0] = VOL_MP2PA(vol->left);
volume.values[1] = VOL_MP2PA(vol->right);
}
- o = pa_context_set_sink_input_volume(priv->context, stream_index,
- &volume, NULL, NULL);
- if (!o) {
- pa_threaded_mainloop_unlock(priv->mainloop);
+ if (!waitop(priv, pa_context_set_sink_input_volume(priv->context,
+ stream_index,
+ &volume,
+ context_success_cb, ao)) ||
+ !priv->retval) {
GENERIC_ERR_MSG("pa_context_set_sink_input_volume() failed");
return CONTROL_ERROR;
}
} else if (cmd == AOCONTROL_SET_MUTE) {
const bool *mute = arg;
- o = pa_context_set_sink_input_mute(priv->context, stream_index,
- *mute, NULL, NULL);
- if (!o) {
- pa_threaded_mainloop_unlock(priv->mainloop);
+ if (!waitop(priv, pa_context_set_sink_input_mute(priv->context,
+ stream_index,
+ *mute,
+ context_success_cb, ao)) ||
+ !priv->retval) {
GENERIC_ERR_MSG("pa_context_set_sink_input_mute() failed");
return CONTROL_ERROR;
}
} else
abort();
- /* We don't wait for completion here */
- pa_operation_unref(o);
- pa_threaded_mainloop_unlock(priv->mainloop);
return CONTROL_OK;
}
diff --git a/common/msg.c b/common/msg.c
index 7e271d1b8b..81c7f654f3 100644
--- a/common/msg.c
+++ b/common/msg.c
@@ -126,6 +126,8 @@ static void update_loglevel(struct mp_log *log)
struct mp_log_root *root = log->root;
pthread_mutex_lock(&root->lock);
log->level = MSGL_STATUS + root->verbose; // default log level
+ if (root->really_quiet)
+ log->level = -1;
for (int n = 0; root->msg_levels && root->msg_levels[n * 2 + 0]; n++) {
if (match_mod(log->verbose_prefix, root->msg_levels[n * 2 + 0]))
log->level = mp_msg_find_level(root->msg_levels[n * 2 + 1]);
@@ -143,8 +145,6 @@ static void update_loglevel(struct mp_log *log)
if (log->root->stats_file)
log->level = MPMAX(log->level, MSGL_STATS);
log->level = MPMIN(log->level, log->max_level);
- if (root->really_quiet)
- log->level = -1;
atomic_store(&log->reload_counter, atomic_load(&log->root->reload_counter));
pthread_mutex_unlock(&root->lock);
}
diff --git a/demux/demux.c b/demux/demux.c
index 348d665d49..53ad455b46 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -121,8 +121,7 @@ const struct m_sub_options demux_conf = {
M_RANGE(0, M_MAX_MEM_BYTES)},
{"demuxer-donate-buffer", OPT_FLAG(donate_fw)},
{"force-seekable", OPT_FLAG(force_seekable)},
- {"cache-secs", OPT_DOUBLE(min_secs_cache), M_RANGE(0, DBL_MAX),
- .deprecation_message = "will use unlimited time"},
+ {"cache-secs", OPT_DOUBLE(min_secs_cache), M_RANGE(0, DBL_MAX)},
{"access-references", OPT_FLAG(access_references)},
{"demuxer-seekable-cache", OPT_CHOICE(seekable_cache,
{"auto", -1}, {"no", 0}, {"yes", 1})},
@@ -2827,7 +2826,7 @@ done:
return out_pkt;
}
-void demuxer_help(struct mp_log *log)
+int demuxer_help(struct mp_log *log, const m_option_t *opt, struct bstr name)
{
int i;
@@ -2837,6 +2836,9 @@ void demuxer_help(struct mp_log *log)
mp_info(log, "%10s %s\n",
demuxer_list[i]->name, demuxer_list[i]->desc);
}
+ mp_info(log, "\n");
+
+ return M_OPT_EXIT;
}
static const char *d_level(enum demux_check level)
diff --git a/demux/demux.h b/demux/demux.h
index 80c9a80fc7..ed414f1816 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -293,7 +293,7 @@ void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
void demuxer_refresh_track(struct demuxer *demuxer, struct sh_stream *stream,
double ref_pts);
-void demuxer_help(struct mp_log *log);
+int demuxer_help(struct mp_log *log, const m_option_t *opt, struct bstr name);
int demuxer_add_attachment(struct demuxer *demuxer, char *name,
char *type, void *data, size_t data_size);
diff --git a/demux/demux_mf.c b/demux/demux_mf.c
index 161e0cb92d..40f94f4e4e 100644
--- a/demux/demux_mf.c
+++ b/demux/demux_mf.c
@@ -121,7 +121,8 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct demuxer *d, char *filename
goto exit_mf;
}
- char *fname = talloc_size(mf, strlen(filename) + 32);
+ size_t fname_avail = strlen(filename) + 32;
+ char *fname = talloc_size(mf, fname_avail);
#if HAVE_GLOB
if (!strchr(filename, '%')) {
@@ -148,10 +149,44 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct demuxer *d, char *filename
}
#endif
+ // We're using arbitrary user input as printf format with 1 int argument.
+ // Any format which uses exactly 1 int argument would be valid, but for
+ // simplicity we reject all conversion specifiers except %% and simple
+ // integer specifier: %[.][NUM]d where NUM is 1-3 digits (%.d is valid)
+ const char *f = filename;
+ int MAXDIGS = 3, nspec = 0, bad_spec = 0, c;
+
+ while (nspec < 2 && (c = *f++)) {
+ if (c != '%')
+ continue;
+ if (*f != '%') {
+ nspec++; // conversion specifier which isn't %%
+ if (*f == '.')
+ f++;
+ for (int ndig = 0; mp_isdigit(*f) && ndig < MAXDIGS; ndig++, f++)
+ /* no-op */;
+ if (*f != 'd') {
+ bad_spec++; // not int, or beyond our validation capacity
+ break;
+ }
+ }
+ // *f is '%' or 'd'
+ f++;
+ }
+
+ // nspec==0 (zero specifiers) is rejected because fname wouldn't advance.
+ if (bad_spec || nspec != 1) {
+ mp_err(log, "unsupported expr format: '%s'\n", filename);
+ goto exit_mf;
+ }
+
mp_info(log, "search expr: %s\n", filename);
while (error_count < 5) {
- sprintf(fname, filename, count++);
+ if (snprintf(fname, fname_avail, filename, count++) >= fname_avail) {
+ mp_err(log, "format result too long: '%s'\n", filename);
+ goto exit_mf;
+ }
if (!mp_path_exists(fname)) {
error_count++;
mp_verbose(log, "file not found: '%s'\n", fname);
@@ -254,6 +289,8 @@ static const struct {
{ "jls", "ljpeg" },
{ "thm", "mjpeg" },
{ "db", "mjpeg" },
+ { "pcd", "photocd" },
+ { "pfm", "pfm" },
{ "pcx", "pcx" },
{ "png", "png" },
{ "pns", "png" },
diff --git a/filters/f_auto_filters.c b/filters/f_auto_filters.c
index a5394dd35a..5dd3a4adff 100644
--- a/filters/f_auto_filters.c
+++ b/filters/f_auto_filters.c
@@ -329,9 +329,9 @@ static void aspeed_process(struct mp_filter *f)
if (req_filter) {
if (req_filter == 1) {
- MP_VERBOSE(f, "adding scaletempo\n");
+ MP_VERBOSE(f, "adding scaletempo2\n");
p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO,
- "scaletempo", NULL);
+ "scaletempo2", NULL);
} else if (req_filter == 2) {
MP_VERBOSE(f, "adding drop\n");
p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO,
diff --git a/filters/f_auto_filters.h b/filters/f_auto_filters.h
index 98043c9301..f315084158 100644
--- a/filters/f_auto_filters.h
+++ b/filters/f_auto_filters.h
@@ -9,5 +9,5 @@ struct mp_filter *mp_deint_create(struct mp_filter *parent);
// Rotate according to mp_image.rotate and VO capabilities.
struct mp_filter *mp_autorotate_create(struct mp_filter *parent);
-// Insert a filter that inserts scaletempo depending on speed settings.
+// Insert a filter that inserts scaletempo2 depending on speed settings.
struct mp_filter *mp_autoaspeed_create(struct mp_filter *parent);
diff --git a/filters/f_decoder_wrapper.c b/filters/f_decoder_wrapper.c
index 397c3a0a98..9be8743e53 100644
--- a/filters/f_decoder_wrapper.c
+++ b/filters/f_decoder_wrapper.c
@@ -110,16 +110,19 @@ struct dec_wrapper_opts {
int64_t audio_reverse_size;
};
-static int decoder_list_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
+static int decoder_list_help(struct mp_log *log, const m_option_t *opt,
+ struct bstr name);
const struct m_sub_options dec_wrapper_conf = {
.opts = (const struct m_option[]){
{"correct-pts", OPT_FLAG(correct_pts)},
{"fps", OPT_DOUBLE(force_fps), M_RANGE(0, DBL_MAX)},
- {"ad", OPT_STRING_VALIDATE(audio_decoders, decoder_list_opt)},
- {"vd", OPT_STRING_VALIDATE(video_decoders, decoder_list_opt)},
- {"audio-spdif", OPT_STRING_VALIDATE(audio_spdif, decoder_list_opt)},
+ {"ad", OPT_STRING(audio_decoders),
+ .help = decoder_list_help},
+ {"vd", OPT_STRING(video_decoders),
+ .help = decoder_list_help},
+ {"audio-spdif", OPT_STRING(audio_spdif),
+ .help = decoder_list_help},
{"video-rotate", OPT_CHOICE(video_rotate, {"no", -1}),
.flags = UPDATE_IMGPAR, M_RANGE(0, 359)},
{"video-aspect-override", OPT_ASPECT(movie_aspect),
@@ -232,11 +235,9 @@ struct priv {
int dropped_frames; // total frames _probably_ dropped
};
-static int decoder_list_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
+static int decoder_list_help(struct mp_log *log, const m_option_t *opt,
+ struct bstr name)
{
- if (!bstr_equals0(param, "help"))
- return 1;
if (strcmp(opt->name, "ad") == 0) {
struct mp_decoder_list *list = audio_decoder_list();
mp_print_decoders(log, MSGL_INFO, "Audio decoders:", list);
diff --git a/options/m_option.c b/options/m_option.c
index 4d222df500..4c199967b3 100644
--- a/options/m_option.c
+++ b/options/m_option.c
@@ -61,6 +61,31 @@ const char m_option_path_separator = OPTION_PATH_SEPARATOR;
#define OPT_INT_MAX(opt, T, Tm) ((opt)->min < (opt)->max \
? ((opt)->max >= (double)(Tm) ? (Tm) : (T)((opt)->max)) : (Tm))
+int m_option_parse(struct mp_log *log, const m_option_t *opt,
+ struct bstr name, struct bstr param, void *dst)
+{
+ int r = M_OPT_INVALID;
+ if (bstr_equals0(param, "help") && opt->help) {
+ r = opt->help(log, opt, name);
+ if (r < 0)
+ return r;
+ }
+
+ r = opt->type->parse(log, opt, name, param, dst);
+ if (r < 0)
+ return r;
+
+ if (opt->validate) {
+ r = opt->validate(log, opt, name, dst);
+ if (r < 0) {
+ if (opt->type->free)
+ opt->type->free(dst);
+ return r;
+ }
+ }
+ return 1;
+}
+
char *m_option_strerror(int code)
{
switch (code) {
@@ -1192,13 +1217,6 @@ const m_option_type_t m_option_type_aspect = {
static int parse_str(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param, void *dst)
{
- m_opt_string_validate_fn validate = opt->priv;
- if (validate) {
- int r = validate(log, opt, name, param);
- if (r < 0)
- return r;
- }
-
if (dst) {
talloc_free(VAL(dst));
VAL(dst) = bstrdup0(NULL, param);
diff --git a/options/m_option.h b/options/m_option.h
index afd86eaf96..322f120a06 100644
--- a/options/m_option.h
+++ b/options/m_option.h
@@ -189,9 +189,15 @@ struct m_opt_choice_alternatives {
const char *m_opt_choice_str(const struct m_opt_choice_alternatives *choices,
int value);
-// For OPT_STRING_VALIDATE(). Behaves like m_option_type.parse().
+// Validator function signatures. Required to properly type the param value.
+typedef int (*m_opt_generic_validate_fn)(struct mp_log *log, const m_option_t *opt,
+ struct bstr name, void *value);
+
typedef int (*m_opt_string_validate_fn)(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
+ struct bstr name, const char **value);
+typedef int (*m_opt_int_validate_fn)(struct mp_log *log, const m_option_t *opt,
+ struct bstr name, const int *value);
+
// m_option.priv points to this if OPT_SUBSTRUCT is used
struct m_sub_options {
@@ -270,6 +276,9 @@ struct m_option_type {
// Parse the data from a string.
/** It is the only required function, all others can be NULL.
+ * Generally should not be called directly outside of the options module,
+ * but instead through \ref m_option_parse which calls additional option
+ * specific callbacks during the process.
*
* \param log for outputting parser error or help messages
* \param opt The option that is parsed.
@@ -384,6 +393,12 @@ struct m_option {
// Print a warning when this option is used (for options with no direct
// replacement.)
const char *deprecation_message;
+
+ // Optional function that validates a param value for this option.
+ m_opt_generic_validate_fn validate;
+
+ // Optional function that displays help. Will replace type-specific help.
+ int (*help)(struct mp_log *log, const m_option_t *opt, struct bstr name);
};
char *format_file_size(int64_t size);
@@ -490,12 +505,13 @@ char *format_file_size(int64_t size);
char *m_option_strerror(int code);
-// Helper to parse options, see \ref m_option_type::parse.
-static inline int m_option_parse(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param, void *dst)
-{
- return opt->type->parse(log, opt, name, param, dst);
-}
+// Base function to parse options. Includes calling help and validation
+// callbacks. Only when this functionality is for some reason required to not
+// happen should the parse function pointer be utilized by itself.
+//
+// See \ref m_option_type::parse.
+int m_option_parse(struct mp_log *log, const m_option_t *opt,
+ struct bstr name, struct bstr param, void *dst);
// Helper to print options, see \ref m_option_type::print.
static inline char *m_option_print(const m_option_t *opt, const void *val_ptr)
@@ -661,9 +677,15 @@ extern const char m_option_path_separator;
#define OPT_CHANNELS(field) \
OPT_TYPED_FIELD(m_option_type_channels, struct m_channels, field)
+#define OPT_INT_VALIDATE(field, validate_fn) \
+ OPT_TYPED_FIELD(m_option_type_int, int, field), \
+ .validate = (m_opt_generic_validate_fn) \
+ MP_EXPECT_TYPE(m_opt_int_validate_fn, validate_fn)
+
#define OPT_STRING_VALIDATE(field, validate_fn) \
OPT_TYPED_FIELD(m_option_type_string, char*, field), \
- .priv = MP_EXPECT_TYPE(m_opt_string_validate_fn, validate_fn)
+ .validate = (m_opt_generic_validate_fn) \
+ MP_EXPECT_TYPE(m_opt_string_validate_fn, validate_fn)
#define M_CHOICES(...) \
.priv = (void *)&(const struct m_opt_choice_alternatives[]){ __VA_ARGS__, {0}}
diff --git a/options/options.c b/options/options.c
index 8aadc87ab9..2a168da4e5 100644
--- a/options/options.c
+++ b/options/options.c
@@ -46,6 +46,7 @@
#include "player/core.h"
#include "player/command.h"
#include "stream/stream.h"
+#include "demux/demux.h"
#if HAVE_DRM
#include "video/out/drm_common.h"
@@ -514,9 +515,9 @@ static const m_option_t mp_opts[] = {
#endif
// demuxer.c - select audio/sub file/demuxer
- {"demuxer", OPT_STRING(demuxer_name)},
- {"audio-demuxer", OPT_STRING(audio_demuxer_name)},
- {"sub-demuxer", OPT_STRING(sub_demuxer_name)},
+ {"demuxer", OPT_STRING(demuxer_name), .help = demuxer_help},
+ {"audio-demuxer", OPT_STRING(audio_demuxer_name), .help = demuxer_help},
+ {"sub-demuxer", OPT_STRING(sub_demuxer_name), .help = demuxer_help},
{"demuxer-thread", OPT_FLAG(demuxer_thread)},
{"demuxer-termination-timeout", OPT_DOUBLE(demux_termination_timeout)},
{"demuxer-cache-wait", OPT_FLAG(demuxer_cache_wait)},
diff --git a/osdep/macosx_menubar.m b/osdep/macosx_menubar.m
index 11fdda5e53..6fefcb97aa 100644
--- a/osdep/macosx_menubar.m
+++ b/osdep/macosx_menubar.m
@@ -218,6 +218,13 @@
@"target" : self,
@"cmd" : @"cycle ontop"
}],
+ [NSMutableDictionary dictionaryWithDictionary:@{
+ @"name" : @"Toggle Visibility on All Workspaces",
+ @"action" : @"cmd:",
+ @"key" : @"",
+ @"target" : self,
+ @"cmd" : @"cycle on-all-workspaces"
+ }],
#if HAVE_MACOS_TOUCHBAR
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
diff --git a/osdep/macosx_touchbar.h b/osdep/macosx_touchbar.h
index 0aa5a052bb..a03b68c0e6 100644
--- a/osdep/macosx_touchbar.h
+++ b/osdep/macosx_touchbar.h
@@ -41,5 +41,6 @@ struct mpv_event;
@property(nonatomic, retain) NSDictionary *touchbarItems;
@property(nonatomic, assign) double duration;
@property(nonatomic, assign) double position;
+@property(nonatomic, assign) int pause;
@end
diff --git a/osdep/macosx_touchbar.m b/osdep/macosx_touchbar.m
index c189e32354..ccce8f773d 100644
--- a/osdep/macosx_touchbar.m
+++ b/osdep/macosx_touchbar.m
@@ -24,6 +24,7 @@
@synthesize touchbarItems = _touchbar_items;
@synthesize duration = _duration;
@synthesize position = _position;
+@synthesize pause = _pause;
- (id)init
{
@@ -86,58 +87,25 @@
@"name": @"Time Left"
}]
};
- }
- return self;
-}
-
--(void)processEvent:(struct mpv_event *)event
-{
- switch (event->event_id) {
- case MPV_EVENT_END_FILE: {
- self.position = 0;
- self.duration = 0;
- break;
- }
- case MPV_EVENT_PROPERTY_CHANGE: {
- [self handlePropertyChange:(mpv_event_property *)event->data];
- break;
- }
- }
-}
-
--(void)handlePropertyChange:(struct mpv_event_property *)property
-{
- NSString *name = [NSString stringWithUTF8String:property->name];
- mpv_format format = property->format;
- if ([name isEqualToString:@"time-pos"] && format == MPV_FORMAT_DOUBLE) {
- self.position = *(double *)property->data;
- self.position = self.position < 0 ? 0 : self.position;
- [self updateTouchBarTimeItems];
- } else if ([name isEqualToString:@"duration"] && format == MPV_FORMAT_DOUBLE) {
- self.duration = *(double *)property->data;
- [self updateTouchBarTimeItems];
- } else if ([name isEqualToString:@"pause"] && format == MPV_FORMAT_FLAG) {
- NSButton *playButton = self.touchbarItems[play][@"view"];
- if (*(int *)property->data) {
- playButton.image = self.touchbarItems[play][@"imageAlt"];
- } else {
- playButton.image = self.touchbarItems[play][@"image"];
- }
+ [self addObserver:self forKeyPath:@"visible" options:0 context:nil];
}
+ return self;
}
- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
{
if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"slider"]) {
- NSSliderTouchBarItem *tbItem = [[NSSliderTouchBarItem alloc] initWithIdentifier:identifier];
- tbItem.slider.minValue = 0.0f;
- tbItem.slider.maxValue = 100.0f;
- tbItem.target = self;
- tbItem.action = @selector(seekbarChanged:);
+ NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
+ NSSlider *slider = [NSSlider sliderWithTarget:self action:@selector(seekbarChanged:)];
+ slider.minValue = 0.0f;
+ slider.maxValue = 100.0f;
+ tbItem.view = slider;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
- [self.touchbarItems[identifier] setObject:tbItem.slider forKey:@"view"];
+ [self.touchbarItems[identifier] setObject:slider forKey:@"view"];
+ [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
+ [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"button"]) {
NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
@@ -146,6 +114,8 @@
tbItem.view = tbButton;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
[self.touchbarItems[identifier] setObject:tbButton forKey:@"view"];
+ [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
+ [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"text"]) {
NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
@@ -154,12 +124,124 @@
tbItem.view = tbText;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
[self.touchbarItems[identifier] setObject:tbText forKey:@"view"];
+ [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
+ [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
}
return nil;
}
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary<NSKeyValueChangeKey,id> *)change
+ context:(void *)context {
+ if ([keyPath isEqualToString:@"visible"]) {
+ NSNumber *visible = [object valueForKey:@"visible"];
+ if (visible.boolValue) {
+ [self updateTouchBarTimeItems];
+ [self updatePlayButton];
+ }
+ }
+}
+
+- (void)updateTouchBarTimeItems
+{
+ if (!self.isVisible)
+ return;
+
+ [self updateSlider];
+ [self updateTimeLeft];
+ [self updateCurrentPosition];
+}
+
+- (void)updateSlider
+{
+ NSCustomTouchBarItem *tbItem = self.touchbarItems[seekBar][@"tbItem"];
+ if (!tbItem.visible)
+ return;
+
+ NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];
+
+ if (self.duration <= 0) {
+ seekSlider.enabled = NO;
+ seekSlider.doubleValue = 0;
+ } else {
+ seekSlider.enabled = YES;
+ if (!seekSlider.highlighted)
+ seekSlider.doubleValue = (self.position/self.duration)*100;
+ }
+}
+
+- (void)updateTimeLeft
+{
+ NSCustomTouchBarItem *tbItem = self.touchbarItems[timeLeft][@"tbItem"];
+ if (!tbItem.visible)
+ return;
+
+ NSTextField *timeLeftItem = self.touchbarItems[timeLeft][@"view"];
+
+ [self removeConstraintForIdentifier:timeLeft];
+ if (self.duration <= 0) {
+ timeLeftItem.stringValue = @"";
+ } else {
+ int left = (int)(floor(self.duration)-floor(self.position));
+ NSString *leftFormat = [self formatTime:left];
+ NSString *durFormat = [self formatTime:self.duration];
+ timeLeftItem.stringValue = [NSString stringWithFormat:@"-%@", leftFormat];
+ [self applyConstraintFromString:[NSString stringWithFormat:@"-%@", durFormat]
+ forIdentifier:timeLeft];
+ }
+}
+
+- (void)updateCurrentPosition
+{
+ NSCustomTouchBarItem *tbItem = self.touchbarItems[currentPosition][@"tbItem"];
+ if (!tbItem.visible)
+ return;
+
+ NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
+ NSString *posFormat = [self formatTime:(int)floor(self.position)];
+ curPosItem.stringValue = posFormat;
+
+ [self removeConstraintForIdentifier:currentPosition];
+ if (self.duration <= 0) {
+ [self applyConstraintFromString:[self formatTime:self.position]
+ forIdentifier:currentPosition];
+ } else {
+ NSString *durFormat = [self formatTime:self.duration];
+ [self applyConstraintFromString:durFormat forIdentifier:currentPosition];
+ }
+}
+
+- (void)updatePlayButton
+{
+ NSCustomTouchBarItem *tbItem = self.touchbarItems[play][@"tbItem"];
+ if (!self.isVisible || !tbItem.visible)
+ return;
+
+ NSButton *playButton = self.touchbarItems[play][@"view"];
+ if (self.pause) {
+ playButton.image = self.touchbarItems[play][@"imageAlt"];
+ } else {
+ playButton.image = self.touchbarItems[play][@"image"];
+ }
+}
+
+- (void)buttonAction:(NSButton *)sender
+{
+ NSString *identifier = [self getIdentifierFromView:sender];
+ [self.app queueCommand:(char *)[self.touchbarItems[identifier][@"cmd"] UTF8String]];
+}
+
+- (void)seekbarChanged:(NSSlider *)slider
+{
+ NSString *identifier = [self getIdentifierFromView:slider];
+ NSString *seek = [NSString stringWithFormat:
+ self.touchbarItems[identifier][@"cmd"], slider.doubleValue];
+ [self.app queueCommand:(char *)[seek UTF8String]];
+}
+
- (NSString *)formatTime:(int)time
{
int seconds = time % 60;
@@ -204,48 +286,6 @@
}
}
-- (void)updateTouchBarTimeItemConstrains
-{
- [self removeConstraintForIdentifier:currentPosition];
- [self removeConstraintForIdentifier:timeLeft];
-
- if (self.duration <= 0) {
- [self applyConstraintFromString:[self formatTime:self.position]
- forIdentifier:currentPosition];
- } else {
- NSString *durFormat = [self formatTime:self.duration];
-
- [self applyConstraintFromString:durFormat forIdentifier:currentPosition];
- [self applyConstraintFromString:[NSString stringWithFormat:@"-%@", durFormat]
- forIdentifier:timeLeft];
- }
-}
-
-- (void)updateTouchBarTimeItems
-{
- NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];
- NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
- NSTextField *timeLeftItem = self.touchbarItems[timeLeft][@"view"];
-
- if (self.duration <= 0) {
- seekSlider.enabled = NO;
- seekSlider.doubleValue = 0;
- timeLeftItem.stringValue = @"";
- }
- else {
- seekSlider.enabled = YES;
- if (!seekSlider.highlighted)
- seekSlider.doubleValue = (self.position/self.duration)*100;
- int left = (int)(floor(self.duration)-floor(self.position));
- NSString *leftFormat = [self formatTime:left];
- timeLeftItem.stringValue = [NSString stringWithFormat:@"-%@", leftFormat];
- }
- NSString *posFormat = [self formatTime:(int)floor(self.position)];
- curPosItem.stringValue = posFormat;
-
- [self updateTouchBarTimeItemConstrains];
-}
-
- (NSString *)getIdentifierFromView:(id)view
{
NSString *identifier;
@@ -255,18 +295,40 @@
return identifier;
}
-- (void)buttonAction:(NSButton *)sender
+- (void)processEvent:(struct mpv_event *)event
{
- NSString *identifier = [self getIdentifierFromView:sender];
- [self.app queueCommand:(char *)[self.touchbarItems[identifier][@"cmd"] UTF8String]];
+ switch (event->event_id) {
+ case MPV_EVENT_END_FILE: {
+ self.position = 0;
+ self.duration = 0;
+ break;
+ }
+ case MPV_EVENT_PROPERTY_CHANGE: {
+ [self handlePropertyChange:(mpv_event_property *)event->data];
+ break;
+ }
+ }
}
-- (void)seekbarChanged:(NSSliderTouchBarItem *)sender
+- (void)handlePropertyChange:(struct mpv_event_property *)property
{
- NSString *identifier = [self getIdentifierFromView:sender.slider];
- NSString *seek = [NSString stringWithFormat:
- self.touchbarItems[identifier][@"cmd"], sender.slider.doubleValue];
- [self.app queueCommand:(char *)[seek UTF8String]];
+ NSString *name = [NSString stringWithUTF8String:property->name];
+ mpv_format format = property->format;
+
+ if ([name isEqualToString:@"time-pos"] && format == MPV_FORMAT_DOUBLE) {
+ double newPosition = *(double *)property->data;
+ newPosition = newPosition < 0 ? 0 : newPosition;
+ if ((int)(floor(newPosition) - floor(self.position)) != 0) {
+ self.position = newPosition;
+ [self updateTouchBarTimeItems];
+ }
+ } else if ([name isEqualToString:@"duration"] && format == MPV_FORMAT_DOUBLE) {
+ self.duration = *(double *)property->data;
+ [self updateTouchBarTimeItems];
+ } else if ([name isEqualToString:@"pause"] && format == MPV_FORMAT_FLAG) {
+ self.pause = *(int *)property->data;
+ [self updatePlayButton];
+ }
}
@end
diff --git a/player/audio.c b/player/audio.c
index 50c0faaf09..bf35bfb02a 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -516,7 +516,8 @@ void reinit_audio_chain(struct MPContext *mpctx)
struct track *track = NULL;
track = mpctx->current_track[0][STREAM_AUDIO];
if (!track || !track->stream) {
- uninit_audio_out(mpctx);
+ if (!mpctx->encode_lavc_ctx)
+ uninit_audio_out(mpctx);
error_on_track(mpctx, track);
return;
}
diff --git a/player/command.c b/player/command.c
index 098a11d24f..e210963447 100644
--- a/player/command.c
+++ b/player/command.c
@@ -3917,6 +3917,7 @@ static const struct property_osd_display {
{"taskbar-progress", "Progress in taskbar"},
{"snap-window", "Snap to screen edges"},
{"ontop", "Stay on top"},
+ {"on-all-workspaces", "Visibility on all workspaces"},
{"border", "Border"},
{"framedrop", "Framedrop"},
{"deinterlace", "Deinterlace"},
@@ -5240,6 +5241,8 @@ static void cmd_track_add(void *p)
struct mp_cmd_ctx *cmd = p;
struct MPContext *mpctx = cmd->mpctx;
int type = *(int *)cmd->priv;
+ bool is_albumart = type == STREAM_VIDEO &&
+ cmd->args[4].v.i;
if (mpctx->stop_play) {
cmd->success = false;
@@ -5259,7 +5262,7 @@ static void cmd_track_add(void *p)
}
}
int first = mp_add_external_file(mpctx, cmd->args[0].v.s, type,
- cmd->abort->cancel);
+ cmd->abort->cancel, is_albumart);
if (first < 0) {
cmd->success = false;
return;
@@ -5322,8 +5325,10 @@ static void cmd_track_reload(void *p)
if (t && t->is_external && t->external_filename) {
char *filename = talloc_strdup(NULL, t->external_filename);
+ bool is_albumart = t->attached_picture;
mp_remove_track(mpctx, t);
- nt_num = mp_add_external_file(mpctx, filename, type, cmd->abort->cancel);
+ nt_num = mp_add_external_file(mpctx, filename, type, cmd->abort->cancel,
+ is_albumart);
talloc_free(filename);
}
@@ -6070,6 +6075,7 @@ const struct mp_cmd_def mp_cmds[] = {
.flags = MP_CMD_OPT_ARG},
{"title", OPT_STRING(v.s), .flags = MP_CMD_OPT_ARG},
{"lang", OPT_STRING(v.s), .flags = MP_CMD_OPT_ARG},
+ {"albumart", OPT_FLAG(v.i), .flags = MP_CMD_OPT_ARG},
},
.priv = &(const int){STREAM_VIDEO},
.spawn_thread = true,
@@ -6471,6 +6477,25 @@ static void update_priority(struct MPContext *mpctx)
#endif
}
+static void update_track_switch(struct MPContext *mpctx, int order, int type)
+{
+ if (!mpctx->playback_initialized)
+ return;
+
+ int tid = mpctx->opts->stream_id[order][type];
+ struct track *track;
+ if (tid == -1) {
+ // If "auto" reset to default track selection
+ track = select_default_track(mpctx, order, type);
+ mark_track_selection(mpctx, order, type, -1);
+ } else {
+ track = mp_track_by_tid(mpctx, type, tid);
+ }
+ mp_switch_track_n(mpctx, order, type, track, (tid == -1) ? 0 : FLAG_MARK_SELECTION);
+ print_track_list(mpctx, "Track switched:");
+ mp_wakeup_core(mpctx);
+}
+
void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags,
bool self_update)
{
@@ -6629,15 +6654,8 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags,
for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
for (int order = 0; order < num_ptracks[type]; order++) {
- if (opt_ptr == &opts->stream_id[order][type] &&
- mpctx->playback_initialized)
- {
- struct track *track =
- mp_track_by_tid(mpctx, type, opts->stream_id[order][type]);
- mp_switch_track_n(mpctx, order, type, track, FLAG_MARK_SELECTION);
- print_track_list(mpctx, "Track switched:");
- mp_wakeup_core(mpctx);
- }
+ if (opt_ptr == &opts->stream_id[order][type])
+ update_track_switch(mpctx, order, type);
}
}
diff --git a/player/core.h b/player/core.h
index f2f0a15a00..fcb513bed1 100644
--- a/player/core.h
+++ b/player/core.h
@@ -528,7 +528,8 @@ void mp_abort_trigger_locked(struct MPContext *mpctx,
struct mp_abort_entry *abort);
void uninit_player(struct MPContext *mpctx, unsigned int mask);
int mp_add_external_file(struct MPContext *mpctx, char *filename,
- enum stream_type filter, struct mp_cancel *cancel);
+ enum stream_type filter, struct mp_cancel *cancel,
+ bool cover_art);
void mark_track_selection(struct MPContext *mpctx, int order,
enum stream_type type, int value);
#define FLAG_MARK_SELECTION 1
diff --git a/player/loadfile.c b/player/loadfile.c
index 058be92c83..6032cf6193 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -758,7 +758,8 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
// cancel will generally be used to abort the loading process, but on success
// the demuxer is changed to be slaved to mpctx->playback_abort instead.
int mp_add_external_file(struct MPContext *mpctx, char *filename,
- enum stream_type filter, struct mp_cancel *cancel)
+ enum stream_type filter, struct mp_cancel *cancel,
+ bool cover_art)
{
struct MPOpts *opts = mpctx->opts;
if (!filename || mp_cancel_test(cancel))
@@ -832,8 +833,8 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename,
t->external_filename = talloc_strdup(t, filename);
t->no_default = sh->type != filter;
t->no_auto_select = t->no_default;
- // filter==STREAM_VIDEO always means cover art.
- t->attached_picture = t->type == STREAM_VIDEO && filter == STREAM_VIDEO;
+ // if we found video, and we are loading cover art, flag as such.
+ t->attached_picture = t->type == STREAM_VIDEO && cover_art;
if (first_num < 0 && (filter == STREAM_TYPE_COUNT || sh->type == filter))
first_num = mpctx->num_tracks - 1;
}
@@ -858,7 +859,9 @@ static void open_external_files(struct MPContext *mpctx, char **files,
files = mp_dup_str_array(tmp, files);
for (int n = 0; files && files[n]; n++)
- mp_add_external_file(mpctx, files[n], filter, mpctx->playback_abort);
+ // when given filter is set to video, we are loading up cover art
+ mp_add_external_file(mpctx, files[n], filter, mpctx->playback_abort,
+ filter == STREAM_VIDEO);
talloc_free(tmp);
}
@@ -897,15 +900,16 @@ void autoload_external_files(struct MPContext *mpctx, struct mp_cancel *cancel)
goto skip;
if (e->type == STREAM_VIDEO && (sc[STREAM_VIDEO] || !sc[STREAM_AUDIO]))
goto skip;
- int first = mp_add_external_file(mpctx, e->fname, e->type, cancel);
+
+ // when given filter is set to video, we are loading up cover art
+ int first = mp_add_external_file(mpctx, e->fname, e->type, cancel,
+ e->type == STREAM_VIDEO);
if (first < 0)
goto skip;
for (int n = first; n < mpctx->num_tracks; n++) {
struct track *t = mpctx->tracks[n];
t->auto_loaded = true;
- t->attached_picture =
- t->type == STREAM_VIDEO && e->type == STREAM_VIDEO;
if (!t->lang)
t->lang = talloc_strdup(t, e->lang);
}
diff --git a/player/lua/stats.lua b/player/lua/stats.lua
index 6054480e9c..6aaf7eef5e 100644
--- a/player/lua/stats.lua
+++ b/player/lua/stats.lua
@@ -426,6 +426,10 @@ local function append_filters(s, prop, prefix)
n = n .. " (disabled)"
end
+ if f.label ~= nil then
+ n = "@" .. f.label .. ": " .. n
+ end
+
local p = {}
for key,value in pairs(f.params) do
p[#p+1] = key .. "=" .. value
diff --git a/player/lua/ytdl_hook.lua b/player/lua/ytdl_hook.lua
index e9a7b3aac7..586d4ea874 100644
--- a/player/lua/ytdl_hook.lua
+++ b/player/lua/ytdl_hook.lua
@@ -507,7 +507,7 @@ local function add_single_video(json)
if requested_formats then
for _, track in pairs(requested_formats) do
- max_bitrate = track.tbr > max_bitrate and
+ max_bitrate = (track.tbr and track.tbr > max_bitrate) and
track.tbr or max_bitrate
end
elseif json.tbr then
diff --git a/player/main.c b/player/main.c
index 71f5d14b53..58be00b6d8 100644
--- a/player/main.c
+++ b/player/main.c
@@ -54,7 +54,6 @@
#include "input/input.h"
#include "audio/out/ao.h"
-#include "demux/demux.h"
#include "misc/thread_tools.h"
#include "sub/osd.h"
#include "test/tests.h"
@@ -202,13 +201,6 @@ static bool handle_help_options(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
struct mp_log *log = mpctx->log;
- if ((opts->demuxer_name && strcmp(opts->demuxer_name, "help") == 0) ||
- (opts->audio_demuxer_name && strcmp(opts->audio_demuxer_name, "help") == 0) ||
- (opts->sub_demuxer_name && strcmp(opts->sub_demuxer_name, "help") == 0)) {
- demuxer_help(log);
- MP_INFO(mpctx, "\n");
- return true;
- }
if (opts->ao_opts->audio_device &&
strcmp(opts->ao_opts->audio_device, "help") == 0)
{
diff --git a/player/scripting.c b/player/scripting.c
index 24e2931539..3db879778c 100644
--- a/player/scripting.c
+++ b/player/scripting.c
@@ -174,9 +174,10 @@ static int64_t mp_load_script(struct MPContext *mpctx, const char *fname)
};
talloc_free(tmp);
+ fname = NULL; // might have been freed so don't touch anymore
if (!arg->client) {
- MP_ERR(mpctx, "Failed to create client for script: %s\n", fname);
+ MP_ERR(mpctx, "Failed to create client for script: %s\n", arg->filename);
talloc_free(arg);
return -1;
}
@@ -185,7 +186,7 @@ static int64_t mp_load_script(struct MPContext *mpctx, const char *fname)
arg->log = mp_client_get_log(arg->client);
int64_t id = mpv_client_id(arg->client);
- MP_DBG(arg, "Loading %s %s...\n", backend->name, fname);
+ MP_DBG(arg, "Loading %s %s...\n", backend->name, arg->filename);
if (backend->no_thread) {
run_script(arg);
diff --git a/stream/stream.h b/stream/stream.h
index 2116fdde27..423ba12d91 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -109,7 +109,7 @@ typedef struct stream_info_st {
// opts is set from ->opts
int (*open)(struct stream *st);
// Alternative to open(). Only either open() or open2() can be set.
- int (*open2)(struct stream *st, struct stream_open_args *args);
+ int (*open2)(struct stream *st, const struct stream_open_args *args);
const char *const *protocols;
bool can_write; // correctly checks for READ/WRITE modes
bool local_fs; // supports STREAM_LOCAL_FS_ONLY
diff --git a/stream/stream_concat.c b/stream/stream_concat.c
index 21f04f9d62..d06bd4a782 100644
--- a/stream/stream_concat.c
+++ b/stream/stream_concat.c
@@ -99,7 +99,7 @@ static int combine_origin(int cur, int new)
return new; // including cur==0
}
-static int open2(struct stream *stream, struct stream_open_args *args)
+static int open2(struct stream *stream, const struct stream_open_args *args)
{
struct priv *p = talloc_zero(stream, struct priv);
stream->priv = p;
diff --git a/stream/stream_file.c b/stream/stream_file.c
index d649ff4bdf..612d4bee38 100644
--- a/stream/stream_file.c
+++ b/stream/stream_file.c
@@ -246,7 +246,7 @@ static bool check_stream_network(int fd)
}
#endif
-static int open_f(stream_t *stream, struct stream_open_args *args)
+static int open_f(stream_t *stream, const struct stream_open_args *args)
{
struct priv *p = talloc_ptrtype(stream, p);
*p = (struct priv) {
diff --git a/stream/stream_lavf.c b/stream/stream_lavf.c
index e95dbc0b43..27a948d5e5 100644
--- a/stream/stream_lavf.c
+++ b/stream/stream_lavf.c
@@ -270,7 +270,7 @@ static int open_f(stream_t *stream)
for (int i = 0; i < sizeof(prefix) / sizeof(prefix[0]); i++)
if (!strncmp(filename, prefix[i], strlen(prefix[i])))
filename += strlen(prefix[i]);
- if (!strncmp(filename, "rtsp:", 5)) {
+ if (!strncmp(filename, "rtsp:", 5) || !strncmp(filename, "rtsps:", 6)) {
/* This is handled as a special demuxer, without a separate
* stream layer. demux_lavf will do all the real work. Note
* that libavformat doesn't even provide a protocol entry for
@@ -411,9 +411,9 @@ const stream_info_t stream_info_ffmpeg = {
.name = "ffmpeg",
.open = open_f,
.protocols = (const char *const[]){
- "rtmp", "rtsp", "http", "https", "mms", "mmst", "mmsh", "mmshttp", "rtp",
- "httpproxy", "rtmpe", "rtmps", "rtmpt", "rtmpte", "rtmpts", "srt", "srtp",
- "gopher", "data",
+ "rtmp", "rtsp", "rtsps", "http", "https", "mms", "mmst", "mmsh", "mmshttp",
+ "rtp", "httpproxy", "rtmpe", "rtmps", "rtmpt", "rtmpte", "rtmpts", "srt",
+ "srtp", "gopher", "gophers", "data",
NULL },
.can_write = true,
.stream_origin = STREAM_ORIGIN_NET,
diff --git a/stream/stream_memory.c b/stream/stream_memory.c
index 7383ff4d6f..e4696a73ad 100644
--- a/stream/stream_memory.c
+++ b/stream/stream_memory.c
@@ -44,7 +44,7 @@ static int64_t get_size(stream_t *s)
return p->data.len;
}
-static int open2(stream_t *stream, struct stream_open_args *args)
+static int open2(stream_t *stream, const struct stream_open_args *args)
{
stream->fill_buffer = fill_buffer;
stream->seek = seek;
diff --git a/stream/stream_mf.c b/stream/stream_mf.c
index 69a6dce58d..4c871c4334 100644
--- a/stream/stream_mf.c
+++ b/stream/stream_mf.c
@@ -39,4 +39,5 @@ const stream_info_t stream_info_mf = {
.name = "mf",
.open = mf_stream_open,
.protocols = (const char*const[]){ "mf", NULL },
+ .stream_origin = STREAM_ORIGIN_FS,
};
diff --git a/stream/stream_slice.c b/stream/stream_slice.c
index d7d0a6bf05..c0dbeeb2af 100644
--- a/stream/stream_slice.c
+++ b/stream/stream_slice.c
@@ -136,7 +136,7 @@ static int parse_slice_range(stream_t *stream)
return STREAM_OK;
}
-static int open2(struct stream *stream, struct stream_open_args *args)
+static int open2(struct stream *stream, const struct stream_open_args *args)
{
struct priv *p = talloc_zero(stream, struct priv);
stream->priv = p;
@@ -151,8 +151,9 @@ static int open2(struct stream *stream, struct stream_open_args *args)
return parse_ret;
}
- args->url = stream->path;
- int inner_ret = stream_create_with_args(args, &p->inner);
+ struct stream_open_args args2 = *args;
+ args2.url = stream->path;
+ int inner_ret = stream_create_with_args(&args2, &p->inner);
if (inner_ret != STREAM_OK) {
return inner_ret;
}
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 15eab18bf9..460334b371 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -63,8 +63,8 @@ static void uninit_avctx(struct mp_filter *vd);
static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags);
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
const enum AVPixelFormat *pix_fmt);
-static int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
+static int hwdec_opt_help(struct mp_log *log, const m_option_t *opt,
+ struct bstr name);
#define HWDEC_DELAY_QUEUE_COUNT 2
@@ -117,7 +117,8 @@ const struct m_sub_options vd_lavc_conf = {
{"no", INT_MAX}, {"yes", 1}), M_RANGE(1, INT_MAX)},
{"vd-lavc-o", OPT_KEYVALUELIST(avopts)},
{"vd-lavc-dr", OPT_FLAG(dr)},
- {"hwdec", OPT_STRING_VALIDATE(hwdec_api, hwdec_validate_opt),
+ {"hwdec", OPT_STRING(hwdec_api),
+ .help = hwdec_opt_help,
.flags = M_OPT_OPTIONAL_PARAM | UPDATE_HWDEC},
{"hwdec-codecs", OPT_STRING(hwdec_codecs)},
{"hwdec-image-format", OPT_IMAGEFORMAT(hwdec_image_format)},
@@ -533,33 +534,30 @@ static void select_and_set_hwdec(struct mp_filter *vd)
}
}
-static int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
+static int hwdec_opt_help(struct mp_log *log, const m_option_t *opt,
+ struct bstr name)
{
- if (bstr_equals0(param, "help")) {
- struct hwdec_info *hwdecs = NULL;
- int num_hwdecs = 0;
- add_all_hwdec_methods(&hwdecs, &num_hwdecs);
+ struct hwdec_info *hwdecs = NULL;
+ int num_hwdecs = 0;
+ add_all_hwdec_methods(&hwdecs, &num_hwdecs);
- mp_info(log, "Valid values (with alternative full names):\n");
+ mp_info(log, "Valid values (with alternative full names):\n");
- for (int n = 0; n < num_hwdecs; n++) {
- struct hwdec_info *hwdec = &hwdecs[n];
+ for (int n = 0; n < num_hwdecs; n++) {
+ struct hwdec_info *hwdec = &hwdecs[n];
- mp_info(log, " %s (%s)\n", hwdec->method_name, hwdec->name);
- }
+ mp_info(log, " %s (%s)\n", hwdec->method_name, hwdec->name);
+ }
- talloc_free(hwdecs);
+ talloc_free(hwdecs);
- mp_info(log, " auto (yes '')\n");
- mp_info(log, " no\n");
- mp_info(log, " auto-safe\n");
- mp_info(log, " auto-copy\n");
- mp_info(log, " auto-copy-safe\n");
+ mp_info(log, " auto (yes '')\n");
+ mp_info(log, " no\n");
+ mp_info(log, " auto-safe\n");
+ mp_info(log, " auto-copy\n");
+ mp_info(log, " auto-copy-safe\n");
- return M_OPT_EXIT;
- }
- return 0;
+ return M_OPT_EXIT;
}
static void force_fallback(struct mp_filter *vd)
@@ -661,7 +659,9 @@ static void init_avctx(struct mp_filter *vd)
if (!ctx->use_hwdec && ctx->vo && lavc_param->dr) {
avctx->opaque = vd;
avctx->get_buffer2 = get_buffer2_direct;
+#if LIBAVCODEC_VERSION_MAJOR < 60
avctx->thread_safe_callbacks = 1;
+#endif
}
avctx->flags |= lavc_param->bitexact ? AV_CODEC_FLAG_BITEXACT : 0;
diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift
index 8dd76f4bc5..dd0738f7e3 100644
--- a/video/out/cocoa_cb_common.swift
+++ b/video/out/cocoa_cb_common.swift
@@ -132,11 +132,11 @@ class CocoaCB: Common {
}
override func windowSetToFullScreen() {
- layer?.update()
+ layer?.update(force: true)
}
override func windowSetToWindow() {
- layer?.update()
+ layer?.update(force: true)
}
override func windowDidUpdateFrame() {
diff --git a/video/out/d3d11/context.c b/video/out/d3d11/context.c
index b77e328b70..57feb27530 100644
--- a/video/out/d3d11/context.c
+++ b/video/out/d3d11/context.c
@@ -28,7 +28,7 @@
static int d3d11_validate_adapter(struct mp_log *log,
const struct m_option *opt,
- struct bstr name, struct bstr param);
+ struct bstr name, const char **value);
struct d3d11_opts {
int feature_level;
@@ -61,7 +61,7 @@ const struct m_sub_options d3d11_conf = {
{"d3d11-flip", OPT_FLAG(flip)},
{"d3d11-sync-interval", OPT_INT(sync_interval), M_RANGE(0, 4)},
{"d3d11-adapter", OPT_STRING_VALIDATE(adapter_name,
- d3d11_validate_adapter)},
+ d3d11_validate_adapter)},
{"d3d11-output-format", OPT_CHOICE(output_format,
{"auto", DXGI_FORMAT_UNKNOWN},
{"rgba8", DXGI_FORMAT_R8G8B8A8_UNORM},
@@ -111,8 +111,9 @@ struct priv {
static int d3d11_validate_adapter(struct mp_log *log,
const struct m_option *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
+ struct bstr param = bstr0(*value);
bool help = bstr_equals0(param, "help");
bool adapter_matched = false;
struct bstr listing = { 0 };
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
index 64c84ca315..727221b6b1 100644
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -54,13 +54,15 @@
static int vt_switcher_pipe[2];
-static int drm_validate_connector_opt(
- struct mp_log *log, const struct m_option *opt, struct bstr name,
- struct bstr param);
+static int drm_connector_opt_help(
+ struct mp_log *log, const struct m_option *opt, struct bstr name);
+
+static int drm_mode_opt_help(
+ struct mp_log *log, const struct m_option *opt, struct bstr name);
static int drm_validate_mode_opt(
struct mp_log *log, const struct m_option *opt, struct bstr name,
- struct bstr param);
+ const char **value);
static void kms_show_available_modes(
struct mp_log *log, const drmModeConnector *connector);
@@ -71,10 +73,10 @@ static double mode_get_Hz(const drmModeModeInfo *mode);
#define OPT_BASE_STRUCT struct drm_opts
const struct m_sub_options drm_conf = {
.opts = (const struct m_option[]) {
- {"drm-connector", OPT_STRING_VALIDATE(drm_connector_spec,
- drm_validate_connector_opt)},
- {"drm-mode", OPT_STRING_VALIDATE(drm_mode_spec,
- drm_validate_mode_opt)},
+ {"drm-connector", OPT_STRING(drm_connector_spec),
+ .help = drm_connector_opt_help},
+ {"drm-mode", OPT_STRING_VALIDATE(drm_mode_spec, drm_validate_mode_opt),
+ .help = drm_mode_opt_help},
{"drm-atomic", OPT_CHOICE(drm_atomic, {"no", 0}, {"auto", 1})},
{"drm-draw-plane", OPT_CHOICE(drm_draw_plane,
{"primary", DRM_OPTS_PRIMARY_PLANE},
@@ -747,31 +749,28 @@ double kms_get_display_fps(const struct kms *kms)
return mode_get_Hz(&kms->mode.mode);
}
-static int drm_validate_connector_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
+static int drm_connector_opt_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name)
{
- if (bstr_equals0(param, "help")) {
- kms_show_available_cards_and_connectors(log);
- return M_OPT_EXIT;
- }
- return 1;
+ kms_show_available_cards_and_connectors(log);
+ return M_OPT_EXIT;
}
-static int drm_validate_mode_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
+static int drm_mode_opt_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name)
{
- if (bstr_equals0(param, "help")) {
- kms_show_available_cards_connectors_and_modes(log);
- return M_OPT_EXIT;
- }
+ kms_show_available_cards_connectors_and_modes(log);
+ return M_OPT_EXIT;
+}
- char *spec = bstrto0(NULL, param);
- if (!parse_mode_spec(spec, NULL)) {
+static int drm_validate_mode_opt(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, const char **value)
+{
+ const char *param = *value;
+ if (!parse_mode_spec(param, NULL)) {
mp_fatal(log, "Invalid value for option drm-mode. Must be a positive number, a string of the format WxH[@R] or 'help'\n");
- talloc_free(spec);
return M_OPT_INVALID;
}
- talloc_free(spec);
return 1;
}
diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c
index 39696fbd83..afbca2b2f4 100644
--- a/video/out/gpu/context.c
+++ b/video/out/gpu/context.c
@@ -110,16 +110,20 @@ static const struct ra_ctx_fns *contexts[] = {
#endif
};
+int ra_ctx_api_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name)
+{
+ mp_info(log, "GPU APIs (contexts):\n");
+ mp_info(log, " auto (autodetect)\n");
+ for (int n = 0; n < MP_ARRAY_SIZE(contexts); n++)
+ mp_info(log, " %s (%s)\n", contexts[n]->type, contexts[n]->name);
+ return M_OPT_EXIT;
+}
+
int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
- if (bstr_equals0(param, "help")) {
- mp_info(log, "GPU APIs (contexts):\n");
- mp_info(log, " auto (autodetect)\n");
- for (int n = 0; n < MP_ARRAY_SIZE(contexts); n++)
- mp_info(log, " %s (%s)\n", contexts[n]->type, contexts[n]->name);
- return M_OPT_EXIT;
- }
+ struct bstr param = bstr0(*value);
if (bstr_equals0(param, "auto"))
return 1;
for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
@@ -129,16 +133,20 @@ int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
return M_OPT_INVALID;
}
+int ra_ctx_context_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name)
+{
+ mp_info(log, "GPU contexts (APIs):\n");
+ mp_info(log, " auto (autodetect)\n");
+ for (int n = 0; n < MP_ARRAY_SIZE(contexts); n++)
+ mp_info(log, " %s (%s)\n", contexts[n]->name, contexts[n]->type);
+ return M_OPT_EXIT;
+}
+
int ra_ctx_validate_context(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
- if (bstr_equals0(param, "help")) {
- mp_info(log, "GPU contexts (APIs):\n");
- mp_info(log, " auto (autodetect)\n");
- for (int n = 0; n < MP_ARRAY_SIZE(contexts); n++)
- mp_info(log, " %s (%s)\n", contexts[n]->name, contexts[n]->type);
- return M_OPT_EXIT;
- }
+ struct bstr param = bstr0(*value);
if (bstr_equals0(param, "auto"))
return 1;
for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
diff --git a/video/out/gpu/context.h b/video/out/gpu/context.h
index 8c35eb0fc0..ca71150f54 100644
--- a/video/out/gpu/context.h
+++ b/video/out/gpu/context.h
@@ -100,7 +100,11 @@ struct ra_ctx *ra_ctx_create(struct vo *vo, const char *context_type,
void ra_ctx_destroy(struct ra_ctx **ctx);
struct m_option;
+int ra_ctx_api_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name);
int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param);
+ struct bstr name, const char **value);
+int ra_ctx_context_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name);
int ra_ctx_validate_context(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param);
+ struct bstr name, const char **value);
diff --git a/video/out/gpu/hwdec.c b/video/out/gpu/hwdec.c
index db75c64b05..4fb6240651 100644
--- a/video/out/gpu/hwdec.c
+++ b/video/out/gpu/hwdec.c
@@ -106,8 +106,9 @@ struct ra_hwdec *ra_hwdec_load_driver(struct ra *ra, struct mp_log *log,
}
int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
+ struct bstr param = bstr0(*value);
bool help = bstr_equals0(param, "help");
if (help)
mp_info(log, "Available hwdecs:\n");
diff --git a/video/out/gpu/hwdec.h b/video/out/gpu/hwdec.h
index 3a1ae3e2ff..050a358c74 100644
--- a/video/out/gpu/hwdec.h
+++ b/video/out/gpu/hwdec.h
@@ -109,7 +109,7 @@ struct ra_hwdec *ra_hwdec_load_driver(struct ra *ra, struct mp_log *log,
bool is_auto);
int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
+ struct bstr name, const char **value);
void ra_hwdec_uninit(struct ra_hwdec *hwdec);
diff --git a/video/out/gpu/lcms.c b/video/out/gpu/lcms.c
index 0f3a0bf646..894506973b 100644
--- a/video/out/gpu/lcms.c
+++ b/video/out/gpu/lcms.c
@@ -67,8 +67,9 @@ static bool parse_3dlut_size(const char *arg, int *p1, int *p2, int *p3)
}
static int validate_3dlut_size_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
+ struct bstr param = bstr0(*value);
int p1, p2, p3;
char s[20];
snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c
index 2aae3171e6..597286a550 100644
--- a/video/out/gpu/video.c
+++ b/video/out/gpu/video.c
@@ -317,7 +317,7 @@ static const struct gl_video_opts gl_video_opts_def = {
},
.scaler_resizes_only = 1,
.scaler_lut_size = 6,
- .interpolation_threshold = 0.0001,
+ .interpolation_threshold = 0.01,
.alpha_mode = ALPHA_BLEND_TILES,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
@@ -337,13 +337,13 @@ static const struct gl_video_opts gl_video_opts_def = {
};
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
+ struct bstr name, const char **value);
static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
+ struct bstr name, const char **value);
static int validate_error_diffusion_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
+ struct bstr name, const char **value);
#define OPT_BASE_STRUCT struct gl_video_opts
@@ -957,9 +957,6 @@ static void init_video(struct gl_video *p)
params.w, params.h);
plane->tex = ra_tex_create(p->ra, &params);
- if (!plane->tex)
- abort(); // shit happens
-
p->use_integer_conversion |= format->ctype == RA_CTYPE_UINT;
}
}
@@ -3588,6 +3585,10 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t
timer_pool_start(p->upload_timer);
for (int n = 0; n < p->plane_count; n++) {
struct texplane *plane = &vimg->planes[n];
+ if (!plane->tex) {
+ timer_pool_stop(p->upload_timer);
+ goto error;
+ }
struct ra_tex_upload_params params = {
.tex = plane->tex,
@@ -4088,8 +4089,9 @@ void gl_video_configure_queue(struct gl_video *p, struct vo *vo)
}
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
+ struct bstr param = bstr0(*value);
char s[20] = {0};
int r = 1;
bool tscale = bstr_equals0(name, "tscale");
@@ -4120,8 +4122,9 @@ static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
}
static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
+ struct bstr param = bstr0(*value);
char s[20] = {0};
int r = 1;
if (bstr_equals0(param, "help")) {
@@ -4145,8 +4148,9 @@ static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
}
static int validate_error_diffusion_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
+ struct bstr param = bstr0(*value);
char s[20] = {0};
int r = 1;
if (bstr_equals0(param, "help")) {
diff --git a/video/out/gpu/video_shaders.c b/video/out/gpu/video_shaders.c
index 7073185eaf..91c9aac865 100644
--- a/video/out/gpu/video_shaders.c
+++ b/video/out/gpu/video_shaders.c
@@ -946,7 +946,7 @@ struct deband_opts {
const struct deband_opts deband_opts_def = {
.iterations = 1,
- .threshold = 64.0,
+ .threshold = 32.0,
.range = 16.0,
.grain = 48.0,
};
diff --git a/video/out/hwdec/hwdec_vaapi_vk.c b/video/out/hwdec/hwdec_vaapi_vk.c
index 1cee9e86b9..afb81be82a 100644
--- a/video/out/hwdec/hwdec_vaapi_vk.c
+++ b/video/out/hwdec/hwdec_vaapi_vk.c
@@ -43,6 +43,7 @@ static bool vaapi_vk_map(struct ra_hwdec_mapper *mapper)
int fd = p->desc.objects[id].fd;
uint32_t size = p->desc.objects[id].size;
uint32_t offset = p->desc.layers[n].offset[0];
+ uint32_t pitch = p->desc.layers[n].pitch[0];
#if PL_API_VER >= 88
// AMD drivers do not return the size in the surface description, so we
@@ -85,6 +86,9 @@ static bool vaapi_vk_map(struct ra_hwdec_mapper *mapper)
#if PL_API_VER >= 88
.drm_format_mod = p->desc.objects[id].drm_format_modifier,
#endif
+#if PL_API_VER >= 106
+ .stride_w = pitch,
+#endif
},
};
diff --git a/video/out/mac/common.swift b/video/out/mac/common.swift
index 6c3d0fcfb3..7ede81cbda 100644
--- a/video/out/mac/common.swift
+++ b/video/out/mac/common.swift
@@ -100,6 +100,7 @@ class Common: NSObject {
}
window.setOnTop(Bool(mpv.opts.ontop), Int(mpv.opts.ontop_level))
+ window.setOnAllWorkspaces(Bool(mpv.opts.all_workspaces))
window.keepAspect = Bool(mpv.opts.keepaspect_window)
window.title = title
window.border = Bool(mpv.opts.border)
@@ -547,6 +548,10 @@ class Common: NSObject {
DispatchQueue.main.async {
self.window?.setOnTop(Bool(mpv.opts.ontop), Int(mpv.opts.ontop_level))
}
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.all_workspaces):
+ DispatchQueue.main.async {
+ self.window?.setOnAllWorkspaces(Bool(mpv.opts.all_workspaces))
+ }
case MPVHelper.getPointer(&mpv.optsPtr.pointee.keepaspect_window):
DispatchQueue.main.async {
self.window?.keepAspect = Bool(mpv.opts.keepaspect_window)
diff --git a/video/out/mac/window.swift b/video/out/mac/window.swift
index a418f2ce75..8b6c5f0518 100644
--- a/video/out/mac/window.swift
+++ b/video/out/mac/window.swift
@@ -30,7 +30,7 @@ class Window: NSWindow, NSWindowDelegate {
var isInFullscreen: Bool = false
var isAnimating: Bool = false
var isMoving: Bool = false
- var forceTargetScreen: Bool = false
+ var previousStyleMask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable]
var unfsContentFramePixel: NSRect { get { return convertToBacking(unfsContentFrame ?? NSRect(x: 0, y: 0, width: 160, height: 90)) } }
var framePixel: NSRect { get { return convertToBacking(frame) } }
@@ -61,6 +61,7 @@ class Window: NSWindow, NSWindowDelegate {
set {
let responder = firstResponder
let windowTitle = title
+ previousStyleMask = super.styleMask
super.styleMask = newValue
makeFirstResponder(responder)
title = windowTitle
@@ -229,7 +230,14 @@ class Window: NSWindow, NSWindowDelegate {
func setToFullScreen() {
guard let targetFrame = targetScreen?.frame else { return }
- styleMask.insert(.fullScreen)
+
+ if #available(macOS 11.0, *) {
+ styleMask = .borderless
+ common.titleBar?.hide(0.0)
+ } else {
+ styleMask.insert(.fullScreen)
+ }
+
NSApp.presentationOptions = [.autoHideMenuBar, .autoHideDock]
setFrame(targetFrame, display: true)
endAnimation()
@@ -240,10 +248,17 @@ class Window: NSWindow, NSWindowDelegate {
func setToWindow() {
guard let tScreen = targetScreen else { return }
+
+ if #available(macOS 11.0, *) {
+ styleMask = previousStyleMask
+ common.titleBar?.hide(0.0)
+ } else {
+ styleMask.remove(.fullScreen)
+ }
+
let newFrame = calculateWindowPosition(for: tScreen, withoutBounds: targetScreen == screen)
NSApp.presentationOptions = []
setFrame(newFrame, display: true)
- styleMask.remove(.fullScreen)
endAnimation()
isInFullscreen = false
mpv?.setOption(fullscreen: isInFullscreen)
@@ -278,6 +293,14 @@ class Window: NSWindow, NSWindowDelegate {
}
}
+ func setOnAllWorkspaces(_ state: Bool) {
+ if state {
+ collectionBehavior.insert(.canJoinAllSpaces)
+ } else {
+ collectionBehavior.remove(.canJoinAllSpaces)
+ }
+ }
+
func setMinimized(_ stateWanted: Bool) {
if isMiniaturized == stateWanted { return }
diff --git a/video/out/placebo/ra_pl.c b/video/out/placebo/ra_pl.c
index 8244acff26..09ff742d38 100644
--- a/video/out/placebo/ra_pl.c
+++ b/video/out/placebo/ra_pl.c
@@ -22,9 +22,7 @@ const struct pl_gpu *ra_pl_get(const struct ra *ra)
return ra->fns == &ra_fns_pl ? get_gpu(ra) : NULL;
}
-#if PL_API_VER >= 60
static struct pl_timer *get_active_timer(const struct ra *ra);
-#endif
struct ra *ra_create_pl(const struct pl_gpu *gpu, struct mp_log *log)
{
@@ -163,32 +161,6 @@ static struct ra_tex *tex_create_pl(struct ra *ra,
const struct ra_tex_params *params)
{
const struct pl_gpu *gpu = get_gpu(ra);
-
- // Check size limits
- bool ok = false;
- switch (params->dimensions) {
- case 1:
- ok = params->w <= gpu->limits.max_tex_1d_dim;
- break;
-
- case 2:
- ok = params->w <= gpu->limits.max_tex_2d_dim &&
- params->h <= gpu->limits.max_tex_2d_dim;
- break;
-
- case 3:
- ok = params->w <= gpu->limits.max_tex_2d_dim &&
- params->h <= gpu->limits.max_tex_2d_dim &&
- params->d <= gpu->limits.max_tex_2d_dim;
- break;
- };
-
- if (!ok) {
- MP_ERR(ra, "Texture size %dx%dx%d exceeds dimension limits!\n",
- params->w, params->h, params->d);
- return NULL;
- }
-
const struct pl_tex *pltex = pl_tex_create(gpu, &(struct pl_tex_params) {
.w = params->w,
.h = params->dimensions >= 2 ? params->h : 0,
@@ -217,6 +189,10 @@ static struct ra_tex *tex_create_pl(struct ra *ra,
return NULL;
}
+ // Keep track of these, so we can correctly bind them later
+ ratex->params.src_repeat = params->src_repeat;
+ ratex->params.src_linear = params->src_linear;
+
return ratex;
}
@@ -238,9 +214,7 @@ static bool tex_upload_pl(struct ra *ra, const struct ra_tex_upload_params *para
.buf = params->buf ? params->buf->priv : NULL,
.buf_offset = params->buf_offset,
.ptr = (void *) params->src,
-#if PL_API_VER >= 60
.timer = get_active_timer(ra),
-#endif
};
const struct pl_buf *staging = NULL;
@@ -293,9 +267,7 @@ static bool tex_download_pl(struct ra *ra, struct ra_tex_download_params *params
.tex = tex,
.ptr = params->dst,
.stride_w = params->stride / texel_size,
-#if PL_API_VER >= 60
.timer = get_active_timer(ra),
-#endif
};
uint8_t *staging = NULL;
@@ -328,19 +300,7 @@ static struct ra_buf *buf_create_pl(struct ra *ra,
[RA_BUF_TYPE_SHARED_MEMORY] = 0,
};
- const struct pl_gpu *gpu = get_gpu(ra);
- size_t max_size[] = {
- [PL_BUF_TEX_TRANSFER] = gpu->limits.max_xfer_size,
- [PL_BUF_UNIFORM] = gpu->limits.max_ubo_size,
- [PL_BUF_STORAGE] = gpu->limits.max_ssbo_size,
- };
-
- if (params->size > max_size[buf_type[params->type]]) {
- MP_ERR(ra, "Buffer size %zu exceeds size limits!\n", params->size);
- return NULL;
- }
-
- const struct pl_buf *plbuf = pl_buf_create(gpu, &(struct pl_buf_params) {
+ const struct pl_buf *plbuf = pl_buf_create(get_gpu(ra), &(struct pl_buf_params) {
.type = buf_type[params->type],
.size = params->size,
.host_mapped = params->host_mapped,
@@ -693,8 +653,6 @@ static void renderpass_run_pl(struct ra *ra,
pl_pass_run(get_gpu(ra), &pl_params);
}
-#if PL_API_VER >= 60
-
struct ra_timer_pl {
// Because libpplacebo only supports one operation per timer, we need
// to use multiple pl_timers to sum up multiple passes/transfers
@@ -766,8 +724,6 @@ static struct pl_timer *get_active_timer(const struct ra *ra)
return t->timers[t->idx_timers++];
}
-#endif // PL_API_VER >= 60
-
static struct ra_fns ra_fns_pl = {
.destroy = destroy_ra_pl,
.tex_create = tex_create_pl,
@@ -786,11 +742,9 @@ static struct ra_fns ra_fns_pl = {
.renderpass_create = renderpass_create_pl,
.renderpass_destroy = renderpass_destroy_pl,
.renderpass_run = renderpass_run_pl,
-#if PL_API_VER >= 60
.timer_create = timer_create_pl,
.timer_destroy = timer_destroy_pl,
.timer_start = timer_start_pl,
.timer_stop = timer_stop_pl,
-#endif
};
diff --git a/video/out/vo_gpu.c b/video/out/vo_gpu.c
index 5d28a30054..fe17e344ec 100644
--- a/video/out/vo_gpu.c
+++ b/video/out/vo_gpu.c
@@ -321,8 +321,12 @@ err_out:
#define OPT_BASE_STRUCT struct gpu_priv
static const m_option_t options[] = {
- {"gpu-context", OPT_STRING_VALIDATE(context_name, ra_ctx_validate_context)},
- {"gpu-api", OPT_STRING_VALIDATE(context_type, ra_ctx_validate_api)},
+ {"gpu-context",
+ OPT_STRING_VALIDATE(context_name, ra_ctx_validate_context),
+ .help = ra_ctx_context_help},
+ {"gpu-api",
+ OPT_STRING_VALIDATE(context_type, ra_ctx_validate_api),
+ .help = ra_ctx_api_help},
{"gpu-debug", OPT_FLAG(opts.debug)},
{"gpu-sw", OPT_FLAG(opts.allow_sw)},
{0}
diff --git a/video/out/vo_wlshm.c b/video/out/vo_wlshm.c
index 8319475f72..a71f9507fc 100644
--- a/video/out/vo_wlshm.c
+++ b/video/out/vo_wlshm.c
@@ -23,6 +23,7 @@
#include <libswscale/swscale.h>
+#include "osdep/endian.h"
#include "sub/osd.h"
#include "video/fmt-conversion.h"
#include "video/mp_image.h"
@@ -184,7 +185,7 @@ static int resize(struct vo *vo)
vo->dheight = height;
vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
p->sws->dst = (struct mp_image_params) {
- .imgfmt = IMGFMT_BGR0,
+ .imgfmt = MP_SELECT_LE_BE(IMGFMT_BGR0, IMGFMT_0RGB),
.w = width,
.h = height,
.p_w = 1,
diff --git a/video/out/vulkan/context.c b/video/out/vulkan/context.c
index 3518d3efd2..9bdeb14d9b 100644
--- a/video/out/vulkan/context.c
+++ b/video/out/vulkan/context.c
@@ -31,8 +31,9 @@ struct vulkan_opts {
};
static int vk_validate_dev(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
+ struct bstr name, const char **value)
{
+ struct bstr param = bstr0(*value);
int ret = M_OPT_INVALID;
VkResult res;
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index fe9c0b531c..df9321fca2 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -71,7 +71,8 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
static int spawn_cursor(struct vo_wayland_state *wl)
{
- if (wl->allocated_cursor_scale == wl->scaling) /* Reuse if size is identical */
+ /* Reuse if size is identical */
+ if (!wl->pointer || wl->allocated_cursor_scale == wl->scaling)
return 0;
else if (wl->cursor_theme)
wl_cursor_theme_destroy(wl->cursor_theme);
@@ -268,6 +269,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
mp_input_put_key(wl->vo->input_ctx, button | state | mpmod);
if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) &&
+ (!wl->vo_opts->fullscreen) && (!wl->vo_opts->window_maximized) &&
(button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN)) {
uint32_t edges;
// Implement an edge resize zone if there are no decorations
@@ -603,6 +605,44 @@ static const struct wl_seat_listener seat_listener = {
seat_handle_caps,
};
+static void greatest_common_divisor(struct vo_wayland_state *wl, int a, int b) {
+ // euclidean algorithm
+ int larger;
+ int smaller;
+ if (a > b) {
+ larger = a;
+ smaller = b;
+ } else {
+ larger = b;
+ smaller = a;
+ }
+ int remainder = larger - smaller * floor(larger/smaller);
+ if (remainder == 0) {
+ wl->gcd = smaller;
+ } else {
+ greatest_common_divisor(wl, smaller, remainder);
+ }
+}
+
+static void set_geometry(struct vo_wayland_state *wl)
+{
+ struct vo *vo = wl->vo;
+
+ struct vo_win_geometry geo;
+ struct mp_rect screenrc = wl->current_output->geometry;
+ vo_calc_window_geometry(vo, &screenrc, &geo);
+ vo_apply_window_geometry(vo, &geo);
+
+ greatest_common_divisor(wl, vo->dwidth, vo->dheight);
+ wl->reduced_width = vo->dwidth / wl->gcd;
+ wl->reduced_height = vo->dheight / wl->gcd;
+
+ wl->vdparams.x0 = 0;
+ wl->vdparams.y0 = 0;
+ wl->vdparams.x1 = vo->dwidth / wl->scaling;
+ wl->vdparams.y1 = vo->dheight / wl->scaling;
+}
+
static void output_handle_geometry(void *data, struct wl_output *wl_output,
int32_t x, int32_t y, int32_t phys_width,
int32_t phys_height, int32_t subpixel,
@@ -637,6 +677,7 @@ static void output_handle_mode(void *data, struct wl_output *wl_output,
static void output_handle_done(void* data, struct wl_output *wl_output)
{
struct vo_wayland_output *o = data;
+ struct vo_wayland_state *wl = o->wl;
o->geometry.x1 += o->geometry.x0;
o->geometry.y1 += o->geometry.y0;
@@ -648,8 +689,21 @@ static void output_handle_done(void* data, struct wl_output *wl_output)
"\tHz: %f\n", o->make, o->model, o->id, o->geometry.x0,
o->geometry.y0, mp_rect_w(o->geometry), o->phys_width,
mp_rect_h(o->geometry), o->phys_height, o->scale, o->refresh_rate);
+
+ /* If we satisfy this conditional, something about the current
+ * output must have changed (resolution, scale, etc). All window
+ * geometry and scaling should be recalculated. */
+ if (wl->current_output && wl->current_output->output == wl_output) {
+ wl->scaling = wl->current_output->scale;
+ spawn_cursor(wl);
+ set_geometry(wl);
+ wl->window_size = wl->vdparams;
+ if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
+ wl->geometry = wl->window_size;
+ wl->pending_vo_events |= VO_EVENT_RESIZE;
+ }
- o->wl->pending_vo_events |= VO_EVENT_WIN_STATE;
+ wl->pending_vo_events |= VO_EVENT_WIN_STATE;
}
static void output_handle_scale(void* data, struct wl_output *wl_output,
@@ -793,44 +847,6 @@ static const struct wl_data_device_listener data_device_listener = {
data_device_handle_selection,
};
-static void greatest_common_divisor(struct vo_wayland_state *wl, int a, int b) {
- // euclidean algorithm
- int larger;
- int smaller;
- if (a > b) {
- larger = a;
- smaller = b;
- } else {
- larger = b;
- smaller = a;
- }
- int remainder = larger - smaller * floor(larger/smaller);
- if (remainder == 0) {
- wl->gcd = smaller;
- } else {
- greatest_common_divisor(wl, smaller, remainder);
- }
-}
-
-static void set_geometry(struct vo_wayland_state *wl)
-{
- struct vo *vo = wl->vo;
-
- struct vo_win_geometry geo;
- struct mp_rect screenrc = wl->current_output->geometry;
- vo_calc_window_geometry(vo, &screenrc, &geo);
- vo_apply_window_geometry(vo, &geo);
-
- greatest_common_divisor(wl, vo->dwidth, vo->dheight);
- wl->reduced_width = vo->dwidth / wl->gcd;
- wl->reduced_height = vo->dheight / wl->gcd;
-
- wl->vdparams.x0 = 0;
- wl->vdparams.y0 = 0;
- wl->vdparams.x1 = vo->dwidth / wl->scaling;
- wl->vdparams.y1 = vo->dheight / wl->scaling;
-}
-
static void rescale_geometry_dimensions(struct vo_wayland_state *wl, double factor)
{
wl->vdparams.x1 *= factor;
@@ -871,7 +887,9 @@ static void surface_handle_enter(void *data, struct wl_surface *wl_surface,
if (wl->scaling != wl->current_output->scale && wl->vo_opts->hidpi_window_scale) {
double factor = (double)wl->scaling / wl->current_output->scale;
wl->scaling = wl->current_output->scale;
+ spawn_cursor(wl);
rescale_geometry_dimensions(wl, factor);
+ wl->pending_vo_events |= VO_EVENT_DPI;
}
if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
@@ -1569,6 +1587,7 @@ int vo_wayland_reconfig(struct vo *vo)
wl->window_size = wl->vdparams;
wl->geometry = wl->window_size;
wl_display_roundtrip(wl->display);
+ wl->pending_vo_events |= VO_EVENT_DPI;
}
wl->pending_vo_events |= VO_EVENT_RESIZE;
@@ -1729,6 +1748,12 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
*(double *)arg = wl->current_output->refresh_rate;
return VO_TRUE;
}
+ case VOCTRL_GET_HIDPI_SCALE: {
+ if (!wl->scaling)
+ return VO_NOTAVAIL;
+ *(double *)arg = wl->scaling;
+ return VO_TRUE;
+ }
case VOCTRL_UPDATE_WINDOW_TITLE:
return update_window_title(wl, (const char *)arg);
case VOCTRL_SET_CURSOR_VISIBILITY:
diff --git a/wscript b/wscript
index 86231b79b1..e9f4d53002 100644
--- a/wscript
+++ b/wscript
@@ -422,6 +422,11 @@ audio_output_features = [
'deps': 'sdl2',
'func': check_true,
}, {
+ 'name': '--oss-audio',
+ 'desc': 'OSSv4 audio output',
+ 'func': check_statement(['sys/soundcard.h'], 'int x = SNDCTL_DSP_SETPLAYVOL'),
+ 'deps': 'posix && gpl',
+ }, {
'name': '--pulse',
'desc': 'PulseAudio audio output',
'func': check_pkg_config('libpulse', '>= 1.0')
@@ -725,7 +730,7 @@ video_output_features = [
}, {
'name': '--libplacebo',
'desc': 'libplacebo support',
- 'func': check_pkg_config('libplacebo >= 1.18.0'),
+ 'func': check_pkg_config('libplacebo >= 2.72.0'),
}, {
'name': '--vulkan',
'desc': 'Vulkan context support',
diff --git a/wscript_build.py b/wscript_build.py
index 14c254e1ec..a12cbf19e1 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -244,6 +244,7 @@ def build(ctx):
( "audio/out/ao_null.c" ),
( "audio/out/ao_openal.c", "openal" ),
( "audio/out/ao_opensles.c", "opensles" ),
+ ( "audio/out/ao_oss.c", "oss-audio" ),
( "audio/out/ao_pcm.c" ),
( "audio/out/ao_pulse.c", "pulse" ),
( "audio/out/ao_sdl.c", "sdl2-audio" ),