Compare commits

..
40 Commits
Author SHA1 Message Date
sfan5 140ec21c89 Release 0.35.1 2023-01-28 17:49:47 +01:00
Sultan Alsawaf db524988b1 x11: remove PresentNotifyMSC from egl/glx/vulkan to fix xpresent timing
PresentNotifyMSC turns out to be not only redundant, but also harmful with
mesa-backed egl/glx/vulkan VOs because for all of them, mesa uses
PresentPixmap behind the scenes when DRI3 is available, which already
spawns a PresentCompleteNotify event when the buffer swap actually
finishes. This is important because without using the timing information
from these PresentCompleteKindPixmap events, there's no way for mpv to know
exactly when a frame becomes visible on the display.

By using PresentNotifyMSC in conjunction with DRI3-enabled mesa, two
problems are created:
1. mpv assumes that a vblank won't elapse (i.e., it assumes the current MSC
   won't change) between the time when mesa enqueues the buffer swap and
   the time when mpv calls PresentNotifyMSC to ask xorg for a notification
   at the next MSC, relative to the current MSC at the time that xorg reads
   it for the PresentNotifyMSC call. This means that mpv could get a
   notification one or more vblanks later than it expects, since the
   intention here is for mpv to get a notification at the MSC that the
   buffer swap completes.
2. mpv assumes that a buffer swap always takes one vblank to complete,
   which isn't always true. A buffer swap (i.e., a page flip) could take
   longer than that depending on hardware conditions (if the GPU is running
   slowly or needs to exit a low-power state), scheduling delays (under
   heavy system or GPU load), or unfortunate timing (if the raster scan
   line happens to be at one of the last few rows of pixels and a vblank
   elapses just before the buffer swap is enqueued).

This causes mpv to have a faulty assumption of when frames become visible.

Since mpv already receives the PresentCompleteNotify events generated by
mesa's buffer swaps under the hood, the PresentNotifyMSC usage is unneeded
and just throws a wrench in mpv's vsync timing when xpresent is enabled.

Simply removing the PresentNotifyMSC usage from the egl, glx, and vulkan
VOs fixes the xpresent vsync timing.
2023-01-28 17:42:34 +01:00
8e2dc8b02b vo_gpu: implement VO_DR_FLAG_HOST_CACHED
For OpenGL, this is based on simply comparing GL_VENDOR strings against
a list of allowed vendors.

Co-authored-by: Nicolas F. <ovdev@fratti.ch>
Co-authored-by: Niklas Haas <git@haasn.dev>
2023-01-24 16:40:38 +01:00
Niklas Haas 16c0ac226a vo_gpu_next: implement VO_DR_FLAG_HOST_CACHED
For sufficiently new versions of libplacebo.
2023-01-24 16:40:37 +01:00
01374b6830 vd_lavc: add "auto" choice for vd-lavc-dr
--vd-lavc-dr defaulted to "yes", which caused issues on certain
hardware. Instead of disabling it, add a new "auto" value and
make it the default.

The "auto" choice will enable DR only when we can request host-cached
buffers (as signalled by the new VO_DR_FLAG_HOST_CACHED).

Co-authored-by: Nicolas F. <ovdev@fratti.ch>
Co-authored-by: Niklas Haas <git@haasn.dev>
2023-01-24 16:40:37 +01:00
Niklas Haas fc115bce2c vo: add int flags to the get_image signature
This is a huge disgusting mess to thread through everywhere. Maybe I'm
stupid for attempting to solve the problem this way.
2023-01-24 16:40:37 +01:00
Niklas Haas b64f9fe646 mp_image: fix XYZ primaries default
This was incorrectly set to BT.2020, when it should be DCI-P3 for pretty
much all real-world XYZ content (which is digital cinema content).
2023-01-24 16:40:37 +01:00
Dudemanguy 3813cc4a14 wayland: store presentation feedbacks in a pool
Officially, the most cursed part of the wayland code in mpv (third or
fourth try now?) This time, let's allocate a pool during init
(arbitrarily set to the maximum swapchain length mpv allows, 8; don't
even know if this actually works in wayland). Then we add/remove
feedbacks from the pool during the lifecycle of the VO, and clean it up
all at the end. Hopefully, this does the trick for good this time.
2023-01-24 16:40:37 +01:00
Dudemanguy 8ddc0e8888 audio: fix crash during uninit on ao_lavc
The buffer state can be null when using --ao=lavc, so just check it
first. Fixes #10175.
2023-01-24 16:39:33 +01:00
Ionen Wolkens 46cd82a68d meson: also search for rst2html with .py extension
This allows using rst2html.py from docutils if present, this
was already done for rst2man.py (unneeded for rst2pdf).
2023-01-24 16:39:33 +01:00
sfan5 222f9c5635 ra_d3d11: fix incorrect type
As it happens, `SIZE_T` and `size_t` are not the same length on 32-bit Windows
2023-01-24 16:39:33 +01:00
Dudemanguy c5e4d65e71 vo_wlshm: cleanup on failure
If failure occurs during preinit, vo_wlshm should goto an error and
cleaup itself like the other VOs.
2023-01-24 16:39:33 +01:00
Christoph Reiter 9f6e1b3ba1 vulkan: fix build error for 32bit builds with clang
vk->surface is a handle and not a pointer, so assign VK_NULL_HANDLE.
This fixes the following build error on 32bit Windows when using clang for example,
which errors out when assigning a 32bit pointer to a 64bit integer:

  ../mpv-0.35.0/video/out/vulkan/utils.c:37:21:
    error: incompatible pointer to integer conversion assigning to 'VkSurfaceKHR' (aka 'unsigned long long') from 'void *' [-Wint-conversion]
          vk->surface = NULL;
                      ^ ~~~~
2023-01-24 16:39:33 +01:00
Dudemanguy 688a321dbb version.py/version.sh: bump copyright year 2023-01-24 16:39:33 +01:00
Simon Ruderich 46d3dc2a42 demux: boost read EBU R128 gain values to ReplayGain's reference level
Without this change the same track encoded as Opus - which requires R128
tagging - and e.g. Vorbis with ReplayGain tagging have different volumes.
This is caused by ReplayGain 2 having a higher reference level of -18 dB
LUFS, while EBU R128 has a lower reference level of -23 dB LUFS.

For the results of gain application to match, the read EBU R128
values need to be boosted according to the difference in reference
levels.

Patch inspired by mpd's source code.
2023-01-24 16:39:33 +01:00
Dudemanguy 5436852bf5 wayland: initalize display_fd as -1
In the event of a failed wayland initalization, the display_fd is
actually 0 instead of -1 which would correctly indicate it doesn't
exist. Also move the fd check inside vo_wayland_dispatch_events since
that function shouldn't ever do anything if we don't actually have a
display fd.
2023-01-24 15:56:57 +01:00
Dudemanguy 6076691b94 draw_bmp: ensure last slice width is less than total width
e97819f88e corrected a special case
condition that lead to an out of bounds access if the total width
happened to be an integer multiple of SLICE_W (256) which could cause a
crash in software VOs. However, it turns out that the functions in this
file evaluate quite differently when using encoding mode (and presumably
libmpv as well according to reports although I could not independently
verify it).

The logic here gets complicated but what ends up happening is that, in
blend_overlay_with_video, the value of x + w can be greater than p->w in
certain cases in encoding mode. The x is the positional value of the
slice which remained unchanged from before, but w can take the full
value of SLICE_W (256) which is not necessarily correct. The width of
the final slice here should be the total remaining width. We can handle
this in mark_rect by simply always adjusting x1 of the last slice to be
equal to total width - SLICE_W * x so it can never extend beyond where
it should be. In practice, this value should be the maximum allowed
here. I'm not sure if the existing x1 value can possibly already be
lower than SLICE_W, but just MPMIN it to be on the safe side.
Fixes #10908.
2023-01-24 15:56:57 +01:00
repojohnray b963113c00 vo: hwdec: fix drmGetDeviceNameFromFd2() related memory leak
This commit fixes a minor memory leak related to drmGetDeviceNameFromFd2().
This function returns a string allocated with strdup().
2023-01-24 15:56:57 +01:00
repojohnray fd279c9a2a hwdec_drmprime: fix memory leak 2023-01-24 15:56:56 +01:00
Li Chang ec2a9d4332 ao_coreaudio: use AudioUnitReset as ao_driver.reset to prevent long restart
[motivation]
Seeking on MacOS appears to be lagged when users connect
to wireless audio output (airpods for example).

This commit attempts to fix mpv-player/mpv#10270

[observation]
1. When using other media player (VLC to be exact) simultaneously,
the lagging on seek disappear. We could guess that the AudioDevice
is on some sort of "warm-up" state.

See mpv-player/mpv#9243 for detailed description.

2. `AudioOutputUnitStart` takes significant longer time after each seek
or pause/play when using wireless output devices compares to wired devices.

[rationale]
After investigate codes in ao_coreaudio.c, it appears that the the `stop`
function was used as `ao_driver.reset` function. Therefore every seek
and pause would call `AudioOutputUnitStop`.

It turns out that `ao_driver.reset` function is used in `ao_reset`.
And `ao_reset` function is used to clean up the state of current `ao`
so I think `AudioUnitReset` is more proper than `AudioOutputUnitStop`
under this semantics.

Since ao_coreaudio use pull base mechanism, audio playback behaviors
upon pause/seek could be handled by callback function
(streaming silence when paused) so there is no need to stop AudioUnit when resetting.
Therefore using `AudioUnitReset` as `ao_driver.reset` looks proper.

Additionally, after using proper reset, the AudioUnit that represents
hardware I/O devices doesn't need to be restart everytime seek/pause actions happen.
Restarting wireless devices simply takes longer in MacOS which is
the root cause of lagging observed by users when they seek or pause/play media.

[method]
Use `AudioUnitReset` for ao_driver.reset.
2023-01-24 15:56:56 +01:00
Kacper Michajłow 666201d716 vo_opengl: do not blindly reject all Microsoft's OpenGL implementations
This change enables mpv to work in the WSL2 (WSLg) environment where
OpenGL is implemented on top of D3D12.

This reverts commit 149d98d244.

Mentioned OpenGL implementation (GDI Generic) in the original change
will be rejected by version check, so there is no need to handle it
manually.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
2023-01-24 15:56:56 +01:00
Nicolas F daf9a3c971 external_files: set log level for potential files to trace
We ask users to freely share log files with us, which is usually
okay, unless a user has some unrelated and potentially embarrassing
files in their working directory. These would show up in the debug
level message output that --log-file enables.

Change the listing of potential external files to trace log level,
to save the users and the developers the embarrassment.
2023-01-24 15:56:56 +01:00
chen70 b960de5fe3 hwdec/d3d11va: fix a possible memory leak 2023-01-24 15:56:56 +01:00
garamond13 c0fa12c958 filter_kernels: fix kaiser
Seems to me that params[1] is set to NAN and here should be the same value as in params[0].
2023-01-24 15:56:56 +01:00
Thomas Weißschuh 7753a73a17 ao_pipewire: clean up when hotplug_init fails 2023-01-24 15:56:56 +01:00
Thomas Weißschuh dadcb2e614 ao_pipewire: destroy context on connection failure 2023-01-24 15:56:56 +01:00
Thomas Weißschuh f8b7170871 ao_pipewire: free properties on failure 2023-01-24 15:56:56 +01:00
rcombs 71346393e2 ao_coreaudio: use device's nominal sample rate for latency properties
Fixes sync issues when using high-latency devices at non-native sample rates

Closes #10984
2023-01-24 15:56:56 +01:00
Dudemanguy ae7ee7417f wayland: check for resize/move in touch event first
8300830951 rearranged/simplified some of
the wayland touch code mainly because what was there before was
completely broken on my machine in those days (dragging was unreliable,
resizing was really buggy, etc.). However, one user said that touch
input no longer worked for him after that change. I could not ever
reproduce it, but it seems the issue was putting down the key before
testing for resize/move in the code. Now who knows why this actually
matters, but apparently it works for the user in question and I don't
observe any unexpected behavior on my end when swapping the order. Like
the mouse/pointer code, we also now do a test for dragging before
actually trying a resize/move which is a little more consistent than
before. Fixes #9771.
2023-01-24 15:56:56 +01:00
Dudemanguy 74baedffec osc: don't spam window-controls bindings on every render
Not sure when this actually started happening, but it's probably been
like this for years. Currently, the logic for the window-controls works
by simply checking if the osc is visible and then either enabling or
disabling the associated keybindings. The problem is that this can just
constantly spam mp.enable_keybindings/disable_key_bindings on every
single render call if the user disables the border at any point in time.
This does a lot of pointless work and also results in the logs being
spammed with lines like "disable-section". Clearly, this should just
work like the code for checking the input keybindings just above it.
Keep track of an internal state variable and check when it doesn't match
the osc visibility. In that case, we can then either enable or disable
the key bindings and just update the variable.
2023-01-24 15:56:56 +01:00
Thomas Weißschuh 495206dd41 ao_pipewire: log sample queueing
This allows us to more easily see the datapath from mpv to pipewire.

We know how often the callbacks are triggered, how big the buffers are
and how much data mpv provides to pipewire.
2023-01-24 15:56:56 +01:00
Dudemanguy 37869fbcd7 meson: fix stdatomic detection on bsd
BSDs use compiler-rt instead of libatomic for atomic types. In this
case, we can handle it similar to how dl is detected. Check for the
library (allowing for it to fail), and then check for a header symbol
while linking latomic. Fixes #10906.
2023-01-24 15:56:56 +01:00
Niklas Haas 954d53612c lcms: fix validate_3dlut_size_opt
Not only was this function needlessly convoluted, it was also broken -
since it returned a bool value where an error code was expected.
2023-01-24 15:56:56 +01:00
Niklas Haas 6860343a25 lcms: always parse lcms2-related options
Currently, the lcms-related options are only defined when lcms2 is
enabled at build time. However, this causes issues (e.g. segfault) for
vo_gpu_next, which relies on the presence of these options (to forward
them to libplacebo).

(That libplacebo internally depends on lcms2 as well is an
implementation detail - compiling mpv *without* lcms against libplacebo
*with* lcms should be possible in principle)

Fixes: #10891
Closes: #10856
2023-01-24 15:56:56 +01:00
Dudemanguy f5ce1fbf44 meson: unbreak dl check on BSDs without libdl
Regression from 1835dfc05c. The actual
libdl dependency is not always required for the function symbol check.
Fixes #10901.
2023-01-24 15:56:56 +01:00
Dudemanguy 214e47854a meson: prepend MPV_CONFDIR path with prefix
Meson uses the sysconfdir option for setting the global config
directory. This conveniently defaults to /etc if the prefix is set to
/usr which is nice for linux distros. BSDs tend to use /usr/local which
causes this value to become 'etc' by default which is not an absolute
path so you would need to set something like -Dsysconfdir=/usr/local/etc
as well in the configuration step. It turns out we can have our cake and
eat it too by just joining the paths of prefix and sysconfdir together.
In the case where -Dprefix=/usr, this still results in /etc/mpv as the
path since the path joining logic just drops the leading '/usr/'. For
the /usr/local case, it ends up as /usr/local/etc/mpv as expected. This
fixes #10882.
2023-01-24 15:56:56 +01:00
Niklas Haas 5fb7a23d37 vo_gpu_next: fix undefined behavior on alpha-first formats
When the alpha channel is the first component, this code block gets
skipped by the continue, meaning the check for c==0 never passes.

Fix it by explicitly keeping track of whether bits have been set.
2023-01-24 15:56:56 +01:00
Dudemanguy f593186e7d wayland: also log refresh rate on surface entrance
Never bothered to log this for some reason, but it's pretty convenient
when debugging.
2023-01-24 15:56:56 +01:00
Dudemanguy 5149805f6b wayland: error out if essential protocol support is missing
Related issue: #10868. While most protocols are in theory optional, a
small amount of them are absolutely essential and nothing will work
without them. We should make sure to error out in those cases and not
try to actually do anything. For wayland support in general,
wl_compositor support is obviously required. If there is no wl_surface,
you can't do anything. Additionally, vo_wlshm quite obviously requires
wl_shm so mark that one as well. vo_dmabuf_wayland needs linux_dmabuf,
viewporter, wl_shm, and wl_subcompositor. In practice, these are all
very standard protocols and shouldn't be missing but the linked issue
above is at least one example where a compositor was stuck on an ancient
version of a wayland interface.
2023-01-24 15:56:56 +01:00
Thomas Weißschuh af1e00b2ce ao_pipewire: log version information and metadata 2023-01-24 15:56:56 +01:00
559 changed files with 20369 additions and 22142 deletions

No files matched your search

-5
View File
@@ -8,8 +8,3 @@ charset = utf-8
insert_final_newline = true
indent_style = space
indent_size = 4
max_line_length = 80
trim_trailing_whitespace = true
[.git/COMMIT*]
max_line_length = 72
+2 -2
View File
@@ -2,7 +2,7 @@
name: 'Report a build Problem'
about: 'Create a report for a build related Issue'
title: ''
labels: 'core:meson'
labels: 'core:waf'
assignees: ''
---
@@ -24,7 +24,7 @@ differs from your own.
### Log file
Upload meson-logs/meson-log.txt or meson setup build output to https://0x0.st/ or attach
Upload build/config.log or ./waf configure output to https://0x0.st/ or attach
it to the github issue, and replace this text with a link to it.
The issue will be closed for ignoring the issue template.
+73 -158
View File
@@ -6,45 +6,29 @@ on:
- master
- ci
- 'release/**'
paths-ignore:
- 'DOCS/**'
- 'TOOLS/lua/**'
pull_request:
branches: [master]
paths-ignore:
- 'DOCS/**'
- 'TOOLS/lua/**'
jobs:
mingw:
runs-on: ubuntu-22.04
env:
CCACHE_BASEDIR: ${{ github.workspace }}
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 500M
strategy:
fail-fast: false
matrix:
target: [i686-w64-mingw32, x86_64-w64-mingw32]
steps:
- uses: actions/checkout@v3
- name: Get time
id: get_time
run: echo "timestamp=`date +%s%N`" >> $GITHUB_OUTPUT
# Increase -N suffix here to force full rebuild after changes
- uses: actions/cache@v3
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ matrix.target }}-${{ steps.get_time.outputs.timestamp }}
restore-keys: ${{ matrix.target }}-
path: mingw_prefix/
key: "${{ matrix.target }}-6"
- name: Install dependencies
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y autoconf automake pkg-config g++-mingw-w64 gcc-multilib python3-pip ninja-build nasm wine wine32 wine64 ccache
sudo python3 -m pip install meson
sudo apt-get install -y autoconf automake pkg-config g++-mingw-w64 gcc-multilib meson nasm
./bootstrap.py
- name: Build libraries
run: |
@@ -53,23 +37,26 @@ jobs:
TARGET: ${{ matrix.target }}
- name: Build with meson
id: build
run: |
./ci/build-mingw64.sh meson pack
./ci/build-mingw64.sh meson
env:
TARGET: ${{ matrix.target }}
- name: Print meson log
if: ${{ failure() && steps.build.outcome == 'failure' }}
if: ${{ failure() }}
run: |
cat ./build/meson-logs/meson-log.txt
- name: Functional test
id: tests
- name: Build with waf
run: |
cd artifact && wine64 ./mpv.com -v --no-config
./ci/build-mingw64.sh waf pack
env:
WINEDEBUG: '+loaddll'
TARGET: ${{ matrix.target }}
- name: Print waf log
if: ${{ failure() }}
run: |
cat ./build/config.log
- uses: actions/upload-artifact@v3
with:
@@ -82,11 +69,9 @@ jobs:
matrix:
cc:
- "clang"
cxx:
- "clang++"
os:
- "macos-11"
- "macos-12"
- "macos-13"
steps:
- uses: actions/checkout@v3
@@ -98,195 +83,125 @@ jobs:
- name: Install dependencies
run: |
brew update
brew install autoconf automake pkg-config libtool python freetype fribidi little-cms2 lua@5.1 libass ffmpeg meson libplacebo
brew install autoconf automake pkg-config libtool python freetype fribidi little-cms2 lua@5.1 libass ffmpeg meson
- name: Build with meson
id: build
run: |
./ci/build-macos.sh
./ci/build-macos.sh meson
env:
CC: "${{ matrix.cc }}"
CXX: "${{ matrix.cxx }}"
TRAVIS_OS_NAME: "${{ matrix.os }}"
- name: Print meson log
if: ${{ failure() && steps.build.outcome == 'failure' }}
if: ${{ failure() }}
run: |
cat ./build/meson-logs/meson-log.txt
- name: Run meson tests
id: tests
- name: Build with waf
run: |
meson test -C build
./ci/build-macos.sh waf
env:
CC: "${{ matrix.cc }}"
TRAVIS_OS_NAME: "${{ matrix.os }}"
- name: Print meson test log
if: ${{ failure() && steps.tests.outcome == 'failure' }}
- name: Print waf log
if: ${{ failure() }}
run: |
cat ./build/meson-logs/testlog.txt
cat ./build/config.log
linux:
runs-on: "ubuntu-22.04"
container:
image: "registry.opensuse.org/home/mia/images/images/mpv-ci:stable-deps"
env:
CC: "${{ matrix.config.cc }}"
CXX: "${{ matrix.config.cxx }}"
CC: "${{ matrix.cc }}"
strategy:
matrix:
config:
- {
cc: "gcc",
cxx: "g++",
}
- {
cc: "clang",
cxx: "clang++",
}
cc:
- "gcc"
- "clang"
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
./bootstrap.py
# workaround to avoid "fatal: unsafe repository" error
git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Build with meson
id: build
run: |
./ci/build-tumbleweed.sh
./ci/build-tumbleweed.sh meson
- name: Print meson log
if: ${{ failure() && steps.build.outcome == 'failure' }}
if: ${{ failure() }}
run: |
cat ./build/meson-logs/meson-log.txt
- name: Run meson tests
id: tests
- name: Build with waf
run: |
meson test -C build
./ci/build-tumbleweed.sh waf
- name: Print meson test log
if: ${{ failure() && steps.tests.outcome == 'failure' }}
- name: Print waf log
if: ${{ failure() }}
run: |
cat ./build/meson-logs/testlog.txt
cat ./build/config.log
freebsd:
runs-on: ubuntu-latest # until https://github.com/actions/runner/issues/385
timeout-minutes: 30 # avoid any weirdness with the VM
runs-on: macos-12 # until https://github.com/actions/runner/issues/385
steps:
- uses: actions/checkout@v3
- name: Test in FreeBSD VM
uses: cross-platform-actions/action@v0.19.1
uses: vmactions/freebsd-vm@v0
with:
operating_system: freebsd
version: '13.2'
run: |
sudo pkg update
sudo pkg install -y \
alsa-lib \
usesh: true
prepare: |
# Requested in ci/build-freebsd.sh
pkg install -y \
cmake \
evdev-proto \
ffmpeg \
git \
iconv \
libplacebo \
libxkbcommon \
libXinerama \
libxpresent \
luajit \
meson \
openal-soft \
pkgconf \
python3 \
sdl2 \
sndio \
vulkan-headers \
wayland-protocols \
#
# Optionally auto-enabled
pkg install -y \
alsa-lib \
jackit \
libXv \
libarchive \
libbluray \
libcaca \
libcdio-paranoia \
libdvdnav \
libplacebo \
libXinerama \
libxkbcommon \
libxpresent \
libXv \
luajit \
meson \
mujs \
openal-soft \
pipewire \
pkgconf \
pulseaudio \
python3 \
rubberband \
sekrit-twc-zimg \
sdl2 \
sndio \
uchardet \
v4l_compat \
vulkan-headers \
wayland-protocols
./ci/build-freebsd.sh
meson test -C build
msys2:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
sys:
- clang32
- clang64
- mingw32
- mingw64
- ucrt64
defaults:
run:
shell: msys2 {0}
steps:
- name: Disable autocrlf
shell: pwsh
#
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.sys }}
update: true
install: git
pacboy: >-
angleproject:p
ca-certificates:p
cc:p
diffutils:p
ffmpeg:p
lcms2:p
libarchive:p
libass:p
libcdio-paranoia:p
libdvdnav:p
libjpeg-turbo:p
libplacebo:p
lua51:p
meson:p
ninja:p
pkgconf:p
python:p
rst2pdf:p
rubberband:p
shaderc:p
spirv-cross:p
uchardet:p
vapoursynth:p
vulkan:p
./ci/build-freebsd.sh
- name: Build with meson
id: build
run: |
./ci/build-msys2.sh meson
- name: Print meson log
if: ${{ failure() }}
run: |
cat ./build/meson-logs/meson-log.txt
- name: Print meson log
if: ${{ failure() && steps.build.outcome == 'failure' }}
run: |
cat ./build/meson-logs/meson-log.txt
- name: Run meson tests
id: tests
run: |
meson test -C build
- name: Print meson test log
if: ${{ failure() && steps.tests.outcome == 'failure' }}
run: |
cat ./build/meson-logs/testlog.txt
- name: Print waf log
if: ${{ failure() }}
run: |
cat ./build/config.log
-58
View File
@@ -1,58 +0,0 @@
name: comment
on:
workflow_run:
workflows: ["build"]
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
script: |
const { owner, repo } = context.repo;
const run_id = ${{ github.event.workflow_run.id }};
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
const issue_number = await(async () => {
const pulls = await github.rest.pulls.list({ owner, repo });
for await (const { data } of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === pull_head_sha) {
return pull.number;
}
}
}
})();
if (issue_number) {
core.info(`Using pull request ${issue_number}`);
} else {
return core.error(`No matching pull request found`);
}
const { data: { artifacts } } = await github.rest.actions.listWorkflowRunArtifacts({ owner, repo, run_id });
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n\n<details><summary>Windows</summary>\n`;
for (const art of artifacts) {
const art_link = `https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip`;
if (art.name.includes('w64')) {
body += `\n* [${art.name}](${art_link})`;
}
}
body += `\n</details>`;
const { data: comments } = await github.rest.issues.listComments({ repo, owner, issue_number });
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
if (existing_comment) {
core.info(`Updating comment ${existing_comment.id}`);
await github.rest.issues.updateComment({ repo, owner, comment_id: existing_comment.id, body });
} else {
core.info(`Creating a comment`);
await github.rest.issues.createComment({ repo, owner, issue_number, body });
}
-24
View File
@@ -1,24 +0,0 @@
name: docs
on:
push:
branches:
- master
- ci
- 'release/**'
paths:
- 'DOCS/**'
pull_request:
branches: [master]
paths:
- 'DOCS/**'
jobs:
check-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Docs
run: |
sudo apt-get install python3-docutils
rst2man --strip-elements-with-class=contents --halt=2 ./DOCS/man/mpv.rst mpv.1
-22
View File
@@ -1,22 +0,0 @@
name: lint
on:
push:
branches:
- master
- ci
- 'release/**'
pull_request:
branches: [master]
jobs:
commit-msg:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 50
- name: Lint
run: |
./ci/lint-commit-msg.py
+40 -3
View File
@@ -1,3 +1,40 @@
/subprojects/*
!/subprojects/packagefiles
!/subprojects/*.wrap
*.o
*.a
*.d
*.exe
*.pyc
.depend
/config.h
/config.mak
/config.log
/mpv
/mpv.app
/version.h
/input/input_conf.h
/tags
/TAGS
/video/out/x11_icon.inc
/demux/ebml_defs.c
/demux/ebml_types.h
/sub/osd_font.h
/player/lua/*.inc
/player/builtin_conf.inc
/DOCS/man/mpv.1
/DOCS/man/mpv.aux
/DOCS/man/mpv.log
/DOCS/man/mpv.out
/DOCS/man/mpv.pdf
/DOCS/man/mpv.toc
/waf
/waf-*
/waf3-*
/build
/.waf*
/.lock-waf_*
/old_build
/Makefile
/subprojects
+7 -7
View File
@@ -4,7 +4,7 @@ mpv as a whole is licensed under the GNU General Public License GPL version 2
or later (called GPLv2+ in this document, see LICENSE.GPL for full license
text) by default, or the GNU Lesser General Public License LGPL version 2 or
later (LGPLv2.1+ in this document, see LICENSE.LGPL for full license text) if
built with the -Dgpl=false configure switch.
built with the --enable-lgpl configure switch.
Most source files are LGPLv2.1+ or GPLv2+, but some files are available under
more liberal licenses, such as BSD, MIT, ISC, and possibly others. Look at the
@@ -31,11 +31,11 @@ contains the complete SVN and CVS history as well.
Some libraries are GPLv2+ or GPLv3+ only. Building mpv with Samba support makes
it GPLv3+.
mpv can be built as LGPLv2.1+ with the -Dgpl=false configure option. To add a
LGPL mode to mpv, MPlayer code had to be relicensed from GPLv2+ to LGPLv2.1+ by
asking the MPlayer authors for permission. Since permission could not be
obtained from everyone, LGPL mode disables the following features, some of them
quite central:
mpv can be built as LGPLv2.1+ with the --enable-lgpl configure option. To add
a LGPL mode to mpv, MPlayer code had to be relicensed from GPLv2+ to LGPLv2.1+
by asking the MPlayer authors for permission. Since permission could not be
obtained from everyone, LGPL mode disables the following features, some of
them quite central:
- Linux X11 video output
- BSD audio output via OSS
- NVIDIA/Linux hardware decoding (vdpau, although nvdec usually works)
@@ -45,7 +45,7 @@ Some of these will be fixed in the future. The intended use for LGPL mode is
with libmpv, and currently it's not recommended to build mpv CLI in LGPL mode
at all.
The following files are still GPL only (-Dgpl=false disables them):
The following files are still GPL only (--enable-lgpl disables them):
audio/out/ao_jack.c will stay GPL
audio/out/ao_oss.c will stay GPL
+70
View File
@@ -0,0 +1,70 @@
# Differences Between Meson and Waf
mpv currently supports two different build systems: waf and meson. In general,
option names between both build systems are mostly the same. In most cases,
``--enable-foo`` in waf becomes ``-Dfoo=enabled`` in meson. Likewise,
``--disable-foo`` becomes ``-Dfoo=disabled``. For the rest of this document,
Waf options will be noted as ``--foo`` while meson options are noted as
``foo``.
## Universal Options
Meson has several [universal options](https://mesonbuild.com/Builtin-options.html#universal-options)
that you get for free. In some cases, these overlapped with custom waf options.
* ``--libmpv-static`` and ``--libmpv-shared`` were combined into one option:
``libmpv``. Use ``default_library`` to control if you want to build static or
shared libraries.
* Waf had a boolean ``--optimize`` option. In meson, this is a universal option,
``optimization``, which can take several different values. In mpv's meson
build, the default is ``2``.
* Instead of ``--debug-build``, meson simply calls it ``debug``. It is enabled
by default.
## Changed Options
* The legacy lua names (``52``, ``52deb``, etc.) for ``--lua`` are not
supported in the meson build. Instead, pass the generic pkg-config values
such as ``lua52``, ``lua5.2``, etc.
* ``--lgpl`` was changed to ``gpl``. If ``gpl`` is false, the build is LGPL2.1+.
### Boolean Options
The following options are all booleans that accept ``true`` or ``false``
instead of ``enabled`` or ``disabled``.
* ``build-date``
* ``cplayer``
* ``gpl``
* ``libmpv``
* ``ta-leak-report``
* ``tests``
## Removed Options
There are options removed with no equivalent in the meson build.
* ``--asm`` was removed since it doesn't do anything.
* ``--android`` was removed since meson knows if the machine is android.
* ``--clang-compilation-database`` was removed. Meson can do this on its own
by invoking ninja (``ninja -t compdb``).
* ``--tvos`` was removed since it doesn't do anything.
* ``--static-build`` was removed. Use ``default_library``.
* ``--swift-static`` was removed. The swift library always dynamically links.
## Renamed Options
These are some other options that were renamed.
* ``--gl-wayland`` was renamed to ``egl-wayland``.
* ``--swift`` was renamed to ``swift-build``.
## Other
* The meson build supports passing the ``SOURCE_DATE_EPOCH`` environment variable
during the compilation step for those who want reproducibility without having to
disable the build date.
* The ``Configuration`` line shown by ``mpv -v`` does not show everything passed on
cli since meson does not have any easy way to access a user's argv. Instead, it
simply shows whatever the value of ``prefix`` is regardless if it was specified
or not.
-4
View File
@@ -32,10 +32,6 @@ API changes
::
--- mpv 0.37.0 ---
2.2 - add mpv_time_ns()
--- mpv 0.36.0 ---
2.1 - add mpv_del_property()
--- mpv 0.35.0 ---
2.0 - remove headers/functions of the obsolete opengl_cb API
- remove mpv_opengl_init_params.extra_exts field
+33 -34
View File
@@ -4,43 +4,28 @@ Compiling for Windows
Compiling for Windows is supported with MinGW-w64. This can be used to produce
both 32-bit and 64-bit executables, and it works for building on Windows and
cross-compiling from Linux and Cygwin. MinGW-w64 is available from:
https://www.mingw-w64.org/
http://mingw-w64.sourceforge.net.
While building a complete MinGW-w64 toolchain yourself is possible, there are a
few build environments and scripts to help ease the process, such as MSYS2 and
MXE. Note that MinGW environments included in Linux distributions are often
broken, outdated and useless, and usually don't use MinGW-w64.
**Warning**: the original MinGW (https://osdn.net/projects/mingw/) is unsupported.
**Warning**: the original MinGW (http://www.mingw.org) is unsupported.
Cross-compilation
=================
When cross-compiling, it is recommended to use a meson crossfile to setup
the cross compiling environment. A minimal example is included below:
When cross-compiling, you have to run mpv's configure with these arguments:
```ini
[binaries]
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
exe_wrapper = 'wine64'
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
```bash
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
```
See [meson's documentation](https://mesonbuild.com/Cross-compilation.html) for
more information.
[MXE](https://mxe.cc) makes it very easy to bootstrap a complete MingGW-w64
environment from a Linux machine. See a working example below.
Alternatively, you can try [mpv-winbuild-cmake](https://github.com/shinchiro/mpv-winbuild-cmake),
Alternatively, you can try [mingw-w64-cmake](https://github.com/lachs0r/mingw-w64-cmake),
which bootstraps a MinGW-w64 environment and builds mpv and dependencies.
Example with MXE
@@ -94,8 +79,11 @@ export PATH=/opt/mxe/usr/bin/:$PATH
cd ..
git clone https://github.com/mpv-player/mpv.git
cd mpv
meson setup build --crossfile crossfile
meson compile -C build
python ./bootstrap.py
DEST_OS=win32 TARGET=i686-w64-mingw32.static ./waf configure
# Or, if 64 bit version,
# DEST_OS=win32 TARGET=x86_64-w64-mingw32.static ./waf configure
./waf build
```
Native compilation with MSYS2
@@ -111,7 +99,7 @@ To build 64-bit mpv on Windows:
Installing MSYS2
----------------
1. Download an installer from https://www.msys2.org/
1. Download an installer from https://msys2.github.io/
Both the i686 and the x86_64 version of MSYS2 can build 32-bit and 64-bit
mpv binaries when running on a 64-bit version of Windows, but the x86_64
@@ -144,30 +132,37 @@ Installing mpv dependencies
```bash
# Install MSYS2 build dependencies and a MinGW-w64 compiler
pacman -S git $MINGW_PACKAGE_PREFIX-{python,pkgconf,gcc,meson}
pacman -S git python $MINGW_PACKAGE_PREFIX-{pkg-config,gcc}
# Install the most important MinGW-w64 dependencies. libass and lcms2 are also
# pulled in as dependencies of ffmpeg.
pacman -S $MINGW_PACKAGE_PREFIX-{ffmpeg,libjpeg-turbo,luajit}
pacman -S $MINGW_PACKAGE_PREFIX-{ffmpeg,libjpeg-turbo,lua51}
```
Building mpv
------------
Clone the latest mpv from git and install waf. **Note:** ``/usr/bin/python3``
is invoked directly here, since an MSYS2 version of Python is required.
```bash
git clone https://github.com/mpv-player/mpv.git && cd mpv
/usr/bin/python3 bootstrap.py
```
Finally, compile and install mpv. Binaries will be installed to
``/mingw64/bin`` or ``/mingw32/bin``.
```bash
meson setup build --prefix=$MSYSTEM_PREFIX
meson compile -C build
/usr/bin/python3 waf configure CC=gcc.exe --check-c-compiler=gcc --prefix=$MSYSTEM_PREFIX
/usr/bin/python3 waf install
```
Or, compile and install both libmpv and mpv:
```bash
meson setup build -Dlibmpv=true --prefix=$MSYSTEM_PREFIX
meson compile -C build
meson install -C build
/usr/bin/python3 waf configure CC=gcc.exe --check-c-compiler=gcc --enable-libmpv-shared --prefix=$MSYSTEM_PREFIX
/usr/bin/python3 waf install
```
Linking libmpv with MSVC programs
@@ -182,11 +177,13 @@ To do this, you need a Visual Studio which supports ``stdint.h`` (recent ones do
and you need to create a import library for the mpv DLL:
```bash
lib /name:mpv-1.dll /out:mpv.lib /MACHINE:X64
lib /def:mpv.def /name:mpv-1.dll /out:mpv.lib /MACHINE:X64
```
The string in the ``/name:`` parameter must match the filename of the DLL (this
is simply the filename the MSVC linker will use).
is simply the filename the MSVC linker will use). The ``mpv.def`` can be
retrieved from the mpv build directory, or can be produced by MingGW's
gendef.exe helper from the mpv DLL.
Static linking is not possible.
@@ -211,4 +208,6 @@ Use of the ANGLE OpenGL backend requires a copy of the D3D compiler DLL that
matches the version of the D3D SDK that ANGLE was built with
(``d3dcompiler_43.dll`` in case of MinGW-built ANGLE) in the path or in the
same folder as mpv. It must be of the same architecture (x86_64 / i686) as the
mpv you compiled.
mpv you compiled. You can find copies here:
https://mpv.srsfckn.biz/d3dcompiler.7z
+8 -9
View File
@@ -140,7 +140,7 @@ Touching user-visible parts may require updating the mpv docs
Code formatting
---------------
mpv uses C11 with K&R formatting, with some exceptions.
mpv uses C99 with K&R formatting, with some exceptions.
- Use the K&R indent style.
- Use 4 spaces of indentation, never use tabs (except in Makefiles).
@@ -153,9 +153,9 @@ mpv uses C11 with K&R formatting, with some exceptions.
some_function(a, b, c);
}
```
- Break lines on 80 columns. There is a hard limit of 100 columns. You may ignore
- Break lines on 80 columns. There is a hard limit of 85 columns. You may ignore
this limit if there's a strong case that not breaking the line will increase
readability. Going over 100 columns might provoke endless discussions about
readability. Going over 85 columns might provoke endless discussions about
whether such a limit is needed or not, so avoid it.
- If the body of an if/for/while statement has more than 1 physical lines, then
always add braces, even if they're technically redundant.
@@ -224,16 +224,15 @@ New code must follow the following conventions:
General coding
--------------
- Use C11. Also freely make use of C11 features if it's appropriate, but do not
use VLA and complex number types.
- Use C99. Also freely make use of C99 features if it's appropriate, such as
stdbool.h. (Except VLA and complex number types.)
- Don't use non-standard language (such as GNU C-only features). In some cases
they may be warranted, if they are optional (such as attributes enabling
printf-like format string checks). "#pragma once" is allowed as an exception.
But in general, standard C11 must be used.
But in general, standard C99 must be used.
- The same applies to libc functions. We have to be Windows-compatible too. Use
functions guaranteed by C11 or POSIX only, unless your use is guarded by a
configure check. Be mindful of MinGW-specifics since C11 support is not always
guaranteed.
functions guaranteed by C99 or POSIX only, unless your use is guarded by a
configure check. There is some restricted use of C11 (ask on IRC for details).
- Prefer fusing declaration and initialization, rather than putting declarations
on the top of a block. Obvious data flow is more important than avoiding
mixing declarations and statements, which is just a C90 artifact.
+2 -156
View File
@@ -26,163 +26,9 @@ Interface changes
::
--- mpv 0.37.0 ---
- `--save-position-on-quit` and its associated commands now store state files
in %LOCALAPPDATA% instead of %APPDATA% directory by default on Windows.
- change `--subs-with-matching-audio` default from `no` to `yes`
- change `--subs-fallback` default from `no` to `default`
- add the `--hdr-peak-percentile` option
- include `--hdr-peak-percentile` in the `gpu-hq` profile
- change `--audiotrack-pcm-float` default from `no` to `yes`
- add video-params/aspect-name
- change type of `--sub-pos` to float
- The remaining time printed in the terminal is now adjusted for speed by default.
You can disable this with `--no-term-remaining-playtime`.
- add `playlist-path` and `playlist/N/playlist-path` properties
- add `--x11-wid-title` option
- add `--libplacebo-opts` option
- add `--audio-file-exts`, `--cover-art-auto-exts`, and `--sub-auto-exts`
- change `slang` default back to NULL
- remove special handling of the `auto` value from `--alang/slang/vlang` options
- add `--subs-match-os-language` as a replacement for `--slang=auto`
- add `always` option to `--subs-fallback-forced`
- remove `auto` choice from `--sub-forced-only`
- remove `auto-forced-only` property
- rename `--sub-forced-only` to `--sub-forced-events-only`
- remove `sub-forced-only-cur` property (`--sub-forced-events-only` is a replacement)
- remove deprecated `video-aspect` property
- add `--video-crop`
- add `video-params/crop-[w,h,x,y]`
- remove `--tone-mapping-mode`
- change `--subs-fallback-forced` so that it works alongside `--slang`
- add `--icc-3dlut-size=auto` and make it the default
- add `--scale=ewa_lanczos4sharpest`
- remove `--scale-wblur`, `--cscale-wblur`, `--dscale-wblur`, `--tscale-wblur`
- remove `bcspline` filter (`bicubic` is now the same as `bcspline`)
- rename `--cache-dir` and `--cache-unlink-files` to `--demuxer-cache-dir` and
`--demuxer-cache-unlink-files`
- enable `--correct-downscaling`, `--linear-downscaling`, `--sigmoid-upscaling`
- `--cscale` defaults to `--scale` if not defined
- change `--tscale` default to `oversample`
- change `--dither-depth` to `auto`
- deprecate `--profile=gpu-hq`, add `--profile=<fast|high-quality>`
- change `--dscale` default to `hermite`
- update defaults to `--hdr-peak-decay-rate=20`, `--hdr-scene-threshold-low=1.0`,
`--hdr-scene-threshold-high=3.0`
- update defaults to `--deband-threshold=48`, `--deband-grain=32`
- add `--directory-mode=auto` and make it the default
- remove deprecated `--profile=opengl-hq`
- remove several legacy fallbacks for old deprecated options (now they will just
error out like normal)
- remove deprecated `drop-frame-count` and `vo-drop-frame-count` property aliases
- remove the ability to write to the `display-fps` property (use `override-display-fps`
instead)
- writing the current value to playlist-pos will no longer restart playback (use
`playlist-play-index` instead)
- remove deprecated `--oaoffset`, `--oafirst`, `--ovoffset`, `--ovfirst`,
`--demuxer-force-retry-on-eof`, `--fit-border` options
- remove deprecated `--record-file` option
- remove deprecated `--vf-defaults` and `--af-defaults` options
- `--drm-connector` no longer allows selecting the card number (use `--drm-device`
instead)
- add `--title-bar` option
- add `--window-corners` option
- rename `--cdrom-device` to `--cdda-device`
- remove `--scale-cutoff`, `--cscale-cutoff`, `--dscale-cutoff`, `--tscale-cutoff`
- remove `--scaler-lut-size`
- deprecate shared-script-properties (user-data is a replacement)
- add `--backdrop-type` option
- add `--window-affinity` option
- `--config-dir` no longer forces cache and state files to also reside in there
- deprecate `--demuxer-cue-codepage` in favor of `--metadata-codepage`
- change the default of `metadata-codepage` to `auto`
- add `playlist-next-playlist` and `playlist-prev-playlist` commands
- change `video-codec` to show description or name, not both
- deprecate `--cdda-toc-bias` option, offsets are always checked now
- disable `--allow-delayed-peak-detect` by default
- rename `--fps` to `--container-fps-override`
- rename `--override-display-fps` to `--display-fps-override`
- rename `--sub-ass-force-style` to `--sub-ass-style-overrides`
- alias `--screenshot-directory` to `--screenshot-dir`
- alias `--watch-later-directory` to `--watch-later-dir`
- rename `--play-dir` to `--play-direction`
- `--js-memory-report` is now used for enabling memory reporting for javascript
scripts
- drop support for `-del` syntax for list options
- `--demuxer-hysteresis-secs` now respects `--cache-secs` and/or
`--demuxer-readahead-secs` as well
- add hdr metadata to `video-params` property
- add `--target-gamut`
- change the way display names are retrieved on macOS, usage of options and properties
`--fs-screen-name`, `--screen-name` and `display-names` needs to be adjusted
- remove OpenGL cocoa backend that was deprecated in 0.29
- remove `border`, `fullscreen`, `ontop`, `osd-level` and `pause`
from default `--watch-later-options`
- add `video-*` and `secondary-sub-visibility` to default `--watch-later-options`
--- mpv 0.36.0 ---
- add `--target-contrast`
- Target luminance value is now also applied when ICC profile is used.
`--icc-use-luma` has been added to use ICC profile luminance value.
If target luminance and ICC luminance is not used, old behavior apply,
defaulting to 203 nits. (Only applies for `--vo=gpu-next`)
- `playlist/N/title` gets set upon opening the file if it wasn't already set
and a title is available.
- add the `--vo=kitty` video output driver, as well as the options
`--vo-kitty-cols`, `--vo-kitty-rows`, `--vo-kitty-width`,
`--vo-kitty-height`, `--vo-kitty-left`, `--vo-kitty-top`,
`--vo-kitty-config-clear`, `--vo-kitty-alt-screen` and
`--vo-kitty-use-shm`
- add `--force-render`
- add `--vo-sixel-config-clear`, `--vo-sixel-alt-screen` and
`--vo-sixel-buffered`
- add `--wayland-content-type`
- deprecate `--vo-sixel-exit-clear` and alias it to
`--vo-sixel-alt-screen`
- deprecate `--drm-atomic`
- add `--demuxer-hysteresis-secs`
- add `--video-sync=display-tempo`
- the `start` option is no longer unconditionally written by
watch-later. It is still written by default but you may
need to explicitly add `start` depending on how you have
`--watch-later-options` configured.
=======
--- mpv 0.35.1 --- (feature backport due to special circumstances)
- add `--vd-lavc-dr=auto` and make it the default
- add support for the fractional scale protocol in wayland
- in wayland, hidpi window scaling now scales the window by the compositor's
dpi scale factor by default (can be disabled with --no-hidpi-window-scale
if fractional scaling support exists).
- change --screenshot-tag-colorspace default value from `no` to `yes`
- undeprecate vf_sub
- add `--tone-mapping=st2094-40` and `--tone-mapping=st2094-10`
- change `--screenshot-jxl-effort` default from `3` to `4`.
- add `--tone-mapping-visualize`
- change type of `--brightness`, `--saturation`, `--contrast`, `--hue` and
`--gamma` to float.
- add `platform` property
- add `--auto-window-resize`
- `--save-position-on-quit` and its associated commands now store state files in
the XDG_STATE_HOME directory by default. This only has an effect on linux/bsd
systems.
- mpv now implictly saves cache files in XDG_CACHE_HOME by default. This only has
an effect if the user enables options that would lead to cache being stored and
only makes a difference on linux/bsd systems.
- `--cache-on-disk` no longer requires explictly setting the `--cache-dir` option
- add `--icc-cache` and `--gpu-shader-cache` options to control whether or not to
save cache files for these features; explictly setting `--icc-cache-dir` and
`--gpu-shader-cache` is no longer required
- remove the `--tone-mapping-crosstalk` option
- add `--gamut-mapping-mode=perceptual|relative|saturation|absolute|linear`
- add `--corner-rounding` option
- change `--subs-with-matching-audio` default from `yes` to `no`
- change `--slang` default from blank to `auto`
- add `--input-cursor-passthrough` option to allow pointer events to completely
passthrough the mpv window
- icc and gpu-shader cache are now saved by default (use --no-icc-shader-cache and
--no-gpu-shader-cache to disable)
- add `--directory-mode=recursive|lazy|ignore`
- `--hwdec=yes` is now mapped to `auto-safe` rather than `auto` (also used
by ctrl+h keybind)
- add `--hdr-contrast-recovery` and `--hdr-contrast-smoothness`
- include `--hdr-contrast-recovery` in the `gpu-hq` profile
--- mpv 0.35.0 ---
- add the `--vo=gpu-next` video output driver, as well as the options
`--allow-delayed-peak-detect`, `--builtin-scalers`,
+13 -29
View File
@@ -19,8 +19,8 @@ syntax is:
The ``--vf`` description describes how libavfilter can be used and how to
workaround deprecated mpv filters.
See ``--vf`` group of options for info on how ``--af-add``, ``--af-pre``,
``--af-clr``, and possibly others work.
See ``--vf`` group of options for info on how ``--af-defaults``, ``--af-add``,
``--af-pre``, ``--af-del``, ``--af-clr``, and possibly others work.
Available filters are:
@@ -100,7 +100,7 @@ Available filters are:
``scaletempo[=option1:option2:...]``
Scales audio tempo without altering pitch, optionally synced to playback
speed.
speed (default).
This works by playing 'stride' ms of audio at normal speed then consuming
'stride*scale' ms of input audio. It pieces the strides together by
@@ -116,8 +116,8 @@ Available filters are:
cause noticeable skips at high scale amounts and an echo at low scale
amounts. Very low values will alter pitch. Increasing improves
performance. (default: 60)
``overlap=<factor>``
Factor of stride to overlap. Decreasing improves performance.
``overlap=<percent>``
Percentage of stride to overlap. Decreasing improves performance.
(default: .20)
``search=<amount>``
Length in milliseconds to search for best overlap position. Decreasing
@@ -166,10 +166,7 @@ Available filters are:
Scales audio tempo without altering pitch.
The algorithm is ported from chromium and uses the
Waveform Similarity Overlap-and-add (WSOLA) method.
It seems to achieves higher audio quality than scaletempo, and rubberband R2
engine, or ``engine=faster``. This filter is inserted automatically if
``audio-pitch-correction`` option is used (on by default) when the playback
speed is changed.
It seems to achieve a higher audio quality than scaletempo and rubberband.
By default, the ``search-interval`` and ``window-size`` parameters
have the same values as in chromium.
@@ -179,33 +176,22 @@ Available filters are:
``max-speed=<speed>``
Mute audio if the playback speed is above ``<speed>``
and ``<speed> != 0``. (default: 8.0)
and ``<speed> != 0``. (default: 4.0)
``search-interval=<amount>``
Length in milliseconds to search for best overlap position. (default: 40)
Length in milliseconds to search for best overlap position. (default: 30)
``window-size=<amount>``
Length in milliseconds of the overlap-and-add window. (default: 12)
Length in milliseconds of the overlap-and-add window. (default: 20)
``rubberband``
High quality pitch correction with librubberband. This can be used in place
of ``scaletempo`` and ``scaletempo2``, and will be used to adjust audio pitch
when playing at speed different from normal. It can also be used to adjust
audio pitch without changing playback speed.
of ``scaletempo``, and will be used to adjust audio pitch when playing
at speed different from normal. It can also be used to adjust audio pitch
without changing playback speed.
``pitch-scale=<amount>``
``<pitch-scale>``
Sets the pitch scaling factor. Frequencies are multiplied by this value.
(default: 1.0)
``engine=<faster|finer>``
Select the core Rubberband engine to be used. There are two available:
:Faster: This is the Rubberband R2 engine. It uses significantly less
CPU than the Finer (R3) engine.
:Finer: This is the Rubberband R3 engine. This engine is only available
with librubberband version 3 or newer. This produces significantly
higher quality output, at the cost of higher CPU usage. (Default
if available)
This filter has a number of additional sub-options. You can list them with
``mpv --af=rubberband=help``. This will also show the default values
@@ -213,8 +199,6 @@ Available filters are:
merely passed to librubberband. Look at the librubberband documentation
to learn what each option does:
https://breakfastquay.com/rubberband/code-doc/classRubberBand_1_1RubberBandStretcher.html
Do note that certain options are only applicable to one of R2 (faster) and
R3 (finer) engines.
(The mapping of the mpv rubberband filter sub-option names and values to
those of librubberband follows a simple pattern: ``"Option" + Name + Value``.)
+2 -7
View File
@@ -128,7 +128,7 @@ Available audio output drivers are:
Set the audio buffer size in milliseconds. A higher value buffers
more data, and has a lower probability of buffer underruns. A smaller
value makes the audio stream react faster, e.g. to playback speed
changes. "native" lets the sound server determine buffers.
changes.
``--pulse-latency-hacks=<yes|no>``
Enable hacks to workaround PulseAudio timing bugs (default: no). If
@@ -154,18 +154,13 @@ Available audio output drivers are:
Set the audio buffer size in milliseconds. A higher value buffers
more data, and has a lower probability of buffer underruns. A smaller
value makes the audio stream react faster, e.g. to playback speed
changes. "native" lets the sound server determine buffers.
changes.
``--pipewire-remote=<remote>``
Specify the PipeWire remote daemon name to connect to via local UNIX
sockets.
An empty <remote> string uses the default remote named ``pipewire-0``.
``--pipewire-volume-mode=<channel|global>``
Specify if the ``ao-volume`` property should apply to the channel
volumes or the global volume.
By default the channel volumes are used.
``sdl``
SDL 1.2+ audio output driver. Should work on any platform supported by SDL
1.2, but may require the ``SDL_AUDIODRIVER`` environment variable to be set
+24 -41
View File
@@ -11,63 +11,63 @@ Keybindings
\`
Show the console.
ESC and Ctrl+[
ESC
Hide the console.
ENTER, Ctrl+j and Ctrl+m
ENTER, Ctrl+J and Ctrl+M
Run the typed command.
Shift+ENTER
Type a literal newline character.
LEFT and Ctrl+b
LEFT and Ctrl+B
Move the cursor to the previous character.
RIGHT and Ctrl+f
RIGHT and Ctrl+F
Move the cursor to the next character.
Ctrl+LEFT and Alt+b
Ctrl+LEFT and Alt+B
Move the cursor to the beginning of the current word, or if between words,
to the beginning of the previous word.
Ctrl+RIGHT and Alt+f
Ctrl+RIGHT and Alt+F
Move the cursor to the end of the current word, or if between words, to the
end of the next word.
HOME and Ctrl+a
HOME and Ctrl+A
Move the cursor to the start of the current line.
END and Ctrl+e
END and Ctrl+E
Move the cursor to the end of the current line.
BACKSPACE and Ctrl+h
BACKSPACE and Ctrl+H
Delete the previous character.
Ctrl+d
Ctrl+D
Hide the console if the current line is empty, otherwise delete the next
character.
Ctrl+BACKSPACE and Ctrl+w
Ctrl+BACKSPACE and Ctrl+W
Delete text from the cursor to the beginning of the current word, or if
between words, to the beginning of the previous word.
Ctrl+DEL and Alt+d
Ctrl+DEL and Alt+D
Delete text from the cursor to the end of the current word, or if between
words, to the end of the next word.
Ctrl+u
Ctrl+U
Delete text from the cursor to the beginning of the current line.
Ctrl+k
Ctrl+K
Delete text from the cursor to the end of the current line.
Ctrl+c
Ctrl+C
Clear the current line.
UP and Ctrl+p
UP and Ctrl+P
Move back in the command history.
DOWN and Ctrl+n
DOWN and Ctrl+N
Move forward in the command history.
PGUP
@@ -79,16 +79,16 @@ PGDN
INSERT
Toggle insert mode.
Ctrl+v
Ctrl+V
Paste text (uses the clipboard on X11 and Wayland).
Shift+INSERT
Paste text (uses the primary selection on X11 and Wayland).
TAB and Ctrl+i
TAB and Ctrl+I
Complete the command or property name at the cursor.
Ctrl+l
Ctrl+L
Clear all log messages from the console.
Commands
@@ -99,13 +99,9 @@ Commands
specifying the initial cursor position as a positive integer starting from
1.
.. admonition:: Examples for input.conf
.. admonition:: Example for input.conf
``% script-message-to console type "seek absolute-percent; keypress ESC" 6``
Enter a percent position to seek to and close the console.
``Ctrl+o script-message-to console type "loadfile ''; keypress ESC" 11``
Enter a file or URL to play. Tab completes paths in the filesystem.
``% script-message-to console type "seek absolute-percent" 6``
Known issues
------------
@@ -139,9 +135,8 @@ Configurable Options
``font``
Default: unset (picks a hardcoded font depending on detected platform)
Set the font used for the REPL and the console.
This has to be a monospaced font for the completion suggestions to be
aligned correctly.
Set the font used for the REPL and the console. This probably doesn't
have to be a monospaced font.
``font_size``
Default: 16
@@ -149,19 +144,7 @@ Configurable Options
Set the font size used for the REPL and the console. This will be
multiplied by "scale".
``border_size``
Default: 1
Set the font border size used for the REPL and the console.
``history_dedup``
Default: true
Remove duplicate entries in history as to only keep the latest one.
multiplied by "scale."
``font_hw_ratio``
Default: 2.0
The ratio of font height to font width.
Adjusts table width of completion suggestions.
+16
View File
@@ -28,6 +28,10 @@ You can encode files from one format/codec to another using this facility.
Specifies the output audio codec. See ``--oac=help`` for a full list of
supported codecs.
``--oaoffset=<value>``
Shifts audio data by the given time (in seconds) by adding/removing
samples at the start. Deprecated.
``--oacopts=<options>``
Specifies the output audio codec options for libavcodec.
See ``--oacopts=help`` for a full list of supported options.
@@ -46,10 +50,18 @@ You can encode files from one format/codec to another using this facility.
``--oacopts=""``
Completely empties the options list.
``--oafirst``
Force the audio stream to become the first stream in the output.
By default, the order is unspecified. Deprecated.
``--ovc=<codec>``
Specifies the output video codec. See ``--ovc=help`` for a full list of
supported codecs.
``--ovoffset=<value>``
Shifts video data by the given time (in seconds) by shifting the pts
values. Deprecated.
``--ovcopts=<options>``
Specifies the output video codec options for libavcodec.
See --ovcopts=help for a full list of supported options.
@@ -71,6 +83,10 @@ You can encode files from one format/codec to another using this facility.
``--ovcopts=""``
Completely empties the options list.
``--ovfirst``
Force the video stream to become the first stream in the output.
By default, the order is unspecified. Deprecated.
``--orawts``
Copies input pts to the output video (not supported by some output
container formats, e.g. AVI). In this mode, discontinuities are not fixed
+56 -182
View File
@@ -186,7 +186,7 @@ character at the value.
Custom quotes also take the content literally, but are more flexible than single
quotes. They start with ````` (back-quote) followed by any ASCII character,
and end at the first occurrence of the same pair in reverse order, e.g.
and end at the first occurance of the same pair in reverse order, e.g.
```-foo-``` or ````bar````. The final pair sequence is not allowed at the
value - in these examples ``-``` and `````` respectively. In the second
example the last character of the value also can't be a back-quote.
@@ -334,9 +334,6 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``set <name> <value>``
Set the given property or option to the given value.
``del <name>``
Delete the given property. Most properties cannot be deleted.
``add <name> [<value>]``
Add the given value to the property or option. On overflow or underflow,
clamp the property to the maximum. If ``<value>`` is omitted, assume ``1``.
@@ -369,7 +366,8 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
behavior depends on the selected video output.
<window>
Save the contents of the mpv window. Typically scaled, with OSD and
subtitles. The exact behavior depends on the selected video output.
subtitles. The exact behavior depends on the selected video output, and
if no support is available, this will act like ``video``.
<each-frame>
Take a screenshot each frame. Issue this command again to stop taking
screenshots. Note that you should disable frame-dropping when using
@@ -386,9 +384,6 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
normal standalone commands, this is always asynchronous, and the flag has
no effect. (This behavior changed with mpv 0.29.0.)
On success, returns a ``mpv_node`` with a ``filename`` field set to the
saved screenshot location.
``screenshot-to-file <filename> <flags>``
Take a screenshot and save it to a given file. The format of the file will
be guessed by the extension (and ``--screenshot-format`` is ignored - the
@@ -422,13 +417,6 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
force
Terminate playback if the first file is being played.
``playlist-next-playlist``
Go to the next entry on the playlist with a different ``playlist-path``.
``playlist-prev-playlist``
Go to the first of the previous entries on the playlist with a different
``playlist-path``.
``playlist-play-index <integer|current|none>``
Start (or restart) playback of the given playlist index. In addition to the
0-based playlist entry index, it supports the following values:
@@ -758,7 +746,7 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``show-text <text> [<duration>|-1 [<level>]]``
Show text on the OSD. The string can contain properties, which are expanded
as described in `Property Expansion`_. This can be used to show playback
time, filename, and so on. ``no-osd`` has no effect on this command.
time, filename, and so on.
<duration>
The time in ms to show the message for. By default, it uses the same
@@ -786,7 +774,7 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``show-progress``
Show the progress bar, the elapsed time and the total duration of the file
on the OSD. ``no-osd`` has no effect on this command.
on the OSD.
``write-watch-later-config``
Write the resume config file that the ``quit-watch-later`` command writes,
@@ -921,6 +909,13 @@ Input Commands that are Possibly Subject to Change
<remove>
Like ``toggle``, but always remove the given filter from the chain.
<del>
Remove the given filters from the video chain. Unlike in the other
cases, the second parameter is a comma separated list of filter names
or integer indexes. ``0`` would denote the first filter. Negative
indexes start from the last filter, and ``-1`` denotes the last
filter. Deprecated, use ``remove``.
<clr>
Remove all filters. Note that like the other sub-commands, this does
not control automatically inserted filters.
@@ -1096,7 +1091,7 @@ Input Commands that are Possibly Subject to Change
Before mpv 0.18.1, you had to do manual "double buffering" when updating
an overlay by replacing it with a different memory buffer. Since mpv
0.18.1, the memory is simply copied and doesn't reference any of the
memory indicated by the command's arguments after the command returns.
memory indicated by the command's arguments after the commend returns.
If you want to use this command before mpv 0.18.1, reads the old docs
to see how to handle this correctly.
@@ -1296,21 +1291,16 @@ Input Commands that are Possibly Subject to Change
The ``flags`` argument is like the first argument to ``screenshot`` and
supports ``subtitles``, ``video``, ``window``.
``vf-command <label> <command> <argument> [<target>]``
Send a command to the filter. Note that currently, this only works with
the ``lavfi`` filter. Refer to the libavfilter documentation for the list
of supported commands for each filter.
``vf-command <label> <command> <argument>``
Send a command to the filter with the given ``<label>``. Use ``all`` to send
it to all filters at once. The command and argument string is filter
specific. Currently, this only works with the ``lavfi`` filter - see
the libavfilter documentation for which commands a filter supports.
``<label>`` is a mpv filter label, use ``all`` to send it to all filters
at once.
Note that the ``<label>`` is a mpv filter label, not a libavfilter filter
name.
``<command>`` and ``<argument>`` are filter-specific strings.
``<target>`` is a filter or filter instance name and defaults to ``all``.
Note that the target is an additional specifier for filters that
support them, such as complex ``lavfi`` filter chains.
``af-command <label> <command> <argument> [<target>]``
``af-command <label> <command> <argument>``
Same as ``vf-command``, but for audio filters.
``apply-profile <name> [<mode>]``
@@ -1393,7 +1383,7 @@ Input Commands that are Possibly Subject to Change
This was mostly created for network streams. For local files, there may
be much better methods to create excerpts and such. There are tons of
much more user-friendly Lua scripts, that will re-encode parts of a file
much more user-friendly Lua scripts, that will reencode parts of a file
by spawning a separate instance of ``ffmpeg``. With network streams,
this is not that easily possible, as the stream would have to be
downloaded again. Even if ``--stream-record`` is used to record the
@@ -1433,8 +1423,8 @@ IPC sees. Note that the C API has separate C-level declarations with
``mpv_event``, which may be slightly different.
Note that events are asynchronous: the player core continues running while
events are delivered to scripts and other clients. In some cases, you can use
hooks to enforce synchronous execution.
events are delivered to scripts and other clients. In some cases, you can hooks
to enforce synchronous execution.
All events can have the following fields:
@@ -1930,11 +1920,6 @@ Property list
This replaces the ``length`` property, which was deprecated after the
mpv 0.9 release. (The semantics are the same.)
This has a sub-property:
``duration/full``
``duration`` with milliseconds.
``avsync``
Last A/V synchronization difference. Unavailable if audio or video is
disabled.
@@ -1949,9 +1934,13 @@ Property list
situations, e.g. when video packets are damaged, or the decoder doesn't
follow the usual rules. Unavailable if video is disabled.
``drop-frame-count`` is a deprecated alias.
``frame-drop-count``
Frames dropped by VO (when using ``--framedrop=vo``).
``vo-drop-frame-count`` is a deprecated alias.
``mistimed-frame-count``
Number of video frames that were not timed correctly in display-sync mode
for the sake of keeping A/V sync. This does not include external
@@ -1981,11 +1970,6 @@ Property list
``time-pos`` (RW)
Position in current file in seconds.
This has a sub-property:
``time-pos/full``
``time-pos`` with milliseconds.
``time-start``
Deprecated. Always returns 0. Before mpv 0.14, this used to return the start
time of the file (could affect e.g. transport streams). See
@@ -1995,41 +1979,21 @@ Property list
Remaining length of the file in seconds. Note that the file duration is not
always exactly known, so this is an estimate.
This has a sub-property:
``time-remaining/full``
``time-remaining`` with milliseconds.
``audio-pts``
Current audio playback position in current file in seconds. Unlike time-pos,
this updates more often than once per frame. For audio-only files, it is
mostly equivalent to time-pos, while for video-only files this property is
not available.
This has a sub-property:
``audio-pts/full``
``audio-pts`` with milliseconds.
``playtime-remaining``
``time-remaining`` scaled by the current ``speed``.
This has a sub-property:
``playtime-remaining/full``
``playtime-remaining`` with milliseconds.
``playback-time`` (RW)
Position in current file in seconds. Unlike ``time-pos``, the time is
clamped to the range of the file. (Inaccurate file durations etc. could
make it go out of range. Useful on attempts to seek outside of the file,
as the seek target time is considered the current position during seeking.)
This has a sub-property:
``playback-time/full``
``playback-time`` with milliseconds.
``chapter`` (RW)
Current chapter number. The number of the first chapter is 0.
@@ -2441,28 +2405,12 @@ Property list
``video-params/dw``, ``video-params/dh``
Video size as integers, scaled for correct aspect ratio.
``video-params/crop-x``, ``video-params/crop-y``
Crop offset of the source video frame.
``video-params/crop-w``, ``video-params/crop-h``
Video size after cropping.
``video-params/aspect``
Display aspect ratio as float.
``video-params/aspect-name``
Display aspect ratio name as string. The name coresponds to motion
picture film format that introduced given aspect ratio in film.
``video-params/par``
Pixel aspect ratio.
``video-params/sar``
Storage aspect ratio.
``video-params/sar-name``
Storage aspect ratio name as string.
``video-params/colormatrix``
The colormatrix in use as string. (Exact values subject to change.)
@@ -2475,7 +2423,7 @@ Property list
``video-params/gamma``
The gamma function in use as string. (Exact values subject to change.)
``video-params/sig-peak`` (deprecated)
``video-params/sig-peak``
The video file's tagged signal peak as float.
``video-params/light``
@@ -2496,33 +2444,6 @@ Property list
(but in future releases, it could change to ``no``). If alpha is
present, this is set to ``straight`` or ``premul``.
``video-params/min-luma``
Minimum luminance, as reported by HDR10 metadata (in cd/m²)
``video-params/max-luma``
Maximum luminance, as reported by HDR10 metadata (in cd/m²)
``video-params/max-cll``
Maximum content light level, as reported by HDR10 metadata (in cd/m²)
``video-params/max-fall``
Maximum frame average light level, as reported by HDR10 metadata (in cd/m²)
``video-params/scene-max-r``
MaxRGB of a scene for R component, as reported by HDR10+ metadata (in cd/m²)
``video-params/scene-max-g``
MaxRGB of a scene for G component, as reported by HDR10+ metadata (in cd/m²)
``video-params/scene-max-b``
MaxRGB of a scene for B component, as reported by HDR10+ metadata (in cd/m²)
``video-params/max-pq-y``
Maximum PQ luminance of a frame, as reported by peak detection (in PQ, 0-1)
``video-params/avg-pq-y``
Average PQ luminance of a frame, as reported by peak detection (in PQ, 0-1)
When querying the property with the client API using ``MPV_FORMAT_NODE``,
or with Lua ``mp.get_property_native``, this will return a mpv_node with
the following contents:
@@ -2549,15 +2470,6 @@ Property list
"stereo-in" MPV_FORMAT_STRING
"average-bpp" MPV_FORMAT_INT64
"alpha" MPV_FORMAT_STRING
"min-luma" MPV_FORMAT_DOUBLE
"max-luma" MPV_FORMAT_DOUBLE
"max-cll" MPV_FORMAT_DOUBLE
"max-fall" MPV_FORMAT_DOUBLE
"scene-max-r" MPV_FORMAT_DOUBLE
"scene-max-g" MPV_FORMAT_DOUBLE
"scene-max-b" MPV_FORMAT_DOUBLE
"max-pq-y" MPV_FORMAT_DOUBLE
"avg-pq-y" MPV_FORMAT_DOUBLE
``dwidth``, ``dheight``
Video display size. This is the video size after filters and aspect scaling
@@ -2647,7 +2559,7 @@ Property list
is unavailable if no video is active.
When setting this property in the fullscreen or maximized state, the behavior
is the same as window-scale. In all other cases, setting the value of this
is the same as window-scale. In all ther cases, setting the value of this
property will always resize the window. This does not affect the value of
``window-scale``.
@@ -2670,6 +2582,11 @@ Property list
available on all platforms. Note that any of the listed facts may change
any time without a warning.
Writing to this property is deprecated. It has the same effect as writing to
``override-display-fps``. Since mpv 0.31.0, this property is unavailable
if no display FPS was reported (e.g. if no video is active), while in older
versions, it returned the ``--display-fps`` option value.
``estimated-display-fps``
The actual rate at which display refreshes seem to occur, measured by
system time. Only available if display-sync mode (as selected by
@@ -2689,6 +2606,13 @@ Property list
It may be saner to report an absolute DPI, however, this is the way HiDPI
support is implemented on most OS APIs. See also ``--hidpi-window-scale``.
``video-aspect`` (RW)
Deprecated. This is tied to ``--video-aspect-override``, but always
reports the current video aspect if video is active.
The read and write components of this option can be split up into
``video-params/aspect`` and ``video-aspect-override`` respectively.
``osd-width``, ``osd-height``
Last known OSD width (can be 0). This is needed if you want to use the
``overlay-add`` command. It gives you the actual OSD/window size (not
@@ -2728,10 +2652,6 @@ Property list
Any of these properties may be unavailable or set to dummy values if the
VO window is not created or visible.
``window-id``
Read-only - mpv's window id. May not always be available, i.e due to window
not being opened yet or not being supported by the VO.
``mouse-pos``
Read-only - last known mouse position, normalizd to OSD dimensions.
@@ -2746,16 +2666,13 @@ Property list
coordinates should be ignored when this value is false, because the
video backends update them only when the pointer hovers the window.
``sub-ass-extradata``
The current ASS subtitle track's extradata. There is no formatting done.
The extradata is returned as a string as-is. This property is not
available for non-ASS subtitle tracks.
``sub-text``
The current subtitle text regardless of sub visibility. Formatting is
stripped. If the subtitle is not text-based (i.e. DVD/BD subtitles), an
empty string is returned.
This property is experimental and might be removed in the future.
``sub-text-ass``
Like ``sub-text``, but return the text in ASS format. Text subtitles in
other formats are converted. For native ASS subtitles, events that do
@@ -2767,6 +2684,8 @@ Property list
header and per-event metadata are not returned. You likely need to do
further filtering on the returned string to make it useful.
This property is experimental and might be removed in the future.
``secondary-sub-text``
Same as ``sub-text``, but for the secondary subtitles.
@@ -2801,9 +2720,10 @@ Property list
(Before mpv 0.33.0, instead of returning -1, this property was unavailable
if no playlist entry was current.)
Writing the current value back to the property will have no effect.
Use ``playlist-play-index`` to restart the playback of the current entry if
desired.
Writing the current value back to the property is subject to change.
Currently, it will restart playback of the playlist entry. But in the
future, writing the current value will be ignored. Use the
``playlist-play-index`` command to get guaranteed behavior.
``playlist-pos-1`` (RW)
Same as ``playlist-pos``, but 1-based.
@@ -2844,11 +2764,6 @@ Property list
``playlist-count``
Number of total playlist entries.
``playlist-path``
The original path of the playlist for the current entry before mpv expanded
the entries. Unavailable if the file was not originally associated with a
playlist in some way.
``playlist``
Playlist, current entry marked. Currently, the raw property value is
useless.
@@ -2871,10 +2786,9 @@ Property list
entry, ``no``/false or unavailable otherwise.
``playlist/N/title``
Name of the Nth entry. Available if the playlist file contains
such fields and mpv's parser supports it for the given
playlist format, or if the playlist entry has been opened before and a
media-title other then then filename has been acquired.
Name of the Nth entry. Only available if the playlist file contains
such fields, and only if mpv's parser supports it for the given
playlist format.
``playlist/N/id``
Unique ID for this entry. This is an automatically assigned integer ID
@@ -2882,11 +2796,6 @@ Property list
instance. Other commands, events, etc. use this as ``playlist_entry_id``
fields.
``playlist/N/playlist-path``
The original path of the playlist for this entry before mpv expanded
it. Unavailable if the file was not originally associated with a playlist
in some way.
When querying the property with the client API using ``MPV_FORMAT_NODE``,
or with Lua ``mp.get_property_native``, this will return a mpv_node with
the following contents:
@@ -2984,12 +2893,6 @@ Property list
``track-list/N/demux-w``, ``track-list/N/demux-h``
Video size hint as indicated by the container. (Not always accurate.)
``track-list/N/demux-crop-x``, ``track-list/N/demux-crop-y``
Crop offset of the source video frame.
``track-list/N/demux-crop-w``, ``track-list/N/demux-crop-h``
Video size after cropping.
``track-list/N/demux-channel-count``
Number of audio channels as indicated by the container. (Not always
accurate - in particular, the track could be decoded as a different
@@ -3052,10 +2955,6 @@ Property list
"decoder-desc" MPV_FORMAT_STRING
"demux-w" MPV_FORMAT_INT64
"demux-h" MPV_FORMAT_INT64
"demux-crop-x" MPV_FORMAT_INT64
"demux-crop-y" MPV_FORMAT_INT64
"demux-crop-w" MPV_FORMAT_INT64
"demux-crop-h" MPV_FORMAT_INT64
"demux-channel-count" MPV_FORMAT_INT64
"demux-channels" MPV_FORMAT_STRING
"demux-samplerate" MPV_FORMAT_INT64
@@ -3174,7 +3073,7 @@ Property list
`Flat command syntax`_ for details.
A list of tags can be found here:
https://aegisub.org/docs/latest/ass_tags/
https://aeg-dev.github.io/AegiSite/docs/3.2/ass_tags/
``vo-configured``
Whether the VO is configured right now. Usually this corresponds to whether
@@ -3350,24 +3249,6 @@ Property list
(There is no way to ensure synchronization if two scripts try to update the
same key at the same time.)
``user-data`` (RW)
This is a recursive key/value map of arbitrary nodes shared between clients for
general use (i.e. scripts, IPC clients, host applications, etc).
The player itself does not use any data in it (although some builtin scripts may).
The property is not preserved across player restarts.
This is a more powerful replacement for ``shared-script-properties``.
Sub-paths can be accessed directly; e.g. ``user-data/my-script/state/a`` can be
read, written, or observed.
The top-level object itself cannot be written directly; write to sub-paths instead.
Converting this property or its sub-properties to strings will give a JSON
representation. If converting a leaf-level object (i.e. not a map or array)
and not using raw mode, the underlying content will be given (e.g. strings will be
printed directly, rather than quoted and JSON-escaped).
``working-directory``
The working directory of the mpv process. Can be useful for JSON IPC users,
because the command line player usually works with relative paths.
@@ -3423,9 +3304,8 @@ Property list
might contain either a release version, or just a git hash.
``mpv-configuration``
The configuration arguments that were passed to the build system. If the
meson version used to compile mpv is older than 1.1.0, then a hardcoded
string of a few, arbitrary options is displayed instead.
The configuration arguments which were passed to the build system
(typically the way ``./waf configure ...`` was invoked).
``ffmpeg-version``
The contents of the ``av_version_info()`` API call. This is a string which
@@ -3439,12 +3319,6 @@ Property list
somewhat weird form (apparently "hex BCD"), indicating the release version
of the libass library linked to mpv.
``platform``
Returns a string describing what target platform mpv was built for. The value
of this is dependent on what the underlying build system detects. Some of the
most common values are: ``windows``, ``darwin`` (macos or ios), ``linux``,
``android``, and ``freebsd``. Note that this is not a complete listing.
``options/<name>`` (RW)
The value of option ``--<name>``. Most options can be changed at runtime by
writing to this property. Note that many options require reloading the file
+2 -2
View File
@@ -193,8 +193,8 @@ And this is the completion:
{"request_id":123,"error":"success","data":null}
By design, you will not get a confirmation that the command was started. If a
command is long running, sending the message will not lead to any reply until
much later when the command finishes.
command is long running, sending the message will lead to any reply until much
later when the command finishes.
Some commands execute synchronously, but these will behave like asynchronous
commands that finished execution immediately.
-2
View File
@@ -95,8 +95,6 @@ success, ``error`` is empty string on success.
``mp.abort_async_command(id)``
``mp.del_property(name)`` (LE)
``mp.get_property(name [,def])`` (LE)
``mp.get_property_osd(name [,def])`` (LE)
+4 -8
View File
@@ -21,15 +21,15 @@ C PLUGINS
You can write C plugins for mpv. These use the libmpv API, although they do not
use the libmpv library itself.
They are enabled by default if compiler supports linking with the ``-rdynamic``
flag on Linux/BSD platforms. On Windows the are always enabled.
They are available on Linux/BSD platforms only and enabled by default if the
compiler supports linking with the ``-rdynamic`` flag.
C plugins location
------------------
C plugins are put into the mpv scripts directory in its config directory
(see the `FILES`_ section for details). They must have a ``.so`` or ``.dll``
file extension. They can also be explicitly loaded with the ``--script`` option.
(see the `FILES`_ section for details). They must have a ``.so`` file extension.
They can also be explicitly loaded with the ``--script`` option.
API
---
@@ -67,10 +67,6 @@ The current implementation requires that your plugins are **not** linked against
libmpv. What your plugins use are not symbols from a libmpv binary, but
symbols from the mpv host binary.
On Windows to make symbols from the host binary available, you have to define
MPV_CPLUGIN_DYNAMIC_SYM when compiling cplugin. This will load symbols
dynamically, before calling ``mpv_open_cplugin()``.
Examples
--------
+7 -17
View File
@@ -62,15 +62,15 @@ that uses the ``.foo`` file extension.
mpv also appends the top level directory of the script to the start of Lua's
package path so you can import scripts from there too. Be aware that this will
shadow Lua libraries that use the same package path. (Single file scripts do not
include mpv specific directories in the Lua package path. This was silently
changed in mpv 0.32.0.)
include mpv specific directory the Lua package path. This was silently changed
in mpv 0.32.0.)
Using a script directory is the recommended way to package a script that
consists of multiple source files, or requires other files (you can use
``mp.get_script_directory()`` to get the location and e.g. load data files).
Making a script a git repository, basically a repository which contains a
``main.lua`` file in the root directory, makes scripts easily updateable
``main.lua``` file in the root directory, makes scripts easily updateable
(without the dangers of auto-updates). Another suggestion is to use git
submodules to share common files or libraries.
@@ -199,12 +199,6 @@ The ``mp`` module is preloaded, although it can be loaded manually with
Whether this works and how long it takes depends on the command and the
situation. The abort call itself is asynchronous. Does not return anything.
``mp.del_property(name)``
Delete the given property. See ``mp.get_property`` and `Properties`_ for more
information about properties. Most properties cannot be deleted.
Returns true on success, or ``nil, error`` on error.
``mp.get_property(name [,def])``
Return the value of the given property as string. These are the same
properties as used in input.conf. See `Properties`_ for a list of
@@ -438,21 +432,17 @@ The ``mp`` module is preloaded, although it can be loaded manually with
that are equal to the ``fn`` parameter. This uses normal Lua ``==``
comparison, so be careful when dealing with closures.
``mp.add_timeout(seconds, fn [, disabled])``
``mp.add_timeout(seconds, fn)``
Call the given function fn when the given number of seconds has elapsed.
Note that the number of seconds can be fractional. For now, the timer's
resolution may be as low as 50 ms, although this will be improved in the
future.
If the ``disabled`` argument is set to ``true`` or a truthy value, the
timer will wait to be manually started with a call to its ``resume()``
method.
This is a one-shot timer: it will be removed when it's fired.
Returns a timer object. See ``mp.add_periodic_timer`` for details.
``mp.add_periodic_timer(seconds, fn [, disabled])``
``mp.add_periodic_timer(seconds, fn)``
Call the given function periodically. This is like ``mp.add_timeout``, but
the timer is re-added after the function fn is run.
@@ -684,13 +674,13 @@ with values found in the config-file and the command-line (in that order).
Example implementation::
require 'mp.options'
local options = {
optionA = "defaultvalueA",
optionB = -0.5,
optionC = true,
}
require "mp.options".read_options(options, "myscript")
read_options(options, "myscript")
print(options.optionA)
+69 -113
View File
@@ -64,7 +64,7 @@ Ctrl+LEFT and Ctrl+RIGHT
Seek to the previous/next subtitle. Subject to some restrictions and
might not always work; see ``sub-seek`` command.
Ctrl+Shift+LEFT and Ctrl+Shift+RIGHT
Ctrl+Shift+Left and Ctrl+Shift+Right
Adjust subtitle delay so that the next or previous subtitle is displayed
now. This is especially useful to sync subtitles to audio.
@@ -94,7 +94,7 @@ Shift+Ctrl+BACKSPACE
ENTER
Go forward in the playlist.
p and SPACE
p / SPACE
Pause (pressing again unpauses).
\.
@@ -144,7 +144,7 @@ w and W
Decrease/increase pan-and-scan range. The ``e`` key does the same as
``W`` currently, but use is discouraged.
o and P
o (also P)
Show progression bar, elapsed time and total duration on the OSD.
O
@@ -166,16 +166,16 @@ l
L
Toggle infinite looping.
Ctrl++ and Ctrl+-
Ctrl + and Ctrl -
Adjust audio delay (A/V sync) by +/- 0.1 seconds.
Shift+g and Shift+f
Adjust subtitle font size by +/- 10%.
u
Switch between applying only ``--sub-ass-*`` overrides (default) to SSA/ASS
subtitles, and overriding them almost completely with the normal subtitle
style. See ``--sub-ass-override`` for more info.
Switch between applying no style overrides to SSA/ASS subtitles, and
overriding them almost completely with the normal subtitle style. See
``--sub-ass-override`` for more info.
V
Toggle subtitle VSFilter aspect compatibility mode. See
@@ -192,7 +192,7 @@ S
Take a screenshot, without subtitles. (Whether this works depends on VO
driver support.)
Ctrl+s
Ctrl s
Take a screenshot, as the window shows it (with subtitles, OSD, and scaled
video).
@@ -211,13 +211,13 @@ d
A
Cycle aspect ratio override.
Ctrl+h
Ctrl h
Toggle hardware video decoding on/off.
Alt+LEFT, Alt+RIGHT, Alt+UP, Alt+DOWN
Move the video rectangle (panning).
Alt++ and Alt+-
Alt + and Alt -
Combining ``Alt`` with the ``+`` or ``-`` keys changes video zoom.
Alt+BACKSPACE
@@ -236,7 +236,7 @@ i and I
file such as codec, framerate, number of dropped frames and so on. See
`STATS`_ for more information.
DEL
del
Cycle OSC visibility between never / auto (mouse-move) / always
\`
@@ -257,16 +257,16 @@ corresponding adjustment.)
7 and 8
Adjust saturation.
Alt+0 (and Command+0 on macOS)
Alt+0 (and command+0 on macOS)
Resize video window to half its original size.
Alt+1 (and Command+1 on macOS)
Alt+1 (and command+1 on macOS)
Resize video window to its original size.
Alt+2 (and Command+2 on macOS)
Alt+2 (and command+2 on macOS)
Resize video window to double its original size.
Command + f (macOS only)
command + f (macOS only)
Toggle fullscreen (see also ``--fs``).
(The following keys are valid if you have a keyboard with multimedia keys.)
@@ -280,8 +280,6 @@ STOP
PREVIOUS and NEXT
Seek backward/forward 1 minute.
ZOOMIN and ZOOMOUT
Changes video zoom.
If you miss some older key bindings, look at ``etc/restore-old-bindings.conf``
in the mpv git repository.
@@ -299,10 +297,10 @@ Forward/Back button
Skip to next/previous entry in playlist.
Wheel up/down
Decrease/increase volume.
Seek forward/backward 10 seconds.
Wheel left/right
Seek forward/backward 10 seconds.
Decrease/increase volume.
USAGE
@@ -463,10 +461,6 @@ Name Meaning
``~~desktop/`` the path to the desktop (win32, macOS)
``~~exe_dir/`` win32 only: the path to the directory containing the exe (for
config file purposes; ``$MPV_HOME`` overrides it)
``~~cache/`` the path to application cache data (``~/.cache/mpv/``)
On some platforms, this will be the same as ``~~home/``.
``~~state/`` the path to application state data (``~/.local/state/mpv/``)
On some platforms, this will be the same as ``~~home/``.
``~~old_home/`` do not use
================ ===============================================================
@@ -525,8 +519,10 @@ String list and path list options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String lists are separated by ``,``. The strings are not parsed or interpreted
by the option system itself. However, most path or file list options use ``:``
(Unix) or ``;`` (Windows) as separator, instead of ``,``.
by the option system itself. However, most
Path or file list options use ``:`` (Unix) or ``;`` (Windows) as separator,
instead of ``,``.
They support the following operations:
@@ -539,6 +535,7 @@ Suffix Meaning
-pre Prepend 1 or more items (same syntax as -set)
-clr Clear the option (remove all items)
-remove Delete item if present (does not interpret escapes)
-del Delete 1 or more items by integer index (deprecated)
-toggle Append an item, or remove if if it already exists (no escapes)
============= ===============================================
@@ -567,7 +564,7 @@ Keys are unique within the list. If an already present key is set, the existing
key is removed before the new value is appended.
If you want to pass a value without interpreting it for escapes or ``,``, it is
recommended to use the ``-append`` variant. When using libmpv, prefer using
recommended to use the ``-add`` variant. When using libmpv, prefer using
``MPV_FORMAT_NODE_MAP``; when using a scripting backend or the JSON IPC, use an
appropriate structured data type.
@@ -589,6 +586,7 @@ Suffix Meaning
-pre Prepend 1 or more filters (same syntax as -set)
-clr Clear the option (remove all filters)
-remove Delete filter if present
-del Delete 1 or more filters by integer index or filter label (deprecated)
-toggle Append a filter, or remove if if it already exists
-help Pseudo operation that prints a help text to the terminal
============= ===============================================
@@ -625,34 +623,30 @@ user-specific one is ``~/.config/mpv/mpv.conf``. For details and platform
specifics (in particular Windows paths) see the `FILES`_ section.
User-specific options override system-wide options and options given on the
command line override both. The syntax of the configuration files is
``option=value``. Everything after a *#* is considered a comment. Options that
work without values can be enabled by setting them to *yes* and disabled by
setting them to *no*, and if the value is omitted, *yes* is implied. Even
suboptions can be specified in this way.
command line override either. The syntax of the configuration files is
``option=value``. Everything after a *#* is considered a comment. Options
that work without values can be enabled by setting them to *yes* and disabled by
setting them to *no*. Even suboptions can be specified in this way.
.. admonition:: Example configuration file
::
# Don't allow new windows to be larger than the screen.
autofit-larger=100%x100%
# Enable hardware decoding if available, =yes is implied.
hwdec
# Spaces don't have to be escaped.
osd-playing-msg=File: ${filename}
# Use GPU-accelerated video output by default.
vo=gpu
# Use quotes for text that can contain spaces:
term-status-msg="Time: ${time-pos}"
Escaping special characters
Escaping spaces and special characters
--------------------------------------
This is done like with command line options. A config entry can be quoted with
``"``, ``'``, as well as with the fixed-length syntax (``%n%``) mentioned
before. This is like passing the exact contents of the quoted string as a
command line option. C-style escapes are currently _not_ interpreted on this
level, although some options do this manually (this is a mess and should
probably be changed at some point). The shell is not involved here, so option
values only need to be quoted to escape ``#`` and ``\``, ``"``, ``'`` or ``%``
at the beginning of the value, and leading and trailing whitespace.
This is done like with command line options. The shell is not involved here,
but option values still need to be quoted as a whole if it contains certain
characters like spaces. A config entry can be quoted with ``"``,
as well as with the fixed-length syntax (``%n%``) mentioned before. This is like
passing the exact contents of the quoted string as command line option. C-style
escapes are currently _not_ interpreted on this level, although some options do
this manually. (This is a mess and should probably be changed at some point.)
Putting Command Line Options into the Configuration File
--------------------------------------------------------
@@ -666,7 +660,7 @@ Option Configuration file entry
``--flag`` ``flag``
``-opt val`` ``opt=val``
``--opt=val`` ``opt=val``
``-opt "has spaces"`` ``opt=has spaces``
``-opt "has spaces"`` ``opt="has spaces"``
======================= ========================
File-specific Configuration Files
@@ -716,7 +710,7 @@ or at runtime with the ``apply-profile <name>`` command.
[slow]
profile-desc="some profile name"
# reference a builtin profile
profile=high-quality
profile=gpu-hq
[fast]
vo=vdpau
@@ -799,10 +793,9 @@ Conditional auto profiles
Profiles which have the ``profile-cond`` option set are applied automatically
if the associated condition matches (unless auto profiles are disabled). The
option takes a string, which is interpreted as Lua expression. If the
expression evaluates as truthy, the profile is applied. If the expression
errors or evaluates as falsy, the profile is not applied. This Lua code
execution is not sandboxed.
option takes a string, which is interpreted as Lua condition. If evaluating the
expression returns true, the profile is applied, if it returns false, it is
ignored. This Lua code execution is not sandboxed.
Any variables in condition expressions can reference properties. If an
identifier is not already defined by Lua or mpv, it is interpreted as property.
@@ -823,13 +816,13 @@ cause errors if used in expressions. These are logged in verbose mode, and the
expression is considered to be false.
Whenever a property referenced by a profile condition changes, the condition
is re-evaluated. If the return value of the condition changes from falsy or
error to truthy, the profile is applied.
is re-evaluated. If the return value of the condition changes from false or
error to true, the profile is applied.
This mechanism tries to "unapply" profiles once the condition changes from
truthy to falsy or error. If you want to use this, you need to set
``profile-restore`` for the profile. Another possibility it to create another
profile with an inverse condition to undo the other profile.
This mechanism tries to "unapply" profiles once the condition changes from true
to false. If you want to use this, you need to set ``profile-restore`` for the
profile. Another possibility it to create another profile with an inverse
condition to undo the other profile.
Recursive profiles can be used. But it is discouraged to reference other
conditional profiles in a conditional profile, since this can lead to tricky
@@ -846,14 +839,6 @@ and unintuitive behavior.
profile-cond=width >= 1280
hue=-50
Make only videos containing "youtube" or "youtu.be" in their path brighter:
::
[youtube]
profile-cond=path:find('youtu%.?be')
gamma=20
If you want the profile to be reverted if the condition goes to false again,
you can set ``profile-restore``:
@@ -1092,8 +1077,7 @@ this with ``--untimed``, but it will likely break, unless the stream has no
audio, and the input feeds data to the player at a constant rate.
Another common problem is with MJPEG streams. These do not signal the correct
framerate. Using ``--untimed`` or ``--no-correct-pts --container-fps-override=60``
might help.
framerate. Using ``--untimed`` or ``--no-correct-pts --fps=60`` might help.
For livestreams, data can build up due to pausing the stream, due to slightly
lower playback rate, or "buffering" pauses. If the demuxer cache is enabled,
@@ -1127,7 +1111,7 @@ course mpv is terminated abruptly and doesn't have the time to save (e.g. with
the KILL Unix signal).
mpv also stores options other than the playback position when they have been
modified after playback began, for example the volume and selected audio/subtitles,
modified after playback began, for example the volume and the fullscreen state,
and restores their values the next time the file is played. Which options are
saved can be configured with the ``--watch-later-options`` option.
@@ -1373,6 +1357,9 @@ A common problem is that Linux desktop environments ignore the standard
screensaver APIs on which mpv relies. In particular, mpv uses the Screen Saver
extension (XSS) on X11, and the idle-inhibit protocol on Wayland.
GNOME in particular still ignores the idle-inhibit protocol, and has its own
D-Bus interfaces for display power management, which mpv does not support.
Before mpv 0.33.0, the X11 backend ran ``xdg-screensaver reset`` in 10 second
intervals when not paused in order to support screensaver inhibition in these
environments. This functionality was removed in 0.33.0, but it is possible to
@@ -1539,7 +1526,6 @@ input command can take an exit code: in this case, that exit code is returned.
FILES
=====
Note that this section assumes Linux/BSD. On other platforms the paths may be different.
For Windows-specifics, see `FILES ON WINDOWS`_ section.
``/usr/local/etc/mpv/mpv.conf``
@@ -1547,18 +1533,6 @@ For Windows-specifics, see `FILES ON WINDOWS`_ section.
in default configuration will use ``/usr/local/etc/mpv/`` as config
directory, while most Linux distributions will set it to ``/etc/mpv/``).
``~/.cache/mpv``
The standard cache directory. Certain options within mpv may cause it to write
cache files to disk. This can be overridden by environment variables, in
ascending order:
:1: If ``$XDG_CACHE_HOME`` is set, then the derived cache directory
will be ``$XDG_CACHE_HOME/mpv``.
:2: If ``$MPV_HOME`` is set, then the derived cache directory will be
``$MPV_HOME``.
If the directory does not exist, mpv will try to create it automatically.
``~/.config/mpv``
The standard configuration directory. This can be overridden by environment
variables, in ascending order:
@@ -1573,9 +1547,7 @@ For Windows-specifics, see `FILES ON WINDOWS`_ section.
``~/.mpv/``
The original (pre 0.5.0) configuration directory. It will continue to be
read if present. If this directory is present and the standard configuration
directory is not present, then cache files and watch later config files will
also be written to this directory.
read if present.
If both this directory and the standard configuration directory are
present, configuration will be read from both with the standard
@@ -1600,8 +1572,11 @@ For Windows-specifics, see `FILES ON WINDOWS`_ section.
fallback subtitle font
``~/.config/mpv/fonts/``
Default location for ``--sub-fonts-dir`` (see `Subtitles`_) and
``--osd-fonts-dir`` (see `OSD`_).
Font files in this directory are used by mpv/libass for subtitles. Useful
if you do not want to install fonts to your system. Note that files in this
directory are loaded into memory before being used by mpv. If you have a
lot of fonts, consider using fonts.conf (see above) to include additional
fonts, which is more memory-efficient.
``~/.config/mpv/scripts/``
All files in this directory are loaded as if they were passed to the
@@ -1611,18 +1586,11 @@ For Windows-specifics, see `FILES ON WINDOWS`_ section.
See `Script location`_ for details.
``~/.local/state/mpv/watch_later/``
``~/.config/mpv/watch_later/``
Contains temporary config files needed for resuming playback of files with
the watch later feature. See for example the ``Q`` key binding, or the
``quit-watch-later`` input command.
This can be overridden by environment variables, in ascending order:
:1: If ``$XDG_STATE_HOME`` is set, then the derived watch later directory
will be ``$XDG_STATE_HOME/mpv/watch_later``.
:2: If ``$MPV_HOME`` is set, then the derived watch later directory will be
``$MPV_HOME/watch_later``.
Each file is a small config file which is loaded if the corresponding media
file is loaded. It contains the playback position and some (not necessarily
all) settings that were changed during playback. The filenames are hashed
@@ -1653,20 +1621,16 @@ You can find the exact path by running ``echo %APPDATA%\mpv\mpv.conf`` in cmd.ex
Other config files (such as ``input.conf``) are in the same directory. See the
`FILES`_ section above.
The cache directory is located at ``%LOCALAPPDATA%/mpv/cache``.
The watch_later directory is located at ``%LOCALAPPDATA%/mpv/watch_later``.
The environment variable ``$MPV_HOME`` completely overrides these, like on
UNIX.
If a directory named ``portable_config`` next to the mpv.exe exists, all
config will be loaded from this directory only. Watch later config files and
cache files are written to this directory as well. (This exists on Windows
only and is redundant with ``$MPV_HOME``. However, since Windows is very
scripting unfriendly, a wrapper script just setting ``$MPV_HOME``, like you
could do it on other systems, won't work. ``portable_config`` is provided for
convenience to get around this restriction.)
config will be loaded from this directory only. Watch later config files are
written to this directory as well. (This exists on Windows only and is redundant
with ``$MPV_HOME``. However, since Windows is very scripting unfriendly, a
wrapper script just setting ``$MPV_HOME``, like you could do it on other
systems, won't work. ``portable_config`` is provided for convenience to get
around this restriction.)
Config files located in the same directory as ``mpv.exe`` are loaded with
lower priority. Some config files are loaded only once, which means that
@@ -1680,11 +1644,3 @@ future.
Note that mpv likes to mix ``/`` and ``\`` path separators for simplicity.
kernel32.dll accepts this, but cmd.exe does not.
FILES ON MACOS
==============
On macOS the watch later directory is located at ``~/.config/mpv/watch_later/``
and the cache directory is set to ``~/Library/Caches/io.mpv/``. These directories
can't be overwritten by enviroment variables.
Everything else is the same as `FILES`_.
+333 -611
View File
File diff suppressed because it is too large. Load diff
+1 -10
View File
@@ -85,7 +85,6 @@ seekbar
============= ================================================
left-click seek to position
mouse wheel seek forward/backward
============= ================================================
time left
@@ -102,7 +101,6 @@ audio and sub
left-click cycle audio/sub tracks forward
right-click cycle audio/sub tracks backwards
shift+L-click show available audio/sub tracks
mouse wheel cycle audio/sub tracks forward/backwards
============= ================================================
vol
@@ -176,7 +174,7 @@ Configurable Options
``seekbarhandlesize``
Default: 0.6
Size ratio of the seek handle if ``seekbarstyle`` is set to ``diamond``
Size ratio of the seek handle if ``seekbarstyle`` is set to ``dimaond``
or ``knob``. This is relative to the full height of the seekbar.
``seekbarkeyframes``
@@ -312,13 +310,6 @@ Configurable Options
Show total time instead of time remaining
``remaining_playtime``
Default: yes
Whether the time-remaining display takes speed into account.
``yes`` - how much playback time remains at the current speed.
``no`` - how much video-time remains.
``timems``
Default: no
+11 -9
View File
@@ -35,6 +35,13 @@ UP Scroll one line up
DOWN Scroll one line down
==== ==================
Font
~~~~
For optimal visual experience, a font with support for many font weights and
monospaced digits is recommended. By default, the open source font
`Source Sans Pro <https://github.com/adobe-fonts/source-sans-pro>`_ is used.
Configuration
-------------
@@ -97,24 +104,19 @@ Configurable Options
Show graphs for vsync and jitter values (page 1). Only when toggled.
``plot_tonemapping_lut``
Default: no
Enable tone-mapping LUT visualization automatically. Only when toggled.
``flush_graph_data``
Default: yes
Clear data buffers used for drawing graphs when toggling.
``font``
Default: sans-serif
Default: Source Sans Pro
Font name. Should support as many font weights as possible for optimal
visual experience.
``font_mono``
Default: monospace
Default: Source Sans Pro
Font name for parts where monospaced characters are necessary to align
text. Currently, monospaced digits are sufficient.
@@ -225,8 +227,8 @@ are missing.
Memory usage is approximate and does not reflect internal fragmentation.
JS scripts memory reporting is disabled by default because collecting the data
at the JS side has an overhead and will increase memory usage. It can be
enabled by setting the ``--js-memory-report`` option before starting mpv.
at the JS side has an overhead. It can be enabled by exporting the env var
``MPV_LEAK_REPORT=1`` before starting mpv, and will increase JS memory usage.
If entries have ``/time`` and ``/cpu`` variants, the former gives the real time
(monotonic clock), while the latter the thread CPU time (only if the
+14 -21
View File
@@ -44,17 +44,19 @@ The exact syntax is:
the ``lavfi`` filter, which uses a very similar syntax as mpv (MPlayer
historically) to specify filters and their parameters.
.. note::
``--vf`` can only take a single track as input, even if the filter supports
dynamic input. Filters that require multiple inputs can't be used.
Use ``--lavfi-complex`` for such a use case. This also applies for ``--af``.
Filters can be manipulated at run time. You can use ``@`` labels as described
above in combination with the ``vf`` command (see `COMMAND INTERFACE`_) to get
more control over this. Initially disabled filters with ``!`` are useful for
this as well.
You can also set defaults for each filter. The defaults are applied before the
normal filter parameters. This is deprecated and never worked for the
libavfilter bridge.
``--vf-defaults=<filter1[=parameter1:parameter2:...],filter2,...>``
Set defaults for each filter. (Deprecated. ``--af-defaults`` is deprecated
as well.)
.. note::
To get a full list of available video filters, see ``--vf=help`` and
@@ -108,6 +110,11 @@ filter list.
from the list if it was present. Matching of filters works as described in
``--vf-remove``.
``--vf-del=filter``
Sort of like ``--vf-remove``, but also accepts an index number. Index
numbers start at 0, negative numbers address the end of the list (-1 is the
last). Deprecated.
``--vf-clr``
Completely empties the filter list.
@@ -134,11 +141,6 @@ Available mpv-only filters are:
For a list of available formats, use ``--vf=format=fmt=help``.
.. note::
Conversion between hardware formats is supported in some cases.
eg: ``cuda`` to ``vulkan``, or ``vaapi`` to ``vulkan``.
``<convert=yes|no>``
Force conversion of color parameters (default: no).
@@ -162,9 +164,6 @@ Available mpv-only filters are:
If input and output video parameters are the same, conversion is always
skipped.
When converting between hardware formats, this parameter has no effect,
and the only conversion that is done is the format conversion.
.. admonition:: Examples
``mpv test.mkv --vf=format:colormatrix=ycgco``
@@ -569,19 +568,13 @@ Available mpv-only filters are:
completely broken (e.g. 0 or NaN). Even if the value is correct,
if another filter changes the real FPS (by dropping or inserting
frames), the value of this variable will not be useful. Note that
the ``--container-fps-override`` command line option overrides this value.
the ``--fps`` command line option overrides this value.
Useful for some filters which insist on having a FPS.
``display_fps``
Refresh rate of the current display. Note that this value can be 0.
``display_res``
Resolution of the current display. This is an integer array with the
first entry corresponding to the width and the second entry coresponding
to the height. These values can be 0. Note that this will not respond to
monitor changes and may not work on all platforms.
``vavpp``
VA-API video post processing. Requires the system to support VA-API,
i.e. Linux/BSD only. Works with ``--vo=vaapi`` and ``--vo=gpu`` only.
+56 -76
View File
@@ -28,11 +28,10 @@ Available video output drivers are:
See `GPU renderer options`_ for options specific to this VO.
By default, mpv utilizes settings that balance quality and performance.
Additionally, two predefined profiles are available: ``fast`` for maximum
performance and ``high-quality`` for superior rendering quality. You can
apply a specific profile using the ``--profile=<name>`` option and inspect
its contents using ``--show-profile=<name>``.
By default, it tries to use fast and fail-safe settings. Use the
``gpu-hq`` profile to use this driver with defaults set to high quality
rendering. The profile can be applied with ``--profile=gpu-hq`` and its
contents can be viewed with ``--show-profile=gpu-hq``.
This VO abstracts over several possible graphics APIs and windowing
contexts, which can be influenced using the ``--gpu-api`` and
@@ -292,12 +291,9 @@ Available video output drivers are:
or VA API hardware decoding. The driver is designed to avoid any GPU to CPU copies,
and to perform scaling and color space conversion using fixed-function hardware,
if available, rather than GPU shaders. This frees up GPU resources for other tasks.
It is highly recommended to use this VO with the appropriate ``--hwdec`` option such
as ``auto-safe``. It can still work in some circumstances without ``--hwdec`` due to
mpv's internal conversion filters, but this is not recommended as it's a needless
extra step. Correct output depends on support from your GPU, drivers, and compositor.
Weston and wlroots-based compositors like Sway and Intel GPUs are known to generally
work.
Currently this driver is experimental and only works with the ``--hwdec=vaapi``
or ``hwdec=drm`` drivers;
OSD is also not supported. Supported compositors : Weston and Sway.
``vaapi``
Intel VA API video output driver with support for hardware decoding. Note
@@ -317,6 +313,25 @@ Available video output drivers are:
nla
``non-linear anamorphic scaling``
``--vo-vaapi-deint-mode=<mode>``
Select deinterlacing algorithm. Note that by default deinterlacing is
initially always off, and needs to be enabled with the ``d`` key
(default key binding for ``cycle deinterlace``).
This option doesn't apply if libva supports video post processing (vpp).
In this case, the default for ``deint-mode`` is ``no``, and enabling
deinterlacing via user interaction using the methods mentioned above
actually inserts the ``vavpp`` video filter. If vpp is not actually
supported with the libva backend in use, you can use this option to
forcibly enable VO based deinterlacing.
no
Don't allow deinterlacing (default for newer libva).
first-field
Show only first field.
bob
bob deinterlacing (default for older libva).
``--vo-vaapi-scaled-osd=<yes|no>``
If enabled, then the OSD is rendered at video resolution and scaled to
display resolution. By default, this is disabled, and the OSD is
@@ -368,54 +383,13 @@ Available video output drivers are:
``--vo-tct-256=<yes|no>`` (default: no)
Use 256 colors - for terminals which don't support true color.
``kitty``
Graphical output for the terminal, using the kitty graphics protocol.
Tested with kitty and Konsole.
You may need to use ``--profile=sw-fast`` to get decent performance.
Kitty size and alignment options:
``--vo-kitty-cols=<columns>``, ``--vo-kitty-rows=<rows>`` (default: 0)
Specify the terminal size in character cells, otherwise (0) read it
from the terminal, or fall back to 80x25.
``--vo-kitty-width=<width>``, ``--vo-kitty-height=<height>`` (default: 0)
Specify the available size in pixels, otherwise (0) read it from the
terminal, or fall back to 320x240.
``--vo-kitty-left=<col>``, ``--vo-kitty-top=<row>`` (default: 0)
Specify the position in character cells where the image starts (1 is
the first column or row). If 0 (default) then try to automatically
determine it according to the other values and the image aspect ratio
and zoom.
``--vo-kitty-config-clear=<yes|no>`` (default: yes)
Whether or not to clear the terminal whenever the output is
reconfigured (e.g. when video size changes).
``--vo-kitty-alt-screen=<yes|no>`` (default: yes)
Whether or not to use the alternate screen buffer and return the
terminal to its previous state on exit. When set to no, the last
kitty image stays on screen after quit, with the cursor following it.
``--vo-kitty-use-shm=<yes|no>`` (default: no)
Use shared memory objects to transfer image data to the terminal.
This is much faster than sending the data as escape codes, but is not
supported by as many terminals. It also only works on the local machine
and not via e.g. SSH connections.
This option is not implemented on Windows.
``sixel``
Graphical output for the terminal, using sixels. Tested with ``mlterm`` and
``xterm``.
Note: the Sixel image output is not synchronized with other terminal
output from mpv, which can lead to broken images.
The option ``--really-quiet`` can help with that, and is recommended.
On some platforms, using the ``--vo-sixel-buffered`` option may work as
well.
Note: the Sixel image output is not synchronized with other terminal output
from mpv, which can lead to broken images. The option ``--really-quiet``
can help with that, and is recommended.
You may need to use ``--profile=sw-fast`` to get decent performance.
@@ -459,25 +433,10 @@ Available video output drivers are:
to take into account padding at the report - this only works correctly
when the overall padding per axis is smaller than the number of cells.
``--vo-sixel-config-clear=<yes|no>`` (default: yes)
Whether or not to clear the terminal whenever the output is
reconfigured (e.g. when video size changes).
``--vo-sixel-alt-screen=<yes|no>`` (default: yes)
Whether or not to use the alternate screen buffer and return the
terminal to its previous state on exit. When set to no, the last
``--vo-sixel-exit-clear=<yes|no>`` (default: yes)
Whether or not to clear the terminal on quit. When set to no - the last
sixel image stays on screen after quit, with the cursor following it.
``--vo-sixel-exit-clear`` is a deprecated alias for this option and
may be removed in the future.
``--vo-sixel-buffered=<yes|no>`` (default: no)
Buffers the full output sequence before writing it to the terminal.
On POSIX platforms, this can help prevent interruption (including from
other applications) and thus broken images, but may come at a
performance cost with some terminals and is subject to implementation
details.
Sixel image quality options:
``--vo-sixel-dither=<algo>``
@@ -570,7 +529,14 @@ Available video output drivers are:
``rpi`` (Raspberry Pi)
Native video output on the Raspberry Pi using the MMAL API.
The following global options are supported by this video output:
This is deprecated. Use ``--vo=gpu`` instead, which is the default and
provides the same functionality. The ``rpi`` VO will be removed in
mpv 0.23.0. Its functionality was folded into --vo=gpu, which now uses
RPI hardware decoding by treating it as a hardware overlay (without applying
GL filtering). Also to be changed in 0.23.0: the --fs flag will be reset to
"no" by default (like on the other platforms).
The following deprecated global options are supported by this video output:
``--rpi-display=<number>``
Select the display number on which the video overlay should be shown
@@ -602,15 +568,18 @@ Available video output drivers are:
The following global options are supported by this video output:
``--drm-connector=<name>``
``--drm-connector=[<gpu_number>.]<name>``
Select the connector to use (usually this is a monitor.) If ``<name>``
is empty or ``auto``, mpv renders the output on the first available
connector. Use ``--drm-connector=help`` to get a list of available
connectors. (default: empty)
connectors. The ``<gpu_number>`` argument can be used to disambiguate
multiple graphic cards, but is deprecated in favor of ``--drm-device``.
(default: empty)
``--drm-device=<path>``
Select the DRM device file to use. If specified this overrides automatic
card selection. (default: empty)
card selection and any card number specified ``--drm-connector``.
(default: empty)
``--drm-mode=<preferred|highest|N|WxH[@R]>``
Mode to use (resolution and frame rate).
@@ -628,6 +597,16 @@ Available video output drivers are:
Use ``--drm-mode=help`` to get a list of available modes for all active
connectors.
``--drm-atomic=<no|auto>``
Toggle use of atomic modesetting. Mostly useful for debugging.
:no: Use legacy modesetting.
:auto: Use atomic modesetting, falling back to legacy modesetting if
not available. (default)
Note: Only affects ``gpu-context=drm``. ``vo=drm`` supports legacy
modesetting only.
``--drm-draw-plane=<primary|overlay|N>``
Select the DRM plane to which video and OSD is drawn to, under normal
circumstances. The plane can be specified as ``primary``, which will
@@ -674,6 +653,7 @@ Available video output drivers are:
just cause the video to get rendered at a different resolution and then
scaled to screen size.
Note: this option is only available with DRM atomic support.
(default: display resolution)
``--drm-vrr-enabled=<no|yes|auto>``
+4 -4
View File
@@ -214,7 +214,7 @@ Command Line Switches
``-af volnorm`` (removed; use acompressor ffmpeg filter instead)
``-afm hwac3`` ``--ad=spdif:ac3,spdif:dts``
``-ao alsa:device=hw=0.3`` ``--ao=alsa:device=[hw:0,3]``
``-aspect`` ``--video-aspect-override``
``-aspect`` ``--video-aspect``
``-ass-bottom-margin`` ``--vf=sub=bottom:top``
``-ass`` ``--sub-ass``
``-audiofile-cache`` (removed; the main cache settings are used)
@@ -229,7 +229,7 @@ Command Line Switches
``-endpos`` ``--length``
``-fixed-vo`` (removed; always the default)
``-font`` ``--osd-font``
``-forcedsubsonly`` ``--sub-forced-events-only``
``-forcedsubsonly`` ``--sub-forced-only``
``-forceidx`` ``--index``
``-format`` ``--audio-format``
``-fsmode-dontuse`` (removed)
@@ -321,9 +321,9 @@ input.conf and Slave Commands
+--------------------------------+----------------------------------------+
| ``pt_step -1 [0|1]`` | ``playlist-prev [weak|force] (same)`` |
+--------------------------------+----------------------------------------+
| ``switch_ratio [<ratio>]`` | ``set video-aspect-override <ratio>`` |
| ``switch_ratio [<ratio>]`` | ``set video-aspect <ratio>`` |
| | |
| | ``set video-aspect-override 0`` (reset)|
| | ``set video-aspect 0`` (reset aspect) |
+--------------------------------+----------------------------------------+
| ``step_property_osd <prop>`` | ``cycle <prop> <step>`` (wraps), |
| ``<step> <dir>`` | ``add <prop> <step>`` (clamps). |
+24 -41
View File
@@ -1,15 +1,13 @@
Release Policy
==============
Once or twice a year, a new release is cut off of the master branch and is
Once or twice a month, a new release is cut off of the master branch and is
assigned a 0.X.Y version number, where X is incremented each time a release
contains breaking changes, such as changed options or added/removed features,
and Y is incremented if a release contains only bugfixes and other minor
changes.
Releases are tagged on the master branch and will not be maintained separately.
Patch releases may be made if the amount or severity of bugs justify it, or in
the event of security issues.
The goal of releases is to provide Linux distributions with something to
package. If you want the newest features, just use the master branch.
@@ -33,31 +31,26 @@ While on master:
- Create signed tag v0.X.Y.
- Push release branch (`release/0.X`) and tag to GitHub.
- Add -UNKNOWN suffix to version in `VERSION` file.
- Commit changes, push release branch (`release/0.X`) and tag to GitHub.
- Create a new GitHub release using the content of `RELEASE_NOTES` related to
the new version.
- Readd -UNKNOWN suffix to version in `VERSION` file.
If necessary (to e.g. exclude commits already on master), the release can
be done on a branch with different commit history. The release branch **must**
then be merged to master so `git describe` will pick up the tag.
This does not apply to patch releases, which are tagged directly on the
`release/0.X` branch. The master branch always remains at v0.X.0.
Release notes template
----------------------
Here is a template that can be used for writing the `RELEASE_NOTES` file:
Here is a template that can be used for writing the `RELEASE_NOTES` file.
```markdown
Release 0.X.Y
=============
This release requires FFmpeg <ver> or newer.
Features
--------
@@ -66,18 +59,20 @@ New
- List of new features
Changed
~~~~~~~
- List of changed features
Removed
~~~~~~~
- List of removed features
Deprecated
~~~~~~~~~~
- List of deprecated features
Behavior
--------
- List of user-visible changes in behavior
Options and Commands
--------------------
@@ -87,52 +82,40 @@ Added
- List of added options and commands
Changed
~~~~~~~
- List of changed options and commands
Renamed
~~~~~~~
- List of renamed options and commands
Deprecated
~~~~~~~~~~
- List of deprecated options and commands
Removed
~~~~~~~
- List of removed options and commands
Fixes and Minor Enhancements
----------------------------
- List of fixes and minor enhancements
This listing is not complete. Check DOCS/client-api-changes.rst for a history
of changes to the client API, and DOCS/interface-changes.rst for a history
of changes to other user-visible interfaces.
A complete changelog can be seen by running `git log <start>..<end>`
in the git repository.
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running `git log <start>..<end>` in
the git repository.
```
Note that the "Release 0.X.Y" title should be removed when creating a new GitHub
release.
When creating a new point release its changes should be added on top of the
`RELEASE_NOTES` file (with the appropriate title) so that all the changes in
the current 0.X branch will be included. This way the `RELEASE_NOTES` file
can be used by distributors as changelog for point releases too.
The changelog of lists all changes since the last release, including those
that have been backported to patch releases already.
Some additional advice:
- Especially for features, try to reword the messages so that the user-visible
change is clear to the reader. But don't simplify too much or be too verbose.
- It often makes sense to merge multiple related changes into one line
- Changes that have been made and reverted within the same release must not
appear in the changelog
- Limit the "Options and Commands" section to relevant changes
- When filling in the GitHub release, remove the "Release 0.X.Y" heading
+26 -25
View File
@@ -430,11 +430,12 @@ like a log file or the internal console.lua script.
Locking
-------
See generally available literature. In mpv, we use mp_thread for this.
See generally available literature. In mpv, we use pthread for this.
Always keep locking clean. Don't skip locking just because it will work "in
practice". (See undefined behavior section.) If your use case is simple, you may
use C11 atomics, but most likely you will only hurt yourself and others.
use C11 atomics (osdep/atomic.h for partial C99 support), but most likely you
will only hurt yourself and others.
Always make clear which fields in a struct are protected by which lock. If a
field is immutable, or simply not thread-safe (e.g. state for a single worker
@@ -555,13 +556,13 @@ Condition variables
-------------------
They're used whenever a thread needs to wait for something, without nonsense
like sleep calls or busy waiting. mpv uses the mp_thread API for this.
There's a lot of literature on condition variables, threading in general. Read it.
like sleep calls or busy waiting. mpv uses the standard pthread API for this.
There's a lot of literature on it. Read it.
For initial understanding, it may be helpful to know that condition variables
are not variables that signal a condition. mp_cond does not have any
state per-se. Maybe mp_cond would better be named mp_interrupt,
because its sole purpose is to interrupt a thread waiting via mp_cond_wait()
are not variables that signal a condition. pthread_cond_t does not have any
state per-se. Maybe pthread_cond_t would better be named pthread_interrupt_t,
because its sole purpose is to interrupt a thread waiting via pthread_cond_wait()
(or similar). The "something" in "waiting for something" can be called
predicate (to avoid confusing it with "condition"). Consult literature for the
proper terms.
@@ -570,24 +571,24 @@ The very short version is...
Shared declarations:
mp_mutex lock;
mp_cond cond_var;
pthread_mutex_t lock;
pthread_cond_t cond_var;
struct something state_var; // protected by lock, changes signaled by cond_var
Waiter thread:
mp_mutex_lock(&lock);
pthread_mutex_lock(&lock);
// Wait for a change in state_var. We want to wait until predicate_fulfilled()
// returns true.
// Must be a loop for 2 reasons:
// 1. cond_var may be associated with other conditions too
// 2. mp_cond_wait() can have sporadic wakeups
// 2. pthread_cond_wait() can have sporadic wakeups
while (!predicate_fulfilled(&state_var)) {
// This unlocks, waits for cond_var to be signaled, and then locks again.
// The _whole_ point of cond_var is that unlocking and waiting for the
// signal happens atomically.
mp_cond_wait(&cond_var, &lock);
pthread_cond_wait(&cond_var, &lock);
}
// Here you may react to the state change. The state cannot change
@@ -595,43 +596,43 @@ Waiter thread:
// and reacquire it).
// ...
mp_mutex_unlock(&lock);
pthread_mutex_unlock(&lock);
Signaler thread:
mp_mutex_lock(&lock);
pthread_mutex_lock(&lock);
// Something changed. Update the shared variable with the new state.
update_state(&state_var);
// Notify that something changed. This will wake up the waiter thread if
// it's blocked in mp_cond_wait(). If not, nothing happens.
mp_cond_broadcast(&cond_var);
// it's blocked in pthread_cond_wait(). If not, nothing happens.
pthread_cond_broadcast(&cond_var);
// Fun fact: good implementations wake up the waiter only when the lock is
// released, to reduce kernel scheduling overhead.
mp_mutex_unlock(&lock);
pthread_mutex_unlock(&lock);
Some basic rules:
1. Always access your state under proper locking
2. Always check your predicate before every call to mp_cond_wait()
(And don't call mp_cond_wait() if the predicate is fulfilled.)
3. Always call mp_cond_wait() in a loop
2. Always check your predicate before every call to pthread_cond_wait()
(And don't call pthread_cond_wait() if the predicate is fulfilled.)
3. Always call pthread_cond_wait() in a loop
(And only if your predicate failed without releasing the lock..)
4. Always call mp_cond_broadcast()/_signal() inside of its associated
4. Always call pthread_cond_broadcast()/_signal() inside of its associated
lock
mpv sometimes violates rule 3, and leaves "retrying" (i.e. looping) to the
caller.
Common pitfalls:
- Thinking that mp_cond is some kind of semaphore, or holds any
- Thinking that pthread_cond_t is some kind of semaphore, or holds any
application state or the user predicate (it _only_ wakes up threads
that are at the same time blocking on mp_cond_wait() and friends,
that are at the same time blocking on pthread_cond_wait() and friends,
nothing else)
- Changing the predicate, but not updating all mp_cond_broadcast()/
- Changing the predicate, but not updating all pthread_cond_broadcast()/
_signal() calls correctly
- Forgetting that mp_cond_wait() unlocks the lock (other threads can
- Forgetting that pthread_cond_wait() unlocks the lock (other threads can
and must acquire the lock)
- Holding multiple nested locks while trying to wait (=> deadlock, violates
the lock order anyway)
+157
View File
@@ -0,0 +1,157 @@
waf build system overview
=========================
mpv's new build system is based on waf and it should completely replace the
custom ./configure + Makefile based system inherited from MPlayer.
Goals and the choice of waf
===========================
The new system comes with some goals, which can be summed up as: be as good as
the old one at what it did well (customizability) and fix some of it's major
shortcomings:
1) The build system must be uniform in how it handles any single feature check.
Repetition and boilerplate have to be avoided.
When adding a new feature using the old configure, one had to add a fair
amount of code to the shell script to do option parsing, detection of the
feature and declaration of variables for the Makefile to pickup. The worst
part is this pieces are spread apart in the configure and copy pasted for
any single case. That brings us to..
2) --enable-feature has to be overridden by the user and helps them understand that
they have libraries missing and should install them for the feature to be
enabled.
3) Must be customizable, hackable, pleasant to the developer eyes and to work
with in general.
4) Must have separate configuration and build steps.
Goal 2 comes as a given on pretty much any build system, since autotools made
this behaviour very popular among users (and rightly so).
Goal 1+3 were somewhat harder to accomplish as it looks like all of the build
systems we evaluated (waf included!) had problems with them. For reference we
had proof of concept build systems with waf, CMake and autotools.
What puts waf apart from CMake and autotools, is that projects using it use
Python to program their build system. Also while the Waf Book shows really
simple API usages, you can write your own build system on top of waf that is
tailored to the project's specific needs.
mpv's custom configure step on top of waf
=========================================
To some extents mpv has a custom build system written on top of waf. This
document will not go over the standard waf behaviour as that is documented in
the `Waf book <https://waf.io/book/>`_.
All of the configuration process is handled with a declarative approach. Lists
of dictionaries define the checks, and some custom Python code traverses these
lists and depending on the check definition it calls into the actual waf API.
A simple example using pkg-config would be::
{
'name': '--vdpau',
'desc': 'VDPAU acceleration',
'deps': [ 'x11' ],
'func': check_pkg_config('vdpau', '>= 0.2'),
}
This defines a feature called ``vdpau`` which can be enabled or disabled by
the users with configure flags (that's the meaning of ``--``). This feature
depends on another feature whose name is ``x11``, and the autodetection check
consists of running ``pkg-config`` and looking for ``vdpau`` with version
``>= 0.2``. If the check succeeds a ``#define HAVE_VDPAU 1`` will be added to
``config.h``, if not ``#define HAVE_VDPAU 0`` will be added.
The defines names are automatically prepended with ``HAVE_``, capitalized and
special characters are replaced with underscores. This happens in
``waftools/inflectors.py``.
Mandatory fields:
-----------------
``name``: indicates the unique identifier used by the custom dependency code
to refer to a feature. If the unique identifier is prepended with ``--``
the build system will also generate options for ``./waf configure`` so that
the feature can be enabled and disabled.
``desc``: this is the textual representation of the feature used in the
interactions with the users.
``func``: function that will perform the check. These functions are defined in
``waftools/checks``. The reusable checks are all functions that return
functions. The return functions will then be applied using waf's configuration
context.
The source code for the reusable checks is a bit convoluted, but it should be
easy to pick up their usage from the ``wscript``. Their signature mirrors
the semantics of some of the shell functions used in mplayer.
If someone expresses some interest, I will extend this document with official
documentation for each check function.
Optional fields
---------------
``deps``: list of dependencies of this feature. It is a list of names of
other features as defined in the ``name`` field (minus the eventual leading
``--``). All of the dependencies must be satisfied. If they are not the check
will be skipped without even running ``func``.
``deps_any``: like deps but it is satisfied even if only one of the dependencies
is satisfied. You can think of ``deps`` as a 'and' condition and ``deps_any``
as a 'or' condition.
``deps_neg``: like deps but it is satisfied when none of the dependencies is
satisfied.
``req``: defaults to False. If set to True makes this feature a hard
dependency of mpv (configuration will fail if autodetection fails). If set to
True you must also provide ``fmsg``.
``fmsg``: string with the failure message in case a required dependency is not
satisfied.
``os_specific_checks``: this takes a dictionary that has ``os-`` dependencies
as keys (such as ``os-win32``), and by values has another dictionary that is
merged on top of the current feature definition only for that specific OS.
For example::
{
'name': '--pthreads',
'desc': 'POSIX threads',
'func': check_pthreads,
'os_specific_checks': {
'os-win32': {
'func': check_pthreads_w32_static.
}
}
}
will override the value of ``func`` with ``check_pthreads_w32_static`` only
if the target OS of the build is Windows.
``groups``: groups a dependency with another one. This can be used to disabled
all the grouped dependencies with one ``--disable-``. At the moment this is
only used for OpenGL backends, where you want to disable them when
``--disable-gl`` is passed to the configure.
mpv's custom build step on top of waf
=====================================
Build step is pretty much vanilla waf. The only difference being that the list
of source files can contain both strings or tuples. If a tuple is found,
the second element in the tuple will be used to match the features detected
in the configure step (the ``name`` field described above). If this feature
was not enabled during configure, the source file will not be compiled in.
All of the custom Python for this is inside the function ``filtered_sources``
contained in the file ``waftools/dependencies.py``.
Also ``dependencies_use`` and ``dependencies_includes`` collect cflags and
ldflags that were generated from the features checks in the configure step.
+40 -15
View File
@@ -36,7 +36,7 @@ Releases can be found on the [release list][releases].
## System requirements
- A not too ancient Linux, Windows 10 or later, or macOS 10.15 or later.
- A not too ancient Linux, Windows 7 or later, or OSX 10.8 or later.
- A somewhat capable CPU. Hardware decoding might help if the CPU is too slow to
decode video in realtime, but must be explicitly enabled with the `--hwdec`
option.
@@ -49,11 +49,6 @@ Releases can be found on the [release list][releases].
output methods can help (such as `--vo=xv` on Linux), but this use is not
recommended or supported.
mpv does not go out of its way to break on older hardware or old, unsupported
operating systems, but development is not done with them in mind. Keeping
compatibility with such setups is not guaranteed. If things work, consider it
a happy accident.
## Downloads
@@ -78,8 +73,14 @@ Changes to the default key bindings are indicated in
Compiling with full features requires development files for several
external libraries. Mpv requires [meson](https://mesonbuild.com/index.html)
to build. Meson can be obtained from your distro or PyPI.
external libraries. One of the two build systems supported by mpv is required:
[meson](https://mesonbuild.com/index.html) or [waf](https://waf.io/). Meson
can be obtained from your distro or PyPI. Waf can be downloaded by using the
`./bootstrap.py` script. It will get the latest version of waf that was tested
with mpv. Some documentation about the differences between the build systems are
located in [build-system-differences][build-system-differences].
### Meson
After creating your build directory (e.g. `meson setup build`), you can view a list
of all the build options via `meson configure build`. You could also just simply
@@ -92,15 +93,38 @@ Example:
meson compile -C build
meson install -C build
### Waf
For a list of the available build options use `./waf configure --help`. If
you think you have support for some feature installed but configure fails to
detect it, the file `build/config.log` may contain information about the
reasons for the failure.
NOTE: To avoid cluttering the output with unreadable spam, `--help` only shows
one of the two switches for each option. If the option is autodetected or
enabled by default, the `--disable-***` switch is printed; if the option is
disabled by default, the `--enable-***` switch is printed. Either way, you can
use `--enable-***` or `--disable-**` regardless of what is printed by `--help`.
To build the software you can use `./waf build`: the result of the compilation
will be located in `build/mpv`. You can use `./waf install` to install mpv
to the *prefix* after it is compiled.
Example:
./bootstrap.py
./waf configure
./waf
./waf install
Essential dependencies (incomplete list):
- gcc or clang
- X development headers (xlib, xrandr, xext, xscrnsaver, xpresent, libvdpau,
- X development headers (xlib, xrandr, xext, xscrnsaver, xinerama, libvdpau,
libGL, GLX, EGL, xv, ...)
- Audio output development headers (libasound/ALSA, pulseaudio)
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavfilter
and either libswresample or libavresample)
- libplacebo
- zlib
- iconv (normally provided by the system libc)
- libass (OSD, OSC, text subtitles)
@@ -142,12 +166,12 @@ or cross-compile from Linux with MinGW. See
## Release cycle
Once or twice a year, a release is cut off from the current development state
and is assigned a 0.X.0 version number. No further maintenance is done, except
in the event of security issues.
Every other month, an arbitrary git snapshot is made, and is assigned
a 0.X.0 version number. No further maintenance is done.
The goal of releases is to make Linux distributions happy. Linux distributions
are also expected to apply their own patches in case of bugs.
are also expected to apply their own patches in case of bugs and security
issues.
Releases other than the latest release are unsupported and unmaintained.
@@ -178,7 +202,7 @@ for ideas on what you could contribute with.
## License
GPLv2 "or later" by default, LGPLv2.1 "or later" with `-Dgpl=false`.
GPLv2 "or later" by default, LGPLv2.1 "or later" with `--enable-lgpl`.
See [details.](https://github.com/mpv-player/mpv/blob/master/Copyright)
## History
@@ -206,3 +230,4 @@ Most activity happens on the IRC channel and the github issue tracker.
[api-changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/client-api-changes.rst
[restore-old-bindings]: https://github.com/mpv-player/mpv/blob/master/etc/restore-old-bindings.conf
[contribute.md]: https://github.com/mpv-player/mpv/blob/master/DOCS/contribute.md
[build-system-differences]: https://github.com/mpv-player/mpv/blob/master/DOCS/build-system-differences.md
+142 -160
View File
@@ -1,43 +1,96 @@
Release 0.37.0
Release 0.35.1
==============
This release requires FFmpeg 4.4 or newer and libplacebo 6.338.0 or newer.
Bug fix release for the 0.35 branch.
This is the first release to unconditionally require libplacebo, but note that
the new improved renderer (vo_gpu_next) is not yet the default.
Fixes and Minor Enhancements
----------------------------
- ao_pipewire: log version and other useful debug info
- wayland: error out if essential protocol support is missing
- wayland: also log refresh rate on surface entrance
- vo_gpu_next: fix undefined behavior on alpha-first formats
- meson: prepend MPV_CONFDIR path with prefix
- meson: unbreak dl check on BSDs without libdl
- lcms: fix crash with lcms2-related options if lcms is disabled
- meson: fix stdatomic detection on bsd
- osc: don't spam window-controls bindings on every render
- wayland: check for resize/move in touch event first
- ao_coreaudio: use device's nominal sample rate for latency properties
- ao_pipewire: properly clean up resources
- filter_kernels: fix kaiser
- hwdec/d3d11va: fix a possible memory leak
- external_files: set log level for potential files to trace
- vo_opengl: do not blindly reject all Microsoft's OpenGL implementations
- ao_coreaudio: use AudioUnitReset as ao_driver.reset to prevent long restart
- hwdec_drmprime: fix memory leak
- vo: hwdec: fix libdrm-related memory leak
- draw_bmp: ensure last slice width is less than total width
- wayland: dispatch and wait for compositor events in uninit
- demux: boost read EBU R128 gain values to ReplayGain's reference level
- version.py/version.sh: bump copyright year
- vulkan: fix build error for 32bit builds with clang
- vo_wlshm: cleanup on failure
- ra_d3d11: fix incorrect type
- meson: also search for rst2html with .py extension
- audio: fix crash during uninit with ao_lavc
- mp_image: fix XYZ primaries default
- vd_lavc: add "auto" choice for vd-lavc-dr
- vo: implement VO_DR_FLAG_HOST_CACHED
- x11: fix issue with xpresent timing feedback
Release 0.35.0
==============
This release requires FFmpeg 4.0 or newer.
For packagers: Note that mpv's build system is only supported on Python 3.
If you use the `bootstrap.py` script it will take care of this, otherwise you
need to explicitly invoke the build system using `python3 waf`.
This release adds the Meson build system as an alternative to waf.
It is supposed to be feature-complete but please report any issues you encounter.
Features
--------
New
~~~
Added
~~~~~
- ao_oss: add SPDIF passthrough support
- hwtransfer: implement support for HW->HW format conversions
- stream/dvbin: add support for delivery system ISDB-T
- audio/chmap: support up to 64 channels (including 22.2 layout)
- libmpv: add mpv_time_ns()
- vo_gpu, vo_gpu_next: add Vulkan support for macOS
- meson: make libplacebo a required dependency
- hwdec: support videotoolbox hwdec with libplacebo
- vo_gpu_next: add new libplacebo-based renderer
- build: add meson build support
- vo_gpu_next: forward Dolby Vision metadata to libplacebo
- ao_pipewire: add PipeWire audio backend
- ao_sndio: readd this audio output again
- vo_gpu_next: apply film grain if such metadata is present
- drm: context_drm_egl: add support for enabling VRR
- demux: add support for R128 replaygain tags
- x11: support X Present extension
- af_rubberband: add support for new engine in rubberband 3.0.0
- hwdec/drmprime: add drmprime hwdec-interop (commonly found on SoCs)
- various: support binding functions keys beyond F12
- audio: support hotplug events for multiple AOs
- sub: use Unicode linebreaking for non-ASS subs and OSD
- vo_gpu: hwdec: add Android hwdec utilizing AImageReader
- vo_dmabuf_wayland: add wayland VO displaying dmabuf buffers (vaapi or drm hwdec only)
- lua/console: add history deduplication
Changed
~~~~~~~
- msg: print warning and error messages to stderr
- options: restore old default subtitle selection behavior
- input.conf: swap wheel up/down with wheel left/right
- filters: re-add vavpp hardware deinterlacing auto-filter (for --deinterlace)
- swresample: don't forcibly clip audio on every conversion
- ytdl_hook: always set HTTP headers to fix yt-dlp on certain sites
Removed
~~~~~~~
- waf: remove waf as a build system
- osc.lua: remove toggle for forced only subpictures (appeared as [F])
- mac: remove runtime checks and compatibility for macOS older than 10.15
- cocoa: remove deprecated OpenGL cocoa backend
- libmpv: remove opengl_cb API and other deprecated symbols
- lua: remove deprecated mp.suspend(), resume() and resume_all()
- build: raise minimum libplacebo version requirement to v4.157.0
Options and Commands
@@ -46,169 +99,98 @@ Options and Commands
Added
~~~~~
- vo_gpu_next: add --hdr-peak-percentile
- player: add --term-remaining-playtime option
- x11: add --x11-wid-title option
- vo_gpu_next: add --libplacebo-opts
- player: add --subs-match-os-language option (replaces 'auto' option)
- vo: add --video-crop
- win32: add --window-corners, --window-affinity, --title-bar, --backdrop-type
- sub: add --sub-stretch-durations option
- vo_gpu_next: add --target-colorspace-hint to facilitate HDR passthrough
- vo_gpu_next: new tone mapping options 'auto', 'spline', 'bt.2446a'
and add --tone-mapping-crosstalk, --inverse-tone-mapping
- vo_gpu: add --gamut-mapping-mode and --tone-mapping-mode
- options: add 'always' choice to --stop-screensaver
- options: add --osd-playing-msg-duration
- player: add --cover-art-whitelist option
- x11: add --x11-present option
Changed
~~~~~~~
- builtin.conf: add --hdr-peak-percentile=99.995 to gpu-hq profile
- player: add 'always' option to --subs-fallback-forced
- demux_playlist: default to --directory-mode=lazy
- builtin.conf: add --allow-delayed-peak-detect=no to gpu-hq profile
- vo_gpu, vo_gpu_next: support --icc-3dlut-size=auto
- demux: prepend some cache options with --demuxer-
- builtin.conf: modernize internal profiles for higher quality rendering by default,
rename 'gpu-hq' profile to 'high-quality', add 'fast' profile
- vo_gpu, vo_gpu_next: default to dscale=hermite
- builtin.conf: remove deprecated 'opengl-hq' profile
- options: remove a bunch of old option fallbacks/deprecated ones
- vo_gpu: allow --deband-iterations to be 0
- stream_cdda: deprecate --cdda-toc-bias and always check for offsets
- options: disable --allow-delayed-peak-detect by default
- options: adjust default of --watch-later-options
- ao_openal: enable --openal-direct-channels by default
- options: only apply --sub-visibility to primary subtitles
- options: make --cover-art-auto=exact the default
- vo_gpu/hwdec: rename and introduce legacy names for some interops
Deprecated
~~~~~~~~~~
- command: deprecate shared-script-properties
- demux_cue: deprecate --demuxer-cue-codepage for --metadata-codepage
- vo_gpu: deprecate --gamma-factor and --gamma-auto
Removed
~~~~~~~
- player: remove special 'auto' option from alang/slang/vlang (previous default)
- vo_gpu: remove --tone-mapping-mode
- vo_gpu: remove --scale-wblur, --scale-cutoff etc.
- vo_gpu: remove --scaler-lut-size
- m_option: drop support for -del for list options
- vo_gpu: remove --gamut-clipping, --gamut-warning, --tone-mapping-desaturate and
--tone-mapping-desaturate-exponent (replacements available)
- vulkan: remove --vulkan-disable-events
Fixes and Minor Enhancements
----------------------------
- build: remove unneeded libdl requirement for vaapi
- zimg: fix abort on subsampled input with odd heights
- video_writer: fix gamma for YUV screenshots
- player/video: fix possible crash when changing lavfi-complex
- ad_spdif: fix segfault due to early deallocation
- ao_pipewire: fix race conditon with setting the media role
- draw_bmp: fix overflowing coordinates in mark_rcs
- ao_sndio: use sio_flush() to improve controls responsiveness
- vo_vdpau: fix hwdec for this vo
- vo_gpu, vo_gpu_next: fix setting an empty dscale
- vd_lavc: repeatedly attempt to fallback if hwdec fails in reinit
- options: fix relative time parsing on negative input
- win32: signal DPI changes correctly
- mp_image: properly infer color levels for some pixfmts
- vo_gpu_next: add ability to use named hook params
- vo_gpu_next: take into account PAR when taking screenshots
- ao_audiotrack: support more channel layouts
- osc.lua: support speed-independent time-remaining display
- sub: fix switching tracks while paused
- audio: fix clipping with gapless audio enabled
- player/video: avoid spamming logs with EOF
- player/command: detect aspect ratio name and show it in stats.lua
- wayland: keyboard input fixes
- demux_playlist: remove len restriction on headerless m3u
- win32: fix display resolution calculation on mulitple monitors
- vo_gpu_next: multiple adjustments and fixes to interpolation
- loadfile: avoid infinite playlist loading loops
- context_drm_egl: don't free EGL properties if they are null
- x11: require xrandr 1.4, remove xinerama support
- drm_common: skip cards that don't have connected outputs
- win32_common: fixes minimized window being focused on launch
- ao/jack: set device_buffer to JACK buffer size
- meson: rename all features with underscores
- input: add new keys: Back, Tools, ZoomIn, ZoomOut
- win32: don't ignore --screen and --fs-screen
- input: add missing keypad key defines and Windows multimedia keys
- player: use audio pts corresponding to playing audio on EOF
- command: add sub-ass-extradata property
- vo_dmabuf_wayland: unmap osd surface when not needed
- player: always write redirect entries for resuming playback
- stats.lua: reorganize how video params are displayed
- stats.lua: display HDR metadata
- osc.lua: add scrolling to the seekbar
- demux_lavf: prefer track durations over container durations to determine total
- vo: vulkan: allow picking devices by UUID
- video: allow overriding container crop if it is present
- vo_gpu, vo_gpu_next, screenshot: support applying crop for screenshots
- sd_lavc: account for floating point inaccuracy, fix sub PTS
- stream: accept dav:// and davs:// urls
- filter_kernels: refine some constants
- filter_kernels: add ewa_lanczos4sharpest
- osc.lua: add scrolling to audio/sub buttons
- demux_mkv: support cropping and rotation
- vo_dmabuf_wayland: support 90 degree rotations
- filter_kernels: add hermite filter
- vo: avoid unnecessary redraws when the OSD shows
- scripting: support DLL cplugins
- af_scaletempo2: various bug fixes
- sdl_gamepad: fix button detection on modern controllers
- vo_dmabuf_wayland: support osd rendering when there's no video
- demux_playlist: add --directory-mode=auto
- vo_gpu_next: use proper color for subtitles
- win32: add an option to change window affinity and control title bar state
- win32: reduce top border thickness to imitate DWM invisible borders
- wayland: remove gnome-specific idle-inhibit warning
- win32: pass window handle to the window-id property
- osc.lua: fix calculation for slider's min-max average
- recorder: fix a couple of memory leaks
- af_scaletempo2: raise max playback rate to 8.0
- osc.lua: move the idle logo behind other overlays
- hwdec_drmprime: add nv16 support
- various: change internal timing code to work in nanoseconds instead of microseconds
- vo: increase display refresh rate estimation limit from 99 Hz to 400 Hz
- external_files: base cover-art-whitelist on cover-art-auto-exts
- path: don't override cache and state paths with --config-dir
- codec_tags: map some more image mimetypes
- af/vf-command: add ability to target a specific lavfi filter
- win32: prevent white window flash on startup
- demux_playlist: use --metacode-codepage when parsing playlist files
- video: revert racey change that led to stutter and deadlocking
- console.lua: various improvements
- command: add playlist-next-playlist and playlist-prev-playlist
- ytdl_hook.lua: set metadata with single tracks
- defaults.lua: add a disabled parameter to timer constructors
- terminal-unix: race condition fixes
- af_scaletempo2: better defaults
- hwtransfer: handle hwcontexts that don't implement frame constraints
- stream_cdda: remove fallback for ancient libcdio versions
- osdep: drop support for C11 without atomics
- dvbin: do a big cleanup on code style and functions
- ytdl_hook.lua: parse the separate cookies field
- sub: update subtitles if current track is an image
- javascript: use --js-memory-report option instead of MPV_LEAK_REPORT
- ao_coreaudio: signal buffer underruns
- ytdl_hook.lua: support thumbnails
- demux: make hysteresis-secs respect cache-secs
- mp_image: pass rotation correctly to/from AVFrame correctly
- various: add new internal mp_thread abstraction and use it
- drm: use present_sync mechanism for presentation feedback
- vo_gpu: apply ICC profile and dithering only to window screenshots
- audio: introduce ao_read_data_nonblocking() and use it in relevant backends
- wayland: obey initial size hints set by the compositor
- command: export storage aspect ratio (sar) properties
- vo: delay vsync samples by at least 10 refreshes to improve convergence time
- vo_sdl: fix broken mouse wheel multiplier
- vo_gpu_next: simplify cache code and don't re-save when unmodified
- context_drm_egl: use gbm_surface_create_with_modifiers
- context_drm_egl: add support for BGR surface formats
- vo_gpu: vulkan: open DRM render fd when using VK_KHR_display
- client API: use symbol visibility attributes
- vo_gpu: hwdec_vaapi: add dma-buf modifiers support
- wayland: fix various issues that could lead to jitter
- osc: fix cache displaying 60s in some cases
- player: make --keep-open=always work with --loop-playlist
- opengl: support driver debug message under OpenGL ES
- vo_gpu: opengl: fixes for OpenGL ES version and extension handling
- f_decoder_wrapper: support frame rotation metadata (used by JPEG)
- wscript: switch shaderc checks to pkgconfig
- vd_lavc: enable hwdec for prores by default
- vo_gpu: add HOOKED_gather for custom shaders
- wayland, x11: sanitize window title for valid UTF-8
- win32: apply geometry position to content instead of window
- filter_kernels: add cosine window
- vo_gpu: hwdec_vaapi: don't probe formats for irrelevant endpoints
- vo_gpu: hwdec: load hwdec interops on-demand by default
- stats.lua: graphs: fix bad rendering due to division by 0
- hwdec: warn on unsupported --hwdec option value
- x11: fix --screen-name option
- x11: avoid wasteful rendering when possible
- video/image_writer: add JPEG XL support
- stream_lavf: enable ipfs, ipns and rist protocol support
- osc.lua: fix crash when calling osc-tracklist while idle
- player: add jxl, tiff, tif to list of image extensions
- various: switch to new AVChannelLayout structure
- lua: command_native_async: always run callback asynchronously (edge case)
- context_x11egl: remove supposed transparency workaround
- vo_gpu: fix 3DLUT precision
- drm: avoid drmModeAtomicCommit races by blocking
- drm_common: skip cards that don't support KMS for autodetection
- hwdec/dmabuf_interop_gl: support basic multi-plane formats
- misc/random: switch to internal xoshiro rng implementation
- ytdl_hook: improve track detection
- ad_lavc: strip non-normalized floats
- mpv.metainfo.xml: add XDG appstream metadata manifest
- mac: avoid unnecessary unsafe conversions; fixes crash in debug builds
- af_scaletempo2: fix crash when the number of channels increases
- wayland: correctly handle non-CLOCK_MONOTONIC clocks
- x11: fix --on-all-workspaces option
- sd_ass: improve handling of subtitles with unknown duration
- hwdec/vaapi: improve probing of supported sw formats
- demux_mf: enable support for QOI, PHM and HDR images
- demux_mkv: add AVS2 and AVS3 to tag list
- TOOLS/lua/autoload: fix incorrect duplicate file loading behavior
- demux/codec_tags: support more WAVEFORMATEXTENSIBLE tags
- sd_ass: never mangle colours on RGB video
This listing is not complete. Check DOCS/client-api-changes.rst for a history
of changes to the client API, and DOCS/interface-changes.rst for a history
of changes to other user-visible interfaces.
A complete changelog can be seen by running `git log v0.36.0..v0.37.0`
A complete changelog can be seen by running `git log v0.34.0..v0.35.1`
in the git repository or by visiting either
https://github.com/mpv-player/mpv/compare/v0.36.0...v0.37.0 or
https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.36.0..v0.37.0
https://github.com/mpv-player/mpv/compare/v0.34.0...v0.35.1 or
https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.34.0..v0.35.1
View File
Whitespace-only changes.
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/bash
set -e
export DEST_OS=win32
export CC=gcc
export PKG_CONFIG=/usr/bin/pkg-config
export PERL=/usr/bin/perl
export PYTHON=/usr/bin/python3
"$PYTHON" bootstrap.py
"$PYTHON" waf configure \
--check-c-compiler=gcc \
--enable-spirv-cross \
--enable-d3d-hwaccel \
--enable-d3d11 \
--enable-jpeg \
--enable-lcms2 \
--enable-libarchive \
--enable-lua \
--enable-rubberband \
--enable-shaderc \
--enable-uchardet \
--enable-vulkan
"$PYTHON" waf build
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/bash
set -e
export PYTHON=/usr/bin/python3
# Write an empty fonts.conf to speed up fc-cache
export FONTCONFIG_FILE=/dummy-fonts.conf
cat >"$FONTCONFIG_FILE" <<EOF
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig></fontconfig>
EOF
# Install build dependencies for mpv
pacman -S --noconfirm --needed \
$MINGW_PACKAGE_PREFIX-toolchain \
$MINGW_PACKAGE_PREFIX-cmake \
$MINGW_PACKAGE_PREFIX-lcms2 \
$MINGW_PACKAGE_PREFIX-libarchive \
$MINGW_PACKAGE_PREFIX-libass \
$MINGW_PACKAGE_PREFIX-libjpeg-turbo \
$MINGW_PACKAGE_PREFIX-libplacebo \
$MINGW_PACKAGE_PREFIX-lua51 \
$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
# Delete unused packages to reduce space used in the Appveyor cache
pacman -Sc --noconfirm
# Compile ffmpeg
(
git clone --depth=1 https://github.com/FFmpeg/ffmpeg.git && cd ffmpeg
mkdir build && cd build
../configure \
--prefix=$MINGW_PREFIX \
--target-os=mingw32 \
--arch=$MSYSTEM_CARCH \
--disable-static \
--disable-doc \
--disable-asm \
--enable-gpl \
--enable-version3 \
--enable-shared \
--enable-pic \
--enable-d3d11va \
--enable-dxva2 \
--enable-schannel
make -j4 install
)
-67
View File
@@ -1,67 +0,0 @@
#!/usr/bin/env python3
"""
Wrapper around docutils rst2x commands,
converting their dependency files to a format understood by meson/ninja.
"""
#
# This file is part of mpv.
#
# mpv is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with mpv. If not, see <http://www.gnu.org/licenses/>.
#
import os
import subprocess
import sys
def convert_depfile(output, depfile):
with open(depfile, 'r') as f:
deps = f.readlines()
with open(depfile, 'w') as f:
f.write(os.path.abspath(output))
f.write(': \\\n')
for dep in deps:
dep = dep[:-1]
f.write('\t')
f.write(os.path.abspath(dep))
f.write(' \\\n')
def remove(path):
try:
os.remove(path)
except FileNotFoundError:
pass
argv = sys.argv[1:]
depfile = None
output = argv[-1]
for opt, optarg in zip(argv, argv[1:]):
if opt == '--record-dependencies':
depfile = optarg
try:
proc = subprocess.run(argv, check=True)
if depfile is not None:
convert_depfile(output, depfile)
except:
remove(output)
if depfile is not None:
remove(depfile)
sys.exit(1)
sys.exit(proc.returncode)
+12 -63
View File
@@ -23,35 +23,10 @@ def is_user_lib(objfile, libname):
not os.path.basename(objfile) in libname and \
not "libswift" in libname
def otool(objfile, rapths):
def otool(objfile):
command = "otool -L '%s' | grep -e '\t' | awk '{ print $1 }'" % objfile
output = subprocess.check_output(command, shell = True, universal_newlines=True)
libs = set(filter(partial(is_user_lib, objfile), output.split()))
libs_resolved = set()
libs_relative = set()
for lib in libs:
lib_path = resolve_lib_path(objfile, lib, rapths)
libs_resolved.add(lib_path)
if lib_path != lib:
libs_relative.add(lib)
return libs_resolved, libs_relative
def get_rapths(objfile):
rpaths = []
command = "otool -l '%s' | grep -A2 LC_RPATH | grep path" % objfile
pathRe = re.compile("^\s*path (.*) \(offset \d*\)$")
try:
result = subprocess.check_output(command, shell = True, universal_newlines=True)
except:
return rpaths
for line in result.splitlines():
rpaths.append(pathRe.search(line).group(1).strip())
return rpaths
return set(filter(partial(is_user_lib, objfile), output.split()))
def get_rpaths_dev_tools(binary):
command = "otool -l '%s' | grep -A2 LC_RPATH | grep path | grep \"Xcode\|CommandLineTools\"" % binary
@@ -64,29 +39,11 @@ def get_rpaths_dev_tools(binary):
return output
def resolve_lib_path(objfile, lib, rapths):
if os.path.exists(lib):
return lib
if lib.startswith('@rpath/'):
lib = lib[len('@rpath/'):]
for rpath in rapths:
lib_path = os.path.join(rpath, lib)
if os.path.exists(lib_path):
return lib_path
elif lib.startswith('@loader_path/'):
lib = lib[len('@loader_path/'):]
lib_path = os.path.normpath(os.path.join(objfile, lib))
if os.path.exists(lib_path):
return lib_path
raise Exception('Could not resolve library: ' + lib)
def install_name_tool_change(old, new, objfile):
subprocess.call(["install_name_tool", "-change", old, new, objfile], stderr=subprocess.DEVNULL)
subprocess.call(["install_name_tool", "-change", old, new, objfile])
def install_name_tool_id(name, objfile):
subprocess.call(["install_name_tool", "-id", name, objfile], stderr=subprocess.DEVNULL)
subprocess.call(["install_name_tool", "-id", name, objfile])
def install_name_tool_add_rpath(rpath, binary):
subprocess.call(["install_name_tool", "-add_rpath", rpath, binary])
@@ -94,17 +51,15 @@ def install_name_tool_add_rpath(rpath, binary):
def install_name_tool_delete_rpath(rpath, binary):
subprocess.call(["install_name_tool", "-delete_rpath", rpath, binary])
def libraries(objfile, result = dict(), result_relative = set(), rapths = []):
rapths = get_rapths(objfile) + rapths
libs_list, libs_relative = otool(objfile, rapths)
def libraries(objfile, result = dict()):
libs_list = otool(objfile)
result[objfile] = libs_list
result_relative |= libs_relative
for lib in libs_list:
if lib not in result:
libraries(lib, result, result_relative, rapths)
libraries(lib, result)
return result, result_relative
return result
def lib_path(binary):
return os.path.join(os.path.dirname(binary), 'lib')
@@ -112,7 +67,7 @@ def lib_path(binary):
def lib_name(lib):
return os.path.join("@executable_path", "lib", os.path.basename(lib))
def process_libraries(libs_dict, libs_dyn, binary):
def process_libraries(libs_dict, binary):
libs_set = set(libs_dict)
# Remove binary from libs_set to prevent a duplicate of the binary being
# added to the libs directory.
@@ -120,7 +75,7 @@ def process_libraries(libs_dict, libs_dyn, binary):
for src in libs_set:
name = lib_name(src)
dst = os.path.join(lib_path(binary), os.path.basename(src))
dst = os.path.join(lib_path(binary), os.path.basename(src))
shutil.copy(src, dst)
os.chmod(dst, 0o755)
@@ -133,12 +88,6 @@ def process_libraries(libs_dict, libs_dyn, binary):
if p in libs_dict[src]:
install_name_tool_change(p, lib_name(p), dst)
for lib in libs_dyn:
install_name_tool_change(lib, lib_name(lib), dst)
for lib in libs_dyn:
install_name_tool_change(lib, lib_name(lib), binary)
def process_swift_libraries(binary):
command = ['xcrun', '--find', 'swift-stdlib-tool']
swiftStdlibTool = subprocess.check_output(command, universal_newlines=True).strip()
@@ -166,10 +115,10 @@ def main():
if not os.path.exists(lib_path(binary)):
os.makedirs(lib_path(binary))
print(">> gathering all linked libraries")
libs, libs_rel = libraries(binary)
libs = libraries(binary)
print(">> copying and processing all linked libraries")
process_libraries(libs, libs_rel, binary)
process_libraries(libs, binary)
print(">> removing rpath definitions towards dev tools")
remove_dev_tools_rapths(binary)
+5 -1
View File
@@ -39,6 +39,10 @@ def file2string(infilename, infile, outfile):
outfile.write('"' + ''.join(conv[c] for c in line) + '"\n')
if __name__ == "__main__":
outfile = open(sys.argv[2], "w")
if len(sys.argv) < 2:
outfile = sys.stdout
else:
outfile = open(sys.argv[2], "w")
with open(sys.argv[1], 'rb') as infile:
file2string(sys.argv[1], infile, outfile)
+1 -1
View File
@@ -22,7 +22,7 @@
testfun()
{
$ILDETECT_MPV "$@" \
--vf-add=lavfi="[idet]" --msg-level=ffmpeg=v \
--vf-add=lavfi="[idet]" --msg-level ffmpeg=v \
--o= --vo=null --no-audio --untimed \
$ILDETECT_MPVFLAGS \
| { if [ -n "$ILDETECT_QUIET" ]; then cat; else tee /dev/stderr; fi } \
+134 -103
View File
@@ -1,70 +1,103 @@
--[[
This script uses the lavfi cropdetect filter and the video-crop property to
automatically crop the currently playing video with appropriate parameters.
This script uses the lavfi cropdetect filter to automatically
insert a crop filter with appropriate parameters for the
currently playing video.
It automatically crops the video when playback starts.
It will automatically crop the video, when playback starts.
You can also manually crop the video by pressing the "C" (shift+c) key.
Pressing it again undoes the crop.
Also It registers the key-binding "C" (shift+c). You can manually
crop the video by pressing the "C" (shift+c) key.
The workflow is as follows: First, it inserts the cropdetect filter. After
<detect_seconds> (default is 1) seconds, it then sets video-crop based on the
vf-metadata values gathered by cropdetect. The cropdetect filter is removed
after video-crop is set as it is no longer needed.
If the "C" key is pressed again, the crop filter is removed
restoring playback to its original state.
Since the crop parameters are determined from the 1 second of video between
inserting the cropdetect filter and setting video-crop, the "C" key should be
pressed at a position in the video where the crop region is unambiguous (i.e.,
not a black frame, black background title card, or dark scene).
The workflow is as follows: First, it inserts the filter
vf=lavfi=cropdetect. After <detect_seconds> (default is 1)
seconds, it then inserts the filter vf=crop=w:h:x:y, where
w,h,x,y are determined from the vf-metadata gathered by
cropdetect. The cropdetect filter is removed immediately after
the crop filter is inserted as it is no longer needed.
If non-copy-back hardware decoding is in use, hwdec is temporarily disabled for
the duration of cropdetect as the filter would fail otherwise.
Since the crop parameters are determined from the 1 second of
video between inserting the cropdetect and crop filters, the "C"
key should be pressed at a position in the video where the crop
region is unambiguous (i.e., not a black frame, black background
title card, or dark scene).
These are the default options. They can be overridden by adding
script-opts-append=autocrop-<parameter>=<value> to mpv.conf.
The default options can be overridden by adding
script-opts-append=autocrop-<parameter>=<value> into mpv.conf
List of available parameters (For default values, see <options>):
auto: bool - Whether to automatically apply crop at the start of
playback. If you don't want to crop automatically, set it to
false or add "script-opts-append=autocrop-auto=no" into
mpv.conf.
auto_delay: seconds - Delay before starting crop in auto mode.
You can try to increase this value to avoid dark scene or
fade in at beginning. Automatic cropping will not occur if
the value is larger than the remaining playback time.
detect_limit: number[0-255] - Black threshold for cropdetect.
Smaller values will generally result in less cropping.
See limit of https://ffmpeg.org/ffmpeg-filters.html#cropdetect
detect_round: number[2^n] - The value which the width/height
should be divisible by. Smaller values ​​have better detection
accuracy. If you have problems with other filters,
you can try to set it to 4 or 16.
See round of https://ffmpeg.org/ffmpeg-filters.html#cropdetect
detect_min_ratio: number[0.0-1.0] - The ratio of the minimum clip
size to the original. If the picture is over cropped or under
cropped, try adjusting this value.
detect_seconds: seconds - How long to gather cropdetect data.
Increasing this may be desirable to allow cropdetect more
time to collect data.
suppress_osd: bool - Whether the OSD shouldn't be used when filters
are applied and removed.
--]]
require "mp.msg"
require 'mp.options'
local options = {
-- Whether to automatically apply crop at the start of playback. If you
-- don't want to crop automatically, add
-- script-opts-append=autocrop-auto=no to mpv.conf.
auto = true,
-- Delay before starting crop in auto mode. You can try to increase this
-- value to avoid dark scenes or fade ins at beginning. Automatic cropping
-- will not occur if the value is larger than the remaining playback time.
auto_delay = 4,
-- Black threshold for cropdetect. Smaller values will generally result in
-- less cropping. See limit of
-- https://ffmpeg.org/ffmpeg-filters.html#cropdetect
detect_limit = "24/255",
-- The value which the width/height should be divisible by. Smaller
-- values have better detection accuracy. If you have problems with
-- other filters, you can try to set it to 4 or 16. See round of
-- https://ffmpeg.org/ffmpeg-filters.html#cropdetect
detect_round = 2,
-- The ratio of the minimum clip size to the original. A number from 0 to
-- 1. If the picture is over cropped, try adjusting this value.
detect_min_ratio = 0.5,
-- How long to gather cropdetect data. Increasing this may be desirable to
-- allow cropdetect more time to collect data.
detect_seconds = 1,
-- Whether the OSD shouldn't be used when cropdetect and video-crop are
-- applied and removed.
suppress_osd = false,
}
read_options(options)
require "mp.options".read_options(options)
local cropdetect_label = mp.get_script_name() .. "-cropdetect"
local label_prefix = mp.get_script_name()
local labels = {
crop = string.format("%s-crop", label_prefix),
cropdetect = string.format("%s-cropdetect", label_prefix)
}
timers = {
auto_delay = nil,
detect_crop = nil
}
local hwdec_backup
local command_prefix = options.suppress_osd and 'no-osd' or ''
function is_filter_present(label)
local filters = mp.get_property_native("vf")
for index, filter in pairs(filters) do
if filter["label"] == label then
return true
end
end
return false
end
function is_enough_time(seconds)
-- Plus 1 second for deviation.
@@ -74,64 +107,54 @@ function is_enough_time(seconds)
return playtime_remaining and time_needed < playtime_remaining
end
function is_cropable(time_needed)
if mp.get_property_native('current-tracks/video/image') ~= false then
mp.msg.warn("autocrop only works for videos.")
return false
end
if not is_enough_time(time_needed) then
mp.msg.warn("Not enough time to detect crop.")
return false
end
return true
end
function remove_cropdetect()
for _, filter in pairs(mp.get_property_native("vf")) do
if filter.label == cropdetect_label then
mp.command(
string.format("%s vf remove @%s", command_prefix, filter.label))
return
function is_cropable()
for _, track in pairs(mp.get_property_native('track-list')) do
if track.type == 'video' and track.selected then
return not track.albumart
end
end
return false
end
function restore_hwdec()
if hwdec_backup then
mp.set_property("hwdec", hwdec_backup)
hwdec_backup = nil
function remove_filter(label)
if is_filter_present(label) then
mp.command(string.format('%s vf remove @%s', command_prefix, label))
return true
end
return false
end
function cleanup()
remove_cropdetect()
-- Remove all existing filters.
for key, value in pairs(labels) do
remove_filter(value)
end
-- Kill all timers.
for index, timer in pairs(timers) do
if timer then
timer:kill()
timers[index] = nil
timer = nil
end
end
restore_hwdec()
end
function detect_crop()
local time_needed = options.detect_seconds
if not is_cropable(time_needed) then
-- If it's not cropable, exit.
if not is_cropable() then
mp.msg.warn("autocrop only works for videos.")
return
end
local hwdec_current = mp.get_property("hwdec-current")
if hwdec_current:find("-copy$") == nil and hwdec_current ~= "no" and
hwdec_current ~= "crystalhd" and hwdec_current ~= "rkmpp" then
hwdec_backup = mp.get_property("hwdec")
mp.set_property("hwdec", "no")
-- Verify if there is enough time to detect crop.
local time_needed = options.detect_seconds
if not is_enough_time(time_needed) then
mp.msg.warn("Not enough time to detect crop.")
return
end
-- Insert the cropdetect filter.
@@ -141,7 +164,7 @@ function detect_crop()
mp.command(
string.format(
'%s vf pre @%s:cropdetect=limit=%s:round=%d:reset=0',
command_prefix, cropdetect_label, limit, round
command_prefix, labels.cropdetect, limit, round
)
)
@@ -152,9 +175,13 @@ end
function detect_end()
-- Get the metadata and remove the cropdetect filter.
local cropdetect_metadata = mp.get_property_native(
"vf-metadata/" .. cropdetect_label)
remove_cropdetect()
local cropdetect_metadata =
mp.get_property_native(
string.format("vf-metadata/%s",
labels.cropdetect
)
)
remove_filter(labels.cropdetect)
-- Remove the timer of detect crop.
if timers.detect_crop then
@@ -162,8 +189,6 @@ function detect_end()
timers.detect_crop = nil
end
restore_hwdec()
local meta = {}
-- Verify the existence of metadata.
@@ -199,6 +224,7 @@ function detect_end()
else
mp.msg.error("Got empty crop data.")
mp.msg.info("You might need to increase detect_seconds.")
return
end
apply_crop(meta)
@@ -207,27 +233,32 @@ end
function apply_crop(meta)
-- Verify if it is necessary to crop.
local is_effective = meta.w and meta.h and meta.x and meta.y and
(meta.x > 0 or meta.y > 0
or meta.w < meta.max_w or meta.h < meta.max_h)
local is_effective = meta.x > 0 or meta.y > 0
or meta.w < meta.max_w or meta.h < meta.max_h
-- Verify it is not over cropped.
local is_excessive = false
if is_effective and (meta.w < meta.min_w or meta.h < meta.min_h) then
mp.msg.info("The area to be cropped is too large.")
mp.msg.info("You might need to decrease detect_min_ratio.")
is_excessive = true
end
if not is_effective or is_excessive then
-- Clear any existing crop.
mp.command(string.format("%s set file-local-options/video-crop ''", command_prefix))
if not is_effective then
mp.msg.info("No area detected for cropping.")
return
end
-- Verify it is not over cropped.
local is_excessive = meta.w < meta.min_w and meta.h < meta.min_h
if is_excessive then
mp.msg.info("The area to be cropped is too large.")
mp.msg.info("You might need to decrease detect_min_ratio.")
return
end
-- Remove existing crop.
remove_filter(labels.crop)
-- Apply crop.
mp.command(string.format("%s set file-local-options/video-crop %sx%s+%s+%s",
command_prefix, meta.w, meta.h, meta.x, meta.y))
mp.command(
string.format("%s vf pre @%s:lavfi-crop=w=%s:h=%s:x=%s:y=%s",
command_prefix, labels.crop, meta.w, meta.h, meta.x, meta.y
)
)
end
function on_start()
@@ -251,7 +282,8 @@ function on_start()
-- Verify if there is enough time for autocrop.
local time_needed = options.auto_delay + options.detect_seconds
if not is_cropable(time_needed) then
if not is_enough_time(time_needed) then
mp.msg.warn("Not enough time for autocrop.")
return
end
@@ -278,8 +310,7 @@ function on_toggle()
end
-- Cropped => Remove it.
if mp.get_property("video-crop") ~= "" then
mp.command(string.format("%s set file-local-options/video-crop ''", command_prefix))
if remove_filter(labels.crop) then
return
end
@@ -289,7 +320,7 @@ function on_toggle()
return
end
-- Neither => Detect crop.
-- Neither => Do delectcrop.
detect_crop()
end
+88 -184
View File
@@ -16,17 +16,11 @@ disabled=no
images=no
videos=yes
audio=yes
additional_image_exts=list,of,ext
additional_video_exts=list,of,ext
additional_audio_exts=list,of,ext
ignore_hidden=yes
same_type=yes
directory_mode=recursive
--]]
MAXENTRIES = 5000
MAXDIRSTACK = 20
local msg = require 'mp.msg'
local options = require 'mp.options'
@@ -37,24 +31,9 @@ o = {
images = true,
videos = true,
audio = true,
additional_image_exts = "",
additional_video_exts = "",
additional_audio_exts = "",
ignore_hidden = true,
same_type = false,
directory_mode = "auto"
ignore_hidden = true
}
options.read_options(o, nil, function(list)
split_option_exts(list.additional_video_exts, list.additional_audio_exts, list.additional_image_exts)
if list.videos or list.additional_video_exts or
list.audio or list.additional_audio_exts or
list.images or list.additional_image_exts then
create_extensions()
end
if list.directory_mode then
validate_directory_mode()
end
end)
options.read_options(o)
function Set (t)
local set = {}
@@ -63,14 +42,10 @@ function Set (t)
end
function SetUnion (a,b)
for k in pairs(b) do a[k] = true end
return a
end
function Split (s)
local set = {}
for v in string.gmatch(s, '([^,]+)') do set[v] = true end
return set
local res = {}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end
EXTENSIONS_VIDEO = Set {
@@ -88,33 +63,17 @@ EXTENSIONS_IMAGES = Set {
'svg', 'tga', 'tif', 'tiff', 'webp'
}
function split_option_exts(video, audio, image)
if video then o.additional_video_exts = Split(o.additional_video_exts) end
if audio then o.additional_audio_exts = Split(o.additional_audio_exts) end
if image then o.additional_image_exts = Split(o.additional_image_exts) end
end
split_option_exts(true, true, true)
EXTENSIONS = Set {}
if o.videos then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_VIDEO) end
if o.audio then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_AUDIO) end
if o.images then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_IMAGES) end
function create_extensions()
EXTENSIONS = {}
if o.videos then SetUnion(SetUnion(EXTENSIONS, EXTENSIONS_VIDEO), o.additional_video_exts) end
if o.audio then SetUnion(SetUnion(EXTENSIONS, EXTENSIONS_AUDIO), o.additional_audio_exts) end
if o.images then SetUnion(SetUnion(EXTENSIONS, EXTENSIONS_IMAGES), o.additional_image_exts) end
end
create_extensions()
function validate_directory_mode()
if o.directory_mode ~= "recursive" and o.directory_mode ~= "lazy" and o.directory_mode ~= "ignore" then
o.directory_mode = nil
end
end
validate_directory_mode()
function add_files(files)
function add_files_at(index, files)
index = index - 1
local oldcount = mp.get_property_number("playlist-count", 1)
for i = 1, #files do
mp.commandv("loadfile", files[i][1], "append")
mp.commandv("playlist-move", oldcount + i - 1, files[i][2])
mp.commandv("loadfile", files[i], "append")
mp.commandv("playlist-move", oldcount + i - 1, index + i - 1)
end
end
@@ -135,79 +94,47 @@ table.filter = function(t, iter)
end
end
table.append = function(t1, t2)
local t1_size = #t1
for i = 1, #t2 do
t1[t1_size + i] = t2[i]
-- splitbynum and alnumcomp from alphanum.lua (C) Andre Bogus
-- Released under the MIT License
-- http://www.davekoelle.com/files/alphanum.lua
-- split a string into a table of number and string values
function splitbynum(s)
local result = {}
for x, y in (s or ""):gmatch("(%d*)(%D*)") do
if x ~= "" then table.insert(result, tonumber(x)) end
if y ~= "" then table.insert(result, y) end
end
return result
end
-- alphanum sorting for humans in Lua
-- http://notebook.kulchenko.com/algorithms/alphanumeric-natural-sorting-for-humans-in-lua
function clean_key(k)
k = (' '..k..' '):gsub("%s+", " "):sub(2, -2):lower()
return splitbynum(k)
end
function alphanumsort(filenames)
local function padnum(n, d)
return #d > 0 and ("%03d%s%.12f"):format(#n, n, tonumber(d) / (10 ^ #d))
or ("%03d%s"):format(#n, n)
-- compare two strings
function alnumcomp(x, y)
local xt, yt = clean_key(x), clean_key(y)
for i = 1, math.min(#xt, #yt) do
local xe, ye = xt[i], yt[i]
if type(xe) == "string" then ye = tostring(ye)
elseif type(ye) == "string" then xe = tostring(xe) end
if xe ~= ye then return xe < ye end
end
local tuples = {}
for i, f in ipairs(filenames) do
tuples[i] = {f:lower():gsub("0*(%d+)%.?(%d*)", padnum), f}
end
table.sort(tuples, function(a, b)
return a[1] == b[1] and #b[2] < #a[2] or a[1] < b[1]
end)
for i, tuple in ipairs(tuples) do filenames[i] = tuple[2] end
return filenames
return #xt < #yt
end
local autoloaded = nil
local added_entries = {}
local autoloaded_dir = nil
function scan_dir(path, current_file, dir_mode, separator, dir_depth, total_files, extensions)
if dir_depth == MAXDIRSTACK then
return
end
msg.trace("scanning: " .. path)
local files = utils.readdir(path, "files") or {}
local dirs = dir_mode ~= "ignore" and utils.readdir(path, "dirs") or {}
local prefix = path == "." and "" or path
table.filter(files, function (v)
-- The current file could be a hidden file, ignoring it doesn't load other
-- files from the current directory.
if (o.ignore_hidden and not (prefix .. v == current_file) and string.match(v, "^%.")) then
return false
end
local ext = get_extension(v)
if ext == nil then
return false
end
return extensions[string.lower(ext)]
end)
table.filter(dirs, function(d)
return not ((o.ignore_hidden and string.match(d, "^%.")))
end)
alphanumsort(files)
alphanumsort(dirs)
for i, file in ipairs(files) do
files[i] = prefix .. file
end
table.append(total_files, files)
if dir_mode == "recursive" then
for _, dir in ipairs(dirs) do
scan_dir(prefix .. dir .. separator, current_file, dir_mode,
separator, dir_depth + 1, total_files, extensions)
end
else
for i, dir in ipairs(dirs) do
dirs[i] = prefix .. dir
end
table.append(total_files, dirs)
end
function get_playlist_filenames()
local filenames = {}
for n = 0, pl_count - 1, 1 do
local filename = mp.get_property('playlist/'..n..'/filename')
local _, file = utils.split_path(filename)
filenames[file] = true
end
return filenames
end
function find_and_add_entries()
@@ -215,39 +142,21 @@ function find_and_add_entries()
local dir, filename = utils.split_path(path)
msg.trace(("dir: %s, filename: %s"):format(dir, filename))
if o.disabled then
msg.debug("stopping: autoload disabled")
msg.verbose("stopping: autoload disabled")
return
elseif #dir == 0 then
msg.debug("stopping: not a local path")
msg.verbose("stopping: not a local path")
return
end
local pl_count = mp.get_property_number("playlist-count", 1)
this_ext = get_extension(filename)
pl_count = mp.get_property_number("playlist-count", 1)
-- check if this is a manually made playlist
if (pl_count > 1 and autoloaded == nil) or
(pl_count == 1 and EXTENSIONS[string.lower(this_ext)] == nil) then
msg.debug("stopping: manually made playlist")
(pl_count == 1 and EXTENSIONS[string.lower(get_extension(filename))] == nil) then
msg.verbose("stopping: manually made playlist")
return
else
if pl_count == 1 then
autoloaded = true
autoloaded_dir = dir
added_entries = {}
end
end
local extensions = {}
if o.same_type then
if EXTENSIONS_VIDEO[string.lower(this_ext)] ~= nil then
extensions = EXTENSIONS_VIDEO
elseif EXTENSIONS_AUDIO[string.lower(this_ext)] ~= nil then
extensions = EXTENSIONS_AUDIO
else
extensions = EXTENSIONS_IMAGES
end
else
extensions = EXTENSIONS
autoloaded = true
end
local pl = mp.get_property_native("playlist", {})
@@ -255,22 +164,33 @@ function find_and_add_entries()
msg.trace(("playlist-pos-1: %s, playlist: %s"):format(pl_current,
utils.to_string(pl)))
local files = {}
do
local dir_mode = o.directory_mode or mp.get_property("directory-mode", "lazy")
local separator = mp.get_property_native("platform") == "windows" and "\\" or "/"
scan_dir(autoloaded_dir, path, dir_mode, separator, 0, files, extensions)
end
if next(files) == nil then
msg.debug("no other files or directories in directory")
local files = utils.readdir(dir, "files")
if files == nil then
msg.verbose("no other files in directory")
return
end
table.filter(files, function (v, k)
-- The current file could be a hidden file, ignoring it doesn't load other
-- files from the current directory.
if (o.ignore_hidden and not (v == filename) and string.match(v, "^%.")) then
return false
end
local ext = get_extension(v)
if ext == nil then
return false
end
return EXTENSIONS[string.lower(ext)]
end)
table.sort(files, alnumcomp)
if dir == "." then
dir = ""
end
-- Find the current pl entry (dir+"/"+filename) in the sorted dir list
local current
for i = 1, #files do
if files[i] == path then
if files[i] == filename then
current = i
break
end
@@ -280,49 +200,33 @@ function find_and_add_entries()
end
msg.trace("current file position in files: "..current)
-- treat already existing playlist entries, independent of how they got added
-- as if they got added by autoload
for _, entry in ipairs(pl) do
added_entries[entry.filename] = true
end
local append = {[-1] = {}, [1] = {}}
local filenames = get_playlist_filenames()
for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1
for i = 1, MAXENTRIES do
local pos = current + i * direction
local file = files[pos]
local file = files[current + i * direction]
if file == nil or file[1] == "." then
break
end
-- skip files that are/were already in the playlist
if not added_entries[file] then
if direction == -1 then
msg.verbose("Prepending " .. file)
table.insert(append[-1], 1, {file, pl_current + i * direction + 1})
else
msg.verbose("Adding " .. file)
if pl_count > 1 then
table.insert(append[1], {file, pl_current + i * direction - 1})
else
mp.commandv("loadfile", file, "append")
end
local filepath = dir .. file
-- skip files already in playlist
if filenames[file] then break end
if direction == -1 then
if pl_current == 1 then -- never add additional entries in the middle
msg.info("Prepending " .. file)
table.insert(append[-1], 1, filepath)
end
else
msg.info("Adding " .. file)
table.insert(append[1], filepath)
end
added_entries[file] = true
end
if pl_count == 1 and direction == -1 and #append[-1] > 0 then
for i = 1, #append[-1] do
mp.commandv("loadfile", append[-1][i][1], "append")
end
mp.commandv("playlist-move", 0, current)
end
end
if pl_count > 1 then
add_files(append[1])
add_files(append[-1])
end
add_files_at(pl_current + 1, append[1])
add_files_at(pl_current, append[-1])
end
mp.register_event("start-file", find_and_add_entries)
+33 -11
View File
@@ -1,13 +1,14 @@
#!/usr/bin/env python3
# This checks for the sdk path, the sdk version, and
# the sdk build version.
# Logic copied from compiler_swift.py in the waf build. This checks for the sdk
# path, the sdk version, and the sdk build version. The sdk path is returned
# along with what is selected as the sdk version.
import re
import os
import string
import subprocess
import sys
from distutils.version import StrictVersion
from shutil import which
from subprocess import check_output
@@ -15,7 +16,6 @@ def find_macos_sdk():
sdk = os.environ.get('MACOS_SDK', '')
sdk_version = os.environ.get('MACOS_SDK_VERSION', '0.0')
xcrun = which('xcrun')
xcodebuild = which('xcodebuild')
if not xcrun:
return sdk,sdk_version
@@ -26,20 +26,42 @@ def find_macos_sdk():
# find macOS SDK paths and version
if sdk_version == '0.0':
# show-sdk-build-version: is not available on older command line tools, but returns a build version (eg 17A360)
# show-sdk-version: is always available, but on older dev tools it's only the major version
sdk_build_version = check_output([xcrun, '--sdk', 'macosx',
'--show-sdk-build-version'], encoding="UTF-8")
sdk_version = check_output([xcrun, '--sdk', 'macosx', '--show-sdk-version'],
encoding="UTF-8")
# use xcode tools when installed, still necessary for xcode versions <12.0
try:
sdk_version = check_output([xcodebuild, '-sdk', 'macosx', '-version', 'ProductVersion'],
encoding="UTF-8", stderr=subprocess.DEVNULL)
except:
pass
if sdk:
build_version = '10.10.0'
# convert build version to a version string
# first 2 two digits are the major version, starting with 15 which is 10.11 (offset of 4)
# 1 char is the minor version, A => 0, B => 1 and ongoing
# last digits are bugfix version, which are not relevant for us
# eg 16E185 => 10.12.4, 17A360 => 10.13, 18B71 => 10.14.1
if sdk_build_version and isinstance(sdk_build_version, str):
verRe = re.compile("(\d+)(\D+)(\d+)")
version_parts = verRe.search(sdk_build_version)
major = int(version_parts.group(1)) - 4
minor = string.ascii_lowercase.index(version_parts.group(2).lower())
build_version = '10.' + str(major) + '.' + str(minor)
# from 20 onwards macOS 11.0 starts
if int(version_parts.group(1)) >= 20:
build_version = '11.' + str(minor)
if not isinstance(sdk_version, str):
sdk_version = '10.10.0'
return sdk.strip(),sdk_version.strip()
# pick the higher version, always pick sdk over build if newer
if StrictVersion(build_version) > StrictVersion(sdk_version):
return sdk,build_version
else:
return sdk,sdk_version
return sdk,sdk_version
if __name__ == "__main__":
sdk_info = find_macos_sdk()
+7 -6
View File
@@ -105,10 +105,6 @@ elements_matroska = (
'DisplayWidth, 54b0, uint',
'DisplayHeight, 54ba, uint',
'DisplayUnit, 54b2, uint',
'PixelCropTop, 54bb, uint',
'PixelCropLeft, 54cc, uint',
'PixelCropRight, 54dd, uint',
'PixelCropBottom, 54aa, uint',
'FrameRate, 2383e3, float',
'ColourSpace, 2eb524, binary',
'StereoMode, 53b8, uint',
@@ -463,10 +459,15 @@ if __name__ == "__main__":
def parse_toplevel(s):
parse_one(s, 0, None, 1 << 63)
if len(sys.argv) < 3:
outfile = sys.stdout
else:
outfile = open(sys.argv[2], "w")
if sys.argv[1] == '--generate-header':
generate_C_header(open(sys.argv[2], "w"))
generate_C_header(outfile)
elif sys.argv[1] == '--generate-definitions':
generate_C_definitions(open(sys.argv[2], "w"))
generate_C_definitions(outfile)
else:
s = open(sys.argv[1], "rb")
while 1:
+1 -1
View File
@@ -79,7 +79,7 @@ EOF
video-bitrate
video-codec
video-format
video-params/aspect
video-aspect
container-fps
width
height
+5 -5
View File
@@ -38,8 +38,9 @@ def apply_plist_template(plist_file, version):
for line in fileinput.input(plist_file, inplace=1):
print(line.rstrip().replace('${VERSION}', version))
def sign_bundle(binary_name):
sh('codesign --force --deep -s - ' + bundle_path(binary_name))
def create_bundle_symlink(binary_name, symlink_name):
os.symlink(os.path.basename(binary_name),
os.path.join(target_directory(binary_name), symlink_name))
def bundle_version():
if os.path.exists('VERSION'):
@@ -71,6 +72,8 @@ def main():
copy_bundle(binary_name)
print("> copying binary")
copy_binary(binary_name)
print("> create bundle symlink")
create_bundle_symlink(binary_name, "mpv-bundle")
print("> generating Info.plist")
apply_plist_template(target_plist(binary_name), version)
@@ -78,9 +81,6 @@ def main():
print("> bundling dependencies")
print(sh(" ".join(["TOOLS/dylib-unhell.py", target_binary(binary_name)])))
print("> signing bundle with ad-hoc pseudo identity")
sign_bundle(binary_name)
print("done.")
if __name__ == "__main__":
+1 -3
View File
@@ -173,7 +173,7 @@
</dict>
</array>
<key>CFBundleExecutable</key>
<string>mpv</string>
<string>mpv-bundle</string>
<key>CFBundleIconFile</key>
<string>icon</string>
<key>CFBundleIdentifier</key>
@@ -192,8 +192,6 @@
<dict>
<key>MallocNanoZone</key>
<string>0</string>
<key>MPVBUNDLE</key>
<string>true</string>
</dict>
<key>CFBundleURLTypes</key>
<array>
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/ruby
class TravisDepsBuilder
def self.make(name)
instance = klass.new(name)
instance.fill_data
instance.deps
instance.build
end
def self.klass
Module.const_get([self.name, self.os.capitalize].join)
rescue NameError
self
end
def self.os
ENV['TRAVIS_OS_NAME']
end
attr_reader :name, :url, :action, :os
def initialize(name)
@name, @os = name, self.class.os
end
def fill_data
data = build_map.fetch(name)
@url, @action = data[:url], data[:action]
end
def build
send(action)
end
def deps; end
private
def package_manager_update
# yes class variable, you wanna update only once across all instances
@@updated ||= false
return if @@updated
sh({'linux' => 'sudo apt-get update -y', 'osx' => 'brew update'}[os])
@@updated = true
end
def package_install(*packages)
cmd = {
'linux' => 'sudo apt-get install %s -y',
'osx' => 'brew install %s'
}[os] % [packages.join(" ")]
sh cmd
end
def git
sh "git clone --depth=1 #{url} #{name}"
compile name
end
def stable
filename = File.basename(url)
sh "wget #{url}"
sh "tar -xzvf #{filename}"
dirname = File.basename(url, ".tar.gz" )
compile dirname
end
def package
package_install(url)
end
def compile(dirname)
sh "cd #{dirname} && #{configure} && make && sudo make install"
sh "cd $TRAVIS_BUILD_DIR"
end
def configure
"./configure"
end
def sh(command)
`#{command}`
end
end
class Libav < TravisDepsBuilder
def build_map
{
"libav-git" => {
:action => :git,
:url => "git://git.libav.org/libav.git"
},
"ffmpeg-git" => {
:action => :git,
:url => "https://github.com/FFmpeg/FFmpeg.git"
}
}
end
def configure
[super, "--cc=#{ENV['CC']} --disable-asm"].join(" ")
end
end
class LibavOsx < Libav
def build_map
{
"ffmpeg-git" => { :action => :package, :url => 'ffmpeg' },
}
end
end
class Libass < TravisDepsBuilder
def build_map
{
"libass-stable" => {
:action => :stable,
:url => 'https://github.com/libass/libass/releases/download/0.12.1/libass-0.12.1.tar.gz'
}
}
end
end
class Dependencies < TravisDepsBuilder
def deps
packages = {
'linux' => 'pkg-config fontconfig libfribidi-dev yasm libxinerama-dev libxrandr-dev',
'osx' => 'pkg-config fontconfig freetype fribidi yasm'
}
package_manager_update
package_install(packages.fetch(os))
end
end
Dependencies.new(:deps).deps
Libass.make(ARGV[0])
Libav.make(ARGV[1])
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
if [ "$TRAVIS_OS_NAME" != "linux" ] || [ "$CC" != "clang" ]; then
# trigger build only on one of the matrix nodes
exit;
fi
if [ "x$TRAVIS_BRANCH" != "xmaster" ]; then
# only rebuild website with pushes to master
exit;
fi
TRAVIS_COMMIT_RANGE=${TRAVIS_COMMIT_RANGE:-HEAD^..}
if git diff --quiet --name-only --exit-code "$TRAVIS_COMMIT_RANGE" DOCS/ VERSION; then
exit;
fi
body='{
"request": {
"branch":"master"
}}'
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Travis-API-Version: 3" \
-H "Authorization: token $WEBSITE_TRAVIS_TOKEN" \
-d "$body" \
https://api.travis-ci.org/repo/mpv-player%2Fmpv.io/requests
+2 -3
View File
@@ -32,7 +32,6 @@ import socket
import errno
import subprocess
import string
import shlex
files = sys.argv[1:]
@@ -53,7 +52,7 @@ def make_abs(filename):
return filename
files = (make_abs(f) for f in files)
SOCK = os.path.join(os.getenv("XDG_RUNTIME_DIR", os.getenv("HOME")), ".umpv_socket")
SOCK = os.path.join(os.getenv("HOME"), ".umpv_socket")
sock = None
try:
@@ -79,7 +78,7 @@ if sock:
else:
# Let mpv recreate socket if it doesn't already exist.
opts = shlex.split(os.getenv("MPV") or "mpv")
opts = (os.getenv("MPV") or "mpv").split()
opts.extend(["--no-terminal", "--force-window", "--input-ipc-server=" + SOCK,
"--"])
opts.extend(files)
+1 -1
View File
@@ -1 +1 @@
0.37.0
0.35.1
+38
View File
@@ -0,0 +1,38 @@
image: Visual Studio 2019
branches:
only:
- master
- appveyor
- ci
environment:
global:
PATH: C:\msys64\usr\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem
matrix:
- MSYSTEM: MINGW64
- MSYSTEM: MINGW32
shallow_clone: true
test: off
install:
# 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
- taskkill /f /fi "MODULES eq msys-2.0.dll"
# Update non-core packages
- C:\msys64\usr\bin\pacman -Suu --noconfirm --noprogressbar --ask=20
# Install required MSYS2 packages
- C:\msys64\usr\bin\pacman -S --noconfirm --noprogressbar --needed base-devel perl python pkgconf
# Now MSYS2 is up to date, do the rest of the install from a bash script
- C:\msys64\usr\bin\bash -lc "cd && exec \"$APPVEYOR_BUILD_FOLDER\"/TOOLS/appveyor-install.sh"
build_script:
- C:\msys64\usr\bin\bash -lc "cd \"$APPVEYOR_BUILD_FOLDER\" && exec ./TOOLS/appveyor-build.sh"
on_failure:
- type "%APPVEYOR_BUILD_FOLDER%\build\config.log"
cache:
- C:\msys64\var\cache\pacman\pkg
+4 -2
View File
@@ -52,7 +52,8 @@ struct mp_aframe *mp_aframe_create(void)
{
struct mp_aframe *frame = talloc_zero(NULL, struct mp_aframe);
frame->av_frame = av_frame_alloc();
MP_HANDLE_OOM(frame->av_frame);
if (!frame->av_frame)
abort();
talloc_set_destructor(frame, free_frame);
mp_aframe_reset(frame);
return frame;
@@ -700,7 +701,8 @@ int mp_aframe_pool_allocate(struct mp_aframe_pool *pool, struct mp_aframe *frame
if (planes > AV_NUM_DATA_POINTERS) {
av_frame->extended_data =
av_calloc(planes, sizeof(av_frame->extended_data[0]));
MP_HANDLE_OOM(av_frame->extended_data);
if (!av_frame->extended_data)
abort();
} else {
av_frame->extended_data = av_frame->data;
}
+1 -11
View File
@@ -52,11 +52,6 @@ static const char *const speaker_names[MP_SPEAKER_ID_COUNT][2] = {
[MP_SPEAKER_ID_SDL] = {"sdl", "surround direct left"},
[MP_SPEAKER_ID_SDR] = {"sdr", "surround direct right"},
[MP_SPEAKER_ID_LFE2] = {"lfe2", "low frequency 2"},
[MP_SPEAKER_ID_TSL] = {"tsl", "top side left"},
[MP_SPEAKER_ID_TSR] = {"tsr", "top side right"},
[MP_SPEAKER_ID_BFC] = {"bfc", "bottom front center"},
[MP_SPEAKER_ID_BFL] = {"bfl", "bottom front left"},
[MP_SPEAKER_ID_BFR] = {"bfr", "bottom front right"},
[MP_SPEAKER_ID_NA] = {"na", "not available"},
};
@@ -88,7 +83,7 @@ static const char *const std_layout_names[][2] = {
{"6.0(front)", "fl-fr-flc-frc-sl-sr"},
{"hexagonal", "fl-fr-fc-bl-br-bc"},
{"6.1", "fl-fr-fc-lfe-bc-sl-sr"},
{"6.1(back)", "fl-fr-fc-lfe-bl-br-bc"},
{"6.1(back)", "fl-fr-fc-lfe-bl-br-bc"}, // lavc calls this "6.1" too
{"6.1(top)", "fl-fr-fc-lfe-bl-br-tc"}, // not in lavc
{"6.1(front)", "fl-fr-lfe-flc-frc-sl-sr"},
{"7.0", "fl-fr-fc-bl-br-sl-sr"},
@@ -98,13 +93,8 @@ static const char *const std_layout_names[][2] = {
{"7.1(alsa)", "fl-fr-bl-br-fc-lfe-sl-sr"}, // not in lavc
{"7.1(wide)", "fl-fr-fc-lfe-bl-br-flc-frc"},
{"7.1(wide-side)", "fl-fr-fc-lfe-flc-frc-sl-sr"},
{"7.1(top)", "fl-fr-fc-lfe-bl-br-tfl-tfr"},
{"7.1(rear)", "fl-fr-fc-lfe-bl-br-sdl-sdr"}, // not in lavc
{"octagonal", "fl-fr-fc-bl-br-bc-sl-sr"},
{"cube", "fl-fr-bl-br-tfl-tfr-tbl-tbr"},
{"hexadecagonal", "fl-fr-fc-bl-br-bc-sl-sr-tfc-tfl-tfr-tbl-tbc-tbr-wl-wr"},
{"downmix", "fl-fr"},
{"22.2", "fl-fr-fc-lfe-bl-br-flc-frc-bc-sl-sr-tc-tfl-tfc-tfr-tbl-tbc-tbr-lfe2-tsl-tsr-bfc-bfl-bfr"},
{"auto", ""}, // not in lavc
{0}
};
+3 -10
View File
@@ -22,7 +22,7 @@
#include <stdbool.h>
#include "misc/bstr.h"
#define MP_NUM_CHANNELS 64
#define MP_NUM_CHANNELS 16
// Speaker a channel can be assigned to.
// This corresponds to WAVEFORMATEXTENSIBLE channel mask bit indexes.
@@ -55,11 +55,6 @@ enum mp_speaker_id {
MP_SPEAKER_ID_SDL, // SURROUND_DIRECT_LEFT
MP_SPEAKER_ID_SDR, // SURROUND_DIRECT_RIGHT
MP_SPEAKER_ID_LFE2, // LOW_FREQUENCY_2
MP_SPEAKER_ID_TSL, // TOP_SIDE_LEFT
MP_SPEAKER_ID_TSR, // TOP_SIDE_RIGHT,
MP_SPEAKER_ID_BFC, // BOTTOM_FRONT_CENTER
MP_SPEAKER_ID_BFL, // BOTTOM_FRONT_LEFT
MP_SPEAKER_ID_BFR, // BOTTOM_FRONT_RIGHT
// Speaker IDs >= 64 are not representable in WAVEFORMATEXTENSIBLE or libav*.
@@ -129,12 +124,10 @@ void mp_chmap_get_reorder(int src[MP_NUM_CHANNELS], const struct mp_chmap *from,
int mp_chmap_diffn(const struct mp_chmap *a, const struct mp_chmap *b);
char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src);
#define mp_chmap_to_str_(m, sz) mp_chmap_to_str_buf((char[sz]){0}, sz, (m))
#define mp_chmap_to_str(m) mp_chmap_to_str_(m, MP_NUM_CHANNELS * 4)
#define mp_chmap_to_str(m) mp_chmap_to_str_buf((char[64]){0}, 64, (m))
char *mp_chmap_to_str_hr_buf(char *buf, size_t buf_size, const struct mp_chmap *src);
#define mp_chmap_to_str_hr_(m, sz) mp_chmap_to_str_hr_buf((char[sz]){0}, sz, (m))
#define mp_chmap_to_str_hr(m) mp_chmap_to_str_hr_(m, MP_NUM_CHANNELS * 4)
#define mp_chmap_to_str_hr(m) mp_chmap_to_str_hr_buf((char[128]){0}, 128, (m))
bool mp_chmap_from_str(struct mp_chmap *dst, bstr src);
+6 -8
View File
@@ -46,7 +46,6 @@
struct priv {
AVCodecContext *avctx;
AVFrame *avframe;
AVPacket *avpkt;
struct mp_chmap force_channel_map;
uint32_t skip_samples, trim_samples;
bool preroll_done;
@@ -60,7 +59,7 @@ struct priv {
#define OPT_BASE_STRUCT struct ad_lavc_params
struct ad_lavc_params {
float ac3drc;
bool downmix;
int downmix;
int threads;
char **avopts;
};
@@ -68,7 +67,7 @@ struct ad_lavc_params {
const struct m_sub_options ad_lavc_conf = {
.opts = (const m_option_t[]) {
{"ac3drc", OPT_FLOAT(ac3drc), M_RANGE(0, 6)},
{"downmix", OPT_BOOL(downmix)},
{"downmix", OPT_FLAG(downmix)},
{"threads", OPT_INT(threads), M_RANGE(0, 16)},
{"o", OPT_KEYVALUELIST(avopts)},
{0}
@@ -76,6 +75,7 @@ const struct m_sub_options ad_lavc_conf = {
.size = sizeof(struct ad_lavc_params),
.defaults = &(const struct ad_lavc_params){
.ac3drc = 0,
.downmix = 0,
.threads = 1,
},
};
@@ -104,8 +104,6 @@ static bool init(struct mp_filter *da, struct mp_codec_params *codec,
lavc_context = avcodec_alloc_context3(lavc_codec);
ctx->avctx = lavc_context;
ctx->avframe = av_frame_alloc();
ctx->avpkt = av_packet_alloc();
MP_HANDLE_OOM(ctx->avctx && ctx->avframe && ctx->avpkt);
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
lavc_context->codec_id = lavc_codec->id;
lavc_context->pkt_timebase = ctx->codec_timebase;
@@ -162,7 +160,6 @@ static void destroy(struct mp_filter *da)
avcodec_free_context(&ctx->avctx);
av_frame_free(&ctx->avframe);
mp_free_av_packet(&ctx->avpkt);
}
static void reset(struct mp_filter *da)
@@ -188,9 +185,10 @@ static int send_packet(struct mp_filter *da, struct demux_packet *mpkt)
if (mpkt && priv->next_pts == MP_NOPTS_VALUE)
priv->next_pts = mpkt->pts;
mp_set_av_packet(priv->avpkt, mpkt, &priv->codec_timebase);
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, &priv->codec_timebase);
int ret = avcodec_send_packet(avctx, mpkt ? priv->avpkt : NULL);
int ret = avcodec_send_packet(avctx, mpkt ? &pkt : NULL);
if (ret < 0)
MP_ERR(da, "Error decoding audio.\n");
return ret;
+8 -17
View File
@@ -41,7 +41,6 @@ struct spdifContext {
struct mp_log *log;
enum AVCodecID codec_id;
AVFormatContext *lavf_ctx;
AVPacket *avpkt;
int out_buffer_len;
uint8_t out_buffer[OUTBUF_SIZE];
bool need_close;
@@ -83,7 +82,6 @@ static void destroy(struct mp_filter *da)
avformat_free_context(lavf_ctx);
spdif_ctx->lavf_ctx = NULL;
}
mp_free_av_packet(&spdif_ctx->avpkt);
}
static void determine_codec_params(struct mp_filter *da, AVPacket *pkt,
@@ -149,12 +147,10 @@ done:
MP_WARN(da, "Failed to parse codec profile.\n");
}
static int init_filter(struct mp_filter *da)
static int init_filter(struct mp_filter *da, AVPacket *pkt)
{
struct spdifContext *spdif_ctx = da->priv;
AVPacket *pkt = spdif_ctx->avpkt;
int profile = FF_PROFILE_UNKNOWN;
int c_rate = 0;
determine_codec_params(da, pkt, &profile, &c_rate);
@@ -171,7 +167,8 @@ static int init_filter(struct mp_filter *da)
goto fail;
void *buffer = av_mallocz(OUTBUF_SIZE);
MP_HANDLE_OOM(buffer);
if (!buffer)
abort();
lavf_ctx->pb = avio_alloc_context(buffer, OUTBUF_SIZE, 1, spdif_ctx, NULL,
write_packet, NULL);
if (!lavf_ctx->pb) {
@@ -298,20 +295,15 @@ static void process(struct mp_filter *da)
struct mp_aframe *out = NULL;
double pts = mpkt->pts;
if (!spdif_ctx->avpkt) {
spdif_ctx->avpkt = av_packet_alloc();
MP_HANDLE_OOM(spdif_ctx->avpkt);
}
mp_set_av_packet(spdif_ctx->avpkt, mpkt, NULL);
spdif_ctx->avpkt->pts = spdif_ctx->avpkt->dts = 0;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
pkt.pts = pkt.dts = 0;
if (!spdif_ctx->lavf_ctx) {
if (init_filter(da) < 0)
if (init_filter(da, &pkt) < 0)
goto done;
assert(spdif_ctx->avpkt);
}
spdif_ctx->out_buffer_len = 0;
int ret = av_write_frame(spdif_ctx->lavf_ctx, spdif_ctx->avpkt);
int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt);
avio_flush(spdif_ctx->lavf_ctx->pb);
if (ret < 0) {
MP_ERR(da, "spdif mux error: '%s'\n", mp_strerror(AVUNERROR(ret)));
@@ -432,7 +424,6 @@ static struct mp_decoder *create(struct mp_filter *parent,
talloc_free(da);
return NULL;
}
return &spdif_ctx->public;
}
+2 -2
View File
@@ -30,7 +30,7 @@ struct f_opts {
int out_srate;
struct m_channels out_channels;
bool fail;
int fail;
};
struct priv {
@@ -135,7 +135,7 @@ const struct mp_user_filter_entry af_format = {
{"out-srate", OPT_INT(out_srate), M_RANGE(1000, 8*48000)},
{"out-channels", OPT_CHANNELS(out_channels),
.flags = M_OPT_CHANNELS_LIMITED},
{"fail", OPT_BOOL(fail)},
{"fail", OPT_FLAG(fail)},
{0}
},
},
+3 -3
View File
@@ -56,7 +56,7 @@ const static uint16_t ac3_bitrate_tab[19] = {
};
struct f_opts {
bool add_iec61937_header;
int add_iec61937_header;
int bit_rate;
int min_channel_num;
char *encoder;
@@ -418,13 +418,13 @@ const struct mp_user_filter_entry af_lavcac3enc = {
.name = "lavcac3enc",
.priv_size = sizeof(OPT_BASE_STRUCT),
.priv_defaults = &(const OPT_BASE_STRUCT) {
.add_iec61937_header = true,
.add_iec61937_header = 1,
.bit_rate = 640,
.min_channel_num = 3,
.encoder = "ac3",
},
.options = (const struct m_option[]) {
{"tospdif", OPT_BOOL(add_iec61937_header)},
{"tospdif", OPT_FLAG(add_iec61937_header)},
{"bitrate", OPT_CHOICE(bit_rate,
{"auto", 0}, {"default", 0}), M_RANGE(32, 640)},
{"minch", OPT_INT(min_channel_num), M_RANGE(2, 6)},
+1 -1
View File
@@ -80,7 +80,7 @@ static bool init_rubberband(struct mp_filter *f)
int opts = p->opts->transients | p->opts->detector | p->opts->phase |
p->opts->window | p->opts->smoothing | p->opts->formant |
p->opts->pitch | p->opts->channels |
p->opts->pitch | p->opts->channels |
#if HAVE_RUBBERBAND_3
p->opts->engine |
#endif
+4 -4
View File
@@ -48,7 +48,7 @@ struct f_opts {
float scale_nominal;
float ms_stride;
float ms_search;
float factor_overlap;
float percent_overlap;
#define SCALE_TEMPO 1
#define SCALE_PITCH 2
int speed_opt;
@@ -400,7 +400,7 @@ static bool reinit(struct mp_filter *f)
update_speed(s, s->speed);
int frames_overlap = s->frames_stride * s->opts->factor_overlap;
int frames_overlap = s->frames_stride * s->opts->percent_overlap;
if (frames_overlap <= 0) {
s->bytes_standing = s->bytes_stride;
s->samples_standing = s->bytes_standing / bps;
@@ -604,7 +604,7 @@ const struct mp_user_filter_entry af_scaletempo = {
.priv_size = sizeof(OPT_BASE_STRUCT),
.priv_defaults = &(const OPT_BASE_STRUCT) {
.ms_stride = 60,
.factor_overlap = .20,
.percent_overlap = .20,
.ms_search = 14,
.speed_opt = SCALE_TEMPO,
.scale_nominal = 1.0,
@@ -612,7 +612,7 @@ const struct mp_user_filter_entry af_scaletempo = {
.options = (const struct m_option[]) {
{"scale", OPT_FLOAT(scale_nominal), M_RANGE(0.01, DBL_MAX)},
{"stride", OPT_FLOAT(ms_stride), M_RANGE(0.01, DBL_MAX)},
{"overlap", OPT_FLOAT(factor_overlap), M_RANGE(0, 1)},
{"overlap", OPT_FLOAT(percent_overlap), M_RANGE(0, 1)},
{"search", OPT_FLOAT(ms_search), M_RANGE(0, DBL_MAX)},
{"speed", OPT_CHOICE(speed_opt,
{"pitch", SCALE_PITCH},
+20 -29
View File
@@ -15,6 +15,7 @@ struct priv {
bool sent_final;
struct mp_aframe *pending;
bool initialized;
double frame_delay;
float speed;
};
@@ -29,7 +30,7 @@ static void process(struct mp_filter *f)
return;
while (!p->initialized || !p->pending ||
!mp_scaletempo2_frames_available(&p->data, p->speed))
!mp_scaletempo2_frames_available(&p->data))
{
bool eof = false;
if (!p->pending || !mp_aframe_get_size(p->pending)) {
@@ -65,15 +66,13 @@ static void process(struct mp_filter *f)
int frame_size = mp_aframe_get_size(p->pending);
uint8_t **planes = mp_aframe_get_data_ro(p->pending);
int read = mp_scaletempo2_fill_input_buffer(&p->data,
planes, frame_size, p->speed);
planes, frame_size, final);
p->frame_delay += read;
mp_aframe_skip_samples(p->pending, read);
}
if (final && p->pending && !p->sent_final) {
mp_scaletempo2_set_final(&p->data);
p->sent_final = true;
}
p->sent_final |= final;
if (mp_scaletempo2_frames_available(&p->data, p->speed)) {
if (mp_scaletempo2_frames_available(&p->data)) {
if (eof) {
mp_pin_out_repeat_eof(p->in_pin); // drain more next time
}
@@ -83,13 +82,16 @@ static void process(struct mp_filter *f)
if (eof) {
mp_pin_in_write(f->ppins[1], MP_EOF_FRAME);
return;
} else if (format_change) {
// go on with proper reinit on the next iteration
p->initialized = false;
p->sent_final = false;
}
// for format change go on with proper reinit on the next iteration
}
}
assert(p->pending);
if (mp_scaletempo2_frames_available(&p->data, p->speed)) {
if (mp_scaletempo2_frames_available(&p->data)) {
struct mp_aframe *out = mp_aframe_new_ref(p->cur_format);
int out_samples = p->data.ola_hop_size;
if (mp_aframe_pool_allocate(p->out_pool, out, out_samples) < 0) {
@@ -107,24 +109,11 @@ static void process(struct mp_filter *f)
(float**)planes, out_samples, p->speed);
double pts = mp_aframe_get_pts(p->pending);
p->frame_delay -= out_samples * p->speed;
if (pts != MP_NOPTS_VALUE) {
double frame_delay = mp_scaletempo2_get_latency(&p->data, p->speed)
+ out_samples * p->speed;
mp_aframe_set_pts(out, pts - frame_delay / mp_aframe_get_effective_rate(out));
if (p->sent_final) {
double remain_pts = pts - mp_aframe_get_pts(out);
double rate = mp_aframe_get_effective_rate(out) / p->speed;
int max_samples = MPMAX(0, (int) (remain_pts * rate));
// truncate final packet to expected length
if (out_samples >= max_samples) {
out_samples = max_samples;
// reset the filter to ensure it stops generating audio
// and mp_scaletempo2_frames_available returns false
mp_scaletempo2_reset(&p->data);
}
}
double delay = p->frame_delay / mp_aframe_get_effective_rate(out);
mp_aframe_set_pts(out, pts - delay);
}
mp_aframe_set_size(out, out_samples);
@@ -148,6 +137,7 @@ static bool init_scaletempo2(struct mp_filter *f)
mp_aframe_reset(p->cur_format);
p->initialized = true;
p->sent_final = false;
p->frame_delay = 0;
mp_aframe_config_copy(p->cur_format, p->pending);
mp_scaletempo2_init(&p->data, mp_aframe_get_channels(p->pending),
@@ -173,6 +163,7 @@ static void reset(struct mp_filter *f)
{
struct priv *p = f->priv;
mp_scaletempo2_reset(&p->data);
p->frame_delay = 0;
p->initialized = false;
TA_FREEP(&p->pending);
}
@@ -234,9 +225,9 @@ const struct mp_user_filter_entry af_scaletempo2 = {
.priv_size = sizeof(OPT_BASE_STRUCT),
.priv_defaults = &(const OPT_BASE_STRUCT) {
.min_playback_rate = 0.25,
.max_playback_rate = 8.0,
.ola_window_size_ms = 12,
.wsola_search_interval_ms = 40,
.max_playback_rate = 4.0,
.ola_window_size_ms = 20,
.wsola_search_interval_ms = 30,
},
.options = (const struct m_option[]) {
{"search-interval",
+51 -128
View File
@@ -421,15 +421,18 @@ static void seek_buffer(struct mp_scaletempo2 *p, int frames)
{
assert(p->input_buffer_frames >= frames);
p->input_buffer_frames -= frames;
if (p->input_buffer_final_frames > 0) {
p->input_buffer_final_frames = MPMAX(0, p->input_buffer_final_frames - frames);
}
for (int i = 0; i < p->channels; ++i) {
memmove(p->input_buffer[i], p->input_buffer[i] + frames,
p->input_buffer_frames * sizeof(float));
}
}
static void read_buffer(struct mp_scaletempo2 *p, int frames, float **dest)
{
peek_buffer(p, frames, 0, 0, dest);
seek_buffer(p, frames);
}
static int write_completed_frames_to(struct mp_scaletempo2 *p,
int requested_frames, int dest_offset, float **dest)
{
@@ -453,31 +456,18 @@ static int write_completed_frames_to(struct mp_scaletempo2 *p,
return rendered_frames;
}
// next output_time for the given playback_rate
static double get_updated_time(struct mp_scaletempo2 *p, double playback_rate)
static bool can_perform_wsola(struct mp_scaletempo2 *p)
{
return p->output_time + p->ola_hop_size * playback_rate;
}
// search_block_index for the given output_time
static int get_search_block_index(struct mp_scaletempo2 *p, double output_time)
{
return (int)(output_time - p->search_block_center_offset + 0.5);
return p->target_block_index + p->ola_window_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
static int frames_needed(struct mp_scaletempo2 *p, double playback_rate)
static int frames_needed(struct mp_scaletempo2 *p)
{
int search_block_index =
get_search_block_index(p, get_updated_time(p, playback_rate));
return MPMAX(0, MPMAX(
p->target_block_index + p->ola_window_size - p->input_buffer_frames,
search_block_index + p->search_block_size - p->input_buffer_frames));
}
static bool can_perform_wsola(struct mp_scaletempo2 *p, double playback_rate)
{
return frames_needed(p, playback_rate) <= 0;
p->search_block_index + p->search_block_size - p->input_buffer_frames));
}
static void resize_input_buffer(struct mp_scaletempo2 *p, int size)
@@ -486,53 +476,27 @@ static void resize_input_buffer(struct mp_scaletempo2 *p, int size)
p->input_buffer = realloc_2d(p->input_buffer, p->channels, size);
}
// pad end with silence until a wsola iteration can be performed
static void add_input_buffer_final_silence(struct mp_scaletempo2 *p, double playback_rate)
{
int needed = frames_needed(p, playback_rate);
if (needed <= 0)
return; // no silence needed for iteration
int required_size = needed + p->input_buffer_frames;
if (required_size > p->input_buffer_size)
resize_input_buffer(p, required_size);
for (int i = 0; i < p->channels; ++i) {
float *ch_input = p->input_buffer[i];
for (int j = 0; j < needed; ++j) {
ch_input[p->input_buffer_frames + j] = 0.0f;
}
}
p->input_buffer_added_silence += needed;
p->input_buffer_frames += needed;
}
void mp_scaletempo2_set_final(struct mp_scaletempo2 *p)
{
if (p->input_buffer_final_frames <= 0) {
p->input_buffer_final_frames = p->input_buffer_frames;
}
}
int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
uint8_t **planes, int frame_size, double playback_rate)
uint8_t **planes, int frame_size, bool final)
{
int needed = frames_needed(p, playback_rate);
int needed = frames_needed(p);
int read = MPMIN(needed, frame_size);
if (read == 0)
return 0;
int total_fill = final ? needed : read;
if (total_fill == 0) return 0;
int required_size = read + p->input_buffer_frames;
int required_size = total_fill + p->input_buffer_frames;
if (required_size > p->input_buffer_size)
resize_input_buffer(p, required_size);
for (int i = 0; i < p->channels; ++i) {
memcpy(p->input_buffer[i] + p->input_buffer_frames,
planes[i], read * sizeof(float));
for (int j = read; j < total_fill; ++j) {
p->input_buffer[p->input_buffer_frames + j] = 0;
}
}
p->input_buffer_frames += read;
p->input_buffer_frames += total_fill;
return read;
}
@@ -623,13 +587,17 @@ static void get_optimal_block(struct mp_scaletempo2 *p)
p->target_block_index = optimal_index + p->ola_hop_size;
}
static void set_output_time(struct mp_scaletempo2 *p, double output_time)
static void update_output_time(struct mp_scaletempo2 *p,
float playback_rate, double time_change)
{
p->output_time = output_time;
p->search_block_index = get_search_block_index(p, output_time);
p->output_time += time_change;
// Center of the search region, in frames.
int search_block_center_index = (int)(p->output_time * playback_rate + 0.5);
p->search_block_index = search_block_center_index
- p->search_block_center_offset;
}
static void remove_old_input_frames(struct mp_scaletempo2 *p)
static void remove_old_input_frames(struct mp_scaletempo2 *p, float playback_rate)
{
const int earliest_used_index = MPMIN(
p->target_block_index, p->search_block_index);
@@ -639,69 +607,46 @@ static void remove_old_input_frames(struct mp_scaletempo2 *p)
// Remove frames from input and adjust indices accordingly.
seek_buffer(p, earliest_used_index);
p->target_block_index -= earliest_used_index;
p->output_time -= earliest_used_index;
p->search_block_index -= earliest_used_index;
// Adjust output index.
double output_time_change = ((double) earliest_used_index) / playback_rate;
assert(p->output_time >= output_time_change);
update_output_time(p, playback_rate, -output_time_change);
}
static bool run_one_wsola_iteration(struct mp_scaletempo2 *p, double playback_rate)
static bool run_one_wsola_iteration(struct mp_scaletempo2 *p, float playback_rate)
{
if (!can_perform_wsola(p, playback_rate)) {
if (!can_perform_wsola(p)){
return false;
}
set_output_time(p, get_updated_time(p, playback_rate));
remove_old_input_frames(p);
assert(p->search_block_index + p->search_block_size <= p->input_buffer_frames);
get_optimal_block(p);
// Overlap-and-add.
for (int k = 0; k < p->channels; ++k) {
float* ch_opt_frame = p->optimal_block[k];
float* ch_output = p->wsola_output[k] + p->num_complete_frames;
if (p->wsola_output_started) {
for (int n = 0; n < p->ola_hop_size; ++n) {
ch_output[n] = ch_output[n] * p->ola_window[p->ola_hop_size + n] +
ch_opt_frame[n] * p->ola_window[n];
}
// Copy the second half to the output.
memcpy(&ch_output[p->ola_hop_size], &ch_opt_frame[p->ola_hop_size],
sizeof(*ch_opt_frame) * p->ola_hop_size);
} else {
// No overlap for the first iteration.
memcpy(ch_output, ch_opt_frame,
sizeof(*ch_opt_frame) * p->ola_window_size);
for (int n = 0; n < p->ola_hop_size; ++n) {
ch_output[n] = ch_output[n] * p->ola_window[p->ola_hop_size + n] +
ch_opt_frame[n] * p->ola_window[n];
}
// Copy the second half to the output.
memcpy(&ch_output[p->ola_hop_size], &ch_opt_frame[p->ola_hop_size],
sizeof(*ch_opt_frame) * p->ola_hop_size);
}
p->num_complete_frames += p->ola_hop_size;
p->wsola_output_started = true;
update_output_time(p, playback_rate, p->ola_hop_size);
remove_old_input_frames(p, playback_rate);
return true;
}
static int read_input_buffer(struct mp_scaletempo2 *p, int dest_size, float **dest)
{
int frames_to_copy = MPMIN(dest_size, p->input_buffer_frames - p->target_block_index);
if (frames_to_copy <= 0)
return 0; // There is nothing to read from input buffer; return.
peek_buffer(p, frames_to_copy, p->target_block_index, 0, dest);
seek_buffer(p, frames_to_copy);
return frames_to_copy;
}
int mp_scaletempo2_fill_buffer(struct mp_scaletempo2 *p,
float **dest, int dest_size, double playback_rate)
float **dest, int dest_size, float playback_rate)
{
if (playback_rate == 0) return 0;
if (p->input_buffer_final_frames > 0) {
add_input_buffer_final_silence(p, playback_rate);
}
// Optimize the muted case to issue a single clear instead of performing
// the full crossfade and clearing each crossfaded frame.
if (playback_rate < p->opts->min_playback_rate
@@ -735,16 +680,9 @@ int mp_scaletempo2_fill_buffer(struct mp_scaletempo2 *p,
// Optimize the most common |playback_rate| ~= 1 case to use a single copy
// instead of copying frame by frame.
if (p->ola_window_size <= faster_step && slower_step >= p->ola_window_size) {
if (p->wsola_output_started) {
p->wsola_output_started = false;
// sync audio precisely again
set_output_time(p, p->target_block_index);
remove_old_input_frames(p);
}
return read_input_buffer(p, dest_size, dest);
int frames_to_copy = MPMIN(dest_size, p->input_buffer_frames);
read_buffer(p, frames_to_copy, dest);
return frames_to_copy;
}
int rendered_frames = 0;
@@ -756,18 +694,9 @@ int mp_scaletempo2_fill_buffer(struct mp_scaletempo2 *p,
return rendered_frames;
}
double mp_scaletempo2_get_latency(struct mp_scaletempo2 *p, double playback_rate)
bool mp_scaletempo2_frames_available(struct mp_scaletempo2 *p)
{
return p->input_buffer_frames - p->output_time
- p->input_buffer_added_silence
+ p->num_complete_frames * playback_rate;
}
bool mp_scaletempo2_frames_available(struct mp_scaletempo2 *p, double playback_rate)
{
return p->input_buffer_final_frames > p->target_block_index
|| can_perform_wsola(p, playback_rate)
|| p->num_complete_frames > 0;
return can_perform_wsola(p) || p->num_complete_frames > 0;
}
void mp_scaletempo2_destroy(struct mp_scaletempo2 *p)
@@ -785,15 +714,12 @@ void mp_scaletempo2_destroy(struct mp_scaletempo2 *p)
void mp_scaletempo2_reset(struct mp_scaletempo2 *p)
{
p->input_buffer_frames = 0;
p->input_buffer_final_frames = 0;
p->input_buffer_added_silence = 0;
p->output_time = 0.0;
p->search_block_index = 0;
p->target_block_index = 0;
// Clear the queue of decoded packets.
zero_2d(p->wsola_output, p->channels, p->wsola_output_size);
p->num_complete_frames = 0;
p->wsola_output_started = false;
}
// Return a "periodic" Hann window. This is the first L samples of an L+1
@@ -810,10 +736,9 @@ void mp_scaletempo2_init(struct mp_scaletempo2 *p, int channels, int rate)
{
p->muted_partial_frame = 0;
p->output_time = 0;
p->search_block_center_offset = 0;
p->search_block_index = 0;
p->target_block_index = 0;
p->num_complete_frames = 0;
p->wsola_output_started = false;
p->channels = channels;
p->samples_per_second = rate;
@@ -865,8 +790,6 @@ void mp_scaletempo2_init(struct mp_scaletempo2 *p, int channels, int rate)
resize_input_buffer(p, 4 * MPMAX(p->ola_window_size, p->search_block_size));
p->input_buffer_frames = 0;
p->input_buffer_final_frames = 0;
p->input_buffer_added_silence = 0;
p->energy_candidate_blocks = realloc(p->energy_candidate_blocks,
sizeof(float) * p->channels * p->num_candidate_blocks);
+12 -25
View File
@@ -53,19 +53,18 @@ struct mp_scaletempo2 {
int samples_per_second;
// If muted, keep track of partial frames that should have been skipped over.
double muted_partial_frame;
// Book keeping of the current time of generated audio, in frames.
// Corresponds to the center of |search_block|. This is increased in
// intervals of |ola_hop_size| multiplied by the current playback_rate,
// for every WSOLA iteration. This tracks the number of advanced frames as
// a double to achieve accurate playback rates beyond the integer precision
// of |search_block_index|.
// Needs to be adjusted like any other index when frames are evicted from
// |input_buffer|.
// Book keeping of the current time of generated audio, in frames. This
// should be appropriately updated when out samples are generated, regardless
// of whether we push samples out when fill_buffer() is called or we store
// audio in |wsola_output| for the subsequent calls to fill_buffer().
// Furthermore, if samples from |audio_buffer| are evicted then this
// member variable should be updated based on |playback_rate|.
// Note that this member should be updated ONLY by calling update_output_time(),
// so that |search_block_index| is update accordingly.
double output_time;
// The offset of the center frame of |search_block| w.r.t. its first frame.
int search_block_center_offset;
// Index of the beginning of the |search_block|, in frames. This may be
// negative, which is handled by |peek_audio_with_zero_prepend|.
// Index of the beginning of the |search_block|, in frames.
int search_block_index;
// Number of Blocks to search to find the most similar one to the target
// frame.
@@ -81,9 +80,6 @@ struct mp_scaletempo2 {
// them and can be copied to output if fill_buffer() is called. It also
// specifies the index where the next WSOLA window has to overlap-and-add.
int num_complete_frames;
// Whether |wsola_output| contains an additional |ola_hop_size| of overlap
// frames for the next iteration.
bool wsola_output_started;
// Overlap-and-add window.
float *ola_window;
// Transition window, used to update |optimal_block| by a weighted sum of
@@ -112,23 +108,14 @@ struct mp_scaletempo2 {
float **input_buffer;
int input_buffer_size;
int input_buffer_frames;
// How many frames in |input_buffer| need to be flushed by padding with
// silence to process the final packet. While this is nonzero, the filter
// appends silence to |input_buffer| until these frames are processed.
int input_buffer_final_frames;
// How many additional frames of silence have been added to |input_buffer|
// for padding after the final packet.
int input_buffer_added_silence;
float *energy_candidate_blocks;
};
void mp_scaletempo2_destroy(struct mp_scaletempo2 *p);
void mp_scaletempo2_reset(struct mp_scaletempo2 *p);
void mp_scaletempo2_init(struct mp_scaletempo2 *p, int channels, int rate);
double mp_scaletempo2_get_latency(struct mp_scaletempo2 *p, double playback_rate);
int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
uint8_t **planes, int frame_size, double playback_rate);
void mp_scaletempo2_set_final(struct mp_scaletempo2 *p);
uint8_t **planes, int frame_size, bool final);
int mp_scaletempo2_fill_buffer(struct mp_scaletempo2 *p,
float **dest, int dest_size, double playback_rate);
bool mp_scaletempo2_frames_available(struct mp_scaletempo2 *p, double playback_rate);
float **dest, int dest_size, float playback_rate);
bool mp_scaletempo2_frames_available(struct mp_scaletempo2 *p);
+5 -4
View File
@@ -102,11 +102,12 @@ static const struct ao_driver * const audio_out_drivers[] = {
#endif
&audio_out_pcm,
&audio_out_lavc,
NULL
};
static bool get_desc(struct m_obj_desc *dst, int index)
{
if (index >= MP_ARRAY_SIZE(audio_out_drivers))
if (index >= MP_ARRAY_SIZE(audio_out_drivers) - 1)
return false;
const struct ao_driver *ao = audio_out_drivers[index];
*dst = (struct m_obj_desc) {
@@ -312,7 +313,7 @@ struct ao *ao_init_best(struct mpv_global *global,
}
if (autoprobe) {
for (int n = 0; n < MP_ARRAY_SIZE(audio_out_drivers); n++) {
for (int n = 0; audio_out_drivers[n]; n++) {
const struct ao_driver *driver = audio_out_drivers[n];
if (driver == &audio_out_null)
break;
@@ -520,7 +521,7 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp,
}
}
for (int n = 0; n < MP_ARRAY_SIZE(audio_out_drivers); n++) {
for (int n = 0; audio_out_drivers[n]; n++) {
const struct ao_driver *d = audio_out_drivers[n];
if (d == &audio_out_null)
break; // don't add unsafe/special entries
@@ -701,7 +702,7 @@ static void convert_plane(int type, void *data, int num_samples)
break;
}
default:
MP_ASSERT_UNREACHABLE();
abort();
}
}
+15 -4
View File
@@ -26,7 +26,8 @@
#include "audio/chmap_sel.h"
enum aocontrol {
// _VOLUME commands take a pointer to float for input/output.
// _VOLUME commands take struct ao_control_vol pointer for input/output.
// If there's only one volume, SET should use average of left/right.
AOCONTROL_GET_VOLUME,
AOCONTROL_SET_VOLUME,
// _MUTE commands take a pointer to bool
@@ -34,6 +35,8 @@ enum aocontrol {
AOCONTROL_SET_MUTE,
// Has char* as argument, which contains the desired stream title.
AOCONTROL_UPDATE_STREAM_TITLE,
// Has enum aocontrol_media_role* argument, which contains the current media role
AOCONTROL_UPDATE_MEDIA_ROLE,
};
// If set, then the queued audio data is the last. Note that after a while, new
@@ -56,8 +59,16 @@ enum {
AO_INIT_STREAM_SILENCE = 1 << 2,
// Force exclusive mode, i.e. lock out the system mixer.
AO_INIT_EXCLUSIVE = 1 << 3,
// Initialize with music role.
AO_INIT_MEDIA_ROLE_MUSIC = 1 << 4,
};
typedef struct ao_control_vol {
float left;
float right;
} ao_control_vol_t;
enum aocontrol_media_role {
AOCONTROL_MEDIA_ROLE_MUSIC,
AOCONTROL_MEDIA_ROLE_MOVIE,
};
struct ao_device_desc {
@@ -98,7 +109,7 @@ void ao_set_gain(struct ao *ao, float gain);
double ao_get_delay(struct ao *ao);
void ao_reset(struct ao *ao);
void ao_start(struct ao *ao);
void ao_set_paused(struct ao *ao, bool paused, bool eof);
void ao_set_paused(struct ao *ao, bool paused);
void ao_drain(struct ao *ao);
bool ao_is_playing(struct ao *ao);
struct mp_async_queue;
+18 -15
View File
@@ -34,6 +34,7 @@
#include <math.h>
#include <string.h>
#include "config.h"
#include "options/options.h"
#include "options/m_config.h"
#include "options/m_option.h"
@@ -56,9 +57,9 @@ struct ao_alsa_opts {
char *mixer_device;
char *mixer_name;
int mixer_index;
bool resample;
bool ni;
bool ignore_chmap;
int resample;
int ni;
int ignore_chmap;
int buffer_time;
int frags;
};
@@ -66,12 +67,12 @@ struct ao_alsa_opts {
#define OPT_BASE_STRUCT struct ao_alsa_opts
static const struct m_sub_options ao_alsa_conf = {
.opts = (const struct m_option[]) {
{"alsa-resample", OPT_BOOL(resample)},
{"alsa-resample", OPT_FLAG(resample)},
{"alsa-mixer-device", OPT_STRING(mixer_device)},
{"alsa-mixer-name", OPT_STRING(mixer_name)},
{"alsa-mixer-index", OPT_INT(mixer_index), M_RANGE(0, 99)},
{"alsa-non-interleaved", OPT_BOOL(ni)},
{"alsa-ignore-chmap", OPT_BOOL(ignore_chmap)},
{"alsa-non-interleaved", OPT_FLAG(ni)},
{"alsa-ignore-chmap", OPT_FLAG(ignore_chmap)},
{"alsa-buffer-time", OPT_INT(buffer_time), M_RANGE(0, INT_MAX)},
{"alsa-periods", OPT_INT(frags), M_RANGE(0, INT_MAX)},
{0}
@@ -79,6 +80,8 @@ static const struct m_sub_options ao_alsa_conf = {
.defaults = &(const struct ao_alsa_opts) {
.mixer_device = "default",
.mixer_name = "Master",
.mixer_index = 0,
.ni = 0,
.buffer_time = 100000,
.frags = 4,
},
@@ -165,13 +168,15 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
switch (cmd) {
case AOCONTROL_SET_VOLUME: {
float *vol = arg;
set_vol = *vol / f_multi + pmin + 0.5;
ao_control_vol_t *vol = arg;
set_vol = vol->left / f_multi + pmin + 0.5;
err = snd_mixer_selem_set_playback_volume(elem, 0, set_vol);
CHECK_ALSA_ERROR("Error setting left channel");
MP_DBG(ao, "left=%li, ", set_vol);
set_vol = vol->right / f_multi + pmin + 0.5;
err = snd_mixer_selem_set_playback_volume(elem, 1, set_vol);
CHECK_ALSA_ERROR("Error setting right channel");
MP_DBG(ao, "right=%li, pmin=%li, pmax=%li, mult=%f\n",
@@ -179,14 +184,12 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
break;
}
case AOCONTROL_GET_VOLUME: {
float *vol = arg;
float left, right;
ao_control_vol_t *vol = arg;
snd_mixer_selem_get_playback_volume(elem, 0, &get_vol);
left = (get_vol - pmin) * f_multi;
vol->left = (get_vol - pmin) * f_multi;
snd_mixer_selem_get_playback_volume(elem, 1, &get_vol);
right = (get_vol - pmin) * f_multi;
*vol = (left + right) / 2.0;
MP_DBG(ao, "vol=%f\n", *vol);
vol->right = (get_vol - pmin) * f_multi;
MP_DBG(ao, "left=%f, right=%f\n", vol->left, vol->right);
break;
}
case AOCONTROL_SET_MUTE: {
@@ -560,7 +563,7 @@ static char *append_params(void *ta_parent, const char *device, const char *p)
/* a simple list of parameters: add it at the end of the list */
return talloc_asprintf(ta_parent, "%s,%s", device, p);
}
MP_ASSERT_UNREACHABLE();
abort();
}
static int try_open_device(struct ao *ao, const char *device, int mode)
+94 -115
View File
@@ -54,7 +54,7 @@ struct priv {
jfloatArray floatarray;
jobject bbuf;
bool cfg_pcm_float;
int cfg_pcm_float;
int cfg_session_id;
bool needs_timestamp_offset;
@@ -62,9 +62,9 @@ struct priv {
bool thread_terminate;
bool thread_created;
mp_thread thread;
mp_mutex lock;
mp_cond wakeup;
pthread_t thread;
pthread_mutex_t lock;
pthread_cond_t wakeup;
};
struct JNIByteBuffer {
@@ -92,6 +92,7 @@ struct JNIAudioTrack {
jmethodID pause;
jmethodID write;
jmethodID writeFloat;
jmethodID writeV23;
jmethodID writeShortV23;
jmethodID writeBufferV21;
jmethodID getBufferSizeInFramesV23;
@@ -125,6 +126,7 @@ struct JNIAudioTrack {
{"android/media/AudioTrack", "pause", "()V", MP_JNI_METHOD, OFFSET(pause), 1},
{"android/media/AudioTrack", "write", "([BII)I", MP_JNI_METHOD, OFFSET(write), 1},
{"android/media/AudioTrack", "write", "([FIII)I", MP_JNI_METHOD, OFFSET(writeFloat), 1},
{"android/media/AudioTrack", "write", "([BIII)I", MP_JNI_METHOD, OFFSET(writeV23), 0},
{"android/media/AudioTrack", "write", "([SIII)I", MP_JNI_METHOD, OFFSET(writeShortV23), 0},
{"android/media/AudioTrack", "write", "(Ljava/nio/ByteBuffer;II)I", MP_JNI_METHOD, OFFSET(writeBufferV21), 1},
{"android/media/AudioTrack", "getBufferSizeInFrames", "()I", MP_JNI_METHOD, OFFSET(getBufferSizeInFramesV23), 0},
@@ -150,14 +152,12 @@ struct JNIAudioTrack {
struct JNIAudioAttributes {
jclass clazz;
jint CONTENT_TYPE_MOVIE;
jint CONTENT_TYPE_MUSIC;
jint USAGE_MEDIA;
struct MPJniField mapping[];
} AudioAttributes = {.mapping = {
#define OFFSET(member) offsetof(struct JNIAudioAttributes, member)
{"android/media/AudioAttributes", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 0},
{"android/media/AudioAttributes", "CONTENT_TYPE_MOVIE", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CONTENT_TYPE_MOVIE), 0},
{"android/media/AudioAttributes", "CONTENT_TYPE_MUSIC", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CONTENT_TYPE_MUSIC), 0},
{"android/media/AudioAttributes", "USAGE_MEDIA", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(USAGE_MEDIA), 0},
{0}
#undef OFFSET
@@ -187,13 +187,19 @@ struct JNIAudioFormat {
jint ENCODING_PCM_16BIT;
jint ENCODING_PCM_FLOAT;
jint ENCODING_IEC61937;
jint ENCODING_AC3;
jint CHANNEL_OUT_MONO;
jint CHANNEL_OUT_STEREO;
jint CHANNEL_OUT_FRONT_LEFT;
jint CHANNEL_OUT_FRONT_RIGHT;
jint CHANNEL_OUT_BACK_LEFT;
jint CHANNEL_OUT_BACK_RIGHT;
jint CHANNEL_OUT_FRONT_CENTER;
jint CHANNEL_OUT_QUAD;
jint CHANNEL_OUT_5POINT1;
jint CHANNEL_OUT_LOW_FREQUENCY;
jint CHANNEL_OUT_BACK_CENTER;
jint CHANNEL_OUT_7POINT1_SURROUND;
jint CHANNEL_OUT_5POINT1;
jint CHANNEL_OUT_SIDE_LEFT;
jint CHANNEL_OUT_SIDE_RIGHT;
struct MPJniField mapping[];
} AudioFormat = {.mapping = {
#define OFFSET(member) offsetof(struct JNIAudioFormat, member)
@@ -201,14 +207,20 @@ struct JNIAudioFormat {
{"android/media/AudioFormat", "ENCODING_PCM_8BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_8BIT), 1},
{"android/media/AudioFormat", "ENCODING_PCM_16BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_16BIT), 1},
{"android/media/AudioFormat", "ENCODING_PCM_FLOAT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_FLOAT), 1},
{"android/media/AudioFormat", "ENCODING_AC3", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_AC3), 0},
{"android/media/AudioFormat", "ENCODING_IEC61937", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_IEC61937), 0},
{"android/media/AudioFormat", "CHANNEL_OUT_MONO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_MONO), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_STEREO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_STEREO), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_FRONT_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_CENTER), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_QUAD", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_QUAD), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_5POINT1", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_5POINT1), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_FRONT_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_LEFT), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_FRONT_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_RIGHT), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_FRONT_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_CENTER), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_LOW_FREQUENCY", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_LOW_FREQUENCY), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_BACK_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_LEFT), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_BACK_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_RIGHT), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_BACK_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_CENTER), 1},
{"android/media/AudioFormat", "CHANNEL_OUT_7POINT1_SURROUND", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_7POINT1_SURROUND), 0},
{"android/media/AudioFormat", "CHANNEL_OUT_SIDE_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_SIDE_LEFT), 0},
{"android/media/AudioFormat", "CHANNEL_OUT_SIDE_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_SIDE_RIGHT), 0},
{0}
#undef OFFSET
}};
@@ -291,9 +303,7 @@ static int AudioTrack_New(struct ao *ao)
MP_JNI_EXCEPTION_LOG(ao);
tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setUsage, AudioAttributes.USAGE_MEDIA);
MP_JNI_DELETELOCAL(tmp);
jint content_type = (ao->init_flags & AO_INIT_MEDIA_ROLE_MUSIC) ?
AudioAttributes.CONTENT_TYPE_MUSIC : AudioAttributes.CONTENT_TYPE_MOVIE;
tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setContentType, content_type);
tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setContentType, AudioAttributes.CONTENT_TYPE_MOVIE);
MP_JNI_DELETELOCAL(tmp);
jobject attr = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.build);
MP_JNI_DELETELOCAL(attr_builder);
@@ -324,7 +334,7 @@ static int AudioTrack_New(struct ao *ao)
p->cfg_session_id
);
}
if (MP_JNI_EXCEPTION_LOG(ao) < 0 || !audiotrack) {
if (!audiotrack || MP_JNI_EXCEPTION_LOG(ao) < 0) {
MP_FATAL(ao, "AudioTrack Init failed\n");
return -1;
}
@@ -372,26 +382,26 @@ static uint32_t AudioTrack_getPlaybackHeadPosition(struct ao *ao)
return 0;
JNIEnv *env = MP_JNI_GET_ENV(ao);
uint32_t pos = 0;
int64_t now = mp_raw_time_ns();
int64_t now = mp_raw_time_us();
int state = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getPlayState);
int stable_count = 20;
int64_t wait = p->timestamp_stable < stable_count ? 50000000 : 3000000000;
int64_t wait = p->timestamp_stable < stable_count ? 50000 : 3000000;
if (state == AudioTrack.PLAYSTATE_PLAYING && p->format != AudioFormat.ENCODING_IEC61937 &&
(p->timestamp_fetched == 0 || now - p->timestamp_fetched >= wait)) {
if (!p->timestamp_fetched)
p->timestamp_stable = 0;
int64_t time1 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime);
int64_t utime1 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000;
if (MP_JNI_CALL_BOOL(p->audiotrack, AudioTrack.getTimestamp, p->timestamp)) {
p->timestamp_set = true;
p->timestamp_fetched = now;
if (p->timestamp_stable < stable_count) {
uint32_t fpos = 0xFFFFFFFFL & MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.framePosition);
int64_t time2 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime);
//MP_VERBOSE(ao, "getTimestamp: fpos= %u / time= %"PRId64" / now= %"PRId64" / stable= %d\n", fpos, time2, now, p->timestamp_stable);
if (time1 != time2 && time2 != 0 && fpos != 0) {
int64_t utime2 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000;
//MP_VERBOSE(ao, "getTimestamp: fpos= %u / time= %"PRId64" / now= %"PRId64" / stable= %d\n", fpos, utime2, now, p->timestamp_stable);
if (utime1 != utime2 && utime2 != 0 && fpos != 0) {
p->timestamp_stable++;
}
}
@@ -404,19 +414,19 @@ static uint32_t AudioTrack_getPlaybackHeadPosition(struct ao *ao)
if (p->timestamp_set) {
pos = 0xFFFFFFFFL & MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.framePosition);
uint32_t fpos = pos;
int64_t time = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime);
if (time == 0)
int64_t utime = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000;
if (utime == 0)
fpos = pos = 0;
if (p->needs_timestamp_offset) {
if (time != 0 && !p->timestamp_offset)
p->timestamp_offset = now - time;
time += p->timestamp_offset;
if (utime != 0 && !p->timestamp_offset)
p->timestamp_offset = now - utime;
utime += p->timestamp_offset;
}
if (fpos != 0 && time != 0 && state == AudioTrack.PLAYSTATE_PLAYING) {
double diff = (double)(now - time) / 1e9;
if (fpos != 0 && utime != 0 && state == AudioTrack.PLAYSTATE_PLAYING) {
double diff = (double)(now - utime) / 1e6;
pos += diff * ao->samplerate;
}
//MP_VERBOSE(ao, "position = %u via getTimestamp (state = %d / fpos= %u / time= %"PRId64")\n", pos, state, fpos, time);
//MP_VERBOSE(ao, "position = %u via getTimestamp (state = %d / fpos= %u / time= %"PRId64")\n", pos, state, fpos, utime);
} else {
pos = 0xFFFFFFFFL & MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getPlaybackHeadPosition);
//MP_VERBOSE(ao, "playbackHeadPosition = %u (reset_pending=%d)\n", pos, p->reset_pending);
@@ -493,14 +503,6 @@ static int AudioTrack_write(struct ao *ao, int len)
if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1;
if (ret > 0) ret *= 2;
} else if (AudioTrack.writeBufferV21) {
// reset positions for reading
jobject bbuf = MP_JNI_CALL_OBJECT(p->bbuf, ByteBuffer.clear);
if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1;
(*env)->DeleteLocalRef(env, bbuf);
ret = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.writeBufferV21, p->bbuf, len, AudioTrack.WRITE_BLOCKING);
if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1;
} else if (p->format == AudioFormat.ENCODING_PCM_FLOAT) {
(*env)->SetFloatArrayRegion(env, p->floatarray, 0, len / sizeof(float), buf);
if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1;
@@ -508,6 +510,13 @@ static int AudioTrack_write(struct ao *ao, int len)
if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1;
if (ret > 0) ret *= sizeof(float);
} else if (AudioTrack.writeBufferV21) {
jobject bbuf = MP_JNI_CALL_OBJECT(p->bbuf, ByteBuffer.clear);
if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1;
(*env)->DeleteLocalRef(env, bbuf);
ret = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.writeBufferV21, p->bbuf, len, AudioTrack.WRITE_BLOCKING);
if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1;
} else {
(*env)->SetByteArrayRegion(env, p->bytearray, 0, len, buf);
if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1;
@@ -549,13 +558,13 @@ static int init_jni(struct ao *ao)
return 0;
}
static MP_THREAD_VOID playthread(void *arg)
static void *playthread(void *arg)
{
struct ao *ao = arg;
struct priv *p = ao->priv;
JNIEnv *env = MP_JNI_GET_ENV(ao);
mp_thread_set_name("ao/audiotrack");
mp_mutex_lock(&p->lock);
mpthread_set_name("audiotrack");
pthread_mutex_lock(&p->lock);
while (!p->thread_terminate) {
int state = AudioTrack.PLAYSTATE_PAUSED;
if (p->audiotrack) {
@@ -563,11 +572,12 @@ static MP_THREAD_VOID playthread(void *arg)
}
if (state == AudioTrack.PLAYSTATE_PLAYING) {
int read_samples = p->chunksize / ao->sstride;
int64_t ts = mp_time_ns();
ts += MP_TIME_S_TO_NS(read_samples / (double)(ao->samplerate));
ts += MP_TIME_S_TO_NS(AudioTrack_getLatency(ao));
int samples = ao_read_data_nonblocking(ao, &p->chunk, read_samples, ts);
int ret = AudioTrack_write(ao, samples * ao->sstride);
int64_t ts = mp_time_us();
ts += (read_samples / (double)(ao->samplerate)) * 1e6;
ts += AudioTrack_getLatency(ao) * 1e6;
int samples = ao_read_data(ao, &p->chunk, read_samples, ts);
int write_samples = read_samples;
int ret = AudioTrack_write(ao, write_samples * ao->sstride);
if (ret >= 0) {
p->written_frames += ret / ao->sstride;
} else if (ret == AudioManager.ERROR_DEAD_OBJECT) {
@@ -579,11 +589,12 @@ static MP_THREAD_VOID playthread(void *arg)
MP_ERR(ao, "AudioTrack.write failed with %d\n", ret);
}
} else {
mp_cond_timedwait(&p->wakeup, &p->lock, MP_TIME_MS_TO_NS(300));
struct timespec wait = mp_rel_time_to_timespec(0.300);
pthread_cond_timedwait(&p->wakeup, &p->lock, &wait);
}
}
mp_mutex_unlock(&p->lock);
MP_THREAD_RETURN();
pthread_mutex_unlock(&p->lock);
return NULL;
}
static void uninit(struct ao *ao)
@@ -597,13 +608,13 @@ static void uninit(struct ao *ao)
MP_JNI_EXCEPTION_LOG(ao);
}
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
p->thread_terminate = true;
mp_cond_signal(&p->wakeup);
mp_mutex_unlock(&p->lock);
pthread_cond_signal(&p->wakeup);
pthread_mutex_unlock(&p->lock);
if (p->thread_created)
mp_thread_join(p->thread);
pthread_join(p->thread, NULL);
if (p->audiotrack) {
MP_JNI_CALL_VOID(p->audiotrack, AudioTrack.release);
@@ -637,8 +648,13 @@ static void uninit(struct ao *ao)
p->timestamp = NULL;
}
mp_cond_destroy(&p->wakeup);
mp_mutex_destroy(&p->lock);
if (p->chunk) {
free(p->chunk);
p->chunk = NULL;
}
pthread_cond_destroy(&p->wakeup);
pthread_mutex_destroy(&p->lock);
uninit_jni(ao);
}
@@ -650,8 +666,8 @@ static int init(struct ao *ao)
if (!env)
return -1;
mp_mutex_init(&p->lock);
mp_cond_init(&p->wakeup);
pthread_mutex_init(&p->lock, NULL);
pthread_cond_init(&p->wakeup, NULL);
if (init_jni(ao) < 0)
return -1;
@@ -660,7 +676,7 @@ static int init(struct ao *ao)
p->format = AudioFormat.ENCODING_IEC61937;
} else if (ao->format == AF_FORMAT_U8) {
p->format = AudioFormat.ENCODING_PCM_8BIT;
} else if (p->cfg_pcm_float && af_fmt_is_float(ao->format)) {
} else if (p->cfg_pcm_float && (ao->format == AF_FORMAT_FLOAT || ao->format == AF_FORMAT_FLOATP)) {
ao->format = AF_FORMAT_FLOAT;
p->format = AudioFormat.ENCODING_PCM_FLOAT;
} else {
@@ -675,48 +691,22 @@ static int init(struct ao *ao)
AudioManager.STREAM_MUSIC
);
if (MP_JNI_EXCEPTION_LOG(ao) == 0) {
ao->samplerate = samplerate;
MP_VERBOSE(ao, "AudioTrack.nativeOutputSampleRate = %d\n", samplerate);
ao->samplerate = MPMIN(samplerate, ao->samplerate);
}
}
p->samplerate = ao->samplerate;
/* https://developer.android.com/reference/android/media/AudioFormat#channelPositionMask */
static const struct mp_chmap layouts[] = {
{0}, // empty
MP_CHMAP_INIT_MONO, // mono
MP_CHMAP_INIT_STEREO, // stereo
MP_CHMAP3(FL, FR, FC), // 3.0
MP_CHMAP4(FL, FR, BL, BR), // quad
MP_CHMAP5(FL, FR, FC, BL, BR), // 5.0
MP_CHMAP6(FL, FR, FC, LFE, BL, BR), // 5.1
MP_CHMAP7(FL, FR, FC, LFE, BL, BR, BC), // 6.1
MP_CHMAP8(FL, FR, FC, LFE, BL, BR, SL, SR), // 7.1
};
const jint layout_map[] = {
0,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER,
AudioFormat.CHANNEL_OUT_QUAD,
AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER,
AudioFormat.CHANNEL_OUT_5POINT1,
AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER,
AudioFormat.CHANNEL_OUT_7POINT1_SURROUND,
};
static_assert(MP_ARRAY_SIZE(layout_map) == MP_ARRAY_SIZE(layouts), "");
if (p->format == AudioFormat.ENCODING_IEC61937) {
p->channel_config = AudioFormat.CHANNEL_OUT_STEREO;
} else if (ao->channels.num == 1) {
p->channel_config = AudioFormat.CHANNEL_OUT_MONO;
} else if (ao->channels.num == 6) {
p->channel_config = AudioFormat.CHANNEL_OUT_5POINT1;
ao->channels = (struct mp_chmap)MP_CHMAP6(FL, FR, FC, LFE, BL, BR);
} else {
struct mp_chmap_sel sel = {0};
for (int i = 0; i < MP_ARRAY_SIZE(layouts); i++) {
if (layout_map[i])
mp_chmap_sel_add_map(&sel, &layouts[i]);
}
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
goto error;
p->channel_config = layout_map[ao->channels.num];
assert(p->channel_config);
p->channel_config = AudioFormat.CHANNEL_OUT_STEREO;
ao->channels = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
}
jint buffer_size = MP_JNI_CALL_STATIC_INT(
@@ -726,48 +716,40 @@ static int init(struct ao *ao)
p->channel_config,
p->format
);
if (MP_JNI_EXCEPTION_LOG(ao) < 0 || buffer_size <= 0) {
if (buffer_size <= 0 || MP_JNI_EXCEPTION_LOG(ao) < 0) {
MP_FATAL(ao, "AudioTrack.getMinBufferSize returned an invalid size: %d", buffer_size);
return -1;
}
// Choose double of the minimum buffer size suggested by the driver, but not
// less than 75ms or more than 150ms.
const int bps = af_fmt_to_bytes(ao->format);
int min = 0.075 * p->samplerate * bps * ao->channels.num;
int min = 0.075 * p->samplerate * af_fmt_to_bytes(ao->format) * ao->channels.num;
int max = min * 2;
min = MP_ALIGN_UP(min, bps);
max = MP_ALIGN_UP(max, bps);
p->size = MPCLAMP(buffer_size * 2, min, max);
MP_VERBOSE(ao, "Setting bufferSize = %d (driver=%d, min=%d, max=%d)\n", p->size, buffer_size, min, max);
assert(p->size % bps == 0);
ao->device_buffer = p->size / bps;
ao->device_buffer = p->size / af_fmt_to_bytes(ao->format);
p->chunksize = p->size;
p->chunk = talloc_size(ao, p->size);
p->chunk = malloc(p->size);
jobject timestamp = MP_JNI_NEW(AudioTimestamp.clazz, AudioTimestamp.ctor);
if (MP_JNI_EXCEPTION_LOG(ao) < 0 || !timestamp) {
if (!timestamp || MP_JNI_EXCEPTION_LOG(ao) < 0) {
MP_FATAL(ao, "AudioTimestamp could not be created\n");
return -1;
}
p->timestamp = (*env)->NewGlobalRef(env, timestamp);
(*env)->DeleteLocalRef(env, timestamp);
// decide and create buffer of right type
if (p->format == AudioFormat.ENCODING_IEC61937) {
jshortArray shortarray = (*env)->NewShortArray(env, p->chunksize / 2);
p->shortarray = (*env)->NewGlobalRef(env, shortarray);
(*env)->DeleteLocalRef(env, shortarray);
} else if (AudioTrack.writeBufferV21) {
MP_VERBOSE(ao, "Using NIO ByteBuffer\n");
jobject bbuf = (*env)->NewDirectByteBuffer(env, p->chunk, p->chunksize);
p->bbuf = (*env)->NewGlobalRef(env, bbuf);
(*env)->DeleteLocalRef(env, bbuf);
} else if (p->format == AudioFormat.ENCODING_PCM_FLOAT) {
jfloatArray floatarray = (*env)->NewFloatArray(env, p->chunksize / sizeof(float));
p->floatarray = (*env)->NewGlobalRef(env, floatarray);
(*env)->DeleteLocalRef(env, floatarray);
} else if (AudioTrack.writeBufferV21) {
jobject bbuf = (*env)->NewDirectByteBuffer(env, p->chunk, p->chunksize);
p->bbuf = (*env)->NewGlobalRef(env, bbuf);
(*env)->DeleteLocalRef(env, bbuf);
} else {
jbyteArray bytearray = (*env)->NewByteArray(env, p->chunksize);
p->bytearray = (*env)->NewGlobalRef(env, bytearray);
@@ -780,7 +762,7 @@ static int init(struct ao *ao)
goto error;
}
if (mp_thread_create(&p->thread, playthread, ao)) {
if (pthread_create(&p->thread, NULL, playthread, ao)) {
MP_ERR(ao, "pthread creation failed\n");
goto error;
}
@@ -827,7 +809,7 @@ static void start(struct ao *ao)
MP_JNI_CALL_VOID(p->audiotrack, AudioTrack.play);
MP_JNI_EXCEPTION_LOG(ao);
mp_cond_signal(&p->wakeup);
pthread_cond_signal(&p->wakeup);
}
#define OPT_BASE_STRUCT struct priv
@@ -840,11 +822,8 @@ const struct ao_driver audio_out_audiotrack = {
.reset = stop,
.start = start,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const OPT_BASE_STRUCT) {
.cfg_pcm_float = 1,
},
.options = (const struct m_option[]) {
{"pcm-float", OPT_BOOL(cfg_pcm_float)},
{"pcm-float", OPT_FLAG(cfg_pcm_float)},
{"session-id", OPT_INT(cfg_session_id)},
{0}
},
+4 -3
View File
@@ -15,6 +15,7 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
@@ -94,9 +95,9 @@ static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
for (int n = 0; n < ao->num_planes; n++)
planes[n] = buffer_list->mBuffers[n].mData;
int64_t end = mp_time_ns();
end += MP_TIME_S_TO_NS(p->device_latency);
end += ca_get_latency(ts) + ca_frames_to_ns(ao, frames);
int64_t end = mp_time_us();
end += p->device_latency * 1e6;
end += ca_get_latency(ts) + ca_frames_to_us(ao, frames);
ao_read_data(ao, planes, frames, end);
return noErr;
}
+17 -23
View File
@@ -17,6 +17,7 @@
#include <CoreAudio/HostTime.h>
#include "config.h"
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
@@ -31,12 +32,12 @@ struct priv {
AudioDeviceID device;
AudioUnit audio_unit;
uint64_t hw_latency_ns;
uint64_t hw_latency_us;
AudioStreamBasicDescription original_asbd;
AudioStreamID original_asbd_stream;
bool change_physical_format;
int change_physical_format;
};
static int64_t ca_get_hardware_latency(struct ao *ao) {
@@ -53,13 +54,13 @@ static int64_t ca_get_hardware_latency(struct ao *ao) {
&size);
CHECK_CA_ERROR("cannot get audio unit latency");
uint64_t audiounit_latency_ns = MP_TIME_S_TO_NS(audiounit_latency_sec);
uint64_t device_latency_ns = ca_get_device_latency_ns(ao, p->device);
uint64_t audiounit_latency_us = audiounit_latency_sec * 1e6;
uint64_t device_latency_us = ca_get_device_latency_us(ao, p->device);
MP_VERBOSE(ao, "audiounit latency [ns]: %lld\n", audiounit_latency_ns);
MP_VERBOSE(ao, "device latency [ns]: %lld\n", device_latency_ns);
MP_VERBOSE(ao, "audiounit latency [us]: %lld\n", audiounit_latency_us);
MP_VERBOSE(ao, "device latency [us]: %lld\n", device_latency_us);
return audiounit_latency_ns + device_latency_ns;
return audiounit_latency_us + device_latency_us;
coreaudio_error:
return 0;
@@ -76,20 +77,13 @@ static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
for (int n = 0; n < ao->num_planes; n++)
planes[n] = buffer_list->mBuffers[n].mData;
int64_t end = mp_time_ns();
end += p->hw_latency_ns + ca_get_latency(ts) + ca_frames_to_ns(ao, frames);
int samples = ao_read_data_nonblocking(ao, planes, frames, end);
if (samples == 0)
*aflags |= kAudioUnitRenderAction_OutputIsSilence;
for (int n = 0; n < buffer_list->mNumberBuffers; n++)
buffer_list->mBuffers[n].mDataByteSize = samples * ao->sstride;
int64_t end = mp_time_us();
end += p->hw_latency_us + ca_get_latency(ts) + ca_frames_to_us(ao, frames);
ao_read_data(ao, planes, frames, end);
return noErr;
}
static int get_volume(struct ao *ao, float *vol) {
static int get_volume(struct ao *ao, struct ao_control_vol *vol) {
struct priv *p = ao->priv;
float auvol;
OSStatus err =
@@ -97,15 +91,15 @@ static int get_volume(struct ao *ao, float *vol) {
kAudioUnitScope_Global, 0, &auvol);
CHECK_CA_ERROR("could not get HAL output volume");
*vol = auvol * 100.0;
vol->left = vol->right = auvol * 100.0;
return CONTROL_TRUE;
coreaudio_error:
return CONTROL_ERROR;
}
static int set_volume(struct ao *ao, float *vol) {
static int set_volume(struct ao *ao, struct ao_control_vol *vol) {
struct priv *p = ao->priv;
float auvol = *vol / 100.0;
float auvol = (vol->left + vol->right) / 200.0;
OSStatus err =
AudioUnitSetParameter(p->audio_unit, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, auvol, 0);
@@ -295,7 +289,7 @@ static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd)
CHECK_CA_ERROR_L(coreaudio_error_audiounit,
"can't link audio unit to selected device");
p->hw_latency_ns = ca_get_hardware_latency(ao);
p->hw_latency_us = ca_get_hardware_latency(ao);
AURenderCallbackStruct render_cb = (AURenderCallbackStruct) {
.inputProc = render_cb_lpcm,
@@ -428,7 +422,7 @@ const struct ao_driver audio_out_coreaudio = {
.list_devs = ca_get_device_list,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]){
{"change-physical-format", OPT_BOOL(change_physical_format)},
{"change-physical-format", OPT_FLAG(change_physical_format)},
{0}
},
.options_prefix = "coreaudio",
-9
View File
@@ -15,8 +15,6 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Availability.h>
#include "common/common.h"
#include "ao_coreaudio_utils.h"
@@ -49,13 +47,6 @@ static const int speaker_map[][2] = {
{ kAudioChannelLabel_LeftWide, MP_SPEAKER_ID_WL },
{ kAudioChannelLabel_RightWide, MP_SPEAKER_ID_WR },
{ kAudioChannelLabel_LFE2, MP_SPEAKER_ID_LFE2 },
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000
{ kAudioChannelLabel_LeftTopSurround, MP_SPEAKER_ID_TSL },
{ kAudioChannelLabel_RightTopSurround, MP_SPEAKER_ID_TSR },
{ kAudioChannelLabel_CenterBottom, MP_SPEAKER_ID_BFC },
{ kAudioChannelLabel_LeftBottom, MP_SPEAKER_ID_BFL },
{ kAudioChannelLabel_RightBottom, MP_SPEAKER_ID_BFR },
#endif
{ kAudioChannelLabel_HeadphonesLeft, MP_SPEAKER_ID_DL },
{ kAudioChannelLabel_HeadphonesRight, MP_SPEAKER_ID_DR },
-2
View File
@@ -20,8 +20,6 @@
#include <AudioToolbox/AudioToolbox.h>
#include "config.h"
struct mp_chmap;
int ca_label_to_mp_speaker_id(AudioChannelLabel label);
+10 -10
View File
@@ -34,17 +34,17 @@
* when you are wanting to do good buffering of audio).
*/
#include <stdatomic.h>
#include <CoreAudio/HostTime.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/intfloat.h>
#include "config.h"
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "osdep/atomic.h"
#include "options/m_option.h"
#include "common/msg.h"
#include "audio/out/ao_coreaudio_chmap.h"
@@ -73,13 +73,13 @@ struct priv {
AudioStreamBasicDescription original_asbd;
// Output s16 physical format, float32 virtual format, ac3/dts mpv format
bool spdif_hack;
int spdif_hack;
bool changed_mixing;
atomic_bool reload_requested;
uint64_t hw_latency_ns;
uint32_t hw_latency_us;
};
static OSStatus property_listener_cb(
@@ -177,9 +177,9 @@ static OSStatus render_cb_compressed(
return kAudioHardwareUnspecifiedError;
}
int64_t end = mp_time_ns();
end += p->hw_latency_ns + ca_get_latency(ts)
+ ca_frames_to_ns(ao, pseudo_frames);
int64_t end = mp_time_us();
end += p->hw_latency_us + ca_get_latency(ts)
+ ca_frames_to_us(ao, pseudo_frames);
ao_read_data(ao, &buf.mData, pseudo_frames, end);
@@ -384,8 +384,8 @@ static int init(struct ao *ao)
MP_WARN(ao, "Using spdif passthrough hack. This could produce noise.\n");
}
p->hw_latency_ns = ca_get_device_latency_ns(ao, p->device);
MP_VERBOSE(ao, "base latency: %lld nanoseconds\n", p->hw_latency_ns);
p->hw_latency_us = ca_get_device_latency_us(ao, p->device);
MP_VERBOSE(ao, "base latency: %d microseconds\n", (int)p->hw_latency_us);
err = enable_property_listener(ao, true);
CHECK_CA_ERROR("cannot install format change listener during init");
@@ -465,7 +465,7 @@ const struct ao_driver audio_out_coreaudio_exclusive = {
.changed_mixing = false,
},
.options = (const struct m_option[]){
{"spdif-hack", OPT_BOOL(spdif_hack)},
{"spdif-hack", OPT_FLAG(spdif_hack)},
{0}
},
.options_prefix = "coreaudio",
+27 -12
View File
@@ -35,6 +35,21 @@
#include <mach/mach_time.h>
#endif
CFStringRef cfstr_from_cstr(char *str)
{
return CFStringCreateWithCString(NULL, str, CA_CFSTR_ENCODING);
}
char *cfstr_get_cstr(CFStringRef cfstr)
{
CFIndex size =
CFStringGetMaximumSizeForEncoding(
CFStringGetLength(cfstr), CA_CFSTR_ENCODING) + 1;
char *buffer = talloc_zero_size(NULL, size);
CFStringGetCString(cfstr, buffer, size, CA_CFSTR_ENCODING);
return buffer;
}
#if HAVE_COREAUDIO
static bool ca_is_output_device(struct ao *ao, AudioDeviceID dev)
{
@@ -292,9 +307,9 @@ bool ca_asbd_is_better(AudioStreamBasicDescription *req,
return true;
}
int64_t ca_frames_to_ns(struct ao *ao, uint32_t frames)
int64_t ca_frames_to_us(struct ao *ao, uint32_t frames)
{
return MP_TIME_S_TO_NS(frames / (double)ao->samplerate);
return frames / (float) ao->samplerate * 1e6;
}
int64_t ca_get_latency(const AudioTimeStamp *ts)
@@ -306,7 +321,7 @@ int64_t ca_get_latency(const AudioTimeStamp *ts)
if (now > out)
return 0;
return out - now;
return (out - now) * 1e-3;
#else
static mach_timebase_info_data_t timebase;
if (timebase.denom == 0)
@@ -318,7 +333,7 @@ int64_t ca_get_latency(const AudioTimeStamp *ts)
if (now > out)
return 0;
return (out - now) * timebase.numer / timebase.denom;
return (out - now) * timebase.numer / timebase.denom / 1e3;
#endif
}
@@ -422,7 +437,7 @@ OSStatus ca_enable_mixing(struct ao *ao, AudioDeviceID device, bool changed)
return noErr;
}
int64_t ca_get_device_latency_ns(struct ao *ao, AudioDeviceID device)
int64_t ca_get_device_latency_us(struct ao *ao, AudioDeviceID device)
{
uint32_t latency_frames = 0;
uint32_t latency_properties[] = {
@@ -449,7 +464,7 @@ int64_t ca_get_device_latency_ns(struct ao *ao, AudioDeviceID device)
MP_VERBOSE(ao, "Device sample rate: %f\n", sample_rate);
}
return MP_TIME_S_TO_NS(latency_frames / sample_rate);
return latency_frames / sample_rate * 1e6;
}
static OSStatus ca_change_format_listener(
@@ -457,8 +472,8 @@ static OSStatus ca_change_format_listener(
const AudioObjectPropertyAddress addresses[],
void *data)
{
mp_sem_t *sem = data;
mp_sem_post(sem);
sem_t *sem = data;
sem_post(sem);
return noErr;
}
@@ -471,7 +486,7 @@ bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
ca_print_asbd(ao, "setting stream physical format:", &change_format);
sem_t wakeup;
if (mp_sem_init(&wakeup, 0, 0)) {
if (sem_init(&wakeup, 0, 0)) {
MP_WARN(ao, "OOM\n");
return false;
}
@@ -500,7 +515,7 @@ bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
/* The AudioStreamSetProperty is not only asynchronous,
* it is also not Atomic, in its behaviour. */
int64_t wait_until = mp_time_ns() + MP_TIME_S_TO_NS(2);
struct timespec timeout = mp_rel_time_to_timespec(2.0);
AudioStreamBasicDescription actual_format = {0};
while (1) {
err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format);
@@ -511,7 +526,7 @@ bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
if (format_set)
break;
if (mp_sem_timedwait(&wakeup, wait_until)) {
if (sem_timedwait(&wakeup, &timeout)) {
MP_VERBOSE(ao, "reached timeout\n");
break;
}
@@ -533,7 +548,7 @@ bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
CHECK_CA_ERROR("can't remove property listener");
coreaudio_error:
mp_sem_destroy(&wakeup);
sem_destroy(&wakeup);
return format_set;
}
#endif
+7 -5
View File
@@ -22,12 +22,14 @@
#include <AudioToolbox/AudioToolbox.h>
#include <inttypes.h>
#include <stdbool.h>
#include "config.h"
#include "common/msg.h"
#include "audio/out/ao.h"
#include "internal.h"
#include "osdep/apple_utils.h"
#define CA_CFSTR_ENCODING kCFStringEncodingUTF8
CFStringRef cfstr_from_cstr(char *str);
char *cfstr_get_cstr(CFStringRef cfstr);
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message);
@@ -62,7 +64,7 @@ bool ca_asbd_is_better(AudioStreamBasicDescription *req,
AudioStreamBasicDescription *old,
AudioStreamBasicDescription *new);
int64_t ca_frames_to_ns(struct ao *ao, uint32_t frames);
int64_t ca_frames_to_us(struct ao *ao, uint32_t frames);
int64_t ca_get_latency(const AudioTimeStamp *ts);
#if HAVE_COREAUDIO
@@ -71,7 +73,7 @@ OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid);
OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid);
OSStatus ca_disable_mixing(struct ao *ao, AudioDeviceID device, bool *changed);
OSStatus ca_enable_mixing(struct ao *ao, AudioDeviceID device, bool changed);
int64_t ca_get_device_latency_ns(struct ao *ao, AudioDeviceID device);
int64_t ca_get_device_latency_us(struct ao *ao, AudioDeviceID device);
bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
AudioStreamBasicDescription change_format);
#endif
+8 -10
View File
@@ -22,7 +22,6 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -34,6 +33,7 @@
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
#include "osdep/atomic.h"
#include "osdep/timer.h"
#include "options/m_config.h"
#include "options/m_option.h"
@@ -47,8 +47,8 @@
struct jack_opts {
char *port;
char *client_name;
bool connect;
bool autostart;
int connect;
int autostart;
int stdlayout;
};
@@ -57,15 +57,15 @@ static const struct m_sub_options ao_jack_conf = {
.opts = (const struct m_option[]){
{"jack-port", OPT_STRING(port)},
{"jack-name", OPT_STRING(client_name)},
{"jack-autostart", OPT_BOOL(autostart)},
{"jack-connect", OPT_BOOL(connect)},
{"jack-autostart", OPT_FLAG(autostart)},
{"jack-connect", OPT_FLAG(connect)},
{"jack-std-channel-layout", OPT_CHOICE(stdlayout,
{"waveext", 0}, {"any", 1})},
{0}
},
.defaults = &(const struct jack_opts) {
.client_name = "mpv",
.connect = true,
.connect = 1,
},
.size = sizeof(struct jack_opts),
};
@@ -122,8 +122,8 @@ static int process(jack_nframes_t nframes, void *arg)
jack_nframes_t jack_latency =
atomic_load(&p->graph_latency_max) + atomic_load(&p->buffer_size);
int64_t end_time = mp_time_ns();
end_time += MP_TIME_S_TO_NS((jack_latency + nframes) / (double)ao->samplerate);
int64_t end_time = mp_time_us();
end_time += (jack_latency + nframes) / (double)ao->samplerate * 1000000.0;
ao_read_data(ao, buffers, nframes, end_time);
@@ -246,8 +246,6 @@ static int init(struct ao *ao)
jack_set_process_callback(p->client, process, ao);
ao->samplerate = jack_get_sample_rate(p->client);
// The actual device buffer can change, but this is enough for pre-buffer
ao->device_buffer = jack_get_buffer_size(p->client);
jack_set_buffer_size_callback(p->client, buffer_size_cb, ao);
jack_set_graph_order_callback(p->client, graph_order_cb, ao);
+18 -4
View File
@@ -173,7 +173,7 @@ static int init(struct ao *ao)
return 0;
fail:
mp_mutex_unlock(&ao->encode_lavc_ctx->lock);
pthread_mutex_unlock(&ao->encode_lavc_ctx->lock);
ac->shutdown = true;
return -1;
}
@@ -182,8 +182,18 @@ fail:
static void uninit(struct ao *ao)
{
struct priv *ac = ao->priv;
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
if (!ac->shutdown) {
double outpts = ac->expected_next_pts;
pthread_mutex_lock(&ectx->lock);
if (!ac->enc->options->rawts)
outpts += ectx->discontinuity_pts_offset;
pthread_mutex_unlock(&ectx->lock);
outpts += encoder_get_offset(ac->enc);
if (!write_frame(ao, MP_EOF_FRAME))
MP_WARN(ao, "could not flush last frame\n");
encoder_encode(ac->enc, NULL);
@@ -200,7 +210,8 @@ static void encode(struct ao *ao, struct mp_aframe *af)
double outpts = mp_aframe_get_pts(af);
AVFrame *frame = mp_aframe_to_avframe(af);
MP_HANDLE_OOM(frame);
if (!frame)
abort();
frame->pts = rint(outpts * av_q2d(av_inv_q(encoder->time_base)));
@@ -261,7 +272,7 @@ static bool audio_write(struct ao *ao, void **data, int samples)
double outpts = pts;
// for ectx PTS fields
mp_mutex_lock(&ectx->lock);
pthread_mutex_lock(&ectx->lock);
if (!ectx->options->rawts) {
// Fix and apply the discontinuity pts offset.
@@ -280,6 +291,9 @@ static bool audio_write(struct ao *ao, void **data, int samples)
outpts = pts + ectx->discontinuity_pts_offset;
}
// Shift pts by the pts offset first.
outpts += encoder_get_offset(ac->enc);
// Calculate expected pts of next audio frame (input side).
ac->expected_next_pts = pts + mp_aframe_get_size(af) / (double) ao->samplerate;
@@ -290,7 +304,7 @@ static bool audio_write(struct ao *ao, void **data, int samples)
ectx->next_in_pts = nextpts;
}
mp_mutex_unlock(&ectx->lock);
pthread_mutex_unlock(&ectx->lock);
mp_aframe_set_pts(af, outpts);
+7 -6
View File
@@ -28,6 +28,7 @@
#include "mpv_talloc.h"
#include "config.h"
#include "osdep/timer.h"
#include "options/m_option.h"
#include "common/common.h"
@@ -43,13 +44,13 @@ struct priv {
int buffersize; // samples
bool playing;
bool untimed;
int untimed;
float bufferlen; // seconds
float speed; // multiplier
float latency_sec; // seconds
float latency; // samples
bool broken_eof;
bool broken_delay;
int broken_eof;
int broken_delay;
// Minimal unit of audio samples that can be written at once. If play() is
// called with sizes not aligned to this, a rounded size will be returned.
@@ -215,13 +216,13 @@ const struct ao_driver audio_out_null = {
.speed = 1,
},
.options = (const struct m_option[]) {
{"untimed", OPT_BOOL(untimed)},
{"untimed", OPT_FLAG(untimed)},
{"buffer", OPT_FLOAT(bufferlen), M_RANGE(0, 100)},
{"outburst", OPT_INT(outburst), M_RANGE(1, 100000)},
{"speed", OPT_FLOAT(speed), M_RANGE(0, 10000)},
{"latency", OPT_FLOAT(latency_sec), M_RANGE(0, 100)},
{"broken-eof", OPT_BOOL(broken_eof)},
{"broken-delay", OPT_BOOL(broken_delay)},
{"broken-eof", OPT_FLAG(broken_eof)},
{"broken-delay", OPT_FLAG(broken_delay)},
{"channel-layouts", OPT_CHANNELS(channel_layouts)},
{"format", OPT_AUDIOFORMAT(format)},
{0}
+8 -6
View File
@@ -19,6 +19,8 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
@@ -56,7 +58,7 @@ struct priv {
ALenum al_format;
int num_buffers;
int num_samples;
bool direct_channels;
int direct_channels;
};
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
@@ -65,13 +67,13 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
case AOCONTROL_GET_VOLUME:
case AOCONTROL_SET_VOLUME: {
ALfloat volume;
float *vol = arg;
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
if (cmd == AOCONTROL_SET_VOLUME) {
volume = *vol / 100.0;
volume = (vol->left + vol->right) / 200.0;
alListenerf(AL_GAIN, volume);
}
alGetListenerf(AL_GAIN, &volume);
*vol = volume * 100;
vol->left = vol->right = volume * 100;
return CONTROL_TRUE;
}
case AOCONTROL_GET_MUTE:
@@ -389,12 +391,12 @@ const struct ao_driver audio_out_openal = {
.priv_defaults = &(const struct priv) {
.num_buffers = 4,
.num_samples = 8192,
.direct_channels = true,
.direct_channels = 1,
},
.options = (const struct m_option[]) {
{"num-buffers", OPT_INT(num_buffers), M_RANGE(2, MAX_BUF)},
{"num-samples", OPT_INT(num_samples), M_RANGE(256, MAX_SAMPLES)},
{"direct-channels", OPT_BOOL(direct_channels)},
{"direct-channels", OPT_FLAG(direct_channels)},
{0}
},
.options_prefix = "openal",
+8 -7
View File
@@ -23,12 +23,13 @@
#include "common/msg.h"
#include "audio/format.h"
#include "options/m_option.h"
#include "osdep/threads.h"
#include "osdep/timer.h"
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <pthread.h>
struct priv {
SLObjectItf sl, output_mix, player;
SLBufferQueueItf buffer_queue;
@@ -36,7 +37,7 @@ struct priv {
SLPlayItf play;
void *buf;
int bytes_per_enqueue;
mp_mutex buffer_lock;
pthread_mutex_t buffer_lock;
double audio_latency;
int frames_per_enqueue;
@@ -61,7 +62,7 @@ static void uninit(struct ao *ao)
p->engine = NULL;
p->play = NULL;
mp_mutex_destroy(&p->buffer_lock);
pthread_mutex_destroy(&p->buffer_lock);
free(p->buf);
p->buf = NULL;
@@ -76,18 +77,18 @@ static void buffer_callback(SLBufferQueueItf buffer_queue, void *context)
SLresult res;
double delay;
mp_mutex_lock(&p->buffer_lock);
pthread_mutex_lock(&p->buffer_lock);
delay = p->frames_per_enqueue / (double)ao->samplerate;
delay += p->audio_latency;
ao_read_data(ao, &p->buf, p->frames_per_enqueue,
mp_time_ns() + MP_TIME_S_TO_NS(delay));
mp_time_us() + 1000000LL * delay);
res = (*buffer_queue)->Enqueue(buffer_queue, p->buf, p->bytes_per_enqueue);
if (res != SL_RESULT_SUCCESS)
MP_ERR(ao, "Failed to Enqueue: %d\n", res);
mp_mutex_unlock(&p->buffer_lock);
pthread_mutex_unlock(&p->buffer_lock);
}
#define CHK(stmt) \
@@ -169,7 +170,7 @@ static int init(struct ao *ao)
goto error;
}
int r = mp_mutex_init(&p->buffer_lock);
int r = pthread_mutex_init(&p->buffer_lock, NULL);
if (r) {
MP_ERR(ao, "Failed to initialize the mutex: %d\n", r);
goto error;
+22 -11
View File
@@ -4,7 +4,7 @@
* 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-2023
* Rozhuk Ivan <rozhuk.im@gmail.com> 2020
*
* This file is part of mpv.
*
@@ -35,6 +35,7 @@
#endif
#include <sys/types.h>
#include "config.h"
#include "audio/format.h"
#include "common/msg.h"
#include "options/options.h"
@@ -52,6 +53,7 @@
struct priv {
int dsp_fd;
bool playing;
double bps; /* Bytes per second. */
};
@@ -114,7 +116,7 @@ static void device_descr_get(size_t dev_idx, char *buf, size_t buf_size)
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 - 1);
strncpy(buf, mi.name, buf_size);
tmp = (buf_size - 1);
}
close(fd);
@@ -186,11 +188,11 @@ static int init(struct ao *ao)
/* Channels count. */
if (af_fmt_is_spdif(format)) {
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;
/* 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};
@@ -239,6 +241,7 @@ static int init(struct ao *ao)
ao->samplerate = samplerate;
ao->channels = channels;
p->bps = (channels.num * samplerate * af_fmt_to_bytes(format));
p->playing = false;
return 0;
@@ -258,12 +261,13 @@ static void uninit(struct ao *ao)
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;
float *vol = arg;
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
int v;
if (p->dsp_fd < 0)
@@ -275,10 +279,11 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
MP_WARN_IOCTL_ERR(ao);
return CONTROL_ERROR;
}
*vol = ((v & 0x00ff) + ((v & 0xff00) >> 8)) / 2.0;
vol->right = ((v & 0xff00) >> 8);
vol->left = (v & 0x00ff);
return CONTROL_OK;
case AOCONTROL_SET_VOLUME:
v = ((int)*vol << 8) | (int)*vol;
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;
@@ -295,6 +300,7 @@ static void reset(struct ao *ao)
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)
{
@@ -314,6 +320,7 @@ static void start(struct ao *ao)
MP_WARN_IOCTL_ERR(ao);
return;
}
p->playing = true;
}
static bool audio_write(struct ao *ao, void **data, int samples)
@@ -330,11 +337,13 @@ static bool audio_write(struct ao *ao, void **data, int samples)
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;
}
@@ -351,6 +360,7 @@ static void get_state(struct ao *ao, struct mp_pcm_state *state)
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;
@@ -358,7 +368,7 @@ static void get_state(struct ao *ao, struct mp_pcm_state *state)
state->free_samples = (info.bytes / ao->sstride);
state->queued_samples = (ao->device_buffer - state->free_samples);
state->delay = (odelay / p->bps);
state->playing = (state->queued_samples != 0);
state->playing = p->playing;
}
static void list_devs(struct ao *ao, struct ao_device_list *list)
@@ -396,5 +406,6 @@ const struct ao_driver audio_out_oss = {
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.dsp_fd = -1,
.playing = false,
},
};
+7 -5
View File
@@ -19,6 +19,8 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -42,8 +44,8 @@
struct priv {
char *outputfilename;
bool waveheader;
bool append;
int waveheader;
int append;
uint64_t data_length;
FILE *fp;
};
@@ -237,11 +239,11 @@ const struct ao_driver audio_out_pcm = {
.start = start,
.reset = reset,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) { .waveheader = true },
.priv_defaults = &(const struct priv) { .waveheader = 1 },
.options = (const struct m_option[]) {
{"file", OPT_STRING(outputfilename), .flags = M_OPT_FILE},
{"waveheader", OPT_BOOL(waveheader)},
{"append", OPT_BOOL(append)},
{"waveheader", OPT_FLAG(waveheader)},
{"append", OPT_FLAG(append)},
{0}
},
.options_prefix = "ao-pcm",
+98 -145
View File
@@ -32,46 +32,42 @@
#include "options/m_option.h"
#include "ao.h"
#include "audio/format.h"
#include "config.h"
#include "generated/version.h"
#include "internal.h"
#include "osdep/timer.h"
// Added in Pipewire 0.3.33
// remove the fallback when we require a newer version
#ifndef PW_KEY_NODE_RATE
#define PW_KEY_NODE_RATE "node.rate"
#endif
// Added in Pipewire 0.3.44
// remove the fallback when we require a newer version
#ifndef PW_KEY_TARGET_OBJECT
#define PW_KEY_TARGET_OBJECT "target.object"
#endif
#if !PW_CHECK_VERSION(0, 3, 50)
static inline int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size) {
return pw_stream_get_time(stream, time);
}
#endif
#if !PW_CHECK_VERSION(0, 3, 57)
// Earlier versions segfault on zeroed hooks
#define spa_hook_remove(hook) if ((hook)->link.prev) spa_hook_remove(hook)
#endif
enum init_state {
INIT_STATE_NONE,
INIT_STATE_SUCCESS,
INIT_STATE_ERROR,
};
enum {
VOLUME_MODE_CHANNEL,
VOLUME_MODE_GLOBAL,
};
struct priv {
struct pw_thread_loop *loop;
struct pw_stream *stream;
struct pw_core *core;
struct spa_hook stream_listener;
struct spa_hook core_listener;
enum init_state init_state;
bool muted;
float volume;
float volume[2];
struct {
int buffer_msec;
char *remote;
int volume_mode;
} options;
struct {
@@ -133,11 +129,6 @@ static enum spa_audio_channel mp_speaker_id_to_spa(struct ao *ao, enum mp_speake
case MP_SPEAKER_ID_SDL: return SPA_AUDIO_CHANNEL_SL;
case MP_SPEAKER_ID_SDR: return SPA_AUDIO_CHANNEL_SL;
case MP_SPEAKER_ID_LFE2: return SPA_AUDIO_CHANNEL_LFE2;
case MP_SPEAKER_ID_TSL: return SPA_AUDIO_CHANNEL_TSL;
case MP_SPEAKER_ID_TSR: return SPA_AUDIO_CHANNEL_TSR;
case MP_SPEAKER_ID_BFC: return SPA_AUDIO_CHANNEL_BC;
case MP_SPEAKER_ID_BFL: return SPA_AUDIO_CHANNEL_BLC;
case MP_SPEAKER_ID_BFR: return SPA_AUDIO_CHANNEL_BRC;
case MP_SPEAKER_ID_NA: return SPA_AUDIO_CHANNEL_NA;
default:
MP_WARN(ao, "Unhandled channel %d\n", mp_speaker_id);
@@ -145,6 +136,7 @@ static enum spa_audio_channel mp_speaker_id_to_spa(struct ao *ao, enum mp_speake
};
}
static void on_process(void *userdata)
{
struct ao *ao = userdata;
@@ -176,12 +168,12 @@ static void on_process(void *userdata)
if (time.rate.num == 0)
time.rate.num = 1;
int64_t end_time = mp_time_ns();
int64_t end_time = mp_time_us();
/* time.queued is always going to be 0, so we don't need to care */
end_time += (nframes * 1e9 / ao->samplerate) +
((double) time.delay * SPA_NSEC_PER_SEC * time.rate.num / time.rate.denom);
end_time += (nframes * 1e6 / ao->samplerate) +
((float) time.delay * SPA_USEC_PER_SEC * time.rate.num / time.rate.denom);
int samples = ao_read_data_nonblocking(ao, data, nframes, end_time);
int samples = ao_read_data(ao, data, nframes, end_time);
b->size = samples;
for (int i = 0; i < buf->n_datas; i++) {
@@ -203,14 +195,6 @@ static void on_param_changed(void *userdata, uint32_t id, const struct spa_pod *
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
/* We want to know when our node is linked.
* As there is no proper callback for this we use the Latency param for this
*/
if (id == SPA_PARAM_Latency) {
p->init_state = INIT_STATE_SUCCESS;
pw_thread_loop_signal(p->loop, false);
}
if (param == NULL || id != SPA_PARAM_Format)
return;
@@ -219,8 +203,7 @@ static void on_param_changed(void *userdata, uint32_t id, const struct spa_pod *
params[0] = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(ao->num_planes),
SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(
buffer_size, 0, INT32_MAX),
SPA_PARAM_BUFFERS_size, SPA_POD_Int(buffer_size),
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(ao->sstride));
if (!params[0]) {
MP_ERR(ao, "Could not build parameter pod\n");
@@ -236,14 +219,10 @@ static void on_param_changed(void *userdata, uint32_t id, const struct spa_pod *
static void on_state_changed(void *userdata, enum pw_stream_state old, enum pw_stream_state state, const char *error)
{
struct ao *ao = userdata;
struct priv *p = ao->priv;
MP_DBG(ao, "Stream state changed: old_state=%s state=%s error=%s\n",
pw_stream_state_as_string(old), pw_stream_state_as_string(state), error);
MP_DBG(ao, "Stream state changed: old_state=%d state=%d error=%s\n", old, state, error);
if (state == PW_STREAM_STATE_ERROR) {
MP_WARN(ao, "Stream in error state, trying to reload...\n");
p->init_state = INIT_STATE_ERROR;
pw_thread_loop_signal(p->loop, false);
ao_request_reload(ao);
}
@@ -283,16 +262,14 @@ static void on_control_info(void *userdata, uint32_t id,
p->muted = control->values[0] >= 0.5;
break;
case SPA_PROP_channelVolumes:
if (p->options.volume_mode != VOLUME_MODE_CHANNEL)
break;
if (control->n_values > 0)
p->volume = volume_avg(control->values, control->n_values);
break;
case SPA_PROP_volume:
if (p->options.volume_mode != VOLUME_MODE_GLOBAL)
break;
if (control->n_values > 0)
p->volume = control->values[0];
if (control->n_values == 2) {
p->volume[0] = control->values[0];
p->volume[1] = control->values[1];
} else if (control->n_values > 0) {
float volume = volume_avg(control->values, control->n_values);
p->volume[0] = volume;
p->volume[1] = volume;
}
break;
}
}
@@ -315,8 +292,9 @@ static void uninit(struct ao *ao)
if (p->stream)
pw_stream_destroy(p->stream);
p->stream = NULL;
if (p->core)
if (p->core) {
pw_context_destroy(pw_core_get_context(p->core));
}
p->core = NULL;
if (p->loop)
pw_thread_loop_destroy(p->loop);
@@ -359,11 +337,6 @@ static void for_each_sink_registry_event_global(void *data, uint32_t id,
}
struct for_each_done_ctx {
struct pw_thread_loop *loop;
bool done;
};
static const struct pw_registry_events for_each_sink_registry_events = {
.version = PW_VERSION_REGISTRY_EVENTS,
.global = for_each_sink_registry_event_global,
@@ -371,9 +344,8 @@ static const struct pw_registry_events for_each_sink_registry_events = {
static void for_each_sink_done(void *data, uint32_t it, int seq)
{
struct for_each_done_ctx *ctx = data;
ctx->done = true;
pw_thread_loop_signal(ctx->loop, false);
struct pw_thread_loop *loop = data;
pw_thread_loop_signal(loop, false);
}
static const struct pw_core_events for_each_sink_core_events = {
@@ -387,16 +359,12 @@ static int for_each_sink(struct ao *ao, void (cb) (struct ao *ao, uint32_t id,
struct priv *priv = ao->priv;
struct pw_registry *registry;
struct spa_hook core_listener;
struct for_each_done_ctx done_ctx = {
.loop = priv->loop,
.done = false,
};
int ret = -1;
pw_thread_loop_lock(priv->loop);
spa_zero(core_listener);
if (pw_core_add_listener(priv->core, &core_listener, &for_each_sink_core_events, &done_ctx) < 0)
if (pw_core_add_listener(priv->core, &core_listener, &for_each_sink_core_events, priv->loop) < 0)
goto unlock_loop;
registry = pw_core_get_registry(priv->core, PW_VERSION_REGISTRY, 0);
@@ -415,8 +383,7 @@ static int for_each_sink(struct ao *ao, void (cb) (struct ao *ao, uint32_t id,
if (pw_registry_add_listener(registry, &registry_listener, &for_each_sink_registry_events, &revents_ctx) < 0)
goto destroy_registry;
while (!done_ctx.done)
pw_thread_loop_wait(priv->loop);
pw_thread_loop_wait(priv->loop);
spa_hook_remove(&registry_listener);
@@ -483,7 +450,7 @@ static int pipewire_init_boilerplate(struct ao *ao)
MP_VERBOSE(ao, "Headers version: %s\n", pw_get_headers_version());
MP_VERBOSE(ao, "Library version: %s\n", pw_get_library_version());
p->loop = pw_thread_loop_new("mpv/ao/pipewire", NULL);
p->loop = pw_thread_loop_new("ao-pipewire", NULL);
if (p->loop == NULL)
return -1;
@@ -492,10 +459,7 @@ static int pipewire_init_boilerplate(struct ao *ao)
if (pw_thread_loop_start(p->loop) < 0)
goto error;
context = pw_context_new(
pw_thread_loop_get_loop(p->loop),
pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", NULL),
0);
context = pw_context_new(pw_thread_loop_get_loop(p->loop), NULL, 0);
if (!context)
goto error;
@@ -504,9 +468,8 @@ static int pipewire_init_boilerplate(struct ao *ao)
pw_properties_new(PW_KEY_REMOTE_NAME, p->options.remote, NULL),
0);
if (!p->core) {
MP_MSG(ao, ao->probing ? MSGL_V : MSGL_ERR,
"Could not connect to context '%s': %s\n",
p->options.remote, strerror(errno));
MP_WARN(ao, "Could not connect to context '%s': %s\n",
p->options.remote, strerror(errno));
pw_context_destroy(context);
goto error;
}
@@ -528,26 +491,6 @@ error:
return -1;
}
static void wait_for_init_done(struct ao *ao)
{
struct priv *p = ao->priv;
struct timespec abstime;
int r;
r = pw_thread_loop_get_time(p->loop, &abstime, 50 * SPA_NSEC_PER_MSEC);
if (r < 0) {
MP_WARN(ao, "Could not get timeout for initialization: %s\n", spa_strerror(r));
return;
}
while (p->init_state == INIT_STATE_NONE) {
r = pw_thread_loop_timed_wait_full(p->loop, &abstime);
if (r < 0) {
MP_WARN(ao, "Could not wait for initialization: %s\n", spa_strerror(r));
return;
}
}
}
static int init(struct ao *ao)
{
@@ -558,7 +501,6 @@ static int init(struct ao *ao)
struct pw_properties *props = pw_properties_new(
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE, ao->init_flags & AO_INIT_MEDIA_ROLE_MUSIC ? "Music" : "Movie",
PW_KEY_NODE_NAME, ao->client_name,
PW_KEY_NODE_DESCRIPTION, ao->client_name,
PW_KEY_APP_NAME, ao->client_name,
@@ -572,12 +514,9 @@ static int init(struct ao *ao)
if (pipewire_init_boilerplate(ao) < 0)
goto error_props;
if (p->options.buffer_msec) {
ao->device_buffer = p->options.buffer_msec * ao->samplerate / 1000;
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d", ao->device_buffer, ao->samplerate);
}
ao->device_buffer = p->options.buffer_msec * ao->samplerate / 1000;
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d", ao->device_buffer, ao->samplerate);
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", ao->samplerate);
enum spa_audio_format spa_format = af_fmt_to_pw(ao, ao->format);
@@ -609,35 +548,32 @@ static int init(struct ao *ao)
pw_thread_loop_lock(p->loop);
p->stream = pw_stream_new(p->core, "audio-src", props);
p->stream = pw_stream_new(
p->core,
"audio-src",
props);
if (p->stream == NULL) {
pw_thread_loop_unlock(p->loop);
goto error;
}
pw_stream_add_listener(p->stream, &p->stream_listener, &stream_events, ao);
enum pw_stream_flags flags = PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_INACTIVE |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS;
if (ao->init_flags & AO_INIT_EXCLUSIVE)
flags |= PW_STREAM_FLAG_EXCLUSIVE;
pw_stream_add_listener(p->stream,
&p->stream_listener,
&stream_events, ao);
if (pw_stream_connect(p->stream,
PW_DIRECTION_OUTPUT, PW_ID_ANY, flags, params, 1) < 0) {
PW_DIRECTION_OUTPUT, PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_INACTIVE |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
params, 1) < 0) {
pw_thread_loop_unlock(p->loop);
goto error;
}
wait_for_init_done(ao);
pw_thread_loop_unlock(p->loop);
if (p->init_state == INIT_STATE_ERROR)
goto error;
return 0;
error_props:
@@ -672,8 +608,9 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
switch (cmd) {
case AOCONTROL_GET_VOLUME: {
float *vol = arg;
*vol = spa_volume_to_mp_volume(p->volume);
struct ao_control_vol *vol = arg;
vol->left = spa_volume_to_mp_volume(p->volume[0]);
vol->right = spa_volume_to_mp_volume(p->volume[1]);
return CONTROL_OK;
}
case AOCONTROL_GET_MUTE: {
@@ -683,27 +620,26 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
}
case AOCONTROL_SET_VOLUME:
case AOCONTROL_SET_MUTE:
case AOCONTROL_UPDATE_STREAM_TITLE: {
case AOCONTROL_UPDATE_STREAM_TITLE:
case AOCONTROL_UPDATE_MEDIA_ROLE: {
int ret;
pw_thread_loop_lock(p->loop);
switch (cmd) {
case AOCONTROL_SET_VOLUME: {
float *vol = arg;
struct ao_control_vol *vol = arg;
uint8_t n = ao->channels.num;
if (p->options.volume_mode == VOLUME_MODE_CHANNEL) {
float values[MP_NUM_CHANNELS] = {0};
for (int i = 0; i < n; i++)
values[i] = mp_volume_to_spa_volume(*vol);
ret = CONTROL_RET(pw_stream_set_control(
p->stream, SPA_PROP_channelVolumes, n, values, 0));
float values[MP_NUM_CHANNELS] = {0};
if (n == 2) {
values[0] = mp_volume_to_spa_volume(vol->left);
values[1] = mp_volume_to_spa_volume(vol->right);
} else {
float value = mp_volume_to_spa_volume(*vol);
ret = CONTROL_RET(pw_stream_set_control(
p->stream, SPA_PROP_volume, 1, &value, 0));
for (int i = 0; i < n; i++)
values[i] = mp_volume_to_spa_volume(vol->left);
}
ret = CONTROL_RET(pw_stream_set_control(p->stream, SPA_PROP_channelVolumes, n, values, 0));
break;
}
}
case AOCONTROL_SET_MUTE: {
bool *muted = arg;
float value = *muted ? 1.f : 0.f;
@@ -717,6 +653,26 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
ret = CONTROL_RET(pw_stream_update_properties(p->stream, &SPA_DICT_INIT(items, MP_ARRAY_SIZE(items))));
break;
}
case AOCONTROL_UPDATE_MEDIA_ROLE: {
enum aocontrol_media_role *role = arg;
struct spa_dict_item items[1];
const char *role_str;
switch (*role) {
case AOCONTROL_MEDIA_ROLE_MOVIE:
role_str = "Movie";
break;
case AOCONTROL_MEDIA_ROLE_MUSIC:
role_str = "Music";
break;
default:
MP_WARN(ao, "Unknown media role %d\n", *role);
role_str = "";
break;
}
items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_ROLE, role_str);
ret = CONTROL_RET(pw_stream_update_properties(p->stream, &SPA_DICT_INIT(items, MP_ARRAY_SIZE(items))));
break;
}
default:
ret = CONTROL_NA;
}
@@ -752,7 +708,7 @@ static void hotplug_registry_global_cb(void *data, uint32_t id,
return;
pw_thread_loop_lock(priv->loop);
struct id_list *item = talloc(ao, struct id_list);
struct id_list *item = talloc_size(ao, sizeof(*item));
item->id = id;
spa_list_init(&item->node);
spa_list_append(&priv->hotplug.sinks, &item->node);
@@ -775,10 +731,10 @@ static void hotplug_registry_global_remove_cb(void *data, uint32_t id)
removed_sink = true;
spa_list_remove(&e->node);
talloc_free(e);
break;
goto done;
}
}
done:
pw_thread_loop_unlock(priv->loop);
if (removed_sink)
@@ -791,6 +747,7 @@ static const struct pw_registry_events hotplug_registry_events = {
.global_remove = hotplug_registry_global_remove_cb,
};
static int hotplug_init(struct ao *ao)
{
struct priv *priv = ao->priv;
@@ -801,12 +758,13 @@ static int hotplug_init(struct ao *ao)
pw_thread_loop_lock(priv->loop);
spa_zero(priv->hotplug);
spa_memzero(&priv->hotplug, sizeof(priv->hotplug));
spa_list_init(&priv->hotplug.sinks);
priv->hotplug.registry = pw_core_get_registry(priv->core, PW_VERSION_REGISTRY, 0);
if (!priv->hotplug.registry)
if (!priv->hotplug.registry) {
goto error;
}
if (pw_registry_add_listener(priv->hotplug.registry, &priv->hotplug.registry_listener, &hotplug_registry_events, ao) < 0) {
pw_proxy_destroy((struct pw_proxy *)priv->hotplug.registry);
@@ -867,17 +825,12 @@ const struct ao_driver audio_out_pipewire = {
{
.loop = NULL,
.stream = NULL,
.init_state = INIT_STATE_NONE,
.options.buffer_msec = 0,
.options.volume_mode = VOLUME_MODE_CHANNEL,
.options.buffer_msec = 20,
},
.options_prefix = "pipewire",
.options = (const struct m_option[]) {
{"buffer", OPT_CHOICE(options.buffer_msec, {"native", 0}),
M_RANGE(1, 2000)},
{"buffer", OPT_INT(options.buffer_msec), M_RANGE(1, 2000)},
{"remote", OPT_STRING(options.remote) },
{"volume-mode", OPT_CHOICE(options.volume_mode,
{"channel", VOLUME_MODE_CHANNEL}, {"global", VOLUME_MODE_GLOBAL})},
{0}
},
};
+23 -11
View File
@@ -24,9 +24,11 @@
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <pthread.h>
#include <pulse/pulseaudio.h>
#include "config.h"
#include "audio/format.h"
#include "common/msg.h"
#include "options/m_option.h"
@@ -55,8 +57,8 @@ struct priv {
char *cfg_host;
int cfg_buffer;
bool cfg_latency_hacks;
bool cfg_allow_suspended;
int cfg_latency_hacks;
int cfg_allow_suspended;
};
#define GENERIC_ERR_MSG(str) \
@@ -678,8 +680,14 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
// we naively copied the struct, without updating pointers etc.
// Pointers might point to invalid data, accessors might fail.
if (cmd == AOCONTROL_GET_VOLUME) {
float *vol = arg;
*vol = VOL_PA2MP(pa_cvolume_avg(&priv->pi.volume));
ao_control_vol_t *vol = arg;
if (priv->pi.volume.channels != 2)
vol->left = vol->right =
VOL_PA2MP(pa_cvolume_avg(&priv->pi.volume));
else {
vol->left = VOL_PA2MP(priv->pi.volume.values[0]);
vol->right = VOL_PA2MP(priv->pi.volume.values[1]);
}
} else if (cmd == AOCONTROL_GET_MUTE) {
bool *mute = arg;
*mute = priv->pi.mute;
@@ -693,11 +701,16 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
priv->retval = 0;
uint32_t stream_index = pa_stream_get_index(priv->stream);
if (cmd == AOCONTROL_SET_VOLUME) {
const float *vol = arg;
const ao_control_vol_t *vol = arg;
struct pa_cvolume volume;
pa_cvolume_reset(&volume, ao->channels.num);
pa_cvolume_set(&volume, volume.channels, VOL_MP2PA(*vol));
if (volume.channels != 2)
pa_cvolume_set(&volume, volume.channels, VOL_MP2PA(vol->left));
else {
volume.values[0] = VOL_MP2PA(vol->left);
volume.values[1] = VOL_MP2PA(vol->right);
}
if (!waitop(priv, pa_context_set_sink_input_volume(priv->context,
stream_index,
&volume,
@@ -716,9 +729,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
GENERIC_ERR_MSG("pa_context_set_sink_input_mute() failed");
return CONTROL_ERROR;
}
} else {
MP_ASSERT_UNREACHABLE();
}
} else
abort();
return CONTROL_OK;
}
@@ -809,8 +821,8 @@ const struct ao_driver audio_out_pulse = {
{"host", OPT_STRING(cfg_host)},
{"buffer", OPT_CHOICE(cfg_buffer, {"native", 0}),
M_RANGE(1, 2000)},
{"latency-hacks", OPT_BOOL(cfg_latency_hacks)},
{"allow-suspended", OPT_BOOL(cfg_allow_suspended)},
{"latency-hacks", OPT_FLAG(cfg_latency_hacks)},
{"allow-suspended", OPT_FLAG(cfg_allow_suspended)},
{0}
},
.options_prefix = "pulse",
+2 -1
View File
@@ -18,6 +18,7 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "audio/format.h"
#include "mpv_talloc.h"
#include "ao.h"
@@ -61,7 +62,7 @@ static void audio_callback(void *userdata, Uint8 *stream, int len)
// fixed latency.
double delay = 2 * len / (double)ao->bps;
ao_read_data(ao, data, len / ao->sstride, mp_time_ns() + MP_TIME_S_TO_NS(delay));
ao_read_data(ao, data, len / ao->sstride, mp_time_us() + 1000000LL * delay);
}
static void uninit(struct ao *ao)
+5 -8
View File
@@ -17,6 +17,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/types.h>
#include <poll.h>
#include <errno.h>
@@ -209,18 +211,18 @@ static void uninit(struct ao *ao)
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct priv *p = ao->priv;
float *vol = arg;
ao_control_vol_t *vol = arg;
switch (cmd) {
case AOCONTROL_GET_VOLUME:
if (!p->havevol)
return CONTROL_FALSE;
*vol = p->vol * 100 / SIO_MAXVOL;
vol->left = vol->right = p->vol * 100 / SIO_MAXVOL;
break;
case AOCONTROL_SET_VOLUME:
if (!p->havevol)
return CONTROL_FALSE;
sio_setvol(p->hdl, *vol * SIO_MAXVOL / 100);
sio_setvol(p->hdl, vol->left * SIO_MAXVOL / 100);
break;
default:
return CONTROL_UNKNOWN;
@@ -235,13 +237,8 @@ static void reset(struct ao *ao)
if (p->playing) {
p->playing = false;
#if HAVE_SNDIO_1_9
if (!sio_flush(p->hdl)) {
MP_ERR(ao, "reset: couldn't sio_flush()\n");
#else
if (!sio_stop(p->hdl)) {
MP_ERR(ao, "reset: couldn't sio_stop()\n");
#endif
}
p->delay = 0;
if (!sio_start(p->hdl)) {
+34 -37
View File
@@ -36,8 +36,7 @@ static UINT64 uint64_scale(UINT64 x, UINT64 num, UINT64 den)
+ ((x % den) * (num % den)) / den;
}
static HRESULT get_device_delay(struct wasapi_state *state, double *delay_ns)
{
static HRESULT get_device_delay(struct wasapi_state *state, double *delay_us) {
UINT64 sample_count = atomic_load(&state->sample_count);
UINT64 position, qpc_position;
HRESULT hr;
@@ -55,7 +54,7 @@ static HRESULT get_device_delay(struct wasapi_state *state, double *delay_ns)
state->format.Format.nSamplesPerSec,
state->clock_frequency);
INT64 diff = sample_count - sample_position;
*delay_ns = diff * 1e9 / state->format.Format.nSamplesPerSec;
*delay_us = diff * 1e6 / state->format.Format.nSamplesPerSec;
// Correct for any delay in IAudioClock_GetPosition above.
// This should normally be very small (<1 us), but just in case. . .
@@ -66,16 +65,16 @@ static HRESULT get_device_delay(struct wasapi_state *state, double *delay_ns)
// ignore the above calculation if it yields more than 10 seconds (due to
// possible overflow inside IAudioClock_GetPosition)
if (qpc_diff < 10 * 10000000) {
*delay_ns -= qpc_diff * 100.0; // convert to ns
*delay_us -= qpc_diff / 10.0; // convert to us
} else {
MP_VERBOSE(state, "Insane qpc delay correction of %g seconds. "
"Ignoring it.\n", qpc_diff / 10000000.0);
}
if (sample_count > 0 && *delay_ns <= 0) {
MP_WARN(state, "Under-run: Device delay: %g ns\n", *delay_ns);
if (sample_count > 0 && *delay_us <= 0) {
MP_WARN(state, "Under-run: Device delay: %g us\n", *delay_us);
} else {
MP_TRACE(state, "Device delay: %g ns\n", *delay_ns);
MP_TRACE(state, "Device delay: %g us\n", *delay_us);
}
return S_OK;
@@ -117,11 +116,11 @@ static bool thread_feed(struct ao *ao)
MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n",
frame_count, padding);
double delay_ns;
hr = get_device_delay(state, &delay_ns);
double delay_us;
hr = get_device_delay(state, &delay_us);
EXIT_ON_ERROR(hr);
// add the buffer delay
delay_ns += frame_count * 1e9 / state->format.Format.nSamplesPerSec;
delay_us += frame_count * 1e6 / state->format.Format.nSamplesPerSec;
BYTE *pData;
hr = IAudioRenderClient_GetBuffer(state->pRenderClient,
@@ -132,7 +131,7 @@ static bool thread_feed(struct ao *ao)
ao_read_data_converted(ao, &state->convert_format,
(void **)data, frame_count,
mp_time_ns() + (int64_t)llrint(delay_ns));
mp_time_us() + (int64_t)llrint(delay_us));
// note, we can't use ao_read_data return value here since we already
// committed to frame_count above in the GetBuffer call
@@ -199,7 +198,7 @@ static DWORD __stdcall AudioThread(void *lpParameter)
{
struct ao *ao = lpParameter;
struct wasapi_state *state = ao->priv;
mp_thread_set_name("ao/wasapi");
mpthread_set_name("wasapi event");
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
state->init_ok = wasapi_thread_init(ao);
@@ -349,10 +348,13 @@ static int thread_control_exclusive(struct ao *ao, enum aocontrol cmd, void *arg
case AOCONTROL_GET_VOLUME:
IAudioEndpointVolume_GetMasterVolumeLevelScalar(
state->pEndpointVolume, &volume);
*(float *)arg = volume;
*(ao_control_vol_t *)arg = (ao_control_vol_t){
.left = 100.0f * volume,
.right = 100.0f * volume,
};
return CONTROL_OK;
case AOCONTROL_SET_VOLUME:
volume = (*(float *)arg) / 100.f;
volume = ((ao_control_vol_t *)arg)->left / 100.f;
IAudioEndpointVolume_SetMasterVolumeLevelScalar(
state->pEndpointVolume, volume, NULL);
return CONTROL_OK;
@@ -379,10 +381,13 @@ static int thread_control_shared(struct ao *ao, enum aocontrol cmd, void *arg)
switch(cmd) {
case AOCONTROL_GET_VOLUME:
ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume, &volume);
*(float *)arg = volume;
*(ao_control_vol_t *)arg = (ao_control_vol_t){
.left = 100.0f * volume,
.right = 100.0f * volume,
};
return CONTROL_OK;
case AOCONTROL_SET_VOLUME:
volume = (*(float *)arg) / 100.f;
volume = ((ao_control_vol_t *)arg)->left / 100.f;
ISimpleAudioVolume_SetMasterVolume(state->pAudioVolume, volume, NULL);
return CONTROL_OK;
case AOCONTROL_GET_MUTE:
@@ -407,28 +412,20 @@ static int thread_control(struct ao *ao, enum aocontrol cmd, void *arg)
if (!state->pSessionControl)
return CONTROL_FALSE;
wchar_t *title = mp_from_utf8(NULL, (const char *)arg);
HRESULT hr = IAudioSessionControl_SetDisplayName(state->pSessionControl,
title,NULL);
wchar_t *title = mp_from_utf8(NULL, (char*)arg);
wchar_t *tmp = NULL;
// There is a weird race condition in the IAudioSessionControl itself --
// it seems that *sometimes* the SetDisplayName does not take effect and
// it still shows the old title. Use this loop to insist until it works.
do {
IAudioSessionControl_SetDisplayName(state->pSessionControl, title, NULL);
SAFE_DESTROY(tmp, CoTaskMemFree(tmp));
IAudioSessionControl_GetDisplayName(state->pSessionControl, &tmp);
} while (wcscmp(title, tmp));
SAFE_DESTROY(tmp, CoTaskMemFree(tmp));
talloc_free(title);
if (SUCCEEDED(hr))
return CONTROL_OK;
MP_WARN(ao, "Error setting audio session name: %s\n",
mp_HRESULT_to_str(hr));
assert(ao->client_name);
if (!ao->client_name)
return CONTROL_ERROR;
// Fallback to client name
title = mp_from_utf8(NULL, ao->client_name);
IAudioSessionControl_SetDisplayName(state->pSessionControl,
title, NULL);
talloc_free(title);
return CONTROL_ERROR;
return CONTROL_OK;
}
return state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE ?
+1 -2
View File
@@ -20,10 +20,8 @@
#ifndef MP_AO_WASAPI_H_
#define MP_AO_WASAPI_H_
#include <stdatomic.h>
#include <stdlib.h>
#include <stdbool.h>
#include <windows.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
@@ -31,6 +29,7 @@
#include <endpointvolume.h>
#include "common/msg.h"
#include "osdep/atomic.h"
#include "osdep/windows_utils.h"
#include "internal.h"
#include "ao.h"
+14 -31
View File
@@ -31,6 +31,8 @@
#include "osdep/strnlen.h"
#include "ao_wasapi.h"
#define MIXER_DEFAULT_LABEL L"mpv - video player"
DEFINE_PROPERTYKEY(mp_PKEY_Device_FriendlyName,
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20,
0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
@@ -143,19 +145,7 @@ static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
wformat->SubFormat = *format_to_subtype(format->mp_format);
wformat->Samples.wValidBitsPerSample = format->used_msb;
uint64_t chans = mp_chmap_to_waveext(channels);
wformat->dwChannelMask = chans;
if (wformat->Format.nChannels > 8 || wformat->dwChannelMask != chans) {
// IAudioClient::IsFormatSupported tend to fallback to stereo for closest
// format match when there are more channels. Remix to standard layout.
// Also if input channel mask has channels outside 32-bits override it
// and hope for the best...
wformat->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
wformat->Format.nChannels = 8;
}
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
update_waveformat_datarate(wformat);
}
@@ -248,8 +238,7 @@ static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
(unsigned) wf->nSamplesPerSec);
return buf;
}
#define waveformat_to_str_(wf, sz) waveformat_to_str_buf((char[sz]){0}, sz, (wf))
#define waveformat_to_str(wf) waveformat_to_str_(wf, MP_NUM_CHANNELS * 4 + 42)
#define waveformat_to_str(wf) waveformat_to_str_buf((char[64]){0}, 64, (wf))
static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
{
@@ -385,7 +374,7 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
"3.0", "3.0(back)",
"quad", "quad(side)", "3.1",
"5.0(side)", "4.1",
"5.1(side)", "6.0", "6.0(front)", "hexagonal",
"5.1(side)", "6.0", "6.0(front)", "hexagonal"
"6.1(back)", "6.1(front)", "7.0", "7.0(front)",
"7.1(wide)", "7.1(wide-side)", "7.1(rear)", "octagonal", NULL};
@@ -556,7 +545,7 @@ exit_label:
return hr;
}
static void init_session_display(struct wasapi_state *state, const char *name) {
static void init_session_display(struct wasapi_state *state) {
HRESULT hr = IAudioClient_GetService(state->pAudioClient,
&IID_IAudioSessionControl,
(void **)&state->pSessionControl);
@@ -571,20 +560,14 @@ static void init_session_display(struct wasapi_state *state, const char *name) {
mp_HRESULT_to_str(hr));
}
assert(name);
if (!name)
return;
wchar_t *title = mp_from_utf8(NULL, name);
hr = IAudioSessionControl_SetDisplayName(state->pSessionControl, title, NULL);
talloc_free(title);
hr = IAudioSessionControl_SetDisplayName(state->pSessionControl,
MIXER_DEFAULT_LABEL, NULL);
EXIT_ON_ERROR(hr);
return;
exit_label:
// if we got here then the session control is useless - release it
SAFE_RELEASE(state->pSessionControl);
MP_WARN(state, "Error setting audio session name: %s\n",
MP_WARN(state, "Error setting audio session display name: %s\n",
mp_HRESULT_to_str(hr));
return;
}
@@ -685,7 +668,7 @@ static HRESULT fix_format(struct ao *ao, bool align_hack)
hr = init_clock(state);
EXIT_ON_ERROR(hr);
init_session_display(state, ao->client_name);
init_session_display(state);
init_volume_control(state);
#if !HAVE_UWP
@@ -945,7 +928,7 @@ bool wasapi_thread_init(struct ao *ao)
{
struct wasapi_state *state = ao->priv;
MP_DBG(ao, "Init wasapi thread\n");
int64_t retry_wait = MP_TIME_US_TO_NS(1);
int64_t retry_wait = 1;
bool align_hack = false;
HRESULT hr;
@@ -1028,13 +1011,13 @@ retry:
goto retry;
case AUDCLNT_E_DEVICE_IN_USE:
case AUDCLNT_E_DEVICE_INVALIDATED:
if (retry_wait > MP_TIME_US_TO_NS(8)) {
if (retry_wait > 8) {
MP_FATAL(ao, "Bad device retry failed\n");
return false;
}
wasapi_thread_uninit(ao);
MP_WARN(ao, "Retrying in %"PRId64" ns\n", retry_wait);
mp_sleep_ns(retry_wait);
MP_WARN(ao, "Retrying in %"PRId64" us\n", retry_wait);
mp_sleep_us(retry_wait);
retry_wait *= 2;
goto retry;
}
+90 -123
View File
@@ -16,6 +16,7 @@
*/
#include <stddef.h>
#include <pthread.h>
#include <inttypes.h>
#include <math.h>
#include <unistd.h>
@@ -38,12 +39,12 @@
struct buffer_state {
// Buffer and AO
mp_mutex lock;
mp_cond wakeup;
pthread_mutex_t lock;
pthread_cond_t wakeup;
// Playthread sleep
mp_mutex pt_lock;
mp_cond pt_wakeup;
pthread_mutex_t pt_lock;
pthread_cond_t pt_wakeup;
// Access from AO driver's thread only.
char *convert_buffer;
@@ -61,7 +62,7 @@ struct buffer_state {
bool playing; // logically playing audio from buffer
bool paused; // logically paused
int64_t end_time_ns; // absolute output time of last played sample
int64_t end_time_us; // absolute output time of last played sample
bool initial_unblocked;
@@ -69,7 +70,7 @@ struct buffer_state {
bool hw_paused; // driver->set_pause() was used successfully
bool recover_pause; // non-hw_paused: needs to recover delay
struct mp_pcm_state prepause_state;
mp_thread thread; // thread shoveling data to AO
pthread_t thread; // thread shoveling data to AO
bool thread_valid; // thread is running
struct mp_aframe *temp_buf;
@@ -78,15 +79,15 @@ struct buffer_state {
bool terminate; // exit thread
};
static MP_THREAD_VOID playthread(void *arg);
static void *playthread(void *arg);
void ao_wakeup_playthread(struct ao *ao)
{
struct buffer_state *p = ao->buffer_state;
mp_mutex_lock(&p->pt_lock);
pthread_mutex_lock(&p->pt_lock);
p->need_wakeup = true;
mp_cond_broadcast(&p->pt_wakeup);
mp_mutex_unlock(&p->pt_lock);
pthread_cond_broadcast(&p->pt_wakeup);
pthread_mutex_unlock(&p->pt_lock);
}
// called locked
@@ -114,8 +115,7 @@ struct mp_async_queue *ao_get_queue(struct ao *ao)
}
// Special behavior with data==NULL: caller uses p->pending.
static int read_buffer(struct ao *ao, void **data, int samples, bool *eof,
bool pad_silence)
static int read_buffer(struct ao *ao, void **data, int samples, bool *eof)
{
struct buffer_state *p = ao->buffer_state;
int pos = 0;
@@ -161,72 +161,44 @@ static int read_buffer(struct ao *ao, void **data, int samples, bool *eof,
}
// pad with silence (underflow/paused/eof)
if (pad_silence) {
for (int n = 0; n < ao->num_planes; n++) {
af_fill_silence((char *)data[n] + pos * ao->sstride,
(samples - pos) * ao->sstride,
ao->format);
}
for (int n = 0; n < ao->num_planes; n++) {
af_fill_silence((char *)data[n] + pos * ao->sstride,
(samples - pos) * ao->sstride,
ao->format);
}
ao_post_process_data(ao, data, pos);
return pos;
}
static int ao_read_data_unlocked(struct ao *ao, void **data, int samples,
int64_t out_time_ns, bool pad_silence)
{
struct buffer_state *p = ao->buffer_state;
assert(!ao->driver->write);
int pos = read_buffer(ao, data, samples, &(bool){0}, pad_silence);
if (pos > 0)
p->end_time_ns = out_time_ns;
if (pos < samples && p->playing && !p->paused) {
p->playing = false;
ao->wakeup_cb(ao->wakeup_ctx);
// For ao_drain().
mp_cond_broadcast(&p->wakeup);
}
return pos;
}
// Read the given amount of samples in the user-provided data buffer. Returns
// the number of samples copied. If there is not enough data (buffer underrun
// or EOF), return the number of samples that could be copied, and fill the
// rest of the user-provided buffer with silence.
// This basically assumes that the audio device doesn't care about underruns.
// If this is called in paused mode, it will always return 0.
// The caller should set out_time_ns to the expected delay until the last sample
// reaches the speakers, in nanoseconds, using mp_time_ns() as reference.
int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_ns)
// The caller should set out_time_us to the expected delay until the last sample
// reaches the speakers, in microseconds, using mp_time_us() as reference.
int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us)
{
struct buffer_state *p = ao->buffer_state;
assert(!ao->driver->write);
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
int pos = ao_read_data_unlocked(ao, data, samples, out_time_ns, true);
int pos = read_buffer(ao, data, samples, &(bool){0});
mp_mutex_unlock(&p->lock);
if (pos > 0)
p->end_time_us = out_time_us;
return pos;
}
if (pos < samples && p->playing && !p->paused) {
p->playing = false;
ao->wakeup_cb(ao->wakeup_ctx);
// For ao_drain().
pthread_cond_broadcast(&p->wakeup);
}
// Like ao_read_data() but does not block and also may return partial data.
// Callers have to check the return value.
int ao_read_data_nonblocking(struct ao *ao, void **data, int samples, int64_t out_time_ns)
{
struct buffer_state *p = ao->buffer_state;
if (mp_mutex_trylock(&p->lock))
return 0;
int pos = ao_read_data_unlocked(ao, data, samples, out_time_ns, false);
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
return pos;
}
@@ -234,13 +206,13 @@ int ao_read_data_nonblocking(struct ao *ao, void **data, int samples, int64_t ou
// Same as ao_read_data(), but convert data according to *fmt.
// fmt->src_fmt and fmt->channels must be the same as the AO parameters.
int ao_read_data_converted(struct ao *ao, struct ao_convert_fmt *fmt,
void **data, int samples, int64_t out_time_ns)
void **data, int samples, int64_t out_time_us)
{
struct buffer_state *p = ao->buffer_state;
void *ndata[MP_NUM_CHANNELS] = {0};
if (!ao_need_conversion(fmt))
return ao_read_data(ao, data, samples, out_time_ns);
return ao_read_data(ao, data, samples, out_time_us);
assert(ao->format == fmt->src_fmt);
assert(ao->channels.num == fmt->channels);
@@ -260,7 +232,7 @@ int ao_read_data_converted(struct ao *ao, struct ao_convert_fmt *fmt,
for (int n = 0; n < planes; n++)
ndata[n] = p->convert_buffer + n * src_plane_size;
int res = ao_read_data(ao, ndata, samples, out_time_ns);
int res = ao_read_data(ao, ndata, samples, out_time_us);
ao_convert_inplace(fmt, ndata, samples);
for (int n = 0; n < planes; n++)
@@ -276,12 +248,12 @@ int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
if (ao->driver->control) {
// Only need to lock in push mode.
if (ao->driver->write)
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
r = ao->driver->control(ao, cmd, arg);
if (ao->driver->write)
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
}
return r;
}
@@ -290,7 +262,7 @@ double ao_get_delay(struct ao *ao)
{
struct buffer_state *p = ao->buffer_state;
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
double driver_delay;
if (ao->driver->write) {
@@ -298,16 +270,16 @@ double ao_get_delay(struct ao *ao)
get_dev_state(ao, &state);
driver_delay = state.delay;
} else {
int64_t end = p->end_time_ns;
int64_t now = mp_time_ns();
driver_delay = MPMAX(0, MP_TIME_NS_TO_S(end - now));
int64_t end = p->end_time_us;
int64_t now = mp_time_us();
driver_delay = MPMAX(0, (end - now) / (1000.0 * 1000.0));
}
int pending = mp_async_queue_get_samples(p->queue);
if (p->pending)
pending += mp_aframe_get_size(p->pending);
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
return driver_delay + pending / (double)ao->samplerate;
}
@@ -318,7 +290,7 @@ void ao_reset(struct ao *ao)
bool wakeup = false;
bool do_reset = false;
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
TA_FREEP(&p->pending);
mp_async_queue_reset(p->queue);
@@ -339,9 +311,9 @@ void ao_reset(struct ao *ao)
p->playing = false;
p->recover_pause = false;
p->hw_paused = false;
p->end_time_ns = 0;
p->end_time_us = 0;
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
if (do_reset)
ao->driver->reset(ao);
@@ -359,7 +331,7 @@ void ao_start(struct ao *ao)
struct buffer_state *p = ao->buffer_state;
bool do_start = false;
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
p->playing = true;
@@ -368,7 +340,7 @@ void ao_start(struct ao *ao)
do_start = true;
}
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
// Pull AOs might call ao_read_data() so do this outside the lock.
if (do_start)
@@ -377,20 +349,15 @@ void ao_start(struct ao *ao)
ao_wakeup_playthread(ao);
}
void ao_set_paused(struct ao *ao, bool paused, bool eof)
void ao_set_paused(struct ao *ao, bool paused)
{
struct buffer_state *p = ao->buffer_state;
bool wakeup = false;
bool do_reset = false, do_start = false;
// If we are going to pause on eof and ao is still playing,
// be sure to drain the ao first for gapless.
if (eof && paused && ao_is_playing(ao))
ao_drain(ao);
pthread_mutex_lock(&p->lock);
mp_mutex_lock(&p->lock);
if ((p->playing || !ao->driver->write) && !p->paused && paused) {
if (p->playing && !p->paused && paused) {
if (p->streaming && !ao->stream_silence) {
if (ao->driver->write) {
if (!p->recover_pause)
@@ -423,7 +390,7 @@ void ao_set_paused(struct ao *ao, bool paused, bool eof)
}
p->paused = paused;
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
if (do_reset)
ao->driver->reset(ao);
@@ -442,9 +409,9 @@ bool ao_is_playing(struct ao *ao)
{
struct buffer_state *p = ao->buffer_state;
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
bool playing = p->playing;
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
return playing;
}
@@ -454,31 +421,30 @@ void ao_drain(struct ao *ao)
{
struct buffer_state *p = ao->buffer_state;
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
while (!p->paused && p->playing) {
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
double delay = ao_get_delay(ao);
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
// Limit to buffer + arbitrary ~250ms max. waiting for robustness.
delay += mp_async_queue_get_samples(p->queue) / (double)ao->samplerate;
struct timespec ts = mp_rel_time_to_timespec(MPMAX(delay, 0) + 0.25);
// Wait for EOF signal from AO.
if (mp_cond_timedwait(&p->wakeup, &p->lock,
MP_TIME_S_TO_NS(MPMAX(delay, 0) + 0.25)))
{
if (pthread_cond_timedwait(&p->wakeup, &p->lock, &ts)) {
MP_VERBOSE(ao, "drain timeout\n");
break;
}
if (!p->playing && mp_async_queue_get_samples(p->queue)) {
MP_WARN(ao, "underrun during draining\n");
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
ao_start(ao);
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
}
}
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
ao_reset(ao);
}
@@ -494,12 +460,12 @@ void ao_uninit(struct ao *ao)
struct buffer_state *p = ao->buffer_state;
if (p && p->thread_valid) {
mp_mutex_lock(&p->pt_lock);
pthread_mutex_lock(&p->pt_lock);
p->terminate = true;
mp_cond_broadcast(&p->pt_wakeup);
mp_mutex_unlock(&p->pt_lock);
pthread_cond_broadcast(&p->pt_wakeup);
pthread_mutex_unlock(&p->pt_lock);
mp_thread_join(p->thread);
pthread_join(p->thread, NULL);
p->thread_valid = false;
}
@@ -513,11 +479,11 @@ void ao_uninit(struct ao *ao)
talloc_free(p->convert_buffer);
talloc_free(p->temp_buf);
mp_cond_destroy(&p->wakeup);
mp_mutex_destroy(&p->lock);
pthread_cond_destroy(&p->wakeup);
pthread_mutex_destroy(&p->lock);
mp_cond_destroy(&p->pt_wakeup);
mp_mutex_destroy(&p->pt_lock);
pthread_cond_destroy(&p->pt_wakeup);
pthread_mutex_destroy(&p->pt_lock);
}
talloc_free(ao);
@@ -538,11 +504,11 @@ bool init_buffer_post(struct ao *ao)
assert(ao->driver->get_state);
}
mp_mutex_init(&p->lock);
mp_cond_init(&p->wakeup);
pthread_mutex_init(&p->lock, NULL);
pthread_cond_init(&p->wakeup, NULL);
mp_mutex_init(&p->pt_lock);
mp_cond_init(&p->pt_wakeup);
pthread_mutex_init(&p->pt_lock, NULL);
pthread_cond_init(&p->pt_wakeup, NULL);
p->queue = mp_async_queue_create();
p->filter_root = mp_filter_create_root(ao->global);
@@ -561,7 +527,7 @@ bool init_buffer_post(struct ao *ao)
mp_filter_graph_set_wakeup_cb(p->filter_root, wakeup_filters, ao);
p->thread_valid = true;
if (mp_thread_create(&p->thread, playthread, ao)) {
if (pthread_create(&p->thread, NULL, playthread, ao)) {
p->thread_valid = false;
return false;
}
@@ -626,7 +592,7 @@ static bool ao_play_data(struct ao *ao)
bool got_eof = false;
if (ao->driver->write_frames) {
TA_FREEP(&p->pending);
samples = read_buffer(ao, NULL, 1, &got_eof, false);
samples = read_buffer(ao, NULL, 1, &got_eof);
planes = (void **)&p->pending;
} else {
if (!realloc_buf(ao, space)) {
@@ -643,7 +609,7 @@ static bool ao_play_data(struct ao *ao)
}
if (!samples) {
samples = read_buffer(ao, planes, space, &got_eof, true);
samples = read_buffer(ao, planes, space, &got_eof);
if (p->paused || (ao->stream_silence && !p->playing))
samples = space; // read_buffer() sets remainder to silent
}
@@ -680,17 +646,17 @@ eof:
}
ao->wakeup_cb(ao->wakeup_ctx);
// For ao_drain().
mp_cond_broadcast(&p->wakeup);
pthread_cond_broadcast(&p->wakeup);
return true;
}
static MP_THREAD_VOID playthread(void *arg)
static void *playthread(void *arg)
{
struct ao *ao = arg;
struct buffer_state *p = ao->buffer_state;
mp_thread_set_name("ao");
mpthread_set_name("ao");
while (1) {
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
bool retry = false;
if (!ao->driver->initially_blocked || p->initial_unblocked)
@@ -698,39 +664,40 @@ static MP_THREAD_VOID playthread(void *arg)
// Wait until the device wants us to write more data to it.
// Fallback to guessing.
int64_t timeout = INT64_MAX;
double timeout = INFINITY;
if (p->streaming && !retry && (!p->paused || ao->stream_silence)) {
// Wake up again if half of the audio buffer has been played.
// Since audio could play at a faster or slower pace, wake up twice
// as often as ideally needed.
timeout = MP_TIME_S_TO_NS(ao->device_buffer / (double)ao->samplerate * 0.25);
timeout = ao->device_buffer / (double)ao->samplerate * 0.25;
}
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
mp_mutex_lock(&p->pt_lock);
pthread_mutex_lock(&p->pt_lock);
if (p->terminate) {
mp_mutex_unlock(&p->pt_lock);
pthread_mutex_unlock(&p->pt_lock);
break;
}
if (!p->need_wakeup && !retry) {
MP_STATS(ao, "start audio wait");
mp_cond_timedwait(&p->pt_wakeup, &p->pt_lock, timeout);
struct timespec ts = mp_rel_time_to_timespec(timeout);
pthread_cond_timedwait(&p->pt_wakeup, &p->pt_lock, &ts);
MP_STATS(ao, "end audio wait");
}
p->need_wakeup = false;
mp_mutex_unlock(&p->pt_lock);
pthread_mutex_unlock(&p->pt_lock);
}
MP_THREAD_RETURN();
return NULL;
}
void ao_unblock(struct ao *ao)
{
if (ao->driver->write) {
struct buffer_state *p = ao->buffer_state;
mp_mutex_lock(&p->lock);
pthread_mutex_lock(&p->lock);
p->initial_unblocked = true;
mp_mutex_unlock(&p->lock);
pthread_mutex_unlock(&p->lock);
ao_wakeup_playthread(ao);
}
}
+5 -6
View File
@@ -18,9 +18,10 @@
#ifndef MP_AO_INTERNAL_H_
#define MP_AO_INTERNAL_H_
#include <stdatomic.h>
#include <stdbool.h>
#include <pthread.h>
#include "osdep/atomic.h"
#include "audio/out/ao.h"
/* global data used by ao.c and ao drivers */
@@ -62,7 +63,7 @@ struct ao {
atomic_uint events_;
// Float gain multiplicator
_Atomic float gain;
mp_atomic_float gain;
int buffer;
double def_buffer;
@@ -199,9 +200,7 @@ struct ao_driver {
// These functions can be called by AOs.
int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_ns);
MP_WARN_UNUSED_RESULT
int ao_read_data_nonblocking(struct ao *ao, void **data, int samples, int64_t out_time_ns);
int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us);
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map);
@@ -232,6 +231,6 @@ void ao_convert_inplace(struct ao_convert_fmt *fmt, void **data, int num_samples
void ao_wakeup_playthread(struct ao *ao);
int ao_read_data_converted(struct ao *ao, struct ao_convert_fmt *fmt,
void **data, int samples, int64_t out_time_ns);
void **data, int samples, int64_t out_time_us);
#endif
Executable
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
# This script simply downloads waf to the current directory
import os, sys, stat, hashlib, subprocess
from urllib.request import urlopen, URLError
WAFRELEASE = "waf-2.0.24"
WAFURLS = ["https://waf.io/" + WAFRELEASE,
"https://www.freehackers.org/~tnagy/release/" + WAFRELEASE]
SHA256HASH = "93909bca823a675f9f40af7c65b24887c3a3c0efdf411ff1978ba827194bdeb0"
if os.path.exists("waf"):
wafver = subprocess.check_output([sys.executable, './waf', '--version']).decode()
if WAFRELEASE.split('-')[1] == wafver.split(' ')[1]:
print("Found 'waf', skipping download.")
sys.exit(0)
if "--no-download" in sys.argv[1:]:
print("Did not find {} and no download was requested.".format(WAFRELEASE))
sys.exit(1)
waf = None
for WAFURL in WAFURLS:
try:
print("Downloading {}...".format(WAFURL))
waf = urlopen(WAFURL).read()
break
except URLError as err:
print("Download failed! ({})".format(err))
if not waf:
print("Could not download {}.".format(WAFRELEASE))
sys.exit(1)
if SHA256HASH == hashlib.sha256(waf).hexdigest():
# Upstream waf is not changing the default interpreter during
# 2.0.x line due to compatibility reasons apparently. So manually
# convert it to use python3 (the script works with both).
expected = b"#!/usr/bin/env python\n"
assert waf.startswith(expected)
waf = b"#!/usr/bin/env python3\n" + waf[len(expected):]
with open("waf", "wb") as wf:
wf.write(waf)
os.chmod("waf", os.stat("waf").st_mode | stat.S_IXUSR)
print("Checksum verified.")
else:
print("The checksum of the downloaded file does not match!")
print(" - got: {}".format(hashlib.sha256(waf).hexdigest()))
print(" - expected: {}".format(SHA256HASH))
print("Please download and verify the file manually.")
sys.exit(1)
+24 -6
View File
@@ -6,16 +6,12 @@ export CXXFLAGS="$CXXFLAGS -isystem/usr/local/include"
export LDFLAGS="$LDFLAGS -L/usr/local/lib"
meson setup build \
--werror \
-Dlibplacebo:werror=false \
-Dc_args="-Wno-error=deprecated -Wno-error=deprecated-declarations" \
-Diconv=disabled \
-Dlibmpv=true \
-Dlua=enabled \
-Degl-drm=enabled \
-Dopenal=enabled \
-Dsdl2=enabled \
-Dsndio=enabled \
-Dtests=true \
-Dvdpau=enabled \
-Dvulkan=enabled \
-Doss-audio=enabled \
@@ -26,4 +22,26 @@ meson setup build \
$NULL
meson compile -C build
./build/mpv -v --no-config
./build/mpv
if [ ! -e "./waf" ] ; then
python3 ./bootstrap.py
fi
python3 ./waf configure \
--enable-libmpv-shared \
--enable-lua \
--enable-egl-drm \
--enable-openal \
--enable-sdl2 \
--enable-sndio \
--enable-vdpau \
--enable-vulkan \
--enable-oss-audio \
$(pkg info -q v4l_compat && echo --enable-dvbin) \
$(pkg info -q libdvdnav && echo --enable-dvdnav) \
$(pkg info -q libcdio-paranoia && echo --enable-cdda) \
$(pkg info -q pipewire && echo --enable-pipewire) \
$NULL
python3 ./waf build
+31 -9
View File
@@ -10,13 +10,35 @@ if [[ -d "./build/${MPV_VARIANT}" ]] ; then
rm -rf "./build/${MPV_VARIANT}"
fi
PKG_CONFIG_PATH="${FFMPEG_SYSROOT}/lib/pkgconfig/" CC="${CC}" CXX="${CXX}" \
meson setup build \
-Dprefix="${MPV_INSTALL_PREFIX}" \
-D{libmpv,tests}=true \
-D{gl,iconv,lcms2,lua,jpeg,plain-gl,zlib}=enabled \
-D{cocoa,coreaudio,gl-cocoa,macos-cocoa-cb,macos-touchbar,videotoolbox-gl}=enabled
if [[ $1 = "meson" ]]; then
PKG_CONFIG_PATH="${FFMPEG_SYSROOT}/lib/pkgconfig/" CC="${CC}" CXX="${CXX}" \
meson setup build \
-Dprefix="${MPV_INSTALL_PREFIX}" \
-Dlibmpv=true \
-D{gl,iconv,lcms2,lua,jpeg,plain-gl,zlib}=enabled \
-D{cocoa,coreaudio,gl-cocoa,macos-cocoa-cb,macos-touchbar,videotoolbox-gl}=enabled
meson compile -C build -j4
meson install -C build
./build/mpv -v --no-config
meson compile -C build -j4
meson install -C build
./build/mpv
fi
if [[ $1 = "waf" ]]; then
if [[ ! -e "./waf" ]] ; then
python3 ./bootstrap.py
fi
PKG_CONFIG_PATH="${FFMPEG_SYSROOT}/lib/pkgconfig/" CC="${CC}" CXX="${CXX}" python3 \
./waf configure \
--variant="${MPV_VARIANT}" \
--prefix="${MPV_INSTALL_PREFIX}" \
--enable-{gl,iconv,lcms2,libmpv-shared,lua,jpeg,plain-gl,zlib} \
--enable-{cocoa,coreaudio,gl-cocoa,macos-cocoa-cb,macos-touchbar,videotoolbox-gl} \
--swift-flags="${CI_SWIFT_FLAGS}"
python3 ./waf build --variant="${MPV_VARIANT}" -j4
python3 ./waf install --variant="${MPV_VARIANT}"
./build/mpv
fi
+87 -165
View File
@@ -6,7 +6,11 @@ ln -snf . "$prefix_dir/usr"
ln -snf . "$prefix_dir/local"
wget="wget -nc --progress=bar:force"
gitclone="git clone --depth=1 --recursive"
gitclone="git clone --depth=10 --recursive"
commonflags="--disable-static --enable-shared"
export PKG_CONFIG_SYSROOT_DIR="$prefix_dir"
export PKG_CONFIG_LIBDIR="$PKG_CONFIG_SYSROOT_DIR/lib/pkgconfig"
# -posix is Ubuntu's variant with pthreads support
export CC=$TARGET-gcc-posix
@@ -18,14 +22,6 @@ export RANLIB=$TARGET-ranlib
export CFLAGS="-O2 -pipe -Wall -D_FORTIFY_SOURCE=2"
export LDFLAGS="-fstack-protector-strong"
# anything that uses pkg-config
export PKG_CONFIG_SYSROOT_DIR="$prefix_dir"
export PKG_CONFIG_LIBDIR="$PKG_CONFIG_SYSROOT_DIR/lib/pkgconfig"
# autotools(-like)
commonflags="--disable-static --enable-shared"
# meson
fam=x86_64
[[ "$TARGET" == "i686-"* ]] && fam=x86
cat >"$prefix_dir/crossfile" <<EOF
@@ -33,13 +29,12 @@ cat >"$prefix_dir/crossfile" <<EOF
buildtype = 'release'
wrap_mode = 'nodownload'
[binaries]
c = ['ccache', '${CC}']
cpp = ['ccache', '${CXX}']
c = '${CC}'
cpp = '${CXX}'
ar = '${AR}'
strip = '${TARGET}-strip'
pkgconfig = 'pkg-config'
windres = '${TARGET}-windres'
dlltool = '${TARGET}-dlltool'
[host_machine]
system = 'windows'
cpu_family = '${fam}'
@@ -47,25 +42,13 @@ cpu = '${TARGET%%-*}'
endian = 'little'
EOF
# CMake
cmake_args=(
-Wno-dev
-DCMAKE_SYSTEM_NAME=Windows
-DCMAKE_FIND_ROOT_PATH="$PKG_CONFIG_SYSROOT_DIR"
-DCMAKE_RC_COMPILER="${TARGET}-windres"
-DCMAKE_BUILD_TYPE=Release
)
export CC="ccache $CC"
export CXX="ccache $CXX"
function builddir {
function builddir () {
[ -d "$1/builddir" ] && rm -rf "$1/builddir"
mkdir -p "$1/builddir"
pushd "$1/builddir"
}
function makeplusinstall {
function makeplusinstall () {
if [ -f build.ninja ]; then
ninja
DESTDIR="$prefix_dir" ninja install
@@ -75,187 +58,135 @@ function makeplusinstall {
fi
}
function gettar {
local name="${1##*/}"
function gettar () {
name="${1##*/}"
[ -d "${name%%.*}" ] && return 0
$wget "$1"
tar -xaf "$name"
}
function build_if_missing {
local name=${1//-/_}
local mark_var=_${name}_mark
local mark_file=$prefix_dir/${!mark_var}
[ -e "$mark_file" ] && return 0
echo "::group::Building $1"
_$name
echo "::endgroup::"
if [ ! -e "$mark_file" ]; then
echo "Error: Build of $1 completed but $mark_file was not created."
return 2
fi
}
## mpv's dependencies
_iconv () {
local ver=1.17
## iconv
if [ ! -e "$prefix_dir/lib/libiconv.dll.a" ]; then
ver=1.17
gettar "https://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ver}.tar.gz"
builddir libiconv-${ver}
../configure --host=$TARGET $commonflags
makeplusinstall
popd
}
_iconv_mark=lib/libiconv.dll.a
fi
_zlib () {
local ver=1.3
## zlib
if [ ! -e "$prefix_dir/lib/libz.dll.a" ]; then
ver=1.2.12
gettar "https://zlib.net/fossils/zlib-${ver}.tar.gz"
pushd zlib-${ver}
make -fwin32/Makefile.gcc clean
make -fwin32/Makefile.gcc PREFIX=$TARGET- CC="$CC" SHARED_MODE=1 \
make -fwin32/Makefile.gcc PREFIX=$TARGET- SHARED_MODE=1 \
DESTDIR="$prefix_dir" install \
BINARY_PATH=/bin INCLUDE_PATH=/include LIBRARY_PATH=/lib
popd
}
_zlib_mark=lib/libz.dll.a
fi
_ffmpeg () {
## ffmpeg
if [ ! -e "$prefix_dir/lib/libavcodec.dll.a" ]; then
[ -d ffmpeg ] || $gitclone https://github.com/FFmpeg/FFmpeg.git ffmpeg
builddir ffmpeg
local args=(
--pkg-config=pkg-config --target-os=mingw32
--enable-cross-compile --cross-prefix=$TARGET- --arch=${TARGET%%-*}
--cc="$CC" --cxx="$CXX" $commonflags
--disable-{doc,programs,muxers,encoders}
--enable-encoder=mjpeg,png
)
pkg-config vulkan && args+=(--enable-vulkan --enable-libshaderc)
../configure "${args[@]}"
../configure --pkg-config=pkg-config --target-os=mingw32 \
--enable-cross-compile --cross-prefix=$TARGET- --arch=${TARGET%%-*} \
$commonflags \
--disable-{doc,programs,muxers,encoders,devices}
makeplusinstall
popd
}
_ffmpeg_mark=lib/libavcodec.dll.a
fi
_shaderc () {
## shaderc
if [ ! -e "$prefix_dir/lib/libshaderc_shared.dll.a" ]; then
if [ ! -d shaderc ]; then
$gitclone https://github.com/google/shaderc.git
(cd shaderc && ./utils/git-sync-deps)
fi
builddir shaderc
cmake .. "${cmake_args[@]}" \
-DBUILD_SHARED_LIBS=OFF -DSHADERC_SKIP_TESTS=ON
cmake .. -DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \
-DSHADERC_SKIP_TESTS=ON -DCMAKE_INSTALL_PREFIX=/
makeplusinstall
popd
}
_shaderc_mark=lib/libshaderc_shared.dll.a
fi
_spirv_cross () {
## spirv-cross
if [ ! -e "$prefix_dir/lib/libspirv-cross-c-shared.dll.a" ]; then
[ -d SPIRV-Cross ] || $gitclone https://github.com/KhronosGroup/SPIRV-Cross
builddir SPIRV-Cross
cmake .. "${cmake_args[@]}" \
cmake .. -DCMAKE_SYSTEM_NAME=Windows \
-DSPIRV_CROSS_SHARED=ON -DSPIRV_CROSS_{CLI,STATIC}=OFF
makeplusinstall
popd
}
_spirv_cross_mark=lib/libspirv-cross-c-shared.dll.a
fi
_vulkan_headers () {
[ -d Vulkan-Headers ] || $gitclone https://github.com/KhronosGroup/Vulkan-Headers -b v1.3.266
builddir Vulkan-Headers
cmake .. "${cmake_args[@]}"
makeplusinstall
popd
}
_vulkan_headers_mark=include/vulkan/vulkan.h
_vulkan_loader () {
[ -d Vulkan-Loader ] || $gitclone https://github.com/KhronosGroup/Vulkan-Loader -b v1.3.266
builddir Vulkan-Loader
cmake .. "${cmake_args[@]}" \
-DENABLE_WERROR=OFF
makeplusinstall
popd
}
_vulkan_loader_mark=lib/libvulkan-1.dll.a
_libplacebo () {
## libplacebo
if [ ! -e "$prefix_dir/lib/libplacebo.dll.a" ]; then
[ -d libplacebo ] || $gitclone https://code.videolan.org/videolan/libplacebo.git
builddir libplacebo
meson setup .. --cross-file "$prefix_dir/crossfile" \
-Ddemos=false -D{opengl,d3d11}=enabled
meson .. --cross-file "$prefix_dir/crossfile"
makeplusinstall
popd
}
_libplacebo_mark=lib/libplacebo.dll.a
fi
_freetype () {
local ver=2.13.1
gettar "https://mirror.netcologne.de/savannah/freetype/freetype-${ver}.tar.xz"
## freetype2
if [ ! -e "$prefix_dir/lib/libfreetype.dll.a" ]; then
ver=2.12.1
gettar "https://download.savannah.gnu.org/releases/freetype/freetype-${ver}.tar.xz"
builddir freetype-${ver}
meson setup .. --cross-file "$prefix_dir/crossfile"
meson .. --cross-file "$prefix_dir/crossfile"
makeplusinstall
popd
}
_freetype_mark=lib/libfreetype.dll.a
fi
_fribidi () {
local ver=1.0.13
## fribidi
if [ ! -e "$prefix_dir/lib/libfribidi.dll.a" ]; then
ver=1.0.12
gettar "https://github.com/fribidi/fribidi/releases/download/v${ver}/fribidi-${ver}.tar.xz"
builddir fribidi-${ver}
meson setup .. --cross-file "$prefix_dir/crossfile" \
meson .. --cross-file "$prefix_dir/crossfile" \
-D{tests,docs}=false
makeplusinstall
popd
}
_fribidi_mark=lib/libfribidi.dll.a
fi
_harfbuzz () {
local ver=8.1.1
## harfbuzz
if [ ! -e "$prefix_dir/lib/libharfbuzz.dll.a" ]; then
ver=5.3.0
gettar "https://github.com/harfbuzz/harfbuzz/releases/download/${ver}/harfbuzz-${ver}.tar.xz"
builddir harfbuzz-${ver}
meson setup .. --cross-file "$prefix_dir/crossfile" \
meson .. --cross-file "$prefix_dir/crossfile" \
-Dtests=disabled
makeplusinstall
popd
}
_harfbuzz_mark=lib/libharfbuzz.dll.a
fi
_libass () {
## libass
if [ ! -e "$prefix_dir/lib/libass.dll.a" ]; then
[ -d libass ] || $gitclone https://github.com/libass/libass.git
builddir libass
[ -f ../configure ] || (cd .. && ./autogen.sh)
../configure --host=$TARGET $commonflags
makeplusinstall
popd
}
_libass_mark=lib/libass.dll.a
fi
_luajit () {
[ -d LuaJIT ] || $gitclone https://github.com/LuaJIT/LuaJIT.git
pushd LuaJIT
local hostcc="ccache cc"
local flags=
[[ "$TARGET" == "i686-"* ]] && { hostcc="$hostcc -m32"; flags=XCFLAGS=-DLUAJIT_NO_UNWIND; }
## luajit
if [ ! -e "$prefix_dir/lib/libluajit-5.1.a" ]; then
ver=2.1.0-beta3
gettar "http://luajit.org/download/LuaJIT-${ver}.tar.gz"
pushd LuaJIT-${ver}
hostcc=cc
[[ "$TARGET" == "i686-"* ]] && hostcc="$hostcc -m32"
make TARGET_SYS=Windows clean
make TARGET_SYS=Windows HOST_CC="$hostcc" CROSS="ccache $TARGET-" \
BUILDMODE=static $flags amalg
make TARGET_SYS=Windows HOST_CC="$hostcc" CROSS=$TARGET- \
BUILDMODE=static amalg
make DESTDIR="$prefix_dir" INSTALL_DEP= FILE_T=luajit.exe install
popd
}
_luajit_mark=lib/libluajit-5.1.a
for x in iconv zlib shaderc spirv-cross; do
build_if_missing $x
done
if [[ "$TARGET" != "i686-"* ]]; then
build_if_missing vulkan-headers
build_if_missing vulkan-loader
fi
for x in ffmpeg libplacebo freetype fribidi harfbuzz libass luajit; do
build_if_missing $x
done
## mpv
@@ -264,43 +195,34 @@ done
CFLAGS+=" -I'$prefix_dir/include'"
LDFLAGS+=" -L'$prefix_dir/lib'"
export CFLAGS LDFLAGS
build=mingw_build
rm -rf $build
rm -rf build
meson setup $build --cross-file "$prefix_dir/crossfile" \
--werror \
-Dlibplacebo:werror=false \
-Dc_args="-Wno-error=deprecated -Wno-error=deprecated-declarations" \
--buildtype debugoptimized \
-Dlibmpv=true -Dlua=luajit \
-D{shaderc,spirv-cross,d3d11}=enabled
if [ "$1" = "meson" ]; then
meson setup build --cross-file "$prefix_dir/crossfile" \
--buildtype debugoptimized \
-D{libmpv,tests}=true -Dlua=luajit \
-D{shaderc,spirv-cross,d3d11,libplacebo}=enabled
meson compile -C $build
ninja -C build --verbose
elif [ "$1" = "waf" ]; then
PKG_CONFIG=pkg-config ./waf configure \
--enable-libmpv-shared --lua=luajit \
--enable-{shaderc,spirv-cross,d3d11,libplacebo,tests}
./waf build --verbose
fi
if [ "$2" = pack ]; then
mkdir -p artifact/tmp
mkdir -p artifact
echo "Copying:"
cp -pv $build/player/mpv.com $build/mpv.exe artifact/
# copy everything we can get our hands on
cp -p "$prefix_dir/bin/"*.dll artifact/tmp/
cp -pv build/mpv.{com,exe} "$prefix_dir/bin/"*.dll artifact/
# ship everything and the kitchen sink
shopt -s nullglob
for file in /usr/lib/gcc/$TARGET/*-posix/*.dll /usr/$TARGET/lib/*.dll; do
cp -p "$file" artifact/tmp/
cp -pv "$file" artifact/
done
# pick DLLs we need
pushd artifact/tmp
dlls=(
libgcc_*.dll lib{ssp,stdc++,winpthread}-[0-9]*.dll # compiler runtime
av*.dll sw*.dll lib{ass,freetype,fribidi,harfbuzz,iconv,placebo}-[0-9]*.dll
lib{shaderc_shared,spirv-cross-c-shared}.dll zlib1.dll
# note: vulkan-1.dll is not here since drivers provide it
)
mv -v "${dlls[@]}" ..
popd
echo "Archiving:"
pushd artifact
rm -rf tmp
zip -9r "../mpv-git-$(date +%F)-$(git rev-parse --short HEAD)-${TARGET%%-*}.zip" -- *
popd
fi
-39
View File
@@ -1,39 +0,0 @@
#!/bin/sh -e
mkdir subprojects
cat > subprojects/libplacebo.wrap <<EOF
[wrap-git]
url = https://code.videolan.org/videolan/libplacebo.git
revision = v6.338.1
depth = 1
clone-recursive = true
EOF
meson setup build \
--werror \
-Dlibplacebo:werror=false \
-Dlibplacebo:demos=false \
-Dlibplacebo:default_library=static \
-Dc_args="-Wno-error=deprecated -Wno-error=deprecated-declarations" \
-D cdda=enabled \
-D d3d-hwaccel=enabled \
-D d3d11=enabled \
-D dvdnav=enabled \
-D egl-angle-lib=enabled \
-D egl-angle-win32=enabled \
-D jpeg=enabled \
-D lcms2=enabled \
-D libarchive=enabled \
-D libbluray=enabled \
-D libmpv=true \
-D lua=enabled \
-D pdf-build=enabled \
-D rubberband=enabled \
-D shaderc=enabled \
-D spirv-cross=enabled \
-D tests=true \
-D uchardet=enabled \
-D vapoursynth=enabled
meson compile -C build
cp ./build/player/mpv.com ./build
./build/mpv.com -v --no-config
+31 -17
View File
@@ -1,20 +1,34 @@
#!/bin/sh
set -e
meson setup build \
--werror \
-Dlibplacebo:werror=false \
-Dc_args="-Wno-error=deprecated -Wno-error=deprecated-declarations" \
-Db_sanitize=address,undefined \
-Dcdda=enabled \
-Ddvbin=enabled \
-Ddvdnav=enabled \
-Dlibarchive=enabled \
-Dlibmpv=true \
-Dmanpage-build=enabled \
-Dpipewire=enabled \
-Dshaderc=enabled \
-Dtests=true \
-Dvulkan=enabled
meson compile -C build
./build/mpv -v --no-config
if [ "$1" = "meson" ]; then
meson setup build \
-Dcdda=enabled \
-Ddvbin=enabled \
-Ddvdnav=enabled \
-Dlibarchive=enabled \
-Dlibmpv=true \
-Dmanpage-build=enabled \
-Dpipewire=enabled \
-Dshaderc=enabled \
-Dtests=true \
-Dvulkan=enabled
meson compile -C build --verbose
./build/mpv --no-config -v --unittest=all-simple
fi
if [ "$1" = "waf" ]; then
python3 ./waf configure \
--enable-cdda \
--enable-dvbin \
--enable-dvdnav \
--enable-libarchive \
--enable-libmpv-shared \
--enable-manpage-build \
--enable-pipewire \
--enable-shaderc \
--enable-tests \
--enable-vulkan
python3 ./waf build --verbose
./build/mpv -v --no-config -v --unittest=all-simple
fi
-116
View File
@@ -1,116 +0,0 @@
#!/usr/bin/env python3
import os, sys, json, subprocess, re
def call(cmd) -> str:
sys.stdout.flush()
ret = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, text=True)
return ret.stdout
lint_rules = {}
def lint_rule(description: str):
def f(func):
assert func.__name__ not in lint_rules.keys()
lint_rules[func.__name__] = (func, description)
return f
def get_commit_range() -> str:
if len(sys.argv) > 1:
return sys.argv[1]
# https://github.com/actions/runner/issues/342#issuecomment-590670059
event_name = os.environ["GITHUB_EVENT_NAME"]
with open(os.environ["GITHUB_EVENT_PATH"], "rb") as f:
event = json.load(f)
if event_name == "push":
if event["created"] or event["forced"]:
print("Skipping logic on branch creation or force-push")
return None
return event["before"] + "..." + event["after"]
elif event_name == "pull_request":
return event["pull_request"]["base"]["sha"] + ".." + event["pull_request"]["head"]["sha"]
def do_lint(commit_range: str) -> bool:
commits = call(["git", "log", "--pretty=format:%h %s", commit_range]).splitlines()
print(f"Linting {len(commits)} commit(s):")
any_failed = False
for commit in commits:
sha, _, _ = commit.partition(' ')
#print(commit)
body = call(["git", "show", "-s", "--format=%B", sha]).splitlines()
failed = []
if len(body) == 0:
failed.append("* Commit message must not be empty")
else:
for k, v in lint_rules.items():
if not v[0](body):
failed.append(f"* {v[1]} [{k}]")
if failed:
any_failed = True
print("-" * 40)
sys.stdout.flush()
subprocess.run(["git", "-P", "show", "-s", sha])
print("\nhas the following issues:")
print("\n".join(failed))
print("-" * 40)
return any_failed
################################################################################
NO_PREFIX_WHITELIST = r"^Revert \"(.*)\"|^Release [0-9]|^Update VERSION$"
@lint_rule("Subject line must contain a prefix identifying the sub system")
def subsystem_prefix(body):
if re.search(NO_PREFIX_WHITELIST, body[0]):
return True
m = re.search(r"^([^:]+):\s", body[0])
if not m:
return False
# a comma-separated list is okay
s = re.sub(r",\s+", "", m.group(1))
# but no spaces otherwise
return not " " in s
@lint_rule("First word after : must be lower case")
def description_lowercase(body):
if re.search(NO_PREFIX_WHITELIST, body[0]):
return True
# Allow all caps for acronyms and such
if re.search(r":\s[A-Z]{2,}\s", body[0]):
return True
return re.search(r":\s+[a-z0-9]", body[0])
@lint_rule("Subject line must not end with a full stop")
def no_dot(body):
return not body[0].rstrip().endswith('.')
@lint_rule("There must be an empty line between subject and extended description")
def empty_line(body):
return len(body) == 1 or body[1].strip() == ""
# been seeing this one all over github lately, must be the webshits
@lint_rule("Do not use 'conventional commits' style")
def no_cc(body):
return not re.search(r"(?i)^(feat|fix|chore|refactor)[!:(]", body[0])
@lint_rule("History must be linear, no merge commits")
def no_merge(body):
return not body[0].startswith("Merge ")
@lint_rule("Subject line should be shorter than 72 characters")
def line_too_long(body):
revert = re.search(r"^Revert \"(.*)\"", body[0])
return True if revert else len(body[0]) <= 72
@lint_rule("Prefix should not include C file extensions (use `vo_gpu: ...` not `vo_gpu.c: ...`)")
def no_file_exts(body):
return not re.search(r"[a-z0-9]\.[ch]:\s", body[0])
################################################################################
if __name__ == "__main__":
commit_range = get_commit_range()
if commit_range is None:
exit(0)
print("Commit range:", commit_range)
any_failed = do_lint(commit_range)
exit(1 if any_failed else 0)
+18 -26
View File
@@ -40,6 +40,20 @@
#include "av_common.h"
#include "codecs.h"
int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size)
{
if (size) {
av_free(avctx->extradata);
avctx->extradata_size = 0;
avctx->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!avctx->extradata)
return -1;
avctx->extradata_size = size;
memcpy(avctx->extradata, ptr, size);
}
return 0;
}
enum AVMediaType mp_to_av_stream_type(int type)
{
switch (type) {
@@ -50,7 +64,7 @@ enum AVMediaType mp_to_av_stream_type(int type)
}
}
AVCodecParameters *mp_codec_params_to_av(const struct mp_codec_params *c)
AVCodecParameters *mp_codec_params_to_av(struct mp_codec_params *c)
{
AVCodecParameters *avp = avcodec_parameters_alloc();
if (!avp)
@@ -111,7 +125,7 @@ error:
}
// Set avctx codec headers for decoding. Returns <0 on failure.
int mp_set_avctx_codec_headers(AVCodecContext *avctx, const struct mp_codec_params *c)
int mp_set_avctx_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c)
{
enum AVMediaType codec_type = avctx->codec_type;
enum AVCodecID codec_id = avctx->codec_id;
@@ -131,7 +145,7 @@ int mp_set_avctx_codec_headers(AVCodecContext *avctx, const struct mp_codec_para
// Pick a "good" timebase, which will be used to convert double timestamps
// back to fractions for passing them through libavcodec.
AVRational mp_get_codec_timebase(const struct mp_codec_params *c)
AVRational mp_get_codec_timebase(struct mp_codec_params *c)
{
AVRational tb = {c->native_tb_num, c->native_tb_den};
if (tb.num < 1 || tb.den < 1) {
@@ -182,11 +196,7 @@ double mp_pts_from_av(int64_t av_pts, AVRational *tb)
// Set duration field only if tb is set.
void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb)
{
dst->side_data = NULL;
dst->side_data_elems = 0;
dst->buf = NULL;
av_packet_unref(dst);
av_init_packet(dst);
dst->data = mpkt ? mpkt->buffer : NULL;
dst->size = mpkt ? mpkt->len : 0;
/* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info
@@ -384,21 +394,3 @@ int mp_set_avopts_pos(struct mp_log *log, void *avobj, void *posargs, char **kv)
}
return success;
}
/**
* Must be used to free an AVPacket that was used with mp_set_av_packet().
*
* We have a particular pattern where we "borrow" buffers and set them
* into an AVPacket to pass data to ffmpeg without extra copies.
* This applies to buf and side_data, so this function clears them before
* freeing.
*/
void mp_free_av_packet(AVPacket **pkt)
{
if (*pkt) {
(*pkt)->side_data = NULL;
(*pkt)->side_data_elems = 0;
(*pkt)->buf = NULL;
}
av_packet_free(pkt);
}
Loaded 100 of 559 files, more files were not shown because too many files have changed in this diff. Show more