Compare commits

..
2 Commits
Author SHA1 Message Date
sfan5 b5d3e43198 Release 0.33.1 2021-04-05 18:45:41 +02:00
Avi Halachmi (:avih) cb3fa04bcb demux_mf: improve format string processing
Before this commit, the user could specify a printf format string
which wasn't verified, and could result in:
- Undefined behavior due to missing or non-matching arguments.
- Buffer overflow due to untested result length.

The offending code was added at commit 103a9609 (2002, mplayer svn):
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@4566 b3059339-0415-0410-9bf9-f77b7e298cf2

It moved around but was not modified meaningfully until now.

Now we reject all conversion specifiers at the format except %%
and a simple subset of the valid specifiers. Also, we now use
snprintf to avoid buffer overflow.

The format string is provided by the user as part of mf:// URI.

Report and initial patch by Stefan Schiller.
Patch reviewed by @jeeb, @sfan5, Stefan Schiller.
2021-04-05 18:14:04 +02:00
502 changed files with 11270 additions and 29301 deletions

No files matched your search

-10
View File
@@ -1,10 +0,0 @@
# To use this config on you editor, follow the instructions at:
# http://editorconfig.org
root = true
[*]
charset = utf-8
insert_final_newline = true
indent_style = space
indent_size = 4
+1 -1
View File
@@ -28,7 +28,7 @@ with --no-config try to first find out which option or script causes your issue.
Describe the reproduction steps as precise as possible. It's very likely that
the bug you experience wasn't reproduced by the developer because the workflow
differs from your own.
differes from your own.
### Expected behavior
+1 -1
View File
@@ -26,7 +26,7 @@ with --no-config try to first find out which option or script causes your issue.
Describe the reproduction steps as precise as possible. It's very likely that
the bug you experience wasn't reproduced by the developer because the workflow
differs from your own.
differes from your own.
### Expected behavior
@@ -26,7 +26,7 @@ with --no-config try to first find out which option or script causes your issue.
Describe the reproduction steps as precise as possible. It's very likely that
the bug you experience wasn't reproduced by the developer because the workflow
differs from your own.
differes from your own.
### Expected behavior
+1 -1
View File
@@ -24,7 +24,7 @@ with --no-config try to first find out which option or script causes your issue.
Describe the reproduction steps as precise as possible. It's very likely that
the bug you experience wasn't reproduced by the developer because the workflow
differs from your own.
differes from your own.
### Expected behavior
+3 -3
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: ''
---
@@ -16,7 +16,7 @@ Releases are listed here: https://github.com/mpv-player/mpv/releases
Describe the reproduction steps as precise as possible. It's very likely that
the bug you experience wasn't reproduced by the developer because the workflow
differs from your own.
differes from your own.
### Expected behavior
@@ -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.
+4 -4
View File
@@ -1,9 +1,9 @@
blank_issues_enabled: false
contact_links:
- name: mpv IRC channel
- name: mpv irc channel
url: https://mpv.io/community/
about: Feel free to ask questions here irc://irc.libera.chat/mpv
- name: mpv IRC developer channel
about: Feel free to ask questions here irc://irc.freenode.net/mpv
- name: mpv irc developer channel
url: https://mpv.io/community/
about: Ask questions related to the development of mpv here
irc://irc.libera.chat/mpv-devel
irc://irc.freenode.net/mpv-devel
-298
View File
@@ -1,298 +0,0 @@
name: build
on:
push:
branches:
- master
- ci
- 'release/**'
pull_request:
branches: [master]
jobs:
mingw:
runs-on: ubuntu-22.04
strategy:
matrix:
target: [i686-w64-mingw32, x86_64-w64-mingw32]
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y autoconf automake pkg-config g++-mingw-w64 gcc-multilib python3-pip ninja-build nasm
sudo python3 -m pip install meson
./bootstrap.py
- name: Build libraries
run: |
./ci/build-mingw64.sh
env:
TARGET: ${{ matrix.target }}
- name: Build with meson
id: build
run: |
./ci/build-mingw64.sh meson pack
env:
TARGET: ${{ matrix.target }}
- name: Print meson log
if: ${{ failure() && steps.build.outcome == 'failure' }}
run: |
cat ./build/meson-logs/meson-log.txt
- name: Build with waf
id: build_waf
run: |
./ci/build-mingw64.sh waf
env:
TARGET: ${{ matrix.target }}
- name: Print waf log
if: ${{ failure() && steps.build_waf.outcome == 'failure' }}
run: |
cat ./build_waf/config.log
- uses: actions/upload-artifact@v3
with:
name: mpv-${{ matrix.target }}
path: mpv-git-*.zip
macos:
runs-on: ${{ matrix.os }}
strategy:
matrix:
cc:
- "clang"
os:
- "macos-11"
- "macos-12"
steps:
- uses: actions/checkout@v3
- name: Remove stray upstream python binary symlinks under /usr/local
run: |
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete -print
brew unlink python && brew link --overwrite python
- name: Install dependencies
run: |
brew update
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 meson
env:
CC: "${{ matrix.cc }}"
TRAVIS_OS_NAME: "${{ matrix.os }}"
- 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: Build with waf
id: build_waf
run: |
./ci/build-macos.sh waf
env:
CC: "${{ matrix.cc }}"
TRAVIS_OS_NAME: "${{ matrix.os }}"
- name: Print waf log
if: ${{ failure() && steps.build_waf.outcome == 'failure' }}
run: |
cat ./build_waf/config.log
linux:
runs-on: "ubuntu-22.04"
container:
image: "registry.opensuse.org/home/mia/images/images/mpv-ci:stable-deps"
env:
CC: "${{ matrix.cc }}"
strategy:
matrix:
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 meson
- 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: Build with waf
id: build_waf
run: |
./ci/build-tumbleweed.sh waf
- name: Print waf log
if: ${{ failure() && steps.build_waf.outcome == 'failure' }}
run: |
cat ./build_waf/config.log
freebsd:
runs-on: macos-12 # until https://github.com/actions/runner/issues/385
steps:
- uses: actions/checkout@v3
- name: Test in FreeBSD VM
uses: vmactions/freebsd-vm@v0
with:
mem: 8192
copyback: false
usesh: true
prepare: |
# Requested in ci/build-freebsd.sh
pkg install -y \
cmake \
evdev-proto \
ffmpeg \
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 \
mujs \
pipewire \
pulseaudio \
rubberband \
sekrit-twc-zimg \
uchardet \
v4l_compat \
#
run: |
./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:
- 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
ninja:p
pkgconf:p
python3.11:p
rst2pdf:p
rubberband:p
shaderc:p
spirv-cross:p
uchardet:p
vapoursynth:p
vulkan:p
- name: Build with meson
id: build
run: |
./ci/build-msys2.sh meson
- 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: |
source ./venv/bin/activate
meson test -C build
- name: Print meson test log
if: ${{ failure() && steps.tests.outcome == 'failure' }}
run: |
cat ./build/meson-logs/testlog.txt
- name: Build with waf
id: build_waf
run: |
./ci/build-msys2.sh waf
- name: Print waf log
if: ${{ failure() && steps.build_waf.outcome == 'failure' }}
run: |
cat ./build_waf/config.log
-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
-2
View File
@@ -36,5 +36,3 @@
/old_build
/Makefile
/subprojects
+177
View File
@@ -0,0 +1,177 @@
language: c
_macbase:
- &mac
os: osx
compiler: clang
env:
- HOMEBREW_NO_AUTO_UPDATE=1
- HOMEBREW_NO_INSTALL_CLEANUP=1
before_cache:
- brew cleanup -s
cache:
directories:
- $HOME/Library/Caches/Homebrew
- /usr/local/Homebrew
_mingwbase:
- &mingw
os: linux
addons:
apt:
packages:
- 'autoconf'
- 'automake'
- 'pkg-config'
- 'gcc-mingw-w64'
- 'gcc-multilib'
- 'nasm'
- 'yasm'
cache:
directories:
- mingw_prefix/
matrix:
include:
- <<: *mac
osx_image: xcode11.6
- <<: *mac
osx_image: xcode10.1
- <<: *mac
osx_image: xcode9.2
env:
- HOMEBREW_NO_AUTO_UPDATE=1
- HOMEBREW_NO_INSTALL_CLEANUP=1
- CI_SWIFT_FLAGS="\-target x86_64-apple-macosx10.12"
- os: freebsd
compiler: clang
- os: linux
compiler: gcc
env: CONTAINER=registry.cirno.systems/kiwi/containers/mpv-ci:stable-deps CI_SCRIPT=ci/build-tumbleweed.sh
- os: linux
compiler: clang
env: CONTAINER=registry.cirno.systems/kiwi/containers/mpv-ci:stable-deps CI_SCRIPT=ci/build-tumbleweed.sh
- <<: *mingw
env: CI_SCRIPT=ci/build-mingw64.sh TARGET=i686-w64-mingw32
- <<: *mingw
env: CI_SCRIPT=ci/build-mingw64.sh TARGET=x86_64-w64-mingw32
allow_failures:
- os: osx
osx_image: xcode9.2
fast_finish: true
dist: focal
services:
- docker
env:
global:
# Coverity token
- secure: "H21mSRlMhk4BKS0xHZvCFGJxteCP0hRVUxTuNfM2Z9HBsyutuLEYMtViLO86VtM+Tqla3xXPzUdS4ozLwI72Ax/5ZUDXACROj73yW6QhFB5D6rLut12+FjqC7M33Qv2hl0xwgNBmR5dsm1ToP37+Wn+ecJQNvN8fkTXF+HVzOEw="
# Travis token for mpv.io
- secure: "nlTVLJK6kRhtXvhKCoJ3YdFGHuKaq/eHowfPw25hqRWuBOZd+HjHY5KIYjV7SxuKFDpJE4GpNcvA3Q31nsqomxpkLYgrwjg6TSazN7ZP+x85ZgV1QGFebrPfGm2n5UR5CAPAwFoeF3pZheLi4bajVzwq1fWW+x3grS188P9OZso="
branches:
only:
- master
- ci
- coverity_scan
- /release\/.*$/
before_install:
- if [ "$TRAVIS_COMPILER" = "clang" ]; then export CXX="clang++"; fi
- if [ "$TRAVIS_COMPILER" = "gcc" ]; then export CXX="g++"; fi
- if [ -n "$CONTAINER" ]; then docker pull $CONTAINER; fi
- |
if [ "$TRAVIS_OS_NAME" = "freebsd" ]; then
# Requested in ci/build-freebsd.sh
sudo pkg install -y \
evdev-proto \
ffmpeg \
libplacebo \
libxkbcommon \
luajit \
openal-soft \
pkgconf \
python3 \
sdl2 \
vulkan-headers \
wayland-protocols \
$NULL
# Optionally auto-enabled
sudo pkg install -y \
alsa-lib \
jackit \
libXv \
libarchive \
libbluray \
libcaca \
libcdio-paranoia \
libdvdnav \
mujs \
pulseaudio \
rubberband \
sekrit-twc-zimg \
uchardet \
v4l_compat \
$NULL
fi
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
remove=$(brew list)
keep="gettext pcre2 git"
install="autoconf automake pkg-config libtool python freetype fribidi little-cms2 luajit libass ffmpeg"
for formula in ${keep[@]}; do remove=("${remove[@]/$formula}"); done
for formula in ${install[@]}; do remove=("${remove[@]/$formula}"); done
brew remove --force $remove --ignore-dependencies
if [[ "$TRAVIS_OSX_IMAGE" == "xcode9.2" ]]; then
brew untap caskroom/cask
fi
brew update
if [[ "$TRAVIS_OSX_IMAGE" == "xcode9.2" ]]; then
pushd "/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core"
git checkout --force 55e02323812604add9a69bab8730319b9255a697
fi
brew install $install
brew link --overwrite python
if [[ "$TRAVIS_OSX_IMAGE" == "xcode9.2" ]]; then
git checkout master
popd
fi
fi
script:
- ./bootstrap.py
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
if [ -n "$CONTAINER" ]; then
docker run --env CC --env TARGET -v $TRAVIS_BUILD_DIR:/build $CONTAINER /bin/sh -c "cd /build && $CI_SCRIPT"
else
$CI_SCRIPT
fi
fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./ci/build-macos.sh; fi
- if [ "$TRAVIS_OS_NAME" = "freebsd" ]; then ./ci/build-freebsd.sh; fi
after_failure: cat ./build/config.log
after_script: TOOLS/travis-rebuild-website
notifications:
email: false
irc:
if: fork = false
channels:
- "irc.freenode.org#mpv-devel"
on_success: change
on_failure: always
addons:
coverity_scan:
project:
name: "mpv-player/mpv"
description: "Build submitted via Travis CI"
notification_email: mpv-team@googlegroups.com
build_command_prepend: "./bootstrap.py && ./waf configure"
build_command: "./waf build"
branch_pattern: coverity_scan
-71
View File
@@ -1,71 +0,0 @@
# 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+.
* ``--tests`` was removed since unit tests now explicitly require meson to run
### 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.
+2 -9
View File
@@ -32,15 +32,6 @@ API changes
::
--- 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
- remove deprecated mpv_detach_destroy. Use mpv_destroy instead.
- remove obsolete mpv_suspend and mpv_resume
- remove deprecated SCRIPT_INPUT_DISPATCH, PAUSE and UNPAUSE, TRACKS_CHANGED
TRACK_SWITCHED, METADATA_UPDATE, CHAPTER_CHANGE events
--- mpv 0.33.0 ---
1.109 - add MPV_RENDER_API_TYPE_SW and related (software rendering API)
- inactivate the opengl_cb API (always fails to initialize now)
@@ -63,6 +54,7 @@ API changes
It is a good idea to write better wrappers for your use, though.
--- mpv 0.31.0 ---
1.107 - Deprecate MPV_EVENT_TICK
--- mpv 0.30.0 ---
1.106 - Add cancel_fn to mpv_stream_cb_info
1.105 - Fix deadlock problems with MPV_RENDER_PARAM_ADVANCED_CONTROL and if
@@ -80,6 +72,7 @@ API changes
- add mpv_abort_async_command()
1.102 - rename struct mpv_opengl_drm_osd_size to mpv_opengl_drm_draw_surface_size
- rename MPV_RENDER_PARAM_DRM_OSD_SIZE to MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE
--- mpv 0.29.0 ---
1.101 - add MPV_RENDER_PARAM_ADVANCED_CONTROL and related API
- add MPV_RENDER_PARAM_NEXT_FRAME_INFO and related symbols
+35 -36
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
[MXE](http://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
@@ -53,7 +38,7 @@ Example with MXE
#
# Refer to
#
# https://mxe.cc/#requirements
# http://mxe.cc/#requirements
#
# Scroll down for disto/OS-specific instructions to install them.
@@ -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
+1 -6
View File
@@ -5,7 +5,7 @@ General
-------
The main contact for mpv development is IRC, specifically #mpv
and #mpv-devel on Libera.chat. Github is used for code review and
and #mpv-devel on Freenode. Github is used for code review and
long term discussions.
Sending patches
@@ -68,11 +68,6 @@ Write good commit messages
Having a prefix gives context, and is especially useful when trying to find
a specific change by looking at the history, or when running ``git blame``.
Sample prefixes: ``vo_gpu: ...``, ``command: ...``, ``DOCS/input: ...``,
``TOOLS/osxbundle: ...``, ``osc.lua: ...``, etc. You can always check the git
log for commits which modify specific files to see which prefixes are used.
- The first word after the ``:`` is lower case.
- Don't end the subject line with a ``.``.
- Put an empty line between the subject line and the commit message.
-20
View File
@@ -239,26 +239,6 @@ title. The subtitle stream will use ``ducks`` as title.
The ``track_meta`` header is not part of the core EDL format. It may be changed
or removed at any time, depending on mpv's internal requirements.
Global metadata
===============
The special ``global_tags`` header can set metadata fields (aka tags) of the EDL
file. This metadata is supposed to be informational, much like for example ID3
tags in audio files. Due to lack of separation of different kinds of metadata it
is unspecified what names are allowed, how they are interpreted, and whether
some of them affect playback functionally. (Much of this is unfortunately
inherited from FFmpeg. Another consequence of this is that FFmpeg "normalized"
tags are recognized, or stuff like replaygain tags.)
Example::
!global_tags,title=bla,something_arbitrary=even_more_arbitrary
Any parameter names are allowed. Repeated use of this adds to the tag list. If
``!new_stream`` is used, the location doesn't matter.
May possibly be ignored in some cases, such as delayed media opening.
Delayed media opening
=====================
+37 -37
View File
@@ -3,26 +3,26 @@ General usage
::
mpv infile --o=outfile [--of=outfileformat] [--ofopts=formatoptions] [--orawts] \
mpv infile -o outfile [-of outfileformat] [-ofopts formatoptions] [-orawts] \
[(any other mpv options)] \
--ovc=outvideocodec [--ovcopts=outvideocodecoptions] \
--oac=outaudiocodec [--oacopts=outaudiocodecoptions]
-ovc outvideocodec [-ovcopts outvideocodecoptions] \
-oac outaudiocodec [-oacopts outaudiocodecoptions]
Help for these options is provided if giving help as parameter, as in::
mpv --ovc=help
mpv -ovc help
The suboptions of these generally are identical to ffmpeg's (as option parsing
is simply delegated to ffmpeg). The option --ocopyts enables copying timestamps
is simply delegated to ffmpeg). The option -ocopyts enables copying timestamps
from the source as-is, instead of fixing them to match audio playback time
(note: this doesn't work with all output container formats); --orawts even turns
(note: this doesn't work with all output container formats); -orawts even turns
off discontinuity fixing.
Note that if neither --ofps nor --oautofps is specified, VFR encoding is assumed
and the time base is 24000fps. --oautofps sets --ofps to a guessed fps number
Note that if neither -ofps nor -oautofps is specified, VFR encoding is assumed
and the time base is 24000fps. -oautofps sets -ofps to a guessed fps number
from the input video. Note that not all codecs and not all formats support VFR
encoding, and some which do have bugs when a target bitrate is specified - use
--ofps or --oautofps to force CFR encoding in these cases.
-ofps or -oautofps to force CFR encoding in these cases.
Of course, the options can be stored in a profile, like this .config/mpv/mpv.conf
section::
@@ -48,7 +48,7 @@ to encoding. This is not the case anymore.)
One can then encode using this profile using the command::
mpv infile --o=outfile.mp4 --profile=myencprofile
mpv infile -o outfile.mp4 -profile myencprofile
Some example profiles are provided in a file
etc/encoding-profiles.conf; as for this, see below.
@@ -62,10 +62,10 @@ for.
Typical MPEG-4 Part 2 ("ASP", "DivX") encoding, AVI container::
mpv infile --o=outfile.avi \
mpv infile -o outfile.avi \
--vf=fps=25 \
--ovc=mpeg4 --ovcopts=qscale=4 \
--oac=libmp3lame --oacopts=b=128k
-ovc mpeg4 -ovcopts qscale=4 \
-oac libmp3lame -oacopts ab=128k
Note: AVI does not support variable frame rate, so the fps filter must be used.
The frame rate should ideally match the input (25 for PAL, 24000/1001 or
@@ -73,22 +73,22 @@ The frame rate should ideally match the input (25 for PAL, 24000/1001 or
Typical MPEG-4 Part 10 ("AVC", "H.264") encoding, Matroska (MKV) container::
mpv infile --o=outfile.mkv \
--ovc=libx264 --ovcopts=preset=medium,crf=23,profile=baseline \
--oac=libopus --oacopts=qscale=3
mpv infile -o outfile.mkv \
-ovc libx264 -ovcopts preset=medium,crf=23,profile=baseline \
-oac libvorbis -oacopts qscale=3
Typical MPEG-4 Part 10 ("AVC", "H.264") encoding, MPEG-4 (MP4) container::
mpv infile --o=outfile.mp4 \
--ovc=libx264 --ovcopts=preset=medium,crf=23,profile=baseline \
--oac=aac --oacopts=b=128k
mpv infile -o outfile.mp4 \
-ovc libx264 -ovcopts preset=medium,crf=23,profile=baseline \
-oac aac -oacopts ab=128k
Typical VP8 encoding, WebM (restricted Matroska) container::
mpv infile -o outfile.mkv \
--of=webm \
--ovc=libvpx --ovcopts=qmin=6,b=1000000k \
--oac=libopus --oacopts=qscale=3
-of webm \
-ovc libvpx -ovcopts qmin=6,b=1000000k \
-oac libvorbis -oacopts qscale=3
Device targets
@@ -111,18 +111,18 @@ section.
Refer to the top of that file for more comments - in a nutshell, the following
options are added by it::
--profile=enc-to-dvdpal # DVD-Video PAL, use dvdauthor -v pal+4:3 -a ac3+en
--profile=enc-to-dvdntsc # DVD-Video NTSC, use dvdauthor -v ntsc+4:3 -a ac3+en
--profile=enc-to-bb-9000 # MP4 for Blackberry Bold 9000
--profile=enc-to-nok-6300 # 3GP for Nokia 6300
--profile=enc-to-psp # MP4 for PlayStation Portable
--profile=enc-to-iphone # MP4 for iPhone
--profile=enc-to-iphone-4 # MP4 for iPhone 4 (double res)
--profile=enc-to-iphone-5 # MP4 for iPhone 5 (even larger res)
-profile enc-to-dvdpal DVD-Video PAL, use dvdauthor -v pal+4:3 -a ac3+en
-profile enc-to-dvdntsc DVD-Video NTSC, use dvdauthor -v ntsc+4:3 -a ac3+en
-profile enc-to-bb-9000 MP4 for Blackberry Bold 9000
-profile enc-to-nok-6300 3GP for Nokia 6300
-profile enc-to-psp MP4 for PlayStation Portable
-profile enc-to-iphone MP4 for iPhone
-profile enc-to-iphone-4 MP4 for iPhone 4 (double res)
-profile enc-to-iphone-5 MP4 for iPhone 5 (even larger res)
You can encode using these with a command line like::
mpv infile --o=outfile.mp4 --profile=enc-to-bb-9000
mpv infile -o outfile.mp4 -profile enc-to-bb-9000
Of course, you are free to override options set by these profiles by specifying
them after the -profile option.
@@ -133,19 +133,19 @@ What works
* Encoding at variable frame rate (default)
* Encoding at constant frame rate using --vf=fps=RATE
* 2-pass encoding (specify flags=+pass1 in the first pass's --ovcopts, specify
* 2-pass encoding (specify flags=+pass1 in the first pass's -ovcopts, specify
flags=+pass2 in the second pass)
* Hardcoding subtitles using vobsub, ass or srt subtitle rendering (just
configure mpv for the subtitles as usual)
* Hardcoding any other mpv OSD (e.g. time codes, using --osdlevel=3 and
--vf=expand=::::1)
* Hardcoding any other mpv OSD (e.g. time codes, using -osdlevel 3 and -vf
expand=::::1)
* Encoding directly from a DVD, network stream, webcam, or any other source
mpv supports
* Using x264 presets/tunings/profiles (by using profile=, tune=, preset= in the
--ovcopts)
-ovcopts)
* Deinterlacing/Inverse Telecine with any of mpv's filters for that
* Audio file converting: mpv --o=outfile.m4a infile.flac --no-video
--oac=aac --oacopts=b=320k
* Audio file converting: mpv -o outfile.mp3 infile.flac -no-video -oac
libmp3lame -oacopts ab=320k
What does not work yet
======================
-112
View File
@@ -26,118 +26,6 @@ Interface changes
::
--- 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.
- 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`,
`--interpolation-preserve` `--lut`, `--lut-type`, `--image-lut`,
`--image-lut-type` and `--target-lut` along with it.
- add `--target-colorspace-hint`
- add `--tone-mapping-crosstalk`
- add `--tone-mapping` options `auto`, `spline` and `bt.2446a`
- add `--inverse-tone-mapping`
- add `--gamut-mapping-mode`, replacing `--gamut-clipping` and `--gamut-warning`
- add `--tone-mapping-mode`, replacing `--tone-mapping-desaturate` and
`--tone-mapping-desaturate-exponent`.
- add `dolbyvision` sub-parameter to `format` video filter
- `--sub-visibility` no longer has any effect on secondary subtitles
- add `film-grain` sub-parameter to `format` video filter
- add experimental `--vo=dmabuf-wayland` video output driver
- add `--x11-present` for controlling whether to use xorg's present extension
- add `engine` option to the `rubberband` audio filter to support the new
engine introduced in rubberband 3.0.0. Defaults to `finer` (new engine).
- add `--wayland-configure-bounds` option
- deprecate `--gamma-factor`
- deprecate `--gamma-auto`
- remove `--vulkan-disable-events`
- add `--glsl-shader-opts`
--- mpv 0.34.0 ---
- deprecate selecting by card number with `--drm-connector`, add
`--drm-device` which can be used instead
- add `--screen-name` and `--fs-screen-name` flags to allow selecting the
screen by its name instead of the index
- add `--macos-geometry-calculation` to change the rectangle used for screen
position and size calculation. the old behavior used the whole screen,
which didn't take the menu bar and Dock into account. The new default
behaviour includes both. To revert to the old behavior set this to
`whole`.
- add an additional optional `albumart` argument to the `video-add` command,
which tells mpv to load the given video as album art.
- undeprecate `--cache-secs` option
- remove `--icc-contrast` and introduce `--icc-force-contrast`. The latter
defaults to the equivalent of the old `--icc-contrast=inf`, and can
instead be used to specifically set the contrast to any value.
- add a `--watch-later-options` option to allow configuring which
options quit-watch-later saves
- make `current-window-scale` writeable and use it in the default input.conf
- add `--input-builtin-bindings` flag to control loading of built-in key
bindings during start-up (default: yes).
- add ``track-list/N/image`` sub-property
- remove `--opengl-restrict` option
- js custom-init: use filename ~~/init.js instead of ~~/.init.js (dot)
--- mpv 0.33.0 ---
- add `--d3d11-exclusive-fs` flag to enable D3D11 exclusive fullscreen mode
when the player enters fullscreen.
+5 -5
View File
@@ -161,14 +161,14 @@ Available filters are:
Would play media at 1.2x normal speed, with audio at normal pitch.
Changing playback speed would change pitch, leaving audio tempo at
1.2x.
``scaletempo2[=option1:option2:...]``
Scales audio tempo without altering pitch.
The algorithm is ported from chromium and uses the
The algorithm is ported from chromium and uses the
Waveform Similarity Overlap-and-add (WSOLA) method.
It seems to achieve a higher audio quality than scaletempo and rubberband.
By default, the ``search-interval`` and ``window-size`` parameters
By default, the ``search-interval`` and ``window-size`` parameters
have the same values as in chromium.
``min-speed=<speed>``
@@ -180,7 +180,7 @@ Available filters are:
``search-interval=<amount>``
Length in milliseconds to search for best overlap position. (default: 30)
``window-size=<amount>``
Length in milliseconds of the overlap-and-add window. (default: 20)
@@ -198,7 +198,7 @@ Available filters are:
for each option. The options are not documented here, because they 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
http://breakfastquay.com/rubberband/code-doc/classRubberBand_1_1RubberBandStretcher.html
(The mapping of the mpv rubberband filter sub-option names and values to
those of librubberband follows a simple pattern: ``"Option" + Name + Value``.)
+11 -40
View File
@@ -14,7 +14,7 @@ in the list.
See ``--ao=help`` for a list of compiled-in audio output drivers. The
driver ``--ao=alsa`` is preferred. ``--ao=pulse`` is preferred on systems
where PulseAudio is used. On BSD systems, ``--ao=oss`` is preferred.
where PulseAudio is used.
Available audio output drivers are:
@@ -30,15 +30,11 @@ Available audio output drivers are:
explicitly reject multichannel output, as there is no way to detect
whether a certain channel layout is actually supported.
You can also try `using the upmix plugin
<https://github.com/mpv-player/mpv/wiki/ALSA-Surround-Sound-and-Upmixing>`_.
You can also try `using the upmix plugin <http://git.io/vfuAy>`_.
This setup enables multichannel audio on the ``default`` device
with automatic upmixing with shared access, so playing stereo
and multichannel audio at the same time will work as expected.
``oss``
OSS audio output driver
``jack``
JACK (Jack Audio Connection Kit) audio output driver.
@@ -69,8 +65,8 @@ Available audio output drivers are:
mode is probably not very useful, other than for debugging or when used
with fixed setups.
``coreaudio`` (macOS only)
Native macOS audio output driver using AudioUnits and the CoreAudio
``coreaudio`` (Mac OS X only)
Native Mac OS X audio output driver using AudioUnits and the CoreAudio
sound server.
Automatically redirects to ``coreaudio_exclusive`` when playing compressed
@@ -96,12 +92,12 @@ Available audio output drivers are:
extreme care.
``coreaudio_exclusive`` (macOS only)
Native macOS audio output driver using direct device access and
``coreaudio_exclusive`` (Mac OS X only)
Native Mac OS X audio output driver using direct device access and
exclusive mode (bypasses the sound server).
``openal``
OpenAL audio output driver.
OpenAL audio output driver. This is broken and does not work.
``--openal-num-buffers=<2-128>``
Specify the number of audio buffers to use. Lower values are better for
@@ -113,7 +109,9 @@ Available audio output drivers are:
``--openal-direct-channels=<yes|no>``
Enable OpenAL Soft's direct channel extension when available to avoid
tinting the sound with ambisonics or HRTF. Default: yes.
tinting the sound with ambisonics or HRTF.
Channels are dropped when when they are not available as downmixing
will be disabled. Default: no.
``pulse``
PulseAudio audio output driver
@@ -128,7 +126,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
@@ -145,27 +143,6 @@ Available audio output drivers are:
Allow mpv to use PulseAudio even if the sink is suspended (default: no).
Can be useful if PulseAudio is running as a bridge to jack and mpv has its sink-input set to the one jack is using.
``pipewire``
PipeWire audio output driver
The following global options are supported by this audio output:
``--pipewire-buffer=<1-2000|native>``
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.
``--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
@@ -239,11 +216,5 @@ Available audio output drivers are:
``no-waveheader`` option - with ``waveheader`` it's broken, because
it will write a WAVE header every time the file is opened.
``sndio``
Audio output to the OpenBSD sndio sound system
(Note: only supports mono, stereo, 4.0, 5.1 and 7.1 channel
layouts.)
``wasapi``
Audio output to the Windows Audio Session API.
+24 -66
View File
@@ -14,83 +14,51 @@ Keybindings
ESC
Hide the console.
ENTER, Ctrl+J and Ctrl+M
ENTER
Run the typed command.
Shift+ENTER
Type a literal newline character.
LEFT and Ctrl+B
Move the cursor to the previous character.
Ctrl+LEFT and Ctrl+RIGHT
Move cursor to previous/next word.
RIGHT and Ctrl+F
Move the cursor to the next character.
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
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
Move the cursor to the start of the current line.
END and Ctrl+E
Move the cursor to the end of the current line.
BACKSPACE and Ctrl+H
Delete the previous character.
Ctrl+D
Hide the console if the current line is empty, otherwise delete the next
character.
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
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
Delete text from the cursor to the beginning of the current line.
Ctrl+K
Delete text from the cursor to the end of the current line.
Ctrl+C
Clear the current line.
UP and Ctrl+P
Move back in the command history.
DOWN and Ctrl+N
Move forward in the command history.
UP and DOWN
Navigate command history.
PGUP
Go to the first command in the history.
PGDN
Stop navigating the command history.
Stop navigating command history.
INSERT
Toggle insert mode.
Ctrl+V
Paste text (uses the clipboard on X11 and Wayland).
Shift+INSERT
Paste text (uses the primary selection on X11 and Wayland).
Paste text (uses the primary selection on X11).
TAB and Ctrl+I
TAB
Complete the command or property name at the cursor.
Ctrl+C
Clear current line.
Ctrl+K
Delete text from the cursor to the end of the line.
Ctrl+L
Clear all log messages from the console.
Ctrl+U
Delete text from the cursor to the beginning of the line.
Ctrl+V
Paste text (uses the clipboard on X11).
Ctrl+W
Delete text from the cursor to the beginning of the current word.
Commands
--------
@@ -142,14 +110,4 @@ Configurable Options
Default: 16
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."
+91 -230
View File
@@ -62,8 +62,8 @@ character), or a symbolic name (as printed by ``--input-keylist``).
command.
``<command>`` is the command itself. It consists of the command name and
multiple (or none) arguments, all separated by whitespace. String arguments
should be quoted, typically with ``"``. See ``Flat command syntax``.
multiple (or none) commands, all separated by whitespace. String arguments
need to be quoted with ``"``. Details see ``Flat command syntax``.
You can bind multiple commands to one key. For example:
@@ -97,12 +97,6 @@ All key names can be combined with the modifiers ``Shift``, ``Ctrl``, ``Alt``,
``Meta``. They must be prefixed to the actual key name, where each modifier
is followed by a ``+`` (for example ``ctrl+q``).
The ``Shift`` modifier requires some attention. For instance ``Shift+2`` should
usually be specified as key-name ``@`` at ``input.conf``, and similarly the
combination ``Alt+Shift+2`` is usually ``Alt+@``, etc. Special key names like
``Shift+LEFT`` work as expected. If in doubt - use ``--input-test`` to check
how a key/combination is seen by mpv.
Symbolic key names and modifier names are case-insensitive. Unicode key names
are case-sensitive because input bindings typically respect the shift key.
@@ -168,36 +162,18 @@ a number of other places.
|
| ``<command> ::= [<prefixes>] <command_name> (<argument>)*``
| ``<argument> ::= (<unquoted> | " <double_quoted> " | ' <single_quoted> ' | `X <custom_quoted> X`)``
| ``<argument> ::= (<string> | " <quoted_string> ")``
``command_name`` is an unquoted string with the command name itself. See
`List of Input Commands`_ for a list.
Arguments are separated by whitespaces even if the command expects only one
argument. Arguments with whitespaces or other special characters must be quoted,
or the command cannot be parsed correctly.
Arguments are separated by whitespace. This applies even to string arguments.
For this reason, string arguments should be quoted with ``"``. If a string
argument contains spaces or certain special characters, quoting and possibly
escaping is mandatory, or the command cannot be parsed correctly.
Double quotes interpret JSON/C-style escaping, like ``\t`` or ``\"`` or ``\\``.
JSON escapes according to RFC 8259, minus surrogate pair escapes. This is the
only form which allows newlines at the value - as ``\n``.
Single quotes take the content literally, and cannot include the single-quote
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.
```-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.
Mixed quoting at the same argument, like ``'foo'"bar"``, is not supported.
Note that argument parsing and property expansion happen at different stages.
First, arguments are determined as described above, and then, where applicable,
properties are expanded - regardless of argument quoting. However, expansion
can still be prevented with the ``raw`` prefix or ``$>``. See `Input Command
Prefixes`_ and `Property Expansion`_.
Inside quotes, C-style escaping can be used. JSON escapes according to RFC 8259,
minus surrogate pair escapes, should be a safe subset that can be used.
Commands specified as arrays
----------------------------
@@ -220,9 +196,6 @@ quotes and escaping. The array command APIs mentioned above pass strings
directly to the argument parsers, or can sidestep them by the ability to pass
non-string values.
Property expansion is disabled by default for these APIs. This can be changed
with the ``expand-properties`` prefix. See `Input Command Prefixes`_.
Sometimes commands have string arguments, that in turn are actually parsed by
other components (e.g. filter strings with ``vf add``) - in these cases, you
you would have to double-escape in input.conf, but not with the array APIs.
@@ -249,9 +222,6 @@ to use APIs that pass arguments as arrays.
Named arguments are not supported in the "flat" input.conf syntax, which means
you cannot use them for key bindings in input.conf at all.
Property expansion is disabled by default for these APIs. This can be changed
with the ``expand-properties`` prefix. See `Input Command Prefixes`_.
List of Input Commands
----------------------
@@ -334,9 +304,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``.
@@ -347,10 +314,6 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
the minimum, on underflow set it to the maximum. If ``up`` or ``down`` is
omitted, assume ``up``.
Whether or not key-repeat is enabled by default depends on the property.
Currently properties with continuous values are repeatable by default (like
``volume``), while discrete values are not (like ``osd-level``).
``multiply <name> <value>``
Similar to ``add``, but multiplies the property or option with the numeric
value.
@@ -369,7 +332,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
@@ -385,9 +349,6 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``async`` flag to make encoding/writing the image file asynchronous. For
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
@@ -436,7 +397,7 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
Playback is stopped. If idle mode (``--idle``) is enabled, the player
will enter idle mode, otherwise it will exit.
This command is similar to ``loadfile`` in that it only manipulates the
This comm and is similar to ``loadfile`` in that it only manipulates the
state of what to play next, without waiting until the current file is
unloaded, and the next one is loaded.
@@ -479,10 +440,6 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
Stop playback and replace the internal playlist with the new one.
<append>
Append the new playlist at the end of the current internal playlist.
<append-play>
Append the new playlist, and if nothing is currently playing, start
playback. (Always starts with the new playlist, even if the internal
playlist was not empty before running this command.)
``playlist-clear``
Clear the playlist, except the currently played file.
@@ -550,15 +507,15 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
argument list.
The first array entry is either an absolute path to the executable, or
a filename with no path components, in which case the executable is
searched in the directories in the ``PATH`` environment variable. On
Unix, this is equivalent to ``posix_spawnp`` and ``execvp`` behavior.
a filename with no path components, in which case the ``PATH``
environment variable. On Unix, this is equivalent to ``posix_spawnp``
and ``execvp`` behavior.
``playback_only`` (``MPV_FORMAT_FLAG``)
Boolean indicating whether the process should be killed when playback
of the current playlist entry terminates (optional, default: true). If
enabled, stopping playback will automatically kill the process, and you
can't start it outside of playback.
terminates (optional, default: true). If enabled, stopping playback
will automatically kill the process, and you can't start it outside of
playback.
``capture_size`` (``MPV_FORMAT_INT64``)
Integer setting the maximum number of stdout plus stderr bytes that can
@@ -586,7 +543,7 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
instead. (Unlike the underlying OS mechanisms, the mpv command cannot
start a process with empty environment. Fortunately, that is completely
useless.) The format of the list is as in the ``execle()`` syscall. Each
string item defines an environment variable as in ``NAME=VALUE``.
string item defines an environment variable as in ``NANME=VALUE``.
On Lua, you may use ``utils.get_env_list()`` to retrieve the current
environment if you e.g. simply want to add a new variable.
@@ -605,14 +562,12 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
The command returns the following result (as ``MPV_FORMAT_NODE_MAP``):
``status`` (``MPV_FORMAT_INT64``)
Typically this is the process exit code (0 or positive) if the process
terminates normally, or negative for other errors (failed to start,
terminated by mpv, and others). The meaning of negative values is
undefined, other than meaning error (and does not correspond to OS low
level exit status values).
The raw exit status of the process. It will be negative on error. The
meaning of negative values is undefined, other than meaning error (and
does not correspond to OS low level exit status values).
On Windows, it can happen that a negative return value is returned even
if the process terminates normally, because the win32 ``UINT`` exit
On Windows, it can happen that a negative return value is returned
even if the process exits gracefully, because the win32 ``UINT`` exit
code is assigned to an ``int`` variable before being set as ``int64_t``
field in the result map. This might be fixed later.
@@ -623,9 +578,9 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
Same as ``stdout``, but for stderr.
``error_string`` (``MPV_FORMAT_STRING``)
Empty string if the process terminated normally. The string ``killed``
if the process was terminated in an unusual way. The string ``init`` if
the process could not be started.
Empty string if the process exited gracefully. The string ``killed`` if
the process was terminated in an unusual way. The string ``init`` if the
process could not be started.
On Windows, ``killed`` is only returned when the process has been
killed by mpv as a result of ``playback_only`` being set to true.
@@ -640,19 +595,17 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
it was somehow killed or returned an error status has to be queried from
the result value.
This command can be asynchronously aborted via API. Also see `Asynchronous
command details`_. Only the ``run`` command can start processes in a truly
detached way.
This command can be asynchronously aborted via API.
.. note:: The subprocess will always be terminated on player exit if it
wasn't started in detached mode, even if ``playback_only`` is
false.
In all cases, the subprocess will be terminated on player exit. Also see
`Asynchronous command details`_. Only the ``run`` command can start
processes in a truly detached way.
.. admonition:: Warning
Don't forget to set the ``playback_only`` field to false if you want
the command to run while the player is in idle mode, or if you don't
want the end of playback to kill the command.
Don't forget to set the ``playback_only`` field if you want the command
run while the player is in idle mode, or if you don't want that end of
playback kills the command.
.. admonition:: Example
@@ -677,7 +630,7 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``quit-watch-later [<code>]``
Exit player, and store current playback position. Playing that file later
will seek to the previous position on start. The (optional) argument is
exactly as in the ``quit`` command. See `RESUMING PLAYBACK`_.
exactly as in the ``quit`` command.
``sub-add <url> [<flags> [<title> [<lang>]]]``
Load the given subtitle file or stream. By default, it is selected as
@@ -716,30 +669,16 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
This works by unloading and re-adding the subtitle track.
``sub-step <skip> <flags>``
``sub-step <skip>``
Change subtitle timing such, that the subtitle event after the next
``<skip>`` subtitle events is displayed. ``<skip>`` can be negative to step
backwards.
Secondary argument:
primary (default)
Steps through the primary subtitles.
secondary
Steps through the secondary subtitles.
``sub-seek <skip> <flags>``
``sub-seek <skip>``
Seek to the next (skip set to 1) or the previous (skip set to -1) subtitle.
This is similar to ``sub-step``, except that it seeks video and audio
instead of adjusting the subtitle delay.
Secondary argument:
primary (default)
Seeks through the primary subtitles.
secondary
Seeks through the secondary subtitles.
For embedded subtitles (like with Matroska), this works only with subtitle
events that have already been displayed, or are within a short prefetch
range.
@@ -852,11 +791,8 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``audio-reload [<id>]``
Reload the given audio tracks. See ``sub-reload`` command.
``video-add <url> [<flags> [<title> [<lang> [<albumart>]]]]``
Load the given video file. See ``sub-add`` command for common options.
``albumart`` (``MPV_FORMAT_FLAG``)
If enabled, mpv will load the given video as album art.
``video-add <url> [<flags> [<title> [<lang>]]]``
Load the given video file. See ``sub-add`` command.
``video-remove [<id>]``
Remove the given video track. See ``sub-remove`` command.
@@ -865,9 +801,9 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
Reload the given video tracks. See ``sub-reload`` command.
``rescan-external-files [<mode>]``
Rescan external files according to the current ``--sub-auto``,
``--audio-file-auto`` and ``--cover-art-auto`` settings. This can be used
to auto-load external files *after* the file was loaded.
Rescan external files according to the current ``--sub-auto`` and
``--audio-file-auto`` settings. This can be used to auto-load external
files *after* the file was loaded.
The ``mode`` argument is one of the following:
@@ -945,7 +881,7 @@ Input Commands that are Possibly Subject to Change
.. admonition:: Example for input.conf
- ``a vf set vflip`` turn the video upside-down on the ``a`` key
- ``a vf set flip`` turn video upside-down on the ``a`` key
- ``b vf set ""`` remove all video filters on ``b``
- ``c vf toggle gradfun`` toggle debanding on ``c``
@@ -1096,7 +1032,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.
@@ -1225,8 +1161,7 @@ Input Commands that are Possibly Subject to Change
``script-message-to <target> [<arg1> [<arg2> [...]]]``
Same as ``script-message``, but send it only to the client named
``<target>``. Each client (scripts etc.) has a unique name. For example,
Lua scripts can get their name via ``mp.get_script_name()``. Note that
client names only consist of alphanumeric characters and ``_``.
Lua scripts can get their name via ``mp.get_script_name()``.
This command has a variable number of arguments, and cannot be used with
named arguments.
@@ -1238,8 +1173,7 @@ Input Commands that are Possibly Subject to Change
The argument is the name of the binding.
It can optionally be prefixed with the name of the script, using ``/`` as
separator, e.g. ``script-binding scriptname/bindingname``. Note that script
names only consist of alphanumeric characters and ``_``.
separator, e.g. ``script-binding scriptname/bindingname``.
For completeness, here is how this command works internally. The details
could change any time. On any matching key event, ``script-message-to``
@@ -1388,7 +1322,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
@@ -1428,8 +1362,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:
@@ -1609,8 +1543,10 @@ This list uses the event name field value, and the C API symbol in brackets:
``data``
The new value of the property.
The following events also happen, but are deprecated: ``idle``, ``tick``
Use ``mpv_observe_property()`` (Lua: ``mp.observe_property()``) instead.
The following events also happen, but are deprecated: ``tracks-changed``,
``track-switched``, ``pause``, ``unpause``, ``metadata-update``, ``idle``,
``tick``, ``chapter-change``. Use ``mpv_observe_property()``
(Lua: ``mp.observe_property()``) instead.
Hooks
~~~~~
@@ -1713,9 +1649,7 @@ prefixes can be specified. They are separated by whitespace.
This is the default for ``input.conf`` commands.
``repeatable``
For some commands, keeping a key pressed doesn't run the command repeatedly.
This prefix forces enabling key repeat in any case. For a list of commands:
the first command determines the repeatability of the whole list (up to and
including version 0.33 - a list was always repeatable).
This prefix forces enabling key repeat in any case.
``async``
Allow asynchronous execution (if possible). Note that only a few commands
will support this (usually this is explicitly documented). Some commands
@@ -1824,7 +1758,7 @@ Property list
.. note::
Most options can be set at runtime via properties as well. Just remove the
Most options can be set as runtime via properties as well. Just remove the
leading ``--`` from the option name. These are not documented below, see
`OPTIONS`_ instead. Only properties which do not exist as option with the
same name, or which have very different behavior from the options are
@@ -1872,9 +1806,6 @@ Property list
.. note:: This is only an estimate. (It's computed from two unreliable
quantities: fps and possibly rounded timestamps.)
``pid``
Process-id of mpv.
``path``
Full path of the currently played file. Usually this is exactly the same
string you pass on the mpv command line or the ``loadfile`` command, even
@@ -2538,15 +2469,6 @@ Property list
(or to be exact, the size the video filters output). ``2`` will set the
double size, ``0.5`` halves the size.
Note that setting a value identical to its previous value will not resize
the window. That's because this property mirrors the ``window-scale``
option, and setting an option to its previous value is ignored. If this
value is set while the window is in a fullscreen, the multiplier is not
applied until the window is taken out of that state. Writing this property
to a maximized window can unmaximize the window depending on the OS and
window manager. If the window does not unmaximize, the multiplier will be
applied if the user unmaximizes the window later.
See ``current-window-scale`` for the value derived from the actual window
size.
@@ -2555,21 +2477,15 @@ Property list
Before mpv 0.31.0, this returned what ``current-window-scale`` returns now,
after the window was created.
``current-window-scale`` (RW)
``current-window-scale``
The ``window-scale`` value calculated from the current window size. This
has the same value as ``window-scale`` if the window size was not changed
since setting the option, and the window size was not restricted in other
ways. If the window is fullscreened, this will return the scale value
calculated from the last non-fullscreen size of the window. The property
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
property will always resize the window. This does not affect the value of
``window-scale``.
ways. The property is unavailable if no video is active.
``focused``
Whether the window has focus. Might not be supported by all VOs.
Whether the window has focus. Currently works only on X11, Wayland and
macOS.
``display-names``
Names of the displays that the mpv window covers. On X11, these
@@ -2600,11 +2516,6 @@ Property list
``vsync-jitter``
Estimated deviation factor of the vsync duration.
``display-width``, ``display-height``
The current display's horizontal and vertical resolution in pixels. Whether
or not these values update as the mpv window changes displays depends on
the windowing backend. It may not be available on all platforms.
``display-hidpi-scale``
The HiDPI scale factor as reported by the windowing backend. If no VO is
active, or if the VO does not report a value, this property is unavailable.
@@ -2620,8 +2531,8 @@ Property list
``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
including decorations drawn by the OS window manager).
``overlay-add`` command. It gives you the actual OSD size, which can be
different from the window size in some cases.
Alias to ``osd-dimensions/w`` and ``osd-dimensions/h``.
@@ -2657,10 +2568,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.
@@ -2680,6 +2587,8 @@ Property list
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
@@ -2691,29 +2600,19 @@ 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.
``secondary-sub-text``
Same as ``sub-text``, but for the secondary subtitles.
This property is experimental and might be removed in the future.
``sub-start``
The current subtitle start time (in seconds). If there's multiple current
subtitles, returns the first start time. If no current subtitle is present
null is returned instead.
``secondary-sub-start``
Same as ``sub-start``, but for the secondary subtitles.
``sub-end``
The current subtitle end time (in seconds). If there's multiple current
subtitles, return the last end time. If no current subtitle is present, or
if it's present but has unknown or incorrect duration, null is returned
instead.
``secondary-sub-end``
Same as ``sub-end``, but for the secondary subtitles.
``sub-forced-only-cur``
Read-only - whether the current subtitle track is being shown in forced-only mode.
``playlist-pos`` (RW)
Current position on playlist. The first entry is on position 0. Writing to
this property may start playback at the new position.
@@ -2794,10 +2693,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
@@ -2849,15 +2747,10 @@ Property list
``track-list/N/lang``
Track language as identified by the file. Not always available.
``track-list/N/image``
``yes``/true if this is a video track that consists of a single
picture, ``no``/false or unavailable otherwise. The heuristic used to
determine if a stream is an image doesn't attempt to detect images in
codecs normally used for videos. Otherwise, it is reliable.
``track-list/N/albumart``
``yes``/true if this is an image embedded in an audio file or external
cover art, ``no``/false or unavailable otherwise.
``yes``/true if this is a video track that consists of a single
picture, ``no``/false or unavailable otherwise. This is used for video
tracks that are really attached pictures in audio files.
``track-list/N/default``
``yes``/true if the track has the default flag set in the file,
@@ -2867,10 +2760,6 @@ Property list
``yes``/true if the track has the forced flag set in the file,
``no``/false or unavailable otherwise.
``track-list/N/auto-forced-only``
``yes``/true if the track was autoselected in forced-only mode,
``no``/false or unavailable otherwise.
``track-list/N/codec``
The codec name used by this track, for example ``h264``. Unavailable
in some rare cases.
@@ -2955,7 +2844,6 @@ Property list
"src-id" MPV_FORMAT_INT64
"title" MPV_FORMAT_STRING
"lang" MPV_FORMAT_STRING
"image" MPV_FORMAT_FLAG
"albumart" MPV_FORMAT_FLAG
"default" MPV_FORMAT_FLAG
"forced" MPV_FORMAT_FLAG
@@ -2991,10 +2879,17 @@ Property list
For example, ``current-tracks/audio/lang`` returns the current audio track's
language field (the same value as ``track-list/N/lang``).
If tracks of the requested type are selected via ``--lavfi-complex``, the
first one is returned.
A sub-entry is accessible only if a track of that type is actually selected.
Tracks selected via ``--lavfi-complex`` never appear under this property.
``current-tracks`` and ``current-tracks/`` are currently not accessible, and
will not return anything.
``chapter-list`` (RW)
Scripts etc. should not use this. They should use ``track-list``, loop over
all tracks, and inspect the ``selected`` field to test whether a track is
selected (or compare the ``id`` field to the ``video`` / ``audio`` etc.
options).
``chapter-list``
List of chapters, current entry marked. Currently, the raw property value
is useless.
@@ -3076,17 +2971,15 @@ Property list
.. admonition:: Example
- ``--osd-msg3='This is ${osd-ass-cc/0}{\\b1}bold text'``
- ``show-text "This is ${osd-ass-cc/0}{\\b1}bold text"``
- ``--osd-status-msg='This is ${osd-ass-cc/0}{\\b1}bold text'``
- ``show-text "This is ${osd-ass-cc/0}{\b1}bold text"``
Any ASS override tags as understood by libass can be used.
Note that you need to escape the ``\`` character, because the string is
processed for C escape sequences before passing it to the OSD code. See
`Flat command syntax`_ for details.
processed for C escape sequences before passing it to the OSD code.
A list of tags can be found here:
https://aeg-dev.github.io/AegiSite/docs/3.2/ass_tags/
A list of tags can be found here: http://docs.aegisub.org/latest/ASS_Tags/
``vo-configured``
Whether the VO is configured right now. Usually this corresponds to whether
@@ -3262,24 +3155,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.
@@ -3335,9 +3210,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
@@ -3351,12 +3225,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
@@ -3428,10 +3296,6 @@ Property list
not a map, as order matters and duplicate entries are possible. Recursive
profiles are not expanded, and show up as special ``profile`` options.
The ``profile-restore`` field is currently missing if it holds the default
value (either because it was not set, or set explicitly to ``default``),
but in the future it might hold the value ``default``.
``command-list``
The list of input commands. This returns an array of maps, where each map
node represents a command. This map currently only has a single entry:
@@ -3489,7 +3353,7 @@ You can access (almost) all options as properties, though there are some
caveats with some properties (due to historical reasons):
``vid``, ``aid``, ``sid``
While playback is active, these return the actually active tracks. For
While playback is active, these result the actually active tracks. For
example, if you set ``aid=5``, and the currently played file contains no
audio track with ID 5, the ``aid`` property will return ``no``.
@@ -3507,8 +3371,8 @@ caveats with some properties (due to historical reasons):
the initial filter chain cannot be created.
This behavior changed in mpv 0.31.0. Before this, the new value was rejected
*iff* a video (for ``vf``) or an audio (for ``af``) track was active. If
playback was not active, the behavior was the same as the current one.
*iff* video (for ``vf``) or audio (for ``af``) was active. If playback was
not active, the behavior was the same as the current behavior.
``playlist``
The property is read-only and returns the current internal playlist. The
@@ -3537,11 +3401,8 @@ command is an exception and not a general rule.)
``i show-text "Filename: ${filename}"``
shows the filename of the current file when pressing the ``i`` key
Whether property expansion is enabled by default depends on which API is used
(see `Flat command syntax`_, `Commands specified as arrays`_ and `Named
arguments`_), but it can always be enabled with the ``expand-properties``
prefix or disabled with the ``raw`` prefix, as described in `Input Command
Prefixes`_.
Within ``input.conf``, property expansion can be inhibited by putting the
``raw`` prefix in front of commands.
The following expansions are supported:
+3 -3
View File
@@ -116,7 +116,7 @@ can also be present. See `List of events`_ for a list of all supported events.
Because events can occur at any time, it may be difficult at times to determine
which response goes with which command. Commands may optionally include a
``request_id`` which, if provided in the command request, will be copied
verbatim into the response. mpv does not interpret the ``request_id`` in any
verbatim into the response. mpv does not intrepret the ``request_id`` in any
way; it is solely for the use of the requester. The only requirement is that
the ``request_id`` field must be an integer (a number without fractional parts
in the range ``-2^63..2^63-1``). Using other types is deprecated and will
@@ -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.
+14 -23
View File
@@ -58,7 +58,7 @@ Language features - ECMAScript 5
The scripting backend which mpv currently uses is MuJS - a compatible minimal
ES5 interpreter. As such, ``String.substring`` is implemented for instance,
while the common but non-standard ``String.substr`` is not. Please consult the
MuJS pages on language features and platform support - https://mujs.com .
MuJS pages on language features and platform support - http://mujs.com .
Unsupported Lua APIs and their JS alternatives
----------------------------------------------
@@ -73,6 +73,12 @@ Unsupported Lua APIs and their JS alternatives
``utils.to_string(v)`` see ``dump`` below.
``mp.suspend()`` JS: none (deprecated).
``mp.resume()`` JS: none (deprecated).
``mp.resume_all()`` JS: none (deprecated).
``mp.get_next_timeout()`` see event loop below.
``mp.dispatch_events([allow_wait])`` see event loop below.
@@ -91,12 +97,10 @@ Where the Lua APIs use ``nil`` to indicate error, JS APIs use ``undefined``.
``mp.command_native(table [,def])`` (LE)
``id = mp.command_native_async(table [,fn])`` (LE) Notes: ``id`` is true-thy on
success, ``error`` is empty string on success.
success, ``fn`` is called always a-sync, ``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)
@@ -175,8 +179,7 @@ success, ``error`` is empty string on success.
``mp.utils.readdir(path [, filter])`` (LE)
``mp.utils.file_info(path)`` (LE) Note: like lua - this does NOT expand
meta-paths like ``~~/foo`` (other JS file functions do expand meta paths).
``mp.utils.file_info(path)`` (LE)
``mp.utils.split_path(path)``
@@ -217,9 +220,8 @@ Additional utilities
``undefined`` if the variable is not defined.
``mp.utils.get_user_path(path)``
Trivial wrapper of the ``expand-path`` mpv command, returns a string.
``read_file``, ``write_file``, ``append_file`` and ``require`` already
expand the path internally and accept mpv meta-paths like ``~~desktop/foo``.
Expands (mpv) meta paths like ``~/x``, ``~~/y``, ``~~desktop/z`` etc.
``read_file``, ``write_file`` and ``require`` already use this internaly.
``mp.utils.read_file(fname [,max])``
Returns the content of file ``fname`` as string. If ``max`` is provided and
@@ -230,12 +232,7 @@ Additional utilities
prefixed with ``file://`` as simple protection against accidental arguments
switch, e.g. ``mp.utils.write_file("file://~/abc.txt", "hello world")``.
``mp.utils.append_file(fname, str)``
Same as ``mp.utils.write_file`` if the file ``fname`` does not exist. If it
does exist then append instead of overwrite.
Note: ``read_file``, ``write_file`` and ``append_file`` throw on errors, allow
text content only.
Note: ``read_file`` and ``write_file`` throw on errors, allow text content only.
``mp.get_time_ms()``
Same as ``mp.get_time()`` but in ms instead of seconds.
@@ -332,16 +329,10 @@ Custom initialization
---------------------
After mpv initializes the JavaScript environment for a script but before it
loads the script - it tries to run the file ``init.js`` at the root of the mpv
loads the script - it tries to run the file ``.init.js`` at the root of the mpv
configuration directory. Code at this file can update the environment further
for all scripts. E.g. if it contains ``mp.module_paths.push("/foo")`` then
``require`` at all scripts will search global module id's also at ``/foo``
(do NOT do ``mp.module_paths = ["/foo"];`` because this will remove existing
paths - like ``<script-dir>/modules`` for scripts which load from a directory).
The custom-init file is ignored if mpv is invoked with ``--no-config``.
Before mpv 0.34, the file name was ``.init.js`` (with dot) at the same dir.
``require`` at all scripts will search global module id's also at ``/foo``.
The event loop
--------------
+6 -6
View File
@@ -10,9 +10,9 @@ Since libmpv merely allows access to underlying mechanisms that can control
mpv, further documentation is spread over a few places:
- https://github.com/mpv-player/mpv/blob/master/libmpv/client.h
- https://mpv.io/manual/master/#options
- https://mpv.io/manual/master/#list-of-input-commands
- https://mpv.io/manual/master/#properties
- http://mpv.io/manual/master/#options
- http://mpv.io/manual/master/#list-of-input-commands
- http://mpv.io/manual/master/#properties
- https://github.com/mpv-player/mpv-examples/tree/master/libmpv
C PLUGINS
@@ -21,8 +21,8 @@ C PLUGINS
You can write C plugins for mpv. These use the libmpv API, although they do not
use the libmpv library itself.
They are available on Linux/BSD platforms only and enabled by default if the
compiler supports linking with the ``-rdynamic`` flag.
Currently, they must be explicitly enabled at build time with
``--enable-cplugins``. They are available on Linux/BSD platforms only.
C plugins location
------------------
@@ -64,7 +64,7 @@ Linkage to libmpv
-----------------
The current implementation requires that your plugins are **not** linked against
libmpv. What your plugins use are not symbols from a libmpv binary, but
libmpv. What your plugins uses are not symbols from a libmpv binary, but
symbols from the mpv host binary.
Examples
+19 -21
View File
@@ -37,12 +37,6 @@ scripting backend to use for it. For Lua, it is ``.lua``. If the extension is
not recognized, an error is printed. (If an error happens, the extension is
either mistyped, or the backend was not compiled into your mpv binary.)
mpv internally loads the script's name by stripping the ``.lua`` extension and
replacing all nonalphanumeric characters with ``_``. E.g., ``my-tools.lua``
becomes ``my_tools``. If there are several scripts with the same name, it is
made unique by appending a number. This is the name returned by
``mp.get_script_name()``.
Entries with ``.disable`` extension are always ignored.
If a script is a directory (either if a directory is passed to ``--script``,
@@ -70,7 +64,7 @@ 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.
@@ -191,20 +185,12 @@ The ``mp`` module is preloaded, although it can be loaded manually with
If starting the command failed for some reason, ``nil, error`` is returned,
and ``fn`` is called indicating failure, using the same error value.
``fn`` is always called asynchronously, even if the command failed to start.
``mp.abort_async_command(t)``
Abort a ``mp.command_native_async`` call. The argument is the return value
of that command (which starts asynchronous execution of the command).
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 [,def])``
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
@@ -488,7 +474,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
the timer callback function fn is run).
Note that these are methods, and you have to call them using ``:`` instead
of ``.`` (Refer to https://www.lua.org/manual/5.2/manual.html#3.4.9 .)
of ``.`` (Refer to http://www.lua.org/manual/5.2/manual.html#3.4.9 .)
Example:
@@ -514,11 +500,11 @@ The ``mp`` module is preloaded, although it can be loaded manually with
Return the name of the current script. The name is usually made of the
filename of the script, with directory and file extension removed. If
there are several scripts which would have the same name, it's made unique
by appending a number. Any nonalphanumeric characters are replaced with ``_``.
by appending a number.
.. admonition:: Example
The script ``/path/to/foo-script.lua`` becomes ``foo_script``.
The script ``/path/to/fooscript.lua`` becomes ``fooscript``.
``mp.get_script_directory()``
Return the directory if this is a script packaged as directory (see
@@ -535,6 +521,18 @@ Advanced mp functions
These also live in the ``mp`` module, but are documented separately as they
are useful only in special situations.
``mp.suspend()``
This function has been deprecated in mpv 0.21.0 and does nothing starting
with mpv 0.23.0 (no replacement).
``mp.resume()``
This function has been deprecated in mpv 0.21.0 and does nothing starting
with mpv 0.23.0 (no replacement).
``mp.resume_all()``
This function has been deprecated in mpv 0.21.0 and does nothing starting
with mpv 0.23.0 (no replacement).
``mp.get_wakeup_pipe()``
Calls ``mpv_get_wakeup_pipe()`` and returns the read end of the wakeup
pipe. This is deprecated, but still works. (See ``client.h`` for details.)
@@ -620,7 +618,7 @@ are useful only in special situations.
``mp.get_osd_size()``
Returns a tuple of ``osd_width, osd_height, osd_par``. The first two give
the size of the OSD in pixels (for video outputs like ``--vo=xv``, this may
the size of the OSD in pixels (for video ouputs like ``--vo=xv``, this may
be "scaled" pixels). The third is the display pixel aspect ratio.
May return invalid/nonsense values if OSD is not initialized yet.
@@ -765,7 +763,7 @@ strictly part of the guaranteed API.
``mtime``
time of last modification
``ctime``
time of last metadata change
time of last metadata change (Linux) / time of creation (Windows)
``is_file``
Whether ``path`` is a regular file (boolean)
``is_dir``
@@ -777,7 +775,7 @@ strictly part of the guaranteed API.
The booleans ``is_file`` and ``is_dir`` are provided as a convenience;
they can be and are derived from ``mode``.
On error (e.g. path does not exist), ``nil, error`` is returned.
On error (eg. path does not exist), ``nil, error`` is returned.
``utils.split_path(path)``
Split a path into directory component and filename component, and return
+78 -147
View File
@@ -39,15 +39,9 @@ LIRC support - configure remotes as input devices instead).
See the ``--input-`` options for ways to customize it.
The following listings are not necessarily complete. See ``etc/input.conf``
in the mpv source files for a list of default bindings. User ``input.conf``
files and Lua scripts can define additional key bindings.
See `COMMAND INTERFACE`_ and `Key names`_ sections for more details on
configuring keybindings.
See also ``--input-test`` for interactive binding details by key, and the
`stats`_ built-in script for key bindings list (including print to terminal).
The following listings are not necessarily complete. See ``etc/input.conf`` for
a list of default bindings. User ``input.conf`` files and Lua scripts can
define additional key bindings.
Keyboard Control
----------------
@@ -110,8 +104,7 @@ q
Q
Like ``q``, but store the current playback position. Playing the same file
later will resume at the old playback position if possible. See
`RESUMING PLAYBACK`_.
later will resume at the old playback position if possible.
/ and *
Decrease/increase volume.
@@ -128,9 +121,6 @@ m
\#
Cycle through the available audio tracks.
E
Cycle through the available Editions.
f
Toggle fullscreen (see also ``--fs``).
@@ -237,7 +227,7 @@ i and I
`STATS`_ for more information.
del
Cycle OSC visibility between never / auto (mouse-move) / always
Cycles visibility between never / auto (mouse-move) / always
\`
Show the console. (ESC closes it again. See `CONSOLE`_.)
@@ -257,16 +247,16 @@ corresponding adjustment.)
7 and 8
Adjust saturation.
Alt+0 (and command+0 on macOS)
Alt+0 (and command+0 on OSX)
Resize video window to half its original size.
Alt+1 (and command+1 on macOS)
Alt+1 (and command+1 on OSX)
Resize video window to its original size.
Alt+2 (and command+2 on macOS)
Alt+2 (and command+2 on OSX)
Resize video window to double its original size.
command + f (macOS only)
command + f (OSX only)
Toggle fullscreen (see also ``--fs``).
(The following keys are valid if you have a keyboard with multimedia keys.)
@@ -400,9 +390,6 @@ It is started with ``%`` and has the following format::
``mpv --vf=foo:option1=%`expr length "$NAME"`%"$NAME" test.avi``
Note: where applicable with JSON-IPC, ``%n%`` is the length in UTF-8 bytes,
after decoding the JSON data.
Suboptions passed to the client API are also subject to escaping. Using
``mpv_set_option_string()`` is exactly like passing ``--name=data`` to the
command line (but without shell processing of the string). Some options
@@ -440,32 +427,23 @@ need to escape special characters. To work this around, the path can be
additionally wrapped in the fixed-length syntax, e.g. ``%n%string_of_length_n``
(see above).
Some mpv options interpret paths starting with ``~``.
Currently, the prefix ``~~home/`` expands to the mpv configuration directory
(usually ``~/.config/mpv/``).
Some mpv options interpret paths starting with ``~``. Currently, the prefix
``~~/`` expands to the mpv configuration directory (usually ``~/.config/mpv/``).
``~/`` expands to the user's home directory. (The trailing ``/`` is always
required.) The following paths are currently recognized:
================ ===============================================================
Name Meaning
================ ===============================================================
``~~/`` If the subpath exists in any of the mpv's config directories
the path of the existing file/dir is returned. Otherwise this
is equivalent to ``~~home/``.
Note that if --no-config is used ``~~/foobar`` will resolve to
``foobar`` which can be unexpected.
``~~/`` mpv config dir (for example ``~/.config/mpv/``)
``~/`` user home directory root (similar to shell, ``$HOME``)
``~~home/`` mpv config dir (for example ``~/.config/mpv/``)
``~~home/`` same as ``~~/``
``~~global/`` the global config path, if available (not on win32)
``~~osxbundle/`` the macOS bundle resource path (macOS only)
``~~desktop/`` the path to the desktop (win32, macOS)
``~~exe_dir/`` win32 only: the path to the directory containing the exe (for
``~~osxbundle/`` the OSX bundle resource path (OSX only)
``~~desktop/`` the path to the desktop (win32, OSX)
``~~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
``~~old_home`` do not use
================ ===============================================================
@@ -533,7 +511,7 @@ They support the following operations:
============= ===============================================
Suffix Meaning
============= ===============================================
-set Set a list of items (using the list separator, escaped with backslash)
-set Set a list of items (using the list separator, interprets escapes)
-append Append single item (does not interpret escapes)
-add Append 1 or more items (same syntax as -set)
-pre Prepend 1 or more items (same syntax as -set)
@@ -778,7 +756,7 @@ the following options:
[something]
profile-restore=copy-equal
vf-add=rotate=PI/2 # rotate by 90 degrees
vf-add=rotate=90
Then running these commands will result in behavior as commented:
@@ -786,9 +764,9 @@ the following options:
set vf vflip
apply-profile something
vf add hflip
vf-add=hflip
apply-profile something
# vf == vflip,rotate=PI/2,hflip,rotate=PI/2
# vf == vflip,rotate=90,hflip,rotate=90
apply-profile something restore
# vf == vflip
@@ -797,18 +775,15 @@ 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.
For example, ``pause`` would return the current pause status. You cannot
reference properties with ``-`` this way since that would denote a subtraction,
but if the variable name contains any ``_`` characters, they are turned into
``-``. For example, ``playback_time`` would return the property
``playback-time``.
For example, ``pause`` would return the current pause status. If the variable
name contains any ``_`` characters, they are turned into ``-``. For example,
``playback_time`` would return the property ``playback-time``.
A more robust way to access properties is using ``p.property_name`` or
``get("property-name", default_value)``. The automatic variable to property
@@ -821,13 +796,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
@@ -844,14 +819,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``:
@@ -861,7 +828,7 @@ and unintuitive behavior.
profile-desc=Mess up video when entering fullscreen
profile-cond=fullscreen
profile-restore=copy
vf-add=rotate=PI/2 # rotate by 90 degrees
vf-add=rotate=90
This appends the ``rotate`` filter to the video filter chain when entering
fullscreen. When leaving fullscreen, the ``vf`` option is set to the value
@@ -874,7 +841,7 @@ and unintuitive behavior.
[something]
profile-cond=fullscreen
vf-add=@rot:rotate=PI/2
vf-add=@rot:rotate=90
[something-inv]
profile-cond=not fullscreen
@@ -1034,7 +1001,7 @@ listed.
- ``AV:`` or ``V:`` (video only) or ``A:`` (audio only)
- The current time position in ``HH:MM:SS`` format (``playback-time`` property)
- The total file duration (absent if unknown) (``duration`` property)
- Playback speed, e.g. ``x2.0``. Only visible if the speed is not normal. This
- Playback speed, e.g. `` x2.0``. Only visible if the speed is not normal. This
is the user-requested speed, and not the actual speed (usually they should
be the same, unless playback is too slow). (``speed`` property.)
- Playback percentage, e.g. ``(13%)``. How much of the file has been played.
@@ -1108,33 +1075,6 @@ Additional options that can be tried:
- without audio ``--framedrop=no --speed=1.01`` may help for live sources
(results can be mixed)
RESUMING PLAYBACK
=================
mpv is capable of storing the playback position of the currently playing file
and resume from there the next time that file is played. This is done with the
commands ``quit-watch-later`` (bound to Shift+Q by default) and
``write-watch-later-config``, and with the ``--save-position-on-quit`` option.
The difference between always quitting with a key bound to ``quit-watch-later``
and using ``--save-position-on-quit`` is that the latter will save the playback
position even when mpv is closed with a method other than a keybinding, for
example if you shutdown your system without closing mpv beforehand, unless of
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 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.
When playing multiple playlist entries, mpv checks if one them has a resume
config file associated, and if it finds one it restarts playback from it. For
example, if you use ``quit-watch-later`` on the 5th episode of a show, and
later play all the episodes, mpv will automatically resume playback from
episode 5.
More options to configure this functionality are listed in `Watch Later`_.
PROTOCOLS
=========
@@ -1320,7 +1260,7 @@ Currently this happens only in the following cases:
or file associations provided by desktop environments)
- if started from explorer.exe on Windows (technically, if it was started on
Windows, and all of the stdout/stderr/stdin handles are unset)
- started out of the bundle on macOS
- started out of the bundle on OSX
- if you manually use ``--player-operation-mode=pseudo-gui`` on the command line
This mode applies options from the builtin profile ``builtin-pseudo-gui``, but
@@ -1358,7 +1298,7 @@ Linux desktop issues
====================
This subsection describes common problems on the Linux desktop. None of these
problems exist on systems like Windows or macOS.
problems exist on systems like Windows or OSX.
Disabling Screensaver
---------------------
@@ -1368,16 +1308,36 @@ a VO using the OS GUI API is active). ``--stop-screensaver=no`` disables this.
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.
extension (XSS) on X11, and the idle-inhibit 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.
GNOME is one of the worst offenders, and ignores even the now widely supported
idle-inhibit protocol. (This is either due to a combination of malice and
incompetence, but since implementing this protocol would only take a few lines
of code, it is most likely the former. You will also notice how GNOME advocates
react offended whenever their sabotage is pointed out, which indicates either
hypocrisy, or even worse ignorance.)
Such incompatible desktop environments (i.e. which ignore standards) typically
require using a DBus API. This is ridiculous in several ways. The immediate
practical problem is that it would require adding a quite unwieldy dependency
for a DBus library, somehow integrating its mainloop into mpv, and other
generally unacceptable things.
However, since mpv does not officially support GNOME, this is not much of a
problem. If you are one of those miserable users who want to use mpv on GNOME,
report a bug on the GNOME issue tracker:
https://gitlab.gnome.org/groups/GNOME/-/issues
Alternatively, you may be able to write a Lua script that calls the
``xdg-screensaver`` command line program. (By the way, this a command line
program is an utterly horrible kludge that tries to identify your DE, and then
tries to send the correct DBus command via a DBus CLI tool.) If you find the
idea of having to write a script just so your screensaver doesn't kick in
ridiculous, do not use GNOME, or use GNOME video software instead of mpv (good
luck).
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
call the ``xdg-screensaver`` command line program from a user script instead.
intervals when not paused. This hack was removed in 0.33.0.
.. include:: options.rst
@@ -1539,7 +1499,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 +1506,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 +1520,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 +1545,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 +1559,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,18 +1594,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``.
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
@@ -1678,11 +1617,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`_.
+379 -870
View File
File diff suppressed because it is too large. Load diff
+8 -52
View File
@@ -43,8 +43,7 @@ pl next
============= ================================================
title
| Displays current media-title, filename, custom title, or target chapter
name while hovering the seekbar.
| Displays current media-title, filename, or custom title
============= ================================================
left-click show playlist position and length and full title
@@ -174,18 +173,18 @@ 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``
Default: yes
Controls the mode used to seek when dragging the seekbar. If set to ``yes``,
default seeking mode is used (usually keyframes, but player defaults and
heuristics can change it to exact). If set to ``no``, exact seeking on
mouse drags will be used instead. Keyframes are preferred, but exact seeks
may be useful in cases where keyframes cannot be found. Note that using
exact seeks can potentially make mouse dragging much slower.
Controls the mode used to seek when dragging the seekbar (default: true). If
set to true, default seeking mode is used (usually keyframes, but player
defaults and heuristics can change it to exact). If set to false, exact
seeking on mouse drags will be used instead. Keyframes are preferred, but
exact seeks may be useful in cases where keyframes cannot be found. Note
that using exact seeks can potentially make mouse dragging much slower.
``seekrangestyle``
Default: inverted
@@ -236,11 +235,6 @@ Configurable Options
Enable the OSC when fullscreen
``idlescreen``
Default: yes
Show the mpv logo and message when idle
``scalewindowed``
Default: 1.0
@@ -315,14 +309,6 @@ Configurable Options
Display timecodes with milliseconds
``tcspace``
Default: 100 (allowed: 50-200)
Adjust space reserved for timecodes (current time and time remaining) in
the ``bottombar`` and ``topbar`` layouts. The timecode width depends on the
font, and with some fonts the spacing near the timecodes becomes too small.
Use values above 100 to increase that spacing, or below 100 to decrease it.
``visibility``
Default: auto (auto hide/show on mouse move)
@@ -389,32 +375,6 @@ Configurable Options
Set to ``yes`` to reduce festivity (i.e. disable santa hat in December.)
``livemarkers``
Default: yes
Update chapter markers positions on duration changes, e.g. live streams.
The updates are unoptimized - consider disabling it on very low-end systems.
``chapters_osd``, ``playlist_osd``
Default: yes
Whether to display the chapters/playlist at the OSD when left-clicking the
next/previous OSC buttons, respectively.
``chapter_fmt``
Default: ``Chapter: %s``
Template for the chapter-name display when hovering the seekbar.
Use ``no`` to disable chapter display on hover. Otherwise it's a lua
``string.format`` template and ``%s`` is replaced with the actual name.
``unicodeminus``
Default: no
Use a Unicode minus sign instead of an ASCII hyphen when displaying
the remaining playback time.
Script Commands
~~~~~~~~~~~~~~~
@@ -437,10 +397,6 @@ to set auto mode (the default) with ``b``::
a script-message osc-visibility never
b script-message osc-visibility auto
``osc-idlescreen``
Controls the visibility of the mpv logo on idle. Valid arguments are ``yes``,
``no``, and ``cycle`` to toggle between yes and no.
``osc-playlist``, ``osc-chapterlist``, ``osc-tracklist``
Shows a limited view of the respective type of list using the OSC. First
argument is duration in seconds.
+21 -28
View File
@@ -24,8 +24,7 @@ stats:
1 Show usual stats
2 Show frame timings (scroll)
3 Input cache stats
4 Active key bindings (scroll)
0 Internal stuff (scroll)
4 Internal stuff (scroll)
==== ==================
On pages which support scroll, these key bindings are also active:
@@ -35,6 +34,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
-------------
@@ -45,6 +51,13 @@ option. The configuration syntax is described in `ON SCREEN CONTROLLER`_.
Configurable Options
~~~~~~~~~~~~~~~~~~~~
``key_oneshot``
Default: i
``key_toggle``
Default: I
Key bindings to display stats.
``key_page_1``
Default: 1
``key_page_2``
@@ -53,8 +66,6 @@ Configurable Options
Default: 3
``key_page_4``
Default: 4
``key_page_0``
Default: 0
Key bindings for page switching while stats are displayed.
@@ -103,13 +114,13 @@ Configurable Options
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.
@@ -160,36 +171,18 @@ Note: colors are given as hexadecimal values and use ASS tag order: BBGGRR
Different key bindings
~~~~~~~~~~~~~~~~~~~~~~
Additional keys can be configured in ``input.conf`` to display the stats::
A different key binding can be defined with the aforementioned options
``key_oneshot`` and ``key_toggle`` but also with commands in ``input.conf``,
for example::
e script-binding stats/display-stats
E script-binding stats/display-stats-toggle
And to display a certain page directly::
Using ``input.conf``, it is also possible to directly display a certain page::
i script-binding stats/display-page-1
e script-binding stats/display-page-2
Active key bindings page
~~~~~~~~~~~~~~~~~~~~~~~~
Lists the active key bindings and the commands they're bound to, excluding the
interactive keys of the stats script itself. See also ``--input-test`` for more
detailed view of each binding.
The keys are grouped automatically using a simple analysis of the command
string, and one should not expect documentation-level grouping accuracy,
however, it should still be reasonably useful.
Using ``--idle --script-opts=stats-bindlist=yes`` will print the list to the
terminal and quit immediately. By default long lines are shortened to 79 chars,
and terminal escape sequences are enabled. A different length limit can be
set by changing ``yes`` to a number (at least 40), and escape sequences can be
disabled by adding ``-`` before the value, e.g. ``...=-yes`` or ``...=-120``.
Like with ``--input-test``, the list includes bindings from ``input.conf`` and
from user scripts. Use ``--no-config`` to list only built-in bindings.
Internal stuff page
~~~~~~~~~~~~~~~~~~~
+6 -22
View File
@@ -60,7 +60,7 @@ libavfilter bridge.
.. note::
To get a full list of available video filters, see ``--vf=help`` and
https://ffmpeg.org/ffmpeg-filters.html .
http://ffmpeg.org/ffmpeg-filters.html .
Also, keep in mind that most actual filters are available via the ``lavfi``
wrapper, which gives you access to most of libavfilter's filters. This
@@ -141,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).
@@ -169,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``
@@ -316,17 +308,9 @@ Available mpv-only filters are:
:709-1886: Scene-referred using the BT709+BT1886 interaction
:gamma1.2: Scene-referred using a pure power OOTF (gamma=1.2)
``<dolbyvision=yes|no>``
Whether or not to include Dolby Vision metadata (default: yes). If
disabled, any Dolby Vision metadata will be stripped from frames.
``<film-grain=yes|no>``
Whether or not to include film grain metadata (default: yes). If
disabled, any film grain metadata will be stripped from frames.
``<stereo-in>``
Set the stereo mode the video is assumed to be encoded in. Use
``--vf=format:stereo-in=help`` to list all available modes. Check with
``--vf format:stereo-in=help`` to list all available modes. Check with
the ``stereo3d`` filter documentation to see what the names mean.
``<stereo-out>``
@@ -405,7 +389,7 @@ Available mpv-only filters are:
option gives the flags which should be passed to libswscale. This
option is numeric and takes a bit-wise combination of ``SWS_`` flags.
See ``https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libswscale/swscale.h``.
See ``http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libswscale/swscale.h``.
``<o>``
Set AVFilterGraph options. These should be documented by FFmpeg.
@@ -473,7 +457,7 @@ Available mpv-only filters are:
::
import vapoursynth as vs
from vapoursynth import core
core = vs.get_core()
core.std.AddBorders(video_in, 10, 10, 20, 20).set_output()
.. warning::
@@ -614,7 +598,7 @@ Available mpv-only filters are:
``reversal-bug=<yes|no>``
:no: Use the API as it was interpreted by older Mesa drivers. While
this interpretation was more obvious and intuitive, it was
this interpretation was more obvious and inuitive, it was
apparently wrong, and not shared by Intel driver developers.
:yes: Use Intel interpretation of surface forward and backwards
references (default). This is what Intel drivers and newer Mesa
@@ -754,7 +738,7 @@ Available mpv-only filters are:
which leads to lost frames.
``print=yes|no``
Print computed fingerprints to the terminal (default: no). This is
Print computed fingerprints the the terminal (default: no). This is
mostly for testing and such. Scripts should use ``vf-metadata`` to
read information from this filter instead.
+74 -161
View File
@@ -21,46 +21,6 @@ in the list.
Available video output drivers are:
``gpu``
General purpose, customizable, GPU-accelerated video output driver. It
supports extended scaling methods, dithering, color management, custom
shaders, HDR, and more.
See `GPU renderer options`_ for options specific to this VO.
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
``--gpu-context`` options.
Hardware decoding over OpenGL-interop is supported to some degree. Note
that in this mode, some corner case might not be gracefully handled, and
color space conversion and chroma upsampling is generally in the hand of
the hardware decoder APIs.
``gpu`` makes use of FBOs by default. Sometimes you can achieve better
quality or performance by changing the ``--fbo-format`` option to
``rgb16f``, ``rgb32f`` or ``rgb``. Known problems include Mesa/Intel not
accepting ``rgb16``, Mesa sometimes not being compiled with float texture
support, and some macOS setups being very slow with ``rgb16`` but fast
with ``rgb32f``. If you have problems, you can also try enabling the
``--gpu-dumb-mode=yes`` option.
``gpu-next``
Experimental video renderer based on ``libplacebo``. This supports almost
the same set of features as ``--vo=gpu``. See `GPU renderer options`_ for a
list.
Should generally be faster and higher quality, but some features may still
be missing or misbehave. Expect (and report!) bugs. See here for a list of
known differences and bugs:
https://github.com/mpv-player/mpv/wiki/GPU-Next-vs-GPU
``xv`` (X11 only)
Uses the XVideo extension to enable hardware-accelerated display. This is
the most compatible VO on X, but may be low-quality, and has issues with
@@ -118,9 +78,7 @@ Available video output drivers are:
``vdpau`` (X11 only)
Uses the VDPAU interface to display and optionally also decode video.
Hardware decoding is used with ``--hwdec=vdpau``. Note that there is
absolutely no reason to use this, other than compatibility. We strongly
recommend that you use ``--vo=gpu`` with ``--hwdec=nvdec`` instead.
Hardware decoding is used with ``--hwdec=vdpau``.
.. note::
@@ -270,6 +228,35 @@ Available video output drivers are:
``--vo-direct3d-exact-backbuffer``
Always resize the backbuffer to window size.
``gpu``
General purpose, customizable, GPU-accelerated video output driver. It
supports extended scaling methods, dithering, color management, custom
shaders, HDR, and more.
See `GPU renderer options`_ for options specific to this VO.
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
``--gpu-context`` options.
Hardware decoding over OpenGL-interop is supported to some degree. Note
that in this mode, some corner case might not be gracefully handled, and
color space conversion and chroma upsampling is generally in the hand of
the hardware decoder APIs.
``gpu`` makes use of FBOs by default. Sometimes you can achieve better
quality or performance by changing the ``--fbo-format`` option to
``rgb16f``, ``rgb32f`` or ``rgb``. Known problems include Mesa/Intel not
accepting ``rgb16``, Mesa sometimes not being compiled with float texture
support, and some OS X setups being very slow with ``rgb16`` but fast
with ``rgb32f``. If you have problems, you can also try enabling the
``--gpu-dumb-mode=yes`` option.
``sdl``
SDL 2.0+ Render video output driver, depending on system with or without
hardware acceleration. Should work on all platforms supported by SDL 2.0.
@@ -286,23 +273,13 @@ Available video output drivers are:
``--sdl-switch-mode``
Instruct SDL to switch the monitor video mode when going fullscreen.
``dmabuf-wayland``
Experimental Wayland output driver designed for use with either drm stateless
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.
``vaapi``
Intel VA API video output driver with support for hardware decoding. Note
that there is absolutely no reason to use this, other than compatibility.
This is low quality, and has issues with OSD. We strongly recommend that
you use ``--vo=gpu`` with ``--hwdec=vaapi`` instead.
This is low quality, and has issues with OSD.
.. note:: This driver is for compatibility with crappy systems. You can
use vaapi hardware decoding with ``--vo=gpu`` too.
The following global options are supported by this video output:
@@ -359,7 +336,7 @@ Available video output drivers are:
``tct``
Color Unicode art video output driver that works on a text console.
By default depends on support of true color by modern terminals to display
the images at full color range, but 256-colors output is also supported (see
the images at full color range, but 256-colors outout is also supported (see
below). On Windows it requires an ansi terminal such as mintty.
Since mpv 0.30.0, you may need to use ``--profile=sw-fast`` to get decent
@@ -386,54 +363,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.
@@ -477,37 +413,18 @@ 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
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>``
Selects the dither algorithm which libsixel should apply.
Can be one of the below list as per libsixel's documentation.
auto (Default)
Let libsixel choose the dithering method.
auto
Choose diffuse type automatically
none
Don't diffuse
atkinson
Diffuse with Bill Atkinson's method.
Diffuse with Bill Atkinson's method. (Default)
fs
Diffuse with Floyd-Steinberg method
jajuni
@@ -527,18 +444,20 @@ Available video output drivers are:
using ``no`` (at the time of writing) will slow down ``xterm``.
``--vo-sixel-reqcolors=<colors>`` (default: 256)
Has no effect with fixed palette. Set up libsixel to use required
number of colors for dynamic palette. This value depends on the
terminal emulator as well. Xterm supports 256 colors. Can set this to
a lower value for faster performance.
Set up libsixel to use required number of colors for dynamic palette.
This value depends on the terminal emulator as well. Xterm supports
256 colors. Can set this to a lower value for faster performance.
This option has no effect if fixed palette is used.
``--vo-sixel-threshold=<threshold>`` (default: -1)
Has no effect with fixed palette. Defines the threshold to change the
When using a dynamic palette, defines the threshold to change the
palette - as percentage of the number of colors, e.g. 20 will change
the palette when the number of colors changed by 20%. It's a simple
measure to reduce the number of palette changes, because it can be slow
in some terminals (``xterm``). The default (-1) will choose a palette
on every frame and will have better quality.
in some terminals (``xterm``), however, it seems that in ``mlterm`` it
causes image corruption. The default (-1) will change the palette
on every frame and will have better quality, and no corruption in
``mlterm``.
``image``
Output each frame into an image file in the current directory. Each file
@@ -577,7 +496,7 @@ Available video output drivers are:
Specify the directory to save the image files to (default: ``./``).
``libmpv``
For use with libmpv direct embedding. As a special case, on macOS it
For use with libmpv direct embedding. As a special case, on OS X it
is used like a normal VO within mpv (cocoa-cb). Otherwise useless in any
other contexts.
(See ``<mpv/render.h>``.)
@@ -631,13 +550,8 @@ Available video output drivers are:
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. 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 and any card number specified ``--drm-connector``.
connectors. When using multiple graphic cards, use the ``<gpu_number>``
argument to disambiguate.
(default: empty)
``--drm-mode=<preferred|highest|N|WxH[@R]>``
@@ -656,6 +570,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
@@ -664,11 +588,11 @@ Available video output drivers are:
based, and related to the CRTC.
(default: primary)
When using this option with the drmprime-overlay hwdec interop, only the
OSD is rendered to this plane.
When using this option with the drmprime-drm hwdec interop, only the OSD
is rendered to this plane.
``--drm-drmprime-video-plane=<primary|overlay|N>``
Select the DRM plane to use for video with the drmprime-overlay hwdec
Select the DRM plane to use for video with the drmprime-drm hwdec
interop (used by e.g. the rkmpp hwdec on RockChip SoCs, and v4l2 hwdec:s
on various other SoC:s). The plane is unused otherwise. This option
accepts the same values as ``--drm-draw-plane``. (default: overlay)
@@ -694,28 +618,17 @@ Available video output drivers are:
``--drm-draw-surface-size=<[WxH]>``
Sets the size of the surface used on the draw plane. The surface will
then be upscaled to the current screen resolution. This option can be
useful when used together with the drmprime-overlay hwdec interop at
high resolutions, as it allows scaling the draw plane (which in this
case only handles the OSD) down to a size the GPU can handle.
useful when used together with the drmprime-drm hwdec interop at high
resolutions, as it allows scaling the draw plane (which in this case
only handles the OSD) down to a size the GPU can handle.
When used without the drmprime-overlay hwdec interop this option will
just cause the video to get rendered at a different resolution and then
When used without the drmprime-drm hwdec interop this option will 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>``
Toggle use of Variable Refresh Rate (VRR), aka Freesync or Adaptive Sync
on compatible systems. VRR allows for the display to be refreshed at any
rate within a range (usually ~40Hz-60Hz for 60Hz displays). This can help
with playback of 24/25/50fps content. Support depends on the use of a
compatible monitor, GPU, and a sufficiently new kernel with drivers
that support the feature.
:no: Do not attempt to enable VRR. (default)
:yes: Attempt to enable VRR, whether the capability is reported or not.
:auto: Attempt to enable VRR if support is reported.
``mediacodec_embed`` (Android)
Renders ``IMGFMT_MEDIACODEC`` frames directly to an ``android.view.Surface``.
Requires ``--hwdec=mediacodec`` for hardware decoding, along with
@@ -725,8 +638,8 @@ Available video output drivers are:
many of mpv's features (subtitle rendering, OSD/OSC, video filters, etc)
are not available with this driver.
To use hardware decoding with ``--vo=gpu`` instead, use ``--hwdec=mediacodec``
or ``mediacodec-copy`` along with ``--gpu-context=android``.
To use hardware decoding with ``--vo=gpu`` instead, use
``--hwdec=mediacodec-copy`` along with ``--gpu-context=android``.
``wlshm`` (Wayland only)
Shared memory video output driver without hardware acceleration that works
+1 -1
View File
@@ -52,7 +52,7 @@ Input
* Classic LIRC support was removed. Install remotes as input devices instead.
This way they will send X11 key events to the mpv window, which can be bound
using the normal ``input.conf``.
Also see: https://github.com/mpv-player/mpv/wiki/IR-remotes
Also see: http://github.com/mpv-player/mpv/wiki/IR-remotes
* Joystick support was removed. It was considered useless and was the cause
of some problems (e.g. a laptop's accelerator being recognized as joystick).
* Support for relative seeking by percentage.
+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
+2 -2
View File
@@ -377,7 +377,7 @@ without causing conflicts with other library users in the same process. To any
piece of code, a "safe" library's API can simply be used, without having to
worry about other API users that may be around somewhere.
Libraries are often not library safe, because they use global mutable state
Libraries are often not library safe, because they they use global mutable state
or other "global" resources. Typical examples include use of signals, simple
global variables (like hsearch() in libc), or internal caches not protected by
locks.
@@ -481,7 +481,7 @@ VOs, AOs, demuxers, and more. The frontend usually calls "down" the usage
hierarchy: mpctx almost on top, then things like vo/ao, and utility code on the
very bottom.
"Callback hell" is when components call both up and down the hierarchy,
"Callback hell" is when when components call both up and down the hierarchy,
which for example leads to accidentally recursion, reentrancy problems, or
locking nightmares. This is avoided by (mostly) calling only down the hierarchy.
Basically the call graph forms a DAG. The other direction is handled by event
+1 -1
View File
@@ -46,7 +46,7 @@ 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/>`_.
the `Waf book <http://docs.waf.googlecode.com/git/book_17/single.html>`_.
All of the configuration process is handled with a declarative approach. Lists
of dictionaries define the checks, and some custom Python code traverses these
+13 -12
View File
@@ -1,8 +1,8 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
@@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
@@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
@@ -303,9 +303,10 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
@@ -335,5 +336,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
+9 -9
View File
@@ -55,7 +55,7 @@ modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
@@ -111,7 +111,7 @@ modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -158,7 +158,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@@ -216,7 +216,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@@ -267,7 +267,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@@ -329,7 +329,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@@ -370,7 +370,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@@ -422,7 +422,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@@ -456,7 +456,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
+16 -36
View File
@@ -21,7 +21,7 @@
* [Wiki](https://github.com/mpv-player/mpv/wiki)
* [FAQ][FAQ]
* [Manual](https://mpv.io/manual/master/)
* [Manual](http://mpv.io/manual/master/)
## Overview
@@ -53,7 +53,7 @@ Releases can be found on the [release list][releases].
For semi-official builds and third-party packages please see
[mpv.io/installation](https://mpv.io/installation/).
[mpv.io/installation](http://mpv.io/installation/).
## Changelog
@@ -73,30 +73,11 @@ Changes to the default key bindings are indicated in
Compiling with full features requires development files for several
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].
external libraries. Below is a list of some important requirements.
**Note**: Building with waf is considered *deprecated* and will be removed in the
future.
### 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
look at the `meson_options.txt` file. Logs are stored in `meson-logs` within
your build directory.
Example:
meson setup build
meson compile -C build
meson install -C build
### Waf
The mpv build system uses [waf](https://waf.io/), but we don't store it in the
repository. The `./bootstrap.py` script will download the latest version
of waf that was tested with the build system.
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
@@ -104,10 +85,10 @@ 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`.
one of the two switches for each option. If the option is autodetected 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
@@ -140,9 +121,9 @@ Libass dependencies (when building libass):
- gcc or clang, yasm on x86 and x86_64
- fribidi, freetype, fontconfig development headers (for libass)
- harfbuzz (required for correct rendering of combining characters, particularly
for correct rendering of non-English text on OSX, and Arabic/Indic scripts on
any platform)
- harfbuzz (optional, required for correct rendering of combining characters,
particularly for correct rendering of non-English text on OSX, and
Arabic/Indic scripts on any platform)
FFmpeg dependencies (when building FFmpeg):
@@ -200,7 +181,7 @@ changes come and talk to us on IRC before you start working on them. It will
make code review easier for both parties later on.
You can check [the wiki](https://github.com/mpv-player/mpv/wiki/Stuff-to-do)
or the [issue tracker](https://github.com/mpv-player/mpv/issues?q=is%3Aopen+is%3Aissue+label%3Ameta%3Afeature-request)
or the [issue tracker](https://github.com/mpv-player/mpv/issues?q=is%3Aopen+is%3Aissue+label%3A%22feature+request%22)
for ideas on what you could contribute with.
## License
@@ -220,8 +201,8 @@ see the [FAQ][FAQ].
Most activity happens on the IRC channel and the github issue tracker.
- **GitHub issue tracker**: [issue tracker][issue-tracker] (report bugs here)
- **User IRC Channel**: `#mpv` on `irc.libera.chat`
- **Developer IRC Channel**: `#mpv-devel` on `irc.libera.chat`
- **User IRC Channel**: `#mpv` on `irc.freenode.net`
- **Developer IRC Channel**: `#mpv-devel` on `irc.freenode.net`
[FAQ]: https://github.com/mpv-player/mpv/wiki/FAQ
[releases]: https://github.com/mpv-player/mpv/releases
@@ -233,4 +214,3 @@ 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
+116 -165
View File
@@ -1,57 +1,78 @@
Release 0.36.0
Release 0.33.1
==============
This release requires FFmpeg 4.4 or newer.
Security fix release for the 0.30 branch. Users are advised to upgrade immediately.
This is the last release to contain the waf build system.
Meson is the primary and actively supported build system while waf is considered deprecated.
Fixes and Minor Enhancements
----------------------------
- demux_mf: improve format string processing
Release 0.33.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`.
Features
--------
New
~~~
Added
~~~~~
- player: add window-id property
- vo_kitty: introduce modern sixel alternative
- wayland: add wp-fractional-scale-v1 support
- player/command: add `user-data` property (replacement for `shared-script-properties`)
- vo_gpu_next: support mapping HDR10+ dynamic metadata
- vo_gpu_next: allow screenshots in native colorspace
- vo_gpu_next: parse Dolby Vision metadata for dynamic scene brightness
- video: allow csp equalizer adjustments with floating point precision
- command: add platform property
- demux_mkv: support ARIB captions
- hwdec_vulkan: add support for Vulkan-based hardware decoding
- player: better handling of forced-only subtitles and a toggle in the OSC
- vo_dmabuf_wayland: add osd support
- scripting: load scripts from directories
- mac: activate logging when started from the bundle
- ytdl_hook.lua: delay load subtitles
- sub: add an option to filter subtitles by regex
- scripting: add a way to run subprocesses as "scripts"
- command: implement asynchronous commands and support for named arguments
- player: add optional separate video decoding thread
- vo_gpu: d3d11: add support for exclusive fullscreen
- w32_common: Support HiDPI on Windows
- vo_x11: add 10 bit support
- vo_gpu: add BT.2390 tone-mapping
- client API: add software rendering API
- audio: add scaletempo2 filter based on chromium
- auto_profiles: add this script
- stream: Implement slice:// for reading slices of streams
- player: add automatic loading of external cover art files
- vo_sixel: implement terminal video output using sixel
Changed
~~~~~~~
- TOOLS/umpv: prefer $XDG_RUNTIME_DIR
- ffmpeg: increase minimum required version to 4.4
- player: choose speed of smallest acceptable factor for display sync
- player: don't force saving `start` in watch-later-options
- build: officially deprecate waf
- vf_sub: undeprecate
- player: set playlist title to media title if not set already
- player: use and respect XDG_STATE_HOME and XDG_CACHE_HOME by default
- wayland: bump required version to 1.20 and wayland-protocols to 1.25
- ao_pipewire: require at least libpipewire 0.3.48
- vo: hwdec: prioritise drmprime over drmprime_overlay
- vo_gpu/vo_gpu_next: enable gpu shader and icc cache by default
- sws_utils: use zimg by default if available
- build: pick up Lua 5.2 by default (preferred over 5.1)
- build: disable RPI vendor blob auto-detection in favor of open-source stack
- build: make C11 atomics mandatory
- build: make libass non-optional
- player: stricter filename matching for external subtitle auto-loading
- lua: support Unicode paths in script loading and IO library
- vo_direct3d: rip out texture video rendering path and dumb down OSD rendering
- audio: rewrite internal audio handling and AO API
- build: disable GLX by default
Removed
~~~~~~~
- drm: remove support for legacy (non-atomic) API
- stream/dvb: drop support for DVB API before Linux 3.7
- hwdec_cuda: drop support for PL_HANDLE_WIN32_KMT (Windows 7 only)
- vo_dmabuf_wayland: drop linux-dmabuf-v2 support
- stream_libarchive: disable tar support due to bugs
- Remove remains of Libav compatibility
- stream_smb: remove due to lack of thread safety and the abundance
of alternatives, FFmpeg still includes SMB support
- command: remove legacy hook API (has been deprecated for a long time)
- client API: remove deprecated qthelper.hpp header
- removed audio outputs: sndio, rsound, oss
- x11: remove xdg-screensaver invocations that supported dbus based idle inhibit
- client API: deactivate the opengl_cb API
- build system: drop Python 2 compatbility
Options and Commands
@@ -60,160 +81,90 @@ Options and Commands
Added
~~~~~
- player: add --force-render option
- demux: add --demuxer-hysteresis-secs option to save power with caching
- ao_pipewire: allow usage of global volume control via --pipewire-volume-mode
- vo_gpu_next: add --tone-mapping-visualize
- sub: add --sub-fonts-dir and --osd-fonts-dir options
- player: add --auto-window-resize
- console.lua: add a script-opt for the border size
- player: add --drag-and-drop option
- vo_gpu_next: add --corner-rounding option
- player: add more precise sub fallback options --subs-fallback and --subs-fallback-forced
- player: add --input-cursor-passthrough option
- TOOLS/lua/autoload: allow extending ext sets from script-opts
- demux_playlist: add --directory-mode option to control recursive directory loading
- vo_gpu_next: add --target-contrast and --hdr-contrast-recovery/smoothness
- demux: add option to disable cache "sharing" between back and forward buffers
- player: add ab-loop-count option/property
- ytdl_hook: add a way to use ytdl's default formats
- ytdl_hook: add all_formats option that loads all formats that were found
- demux_mkv: document probe-start-time option and enable it by default
- command: extend osd-overlay command with bounds reporting
- player: a number of new playlist contol commands/properties
- ipc: add --input-ipc-client option
- options: add option to control display-sync factor
- vo_gpu: add better gamut clipping option
- vo_gpu: vulkan: add ability to disable events
- x11: add option to make window appear on a specific workspace
- wayland: expose wayland-app-id as a user option
- player: add --subs-with-matching-audio option
- command: add read-only focused property
- screenshot: option to use software rendering for screenshots
- command: add delete-watch-later-config
- command: new property mouse-pos with current position and hover state
Changed
~~~~~~~
- vd_lavc: add "auto" choice for --vd-lavc-dr and make it the default
(notably this change was also backported to v0.35.1)
- wayland: add auto choice to --wayland-configure-bounds
- image_writer: change --screenshot-tag-colorspace default to yes
- vo_gpu_next: expose --tone-mapping=st2094-40 and st2094-10
- wayland: make --wayland-edge-pixels-pointer default to 16
- options: enable scripts related opts also with cplugins
- player/screenshot: add filename return field
- options: set --subs-with-matching-audio to off by default
- options: default --slang to auto, which uses the settings of the user's OS
- vd_lavc: allow user to specify a priority list in --hwdec
- vd_lavc: map hwdec=yes to hwdec=auto-safe and change ctrl+h to use auto-safe
- options: remove deprecation warning for "-foo bar" syntax
- player: make audio hr-seek default
- ad_lavc: disable decoder downmix by default
- command: support save-position-on-quit for "stop" command too
- command: extend subprocess command
- options: do not accept ":" as separator anymore in key/value lists
Deprecated
~~~~~~~~~~
- drm: deprecate `--drm-atomic` (now always enabled)
- demux: deprecate --cache-secs
Removed
~~~~~~~
- vo_gpu_next: remove --tone-mapping-crosstalk
- wayland: remove wayland-frame-wait-offset option
- input: remove deprecated --input-file option
- vo_vdpau: remove deprecated/inactive --vo-vdpau-deint option
Fixes and Minor Enhancements
----------------------------
- ao_pipewire: log version and other useful debug info
- wayland: error out if essential protocol support is missing
- wayland: add support for content-type protocol
- 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
- hwdec_drmprime: support yuv420p format
- ao_pipewire: properly clean up resources
- vo/{sixel,tct}: use the alternate buffer to restore terminal
- vo_sixel: add option to skip clear while drawing
- filter_kernels: fix kaiser
- hwdec/d3d11va: fix a possible memory leak
- external_files: set log level for potential files to trace
- external_files: recognize webp files as cover art
- 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
- demux: boost read EBU R128 gain values to ReplayGain's reference level
- version.py: bump copyright year
- TOOLS/umpv: support shell-quotes in $MPV
- vulkan: fix build error for 32bit builds with clang
- TOOLS/lua/autoload: improve and optimize the natural sorting
- vo_wlshm: properly support video panscan
- vo_dmabuf_wayland: support panscan and panning
- ra_d3d11: fix incorrect type
- meson: also search for rst2html with .py extension
- audio: fix crash during uninit with ao_lavc
- wayland: only warn about GNOME when actually missing idle inhibit support
- mp_image: fix XYZ primaries default
- msg: preserve early messages when log-file is set in mpv.conf
- x11: fix issue with xpresent timing feedback
- meson: reuse libmpv objects for cplayer to save 50% of compile steps
- player/client: support observing sub-properties
- m_property: implement new deletion operation on properties
- vo_lavc: set frame rate on encoder to fix non-conforming outputs
- player/video: don't resync audio if video is an image
- stream: accept webdav:// and webdavs:// urls
- video/image_writer: avoid stripping colorspace info when writing image
- stats.lua: display HDR peak in nits
- image_pool: fix memory leak with frames
- options: transition from OPT_FLAG to OPT_BOOL
- vd_lavc: sort hwdecs without hwdevices last for autoprobing
- ytdl_hook: don't overwrite force-media-title
- lua: use user-data for interop between osc.lua and console.lua
- vo: fix race condition with redraw requests
- various: more fixes to ensure correct playback of XYZ colorspace
- ytdl_hook: fix fragment-related issue that broke YouTube DASH playback
- command: expose hls-bitrate and program-id as track-list subproperties
- ao_pipewire: use realtime scheduling for data thread
- auto_profiles: check for non-existent properties
- d3d11: retry device creation without debug, if SDK is not available
- win32: follow Windows settings and update dark mode state
- ytdl_hook.lua: fix clip start and end
- hwdec_drmprime: support custom rpi4_8 and rpi4_10 formats
- sub: fix UPDATE_SUB_HARD for converted and external subtitles
- charset_conv: fix memory corruption in mp_iconv_to_utf8
- options: read config file as stream
- wayland: improve guessing when mpv is focused
- player: always try to detect subtitle language from file name
- vo: fix mp_frame_perf thread safety
- lua: read_options: find script-opts prefix at index 1 exactly
- path: handle URLs consistently in mp_basename
- ao_wasapi: remove infinite loop hack in AOCONTROL_UPDATE_STREAM_TITLE
- ao_wasapi: use client name instead of hardcoded string
- vd_lavc: fix crash if hwdec devices fail to create
- vo_drm: fix null dereference and using closed fd
- image_writer: respect jpeg-quality when using ffmpeg for writing
- image_writer: add support for AVIF
- TOOLS/lua/autoload: avoid unnecessary playlist manipulation, performance
- various: correctly ignore cache files with --no-config
- terminal-unix: better error detection logic
- json: raise parse depth to 50 for the sake of utils.parse_json()
- player: delete watch_later file only after successful load
- wayland: fix memory leak with multiple monitors
- vd_lavc: prefer d3d11va-copy over dxva2-copy
- vd_lavc: fix hwdec for videos with less than less than 3 frames
- wayland: add support for suspended toplevel state
- draw_bmp: ensure last slice is less than total width (again)
- console.lua: sort the output from the help command
- vd_lavc: try other hwdecs when falling back after an hwdec failure
- sd_ass: don't reconfigure ass on every frame (performance regression)
- sd_ass: fix converted subtitles having too-wide borders
- loadfile: compute audio language for sub selection when using lavfi-complex
- wayland: fix modifier keys in certain situations
- vd_lavc: corrections towards hwdec and swdec fallback
- zimg: fix broken sig_peak (HDR) handling
- screenshot: implement `screenshot window` in sw for most VOs
- osc: don't add margins to osc-deadzonesize
- vo_vaapi: fix segfault in draw_osd
- vo_dmabuf_wayland: correctly handle force-window
- ao_pipewire: fix error with pipewire 0.3.75 or later
- options: stop hiding deprecated options from --help output
- lua, js: add mp.get_script_directory() function
- lua: fix security relevant loading order issue with scripts
- player: make screenshot each-frame mode more accurate
- bash completion: complete ao/af/vo/vf options
- zimg: add alpha support
- wayland: make resizing better
- edl: add mechanism for delay loading streams
- stream_file: fix caching-related performance regression on CIFS
- cocoa-cb: fix crashes and issues with UI updates
- sub, demux: improve behavior with negative subtitle delay/muxed subs
- demux: make seek ranges work for static images + audio
- cocoa-cb: support maximize/minimize on startup
- umpv: change from legacy FIFO to socket
- stats: move input speed to cache page, make it a graph
- stats: add fourth page with performance graphs
- command: print edition title to OSD when cycling
- zimg: add support for big endian input and output
- demux_mkv: add png intra support
- build: detect VT_GETMODE on FreeBSD and DragonFly
- win32: use windows 10 native virtual-terminal if available
- vo_gpu: enable frame caching for still frames
- command: add property to return text subtitles in ASS
- vo_gpu: ra_pl: add timers support
- build: allow vo_wlshm on more Wayland platforms (e.g. FreeBSD)
- zimg: add slice threading and use it by default
- command: add a way to access properties of a current track
- vo_gpu: EGL: fix transparency on X11/EGL/Mesa
- vd_lavc: add AV1 to the default allowed hwdec codec list
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.35.0..v0.36.0`
A complete changelog can be seen by running `git log v0.32.0..v0.33.1`
in the git repository or by visiting either
https://github.com/mpv-player/mpv/compare/v0.35.0...v0.36.0 or
https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.35.0..v0.36.0
https://github.com/mpv-player/mpv/compare/v0.32.0...v0.33.1 or
https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.32.0..v0.33.1
+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
+72
View File
@@ -0,0 +1,72 @@
#!/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-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
)
# Compile shaderc
(
git clone --depth=1 https://github.com/google/shaderc && cd shaderc
"$PYTHON" utils/git-sync-deps
mkdir build && cd build
cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DSHADERC_SKIP_TESTS=ON \
-DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX ..
ninja install
)
# Compile SPIRV-Cross
(
git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Cross && cd SPIRV-Cross
mkdir build && cd build
cmake -GNinja -DSPIRV_CROSS_SHARED=ON -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX ..
ninja install
)
-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)
+2 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python
import re
import os
@@ -69,7 +69,7 @@ def lib_name(lib):
def process_libraries(libs_dict, binary):
libs_set = set(libs_dict)
# Remove binary from libs_set to prevent a duplicate of the binary being
# Remove binary from libs_set to prevent a duplicate of the binary being
# added to the libs directory.
libs_set.remove(binary)
+1 -6
View File
@@ -39,10 +39,5 @@ def file2string(infilename, infile, outfile):
outfile.write('"' + ''.join(conv[c] for c in line) + '"\n')
if __name__ == "__main__":
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)
file2string(sys.argv[1], infile, sys.stdout)
+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 } \
+1 -1
View File
@@ -2,7 +2,7 @@
-- filter including key bindings for adjusting parameters.
--
-- See https://ffmpeg.org/ffmpeg-filters.html#acompressor for explanation
-- of the parameters.
-- of the parameteres.
local mp = require 'mp'
local options = require 'mp.options'
+24 -25
View File
@@ -56,9 +56,6 @@ detect_min_ratio: number[0.0-1.0] - The ratio of the minimum clip
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"
@@ -70,8 +67,7 @@ local options = {
detect_limit = "24/255",
detect_round = 2,
detect_min_ratio = 0.5,
detect_seconds = 1,
suppress_osd = false,
detect_seconds = 1
}
read_options(options)
@@ -86,8 +82,6 @@ timers = {
detect_crop = nil
}
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
@@ -107,23 +101,18 @@ 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
function is_cropable()
local vid = mp.get_property_native("vid")
local is_album = vid and mp.get_property_native(
string.format("track-list/%d/albumart", vid)
) or false
if not is_enough_time(time_needed) then
mp.msg.warn("Not enough time to detect crop.")
return false
end
return true
return vid and not is_album
end
function remove_filter(label)
if is_filter_present(label) then
mp.command(string.format('%s vf remove @%s', command_prefix, label))
mp.command(string.format('vf remove @%s', label))
return true
end
return false
@@ -146,9 +135,18 @@ function cleanup()
end
function detect_crop()
-- If it's not cropable, exit.
if not is_cropable() then
mp.msg.warn("autocrop only works for videos.")
return
end
-- Verify if there is enough time to detect crop.
local time_needed = options.detect_seconds
if not is_cropable(time_needed) then
if not is_enough_time(time_needed) then
mp.msg.warn("Not enough time to detect crop.")
return
end
@@ -158,8 +156,8 @@ function detect_crop()
mp.command(
string.format(
'%s vf pre @%s:cropdetect=limit=%s:round=%d:reset=0',
command_prefix, labels.cropdetect, limit, round
'vf pre @%s:cropdetect=limit=%s:round=%d:reset=0',
labels.cropdetect, limit, round
)
)
@@ -250,8 +248,8 @@ function apply_crop(meta)
-- Apply crop.
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
string.format("vf pre @%s:lavfi-crop=w=%s:h=%s:x=%s:y=%s",
labels.crop, meta.w, meta.h, meta.x, meta.y
)
)
end
@@ -277,7 +275,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
+54 -80
View File
@@ -16,10 +16,6 @@ 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
--]]
@@ -33,11 +29,7 @@ o = {
disabled = false,
images = true,
videos = true,
audio = true,
additional_image_exts = "",
additional_video_exts = "",
additional_audio_exts = "",
ignore_hidden = true
audio = true
}
options.read_options(o)
@@ -54,41 +46,29 @@ function SetUnion (a,b)
return res
end
function Split (s)
local set = {}
for v in string.gmatch(s, '([^,]+)') do set[v] = true end
return set
end
EXTENSIONS_VIDEO = Set {
'3g2', '3gp', 'avi', 'flv', 'm2ts', 'm4v', 'mj2', 'mkv', 'mov',
'mp4', 'mpeg', 'mpg', 'ogv', 'rmvb', 'webm', 'wmv', 'y4m'
'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp'
}
EXTENSIONS_AUDIO = Set {
'aiff', 'ape', 'au', 'flac', 'm4a', 'mka', 'mp3', 'oga', 'ogg',
'ogm', 'opus', 'wav', 'wma'
'mp3', 'wav', 'ogm', 'flac', 'm4a', 'wma', 'ogg', 'opus'
}
EXTENSIONS_IMAGES = Set {
'avif', 'bmp', 'gif', 'j2k', 'jp2', 'jpeg', 'jpg', 'jxl', 'png',
'svg', 'tga', 'tif', 'tiff', 'webp'
'jpg', 'jpeg', 'png', 'tif', 'tiff', 'gif', 'webp', 'svg', 'bmp'
}
EXTENSIONS_VIDEO = SetUnion(EXTENSIONS_VIDEO, Split(o.additional_video_exts))
EXTENSIONS_AUDIO = SetUnion(EXTENSIONS_AUDIO, Split(o.additional_audio_exts))
EXTENSIONS_IMAGES = SetUnion(EXTENSIONS_IMAGES, Split(o.additional_image_exts))
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 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
@@ -109,37 +89,39 @@ table.filter = function(t, iter)
end
end
-- alphanum sorting for humans in Lua
-- http://notebook.kulchenko.com/algorithms/alphanumeric-natural-sorting-for-humans-in-lua
-- splitbynum and alnumcomp from alphanum.lua (C) Andre Bogus
-- Released under the MIT License
-- http://www.davekoelle.com/files/alphanum.lua
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)
-- 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
local tuples = {}
for i, f in ipairs(filenames) do
tuples[i] = {f:lower():gsub("0*(%d+)%.?(%d*)", padnum), f}
function clean_key(k)
k = (' '..k..' '):gsub("%s+", " "):sub(2, -2):lower()
return splitbynum(k)
end
-- 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
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
function get_playlist_filenames(playlist)
local filenames = {}
for i = 1, #playlist do
local _, file = utils.split_path(playlist[i].filename)
filenames[file] = true
end
return filenames
end
function find_and_add_entries()
local path = mp.get_property("path", "")
local dir, filename = utils.split_path(path)
@@ -152,7 +134,7 @@ function find_and_add_entries()
return
end
pl_count = mp.get_property_number("playlist-count", 1)
local 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(get_extension(filename))] == nil) then
@@ -173,9 +155,7 @@ function find_and_add_entries()
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
if string.match(v, "^%.") then
return false
end
local ext = get_extension(v)
@@ -184,7 +164,7 @@ function find_and_add_entries()
end
return EXTENSIONS[string.lower(ext)]
end)
alphanumsort(files)
table.sort(files, alnumcomp)
if dir == "." then
dir = ""
@@ -204,43 +184,37 @@ function find_and_add_entries()
msg.trace("current file position in files: "..current)
local append = {[-1] = {}, [1] = {}}
local filenames = get_playlist_filenames(pl)
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]
local pl_e = pl[pl_current + i * direction]
if file == nil or file[1] == "." then
break
end
local filepath = dir .. file
-- skip files already in playlist
if not filenames[file] then
if direction == -1 then
msg.info("Prepending " .. file)
table.insert(append[-1], 1, {filepath, pos - 1})
else
msg.info("Adding " .. file)
if pl_count > 1 then
table.insert(append[1], {filepath, pos - 1})
else
mp.commandv("loadfile", filepath, "append")
end
if pl_e then
-- If there's a playlist entry, and it's the same file, stop.
msg.trace(pl_e.filename.." == "..filepath.." ?")
if pl_e.filename == filepath then
break
end
end
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")
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
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)
-68
View File
@@ -1,68 +0,0 @@
#!/usr/bin/env python3
# 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 sys
from distutils.version import StrictVersion
from shutil import which
from subprocess import check_output
def find_macos_sdk():
sdk = os.environ.get('MACOS_SDK', '')
sdk_version = os.environ.get('MACOS_SDK_VERSION', '0.0')
xcrun = which('xcrun')
if not xcrun:
return sdk,sdk_version
if not sdk:
sdk = check_output([xcrun, '--sdk', 'macosx', '--show-sdk-path'],
encoding="UTF-8")
# 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")
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'
# 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()
sys.stdout.write(','.join(sdk_info))
-42
View File
@@ -1,42 +0,0 @@
#!/usr/bin/env python3
# Finds the macos swift library directory and prints the full path to stdout.
# First argument is the path to the swift executable.
import os
import sys
from shutil import which
from subprocess import check_output
def find_swift_lib():
swift_lib_dir = os.environ.get('SWIFT_LIB_DYNAMIC', '')
if swift_lib_dir:
return swift_lib_dir
# first check for lib dir relative to swift executable
xcode_dir = os.path.dirname(os.path.dirname(sys.argv[1]))
swift_lib_dir = os.path.join(xcode_dir, "lib", "swift", "macosx")
if os.path.isdir(swift_lib_dir):
return swift_lib_dir
# fallback to xcode-select path
xcode_select = which("xcode-select")
if not xcode_select:
sys.exit(1)
xcode_path = check_output([xcode_select, "-p"], encoding="UTF-8")
swift_lib_dir = os.path.join(xcode_path, "Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx")
if os.path.isdir(swift_lib_dir):
return swift_lib_dir
# last resort if we still haven't found a path
swift_lib_dir = os.path.join(xcode_path, "usr/lib/swift/macosx")
if not os.path.isdir(swift_lib_dir):
sys.exit(1)
return swift_lib_dir
if __name__ == "__main__":
swift_lib_dir = find_swift_lib()
sys.stdout.write(swift_lib_dir)
+8 -9
View File
@@ -21,6 +21,8 @@ Can also be used to directly parse Matroska files and display their contents.
# License along with mpv. If not, see <http://www.gnu.org/licenses/>.
#
# for compatibility with Python 2.x
from __future__ import print_function
elements_ebml = (
'EBML, 1a45dfa3, sub', (
@@ -298,8 +300,8 @@ parse_elems(elements_ebml, 'EBML')
parse_elems(elements_matroska, 'MATROSKA')
def printf(out, *args):
out.write(' '.join(str(x) for x in args))
out.write('\n')
out.write(u' '.join([str(x) for x in args]))
out.write(u'\n')
def generate_C_header(out):
printf(out, '// Generated by TOOLS/matroska.py, do not edit manually')
@@ -459,16 +461,13 @@ 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(outfile)
generate_C_header(sys.stdout)
elif sys.argv[1] == '--generate-definitions':
generate_C_definitions(outfile)
generate_C_definitions(sys.stdout)
else:
if sys.version_info.major < 3:
raise Exception("Dumping requires Python 3.")
s = open(sys.argv[1], "rb")
while 1:
start = s.tell()
+2 -1
View File
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
#!/usr/bin/env python
from __future__ import print_function
import os
import shutil
import sys
@@ -188,11 +188,6 @@
<string>${VERSION}</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSEnvironment</key>
<dict>
<key>MallocNanoZone</key>
<string>0</string>
</dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
@@ -223,7 +218,6 @@
<string>udp</string>
<string>smb</string>
<string>srt</string>
<string>rist</string>
</array>
</dict>
<dict>
+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 [ "x$TARGET" != "xx86_64-w64-mingw32" ]; 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
+5 -4
View File
@@ -31,8 +31,9 @@ import os
import socket
import errno
import subprocess
import fcntl
import stat
import string
import shlex
files = sys.argv[1:]
@@ -51,9 +52,9 @@ def make_abs(filename):
if not is_url(filename):
return os.path.abspath(filename)
return filename
files = (make_abs(f) for f in files)
files = [make_abs(f) for f in files]
SOCK = os.path.join(os.getenv("XDG_RUNTIME_DIR", os.getenv("HOME")), ".umpv_socket")
SOCK = os.path.join(os.getenv("HOME"), ".umpv_socket")
sock = None
try:
@@ -79,7 +80,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.36.0
0.33.1
+46
View File
@@ -0,0 +1,46 @@
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:
# Work around age/brokenness of Appveyor's msys2 by adding the new
# maintainer's keyring.
- bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
- bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- bash -lc "pacman -U --noconfirm msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
# Support for Ada and Objective-C was removed from MSYS2. GCC won't update if
# these packages are installed.
- >-
C:\msys64\usr\bin\pacman -R --noconfirm --noprogressbar
mingw-w64-i686-gcc-ada mingw-w64-i686-gcc-objc mingw-w64-x86_64-gcc-ada
mingw-w64-x86_64-gcc-objc
# 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 pkg-config
# 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
+8 -75
View File
@@ -18,12 +18,9 @@
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include "config.h"
#include "common/common.h"
#include "chmap.h"
#include "chmap_avchannel.h"
#include "fmt-conversion.h"
#include "format.h"
#include "aframe.h"
@@ -52,7 +49,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;
@@ -123,16 +121,6 @@ struct mp_aframe *mp_aframe_from_avframe(struct AVFrame *av_frame)
if (!av_frame || av_frame->width > 0 || av_frame->height > 0)
return NULL;
#if HAVE_AV_CHANNEL_LAYOUT
if (!av_channel_layout_check(&av_frame->ch_layout))
return NULL;
struct mp_chmap converted_map = { 0 };
if (!mp_chmap_from_av_layout(&converted_map, &av_frame->ch_layout)) {
return NULL;
}
#endif
int format = af_from_avformat(av_frame->format);
if (!format && av_frame->format != AV_SAMPLE_FMT_NONE)
return NULL;
@@ -144,15 +132,11 @@ struct mp_aframe *mp_aframe_from_avframe(struct AVFrame *av_frame)
abort();
frame->format = format;
#if !HAVE_AV_CHANNEL_LAYOUT
mp_chmap_from_lavc(&frame->chmap, frame->av_frame->channel_layout);
// FFmpeg being a stupid POS again
if (frame->chmap.num != frame->av_frame->channels)
mp_chmap_from_channels(&frame->chmap, av_frame->channels);
#else
frame->chmap = converted_map;
#endif
if (av_frame->opaque_ref) {
struct avframe_opaque *op = (void *)av_frame->opaque_ref->data;
@@ -220,16 +204,9 @@ void mp_aframe_config_copy(struct mp_aframe *dst, struct mp_aframe *src)
dst->av_frame->sample_rate = src->av_frame->sample_rate;
dst->av_frame->format = src->av_frame->format;
#if !HAVE_AV_CHANNEL_LAYOUT
dst->av_frame->channel_layout = src->av_frame->channel_layout;
// FFmpeg being a stupid POS again
dst->av_frame->channels = src->av_frame->channels;
#else
if (av_channel_layout_copy(&dst->av_frame->ch_layout,
&src->av_frame->ch_layout) < 0)
abort();
#endif
}
// Copy "soft" attributes from src to dst, excluding things which affect
@@ -338,21 +315,13 @@ bool mp_aframe_set_chmap(struct mp_aframe *frame, struct mp_chmap *in)
return false;
if (mp_aframe_is_allocated(frame) && in->num != frame->chmap.num)
return false;
#if !HAVE_AV_CHANNEL_LAYOUT
uint64_t lavc_layout = mp_chmap_to_lavc_unchecked(in);
if (!lavc_layout && in->num)
return false;
#endif
frame->chmap = *in;
#if !HAVE_AV_CHANNEL_LAYOUT
frame->av_frame->channel_layout = lavc_layout;
// FFmpeg being a stupid POS again
frame->av_frame->channels = frame->chmap.num;
#else
mp_chmap_to_av_layout(&frame->av_frame->ch_layout, in);
#endif
return true;
}
@@ -465,37 +434,6 @@ void mp_aframe_skip_samples(struct mp_aframe *f, int samples)
f->pts += samples / mp_aframe_get_effective_rate(f);
}
// sanitize a floating point sample value
#define sanitizef(f) do { \
if (!isnormal(f)) \
(f) = 0; \
} while (0)
void mp_aframe_sanitize_float(struct mp_aframe *mpa)
{
int format = af_fmt_from_planar(mp_aframe_get_format(mpa));
if (format != AF_FORMAT_FLOAT && format != AF_FORMAT_DOUBLE)
return;
int num_planes = mp_aframe_get_planes(mpa);
uint8_t **planes = mp_aframe_get_data_rw(mpa);
if (!planes)
return;
for (int p = 0; p < num_planes; p++) {
void *ptr = planes[p];
int total = mp_aframe_get_total_plane_samples(mpa);
switch (format) {
case AF_FORMAT_FLOAT:
for (int s = 0; s < total; s++)
sanitizef(((float *)ptr)[s]);
break;
case AF_FORMAT_DOUBLE:
for (int s = 0; s < total; s++)
sanitizef(((double *)ptr)[s]);
break;
}
}
}
// Return the timestamp of the sample just after the end of this frame.
double mp_aframe_end_pts(struct mp_aframe *f)
{
@@ -697,23 +635,18 @@ int mp_aframe_pool_allocate(struct mp_aframe_pool *pool, struct mp_aframe *frame
AVFrame *av_frame = frame->av_frame;
if (av_frame->extended_data != av_frame->data)
av_freep(&av_frame->extended_data); // sigh
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);
} else {
av_frame->extended_data = av_frame->data;
}
av_frame->extended_data =
av_mallocz_array(planes, sizeof(av_frame->extended_data[0]));
if (!av_frame->extended_data)
abort();
av_frame->buf[0] = av_buffer_pool_get(pool->avpool);
if (!av_frame->buf[0])
return -1;
av_frame->linesize[0] = samples * sstride;
for (int n = 0; n < planes; n++)
av_frame->extended_data[n] = av_frame->buf[0]->data + n * plane_size;
if (planes > AV_NUM_DATA_POINTERS) {
for (int n = 0; n < AV_NUM_DATA_POINTERS; n++)
av_frame->data[n] = av_frame->extended_data[n];
}
for (int n = 0; n < MPMIN(planes, AV_NUM_DATA_POINTERS); n++)
av_frame->data[n] = av_frame->extended_data[n];
av_frame->nb_samples = samples;
return 0;
-1
View File
@@ -60,7 +60,6 @@ char *mp_aframe_format_str_buf(char *buf, size_t buf_size, struct mp_aframe *fmt
#define mp_aframe_format_str(fmt) mp_aframe_format_str_buf((char[32]){0}, 32, (fmt))
void mp_aframe_skip_samples(struct mp_aframe *f, int samples);
void mp_aframe_sanitize_float(struct mp_aframe *f);
double mp_aframe_end_pts(struct mp_aframe *f);
double mp_aframe_duration(struct mp_aframe *f);
void mp_aframe_clip_timestamps(struct mp_aframe *f, double start, double end);
-17
View File
@@ -470,23 +470,6 @@ char *mp_chmap_to_str_hr_buf(char *buf, size_t buf_size, const struct mp_chmap *
return mp_chmap_to_str_buf(buf, buf_size, &map);
}
mp_ch_layout_tuple *mp_iterate_builtin_layouts(void **opaque)
{
uintptr_t i = (uintptr_t)*opaque;
if (i >= MP_ARRAY_SIZE(std_layout_names) ||
!std_layout_names[i][0])
return NULL;
*opaque = (void *)(i + 1);
if (std_layout_names[i][1][0] == '\0') {
return mp_iterate_builtin_layouts(opaque);
}
return &std_layout_names[i];
}
void mp_chmap_print_help(struct mp_log *log)
{
mp_info(log, "Speakers:\n");
-15
View File
@@ -75,8 +75,6 @@ struct mp_chmap {
uint8_t speaker[MP_NUM_CHANNELS];
};
typedef const char * const (mp_ch_layout_tuple)[2];
#define MP_SP(speaker) MP_SPEAKER_ID_ ## speaker
#define MP_CHMAP2(a, b) \
@@ -131,19 +129,6 @@ char *mp_chmap_to_str_hr_buf(char *buf, size_t buf_size, const struct mp_chmap *
bool mp_chmap_from_str(struct mp_chmap *dst, bstr src);
/**
* Iterate over all built-in channel layouts which have mapped channels.
*
* @param opaque a pointer where the iteration state is stored. Must point
* to nullptr to start the iteration.
*
* @return nullptr when the iteration is finished.
* Otherwise a pointer to an array of two char pointers.
* - [0] being the human-readable layout name.
* - [1] being the string representation of the layout.
*/
mp_ch_layout_tuple *mp_iterate_builtin_layouts(void **opaque);
struct mp_log;
void mp_chmap_print_help(struct mp_log *log);
-51
View File
@@ -1,51 +0,0 @@
/*
* 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/>.
*/
#include <libavutil/channel_layout.h>
#include "chmap.h"
#include "chmap_avchannel.h"
bool mp_chmap_from_av_layout(struct mp_chmap *dst, const AVChannelLayout *src)
{
*dst = (struct mp_chmap) {0};
switch (src->order) {
case AV_CHANNEL_ORDER_UNSPEC:
mp_chmap_from_channels(dst, src->nb_channels);
return dst->num == src->nb_channels;
case AV_CHANNEL_ORDER_NATIVE:
mp_chmap_from_lavc(dst, src->u.mask);
return dst->num == src->nb_channels;
default:
// TODO: handle custom layouts
return false;
}
}
void mp_chmap_to_av_layout(AVChannelLayout *dst, const struct mp_chmap *src)
{
*dst = (AVChannelLayout){
.order = AV_CHANNEL_ORDER_UNSPEC,
.nb_channels = src->num,
};
// TODO: handle custom layouts
if (!mp_chmap_is_unknown(src)) {
av_channel_layout_from_mask(dst, mp_chmap_to_lavc(src));
}
}
-32
View File
@@ -1,32 +0,0 @@
/*
* 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/>.
*/
#pragma once
#include <libavutil/channel_layout.h>
#include "config.h"
#include "chmap.h"
#if HAVE_AV_CHANNEL_LAYOUT
bool mp_chmap_from_av_layout(struct mp_chmap *dst, const AVChannelLayout *src);
void mp_chmap_to_av_layout(AVChannelLayout *dst, const struct mp_chmap *src);
#endif
+8 -30
View File
@@ -26,11 +26,8 @@
#include <libavutil/common.h>
#include <libavutil/intreadwrite.h>
#include "config.h"
#include "mpv_talloc.h"
#include "audio/aframe.h"
#include "audio/chmap_avchannel.h"
#include "audio/fmt-conversion.h"
#include "common/av_common.h"
#include "common/codecs.h"
@@ -46,7 +43,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 +56,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 +64,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 +72,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,
},
};
@@ -88,7 +85,7 @@ static bool init(struct mp_filter *da, struct mp_codec_params *codec,
struct ad_lavc_params *opts =
mp_get_config_group(ctx, da->global, &ad_lavc_conf);
AVCodecContext *lavc_context;
const AVCodec *lavc_codec;
AVCodec *lavc_codec;
ctx->codec_timebase = mp_get_codec_timebase(codec);
@@ -104,29 +101,13 @@ 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;
if (opts->downmix && mpopts->audio_output_channels.num_chmaps == 1) {
const struct mp_chmap *requested_layout =
&mpopts->audio_output_channels.chmaps[0];
#if !HAVE_AV_CHANNEL_LAYOUT
lavc_context->request_channel_layout =
mp_chmap_to_lavc(requested_layout);
#else
AVChannelLayout av_layout = { 0 };
mp_chmap_to_av_layout(&av_layout, requested_layout);
// Always try to set requested output layout - currently only something
// supported by AC3, MLP/TrueHD, DTS and the fdk-aac wrapper.
av_opt_set_chlayout(lavc_context, "downmix", &av_layout,
AV_OPT_SEARCH_CHILDREN);
av_channel_layout_uninit(&av_layout);
#endif
mp_chmap_to_lavc(&mpopts->audio_output_channels.chmaps[0]);
}
// Always try to set - option only exists for AC3 at the moment
@@ -162,7 +143,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 +168,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;
@@ -262,9 +243,6 @@ static int receive_frame(struct mp_filter *da, struct mp_frame *out)
priv->trim_samples -= trim;
}
// Strip possibly bogus float values like Infinity, NaN, denormalized
mp_aframe_sanitize_float(mpframe);
if (mp_aframe_get_size(mpframe) > 0) {
*out = MAKE_FRAME(MP_FRAME_AUDIO, mpframe);
} else {
+8 -12
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,
@@ -118,7 +116,7 @@ static void determine_codec_params(struct mp_filter *da, AVPacket *pkt,
if (profile != FF_PROFILE_UNKNOWN || spdif_ctx->codec_id != AV_CODEC_ID_DTS)
return;
const AVCodec *codec = avcodec_find_decoder(spdif_ctx->codec_id);
AVCodec *codec = avcodec_find_decoder(spdif_ctx->codec_id);
if (!codec)
goto done;
@@ -169,7 +167,8 @@ static int init_filter(struct mp_filter *da, AVPacket *pkt)
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) {
@@ -296,14 +295,15 @@ static void process(struct mp_filter *da)
struct mp_aframe *out = NULL;
double pts = mpkt->pts;
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, spdif_ctx->avpkt) < 0)
if (init_filter(da, &pkt) < 0)
goto done;
}
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)));
@@ -424,10 +424,6 @@ static struct mp_decoder *create(struct mp_filter *parent,
talloc_free(da);
return NULL;
}
spdif_ctx->avpkt = av_packet_alloc();
MP_HANDLE_OOM(spdif_ctx->avpkt);
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}
},
},
+41 -90
View File
@@ -31,10 +31,7 @@
#include <libavutil/bswap.h>
#include <libavutil/mem.h>
#include "config.h"
#include "audio/aframe.h"
#include "audio/chmap_avchannel.h"
#include "audio/chmap_sel.h"
#include "audio/fmt-conversion.h"
#include "audio/format.h"
@@ -50,13 +47,13 @@
#define AC3_MAX_CHANNELS 6
#define AC3_MAX_CODED_FRAME_SIZE 3840
#define AC3_FRAME_SIZE (6 * 256)
const static uint16_t ac3_bitrate_tab[19] = {
const uint16_t ac3_bitrate_tab[19] = {
32, 40, 48, 56, 64, 80, 96, 112, 128,
160, 192, 224, 256, 320, 384, 448, 512, 576, 640
};
struct f_opts {
bool add_iec61937_header;
int add_iec61937_header;
int bit_rate;
int min_channel_num;
char *encoder;
@@ -71,9 +68,8 @@ struct priv {
struct mp_aframe *in_frame;
struct mp_aframe_pool *out_pool;
const struct AVCodec *lavc_acodec;
struct AVCodec *lavc_acodec;
struct AVCodecContext *lavc_actx;
AVPacket *lavc_pkt;
int bit_rate;
int out_samples; // upper bound on encoded output per AC3 frame
};
@@ -107,13 +103,8 @@ static bool reinit(struct mp_filter *f)
// Put sample parameters
s->lavc_actx->sample_fmt = af_to_avformat(format);
#if !HAVE_AV_CHANNEL_LAYOUT
s->lavc_actx->channels = chmap.num;
s->lavc_actx->channel_layout = mp_chmap_to_lavc(&chmap);
#else
mp_chmap_to_av_layout(&s->lavc_actx->ch_layout, &chmap);
#endif
s->lavc_actx->sample_rate = rate;
s->lavc_actx->bit_rate = bit_rate;
@@ -143,7 +134,6 @@ static void destroy(struct mp_filter *f)
struct priv *s = f->priv;
reset(f);
av_packet_free(&s->lavc_pkt);
avcodec_free_context(&s->lavc_actx);
}
@@ -162,57 +152,57 @@ static void process(struct mp_filter *f)
bool err = true;
struct mp_aframe *out = NULL;
AVPacket *pkt = s->lavc_pkt;
AVPacket pkt = {0};
av_init_packet(&pkt);
// Send input as long as it wants.
while (1) {
if (avcodec_is_open(s->lavc_actx)) {
int lavc_ret = avcodec_receive_packet(s->lavc_actx, pkt);
int lavc_ret = avcodec_receive_packet(s->lavc_actx, &pkt);
if (lavc_ret >= 0)
break;
if (lavc_ret < 0 && lavc_ret != AVERROR(EAGAIN)) {
MP_FATAL(f, "Encode failed (receive).\n");
goto error;
goto done;
}
}
AVFrame *frame = NULL;
struct mp_frame input = mp_pin_out_read(s->in_pin);
// The following code assumes no sample data buffering in the encoder.
switch (input.type) {
case MP_FRAME_NONE:
goto done; // no data yet
case MP_FRAME_EOF:
if (input.type == MP_FRAME_EOF) {
mp_pin_in_write(f->ppins[1], input);
goto done;
case MP_FRAME_AUDIO:
return;
} else if (input.type == MP_FRAME_AUDIO) {
TA_FREEP(&s->in_frame);
s->in_frame = input.data;
frame = mp_frame_to_av(input, NULL);
if (!frame)
goto error;
goto done;
if (mp_aframe_get_channels(s->in_frame) < s->opts->min_channel_num) {
// Just pass it through.
s->in_frame = NULL;
mp_pin_in_write(f->ppins[1], input);
goto done;
return;
}
if (!mp_aframe_config_equals(s->in_frame, s->cur_format)) {
if (!reinit(f))
goto error;
goto done;
}
break;
default: goto error; // unexpected packet type
} else if (input.type) {
goto done;
} else {
return; // no data yet
}
int lavc_ret = avcodec_send_frame(s->lavc_actx, frame);
av_frame_free(&frame);
if (lavc_ret < 0 && lavc_ret != AVERROR(EAGAIN)) {
MP_FATAL(f, "Encode failed (send).\n");
goto error;
goto done;
}
}
if (!s->in_frame)
goto error;
goto done;
out = mp_aframe_create();
mp_aframe_set_format(out, AF_FORMAT_S_AC3);
@@ -220,18 +210,18 @@ static void process(struct mp_filter *f)
mp_aframe_set_rate(out, 48000);
if (mp_aframe_pool_allocate(s->out_pool, out, s->out_samples) < 0)
goto error;
goto done;
int sstride = mp_aframe_get_sstride(out);
mp_aframe_copy_attributes(out, s->in_frame);
int frame_size = pkt->size;
int frame_size = pkt.size;
int header_len = 0;
char hdr[8];
if (s->opts->add_iec61937_header && pkt->size > 5) {
int bsmod = pkt->data[5] & 0x7;
if (s->opts->add_iec61937_header && pkt.size > 5) {
int bsmod = pkt.data[5] & 0x7;
int len = frame_size;
frame_size = AC3_FRAME_SIZE * 2 * 2;
@@ -249,22 +239,20 @@ static void process(struct mp_filter *f)
uint8_t **planes = mp_aframe_get_data_rw(out);
if (!planes)
goto error;
goto done;
char *buf = planes[0];
memcpy(buf, hdr, header_len);
memcpy(buf + header_len, pkt->data, pkt->size);
memset(buf + header_len + pkt->size, 0,
frame_size - (header_len + pkt->size));
swap_16((uint16_t *)(buf + header_len), pkt->size / 2);
memcpy(buf + header_len, pkt.data, pkt.size);
memset(buf + header_len + pkt.size, 0,
frame_size - (header_len + pkt.size));
swap_16((uint16_t *)(buf + header_len), pkt.size / 2);
mp_aframe_set_size(out, frame_size / sstride);
mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out));
out = NULL;
err = 0;
done:
err = false;
// fall through
error:
av_packet_unref(pkt);
av_packet_unref(&pkt);
talloc_free(out);
if (err)
mp_filter_internal_mark_failed(f);
@@ -278,38 +266,6 @@ static const struct mp_filter_info af_lavcac3enc_filter = {
.destroy = destroy,
};
static void add_chmaps_to_autoconv(struct mp_filter *f,
struct mp_autoconvert *conv,
const struct AVCodec *codec)
{
#if !HAVE_AV_CHANNEL_LAYOUT
const uint64_t *lch = codec->channel_layouts;
for (int n = 0; lch && lch[n]; n++) {
struct mp_chmap chmap = {0};
mp_chmap_from_lavc(&chmap, lch[n]);
if (mp_chmap_is_valid(&chmap))
mp_autoconvert_add_chmap(conv, &chmap);
}
#else
const AVChannelLayout *lch = codec->ch_layouts;
for (int n = 0; lch && lch[n].nb_channels; n++) {
struct mp_chmap chmap = {0};
if (!mp_chmap_from_av_layout(&chmap, &lch[n])) {
char layout[128] = {0};
MP_VERBOSE(f, "Skipping unsupported channel layout: %s\n",
av_channel_layout_describe(&lch[n],
layout, 128) < 0 ?
"undefined" : layout);
continue;
}
if (mp_chmap_is_valid(&chmap))
mp_autoconvert_add_chmap(conv, &chmap);
}
#endif
}
static struct mp_filter *af_lavcac3enc_create(struct mp_filter *parent,
void *options)
{
@@ -339,23 +295,14 @@ static struct mp_filter *af_lavcac3enc_create(struct mp_filter *parent,
goto error;
}
s->lavc_pkt = av_packet_alloc();
if (!s->lavc_pkt)
goto error;
if (mp_set_avopts(f->log, s->lavc_actx, s->opts->avopts) < 0)
goto error;
// For this one, we require the decoder to export lists of all supported
// For this one, we require the decoder to expert lists of all supported
// parameters. (Not all decoders do that, but the ones we're interested
// in do.)
if (!s->lavc_acodec->sample_fmts ||
#if !HAVE_AV_CHANNEL_LAYOUT
!s->lavc_acodec->channel_layouts
#else
!s->lavc_acodec->ch_layouts
#endif
)
!s->lavc_acodec->channel_layouts)
{
MP_ERR(f, "Audio encoder doesn't list supported parameters.\n");
goto error;
@@ -387,7 +334,13 @@ static struct mp_filter *af_lavcac3enc_create(struct mp_filter *parent,
mp_autoconvert_add_afmt(conv, mpfmt);
}
add_chmaps_to_autoconv(f, conv, s->lavc_acodec);
const uint64_t *lch = s->lavc_acodec->channel_layouts;
for (int n = 0; lch && lch[n]; n++) {
struct mp_chmap chmap = {0};
mp_chmap_from_lavc(&chmap, lch[n]);
if (mp_chmap_is_valid(&chmap))
mp_autoconvert_add_chmap(conv, &chmap);
}
// At least currently, the AC3 encoder doesn't export sample rates.
mp_autoconvert_add_srate(conv, 48000);
@@ -404,8 +357,6 @@ static struct mp_filter *af_lavcac3enc_create(struct mp_filter *parent,
return f;
error:
av_packet_free(&s->lavc_pkt);
avcodec_free_context(&s->lavc_actx);
talloc_free(f);
return NULL;
}
@@ -418,13 +369,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)},
+2 -15
View File
@@ -20,8 +20,6 @@
#include <rubberband/rubberband-c.h>
#include "config.h"
#include "audio/aframe.h"
#include "audio/format.h"
#include "common/common.h"
@@ -33,7 +31,7 @@
// command line options
struct f_opts {
int transients, detector, phase, window,
smoothing, formant, pitch, channels, engine;
smoothing, formant, pitch, channels;
double scale;
};
@@ -80,10 +78,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 |
#if HAVE_RUBBERBAND_3
p->opts->engine |
#endif
p->opts->pitch | p-> opts->channels |
RubberBandOptionProcessRealTime;
int rate = mp_aframe_get_rate(p->pending);
@@ -336,9 +331,6 @@ const struct mp_user_filter_entry af_rubberband = {
.transients = RubberBandOptionTransientsMixed,
.formant = RubberBandOptionFormantPreserved,
.channels = RubberBandOptionChannelsTogether,
#if HAVE_RUBBERBAND_3
.engine = RubberBandOptionEngineFiner,
#endif
},
.options = (const struct m_option[]) {
{"transients", OPT_CHOICE(transients,
@@ -369,11 +361,6 @@ const struct mp_user_filter_entry af_rubberband = {
{"channels", OPT_CHOICE(channels,
{"apart", RubberBandOptionChannelsApart},
{"together", RubberBandOptionChannelsTogether})},
#if HAVE_RUBBERBAND_3
{"engine", OPT_CHOICE(engine,
{"finer", RubberBandOptionEngineFiner},
{"faster", RubberBandOptionEngineFaster})},
#endif
{"pitch-scale", OPT_DOUBLE(scale), M_RANGE(0.01, 100)},
{0}
},
+1 -1
View File
@@ -230,7 +230,7 @@ const struct mp_user_filter_entry af_scaletempo2 = {
.wsola_search_interval_ms = 30,
},
.options = (const struct m_option[]) {
{"search-interval",
{"search-interval",
OPT_FLOAT(wsola_search_interval_ms), M_RANGE(1, 1000)},
{"window-size",
OPT_FLOAT(ola_window_size_ms), M_RANGE(1, 1000)},
+14 -86
View File
@@ -4,8 +4,6 @@
#include "audio/chmap.h"
#include "audio/filter/af_scaletempo2_internals.h"
#include "config.h"
// Algorithm overview (from chromium):
// Waveform Similarity Overlap-and-add (WSOLA).
//
@@ -106,10 +104,6 @@ static float multi_channel_similarity_measure(
return similarity_measure;
}
#if HAVE_VECTOR
typedef float v8sf __attribute__ ((vector_size (32), aligned (1)));
// Dot-product of channels of two AudioBus. For each AudioBus an offset is
// given. |dot_product[k]| is the dot-product of channel |k|. The caller should
// allocate sufficient space for |dot_product|.
@@ -122,79 +116,16 @@ static void multi_channel_dot_product(
assert(frame_offset_a >= 0);
assert(frame_offset_b >= 0);
memset(dot_product, 0, sizeof(*dot_product) * channels);
for (int k = 0; k < channels; ++k) {
const float* ch_a = a[k] + frame_offset_a;
const float* ch_b = b[k] + frame_offset_b;
float sum = 0.0;
if (num_frames < 32)
goto rest;
const v8sf *va = (const v8sf *) ch_a;
const v8sf *vb = (const v8sf *) ch_b;
v8sf vsum[4] = {
// Initialize to product of first 32 floats
va[0] * vb[0],
va[1] * vb[1],
va[2] * vb[2],
va[3] * vb[3],
};
va += 4;
vb += 4;
// Process `va` and `vb` across four vertical stripes
for (int n = 1; n < num_frames / 32; n++) {
vsum[0] += va[0] * vb[0];
vsum[1] += va[1] * vb[1];
vsum[2] += va[2] * vb[2];
vsum[3] += va[3] * vb[3];
va += 4;
vb += 4;
for (int n = 0; n < num_frames; ++n) {
dot_product[k] += *ch_a++ * *ch_b++;
}
// Vertical sum across `vsum` entries
vsum[0] += vsum[1];
vsum[2] += vsum[3];
vsum[0] += vsum[2];
// Horizontal sum across `vsum[0]`, could probably be done better but
// this section is not super performance critical
float *vf = (float *) &vsum[0];
sum = vf[0] + vf[1] + vf[2] + vf[3] + vf[4] + vf[5] + vf[6] + vf[7];
ch_a = (const float *) va;
ch_b = (const float *) vb;
rest:
// Process the remainder
for (int n = 0; n < num_frames % 32; n++)
sum += *ch_a++ * *ch_b++;
dot_product[k] = sum;
}
}
#else // !HAVE_VECTOR
static void multi_channel_dot_product(
float **a, int frame_offset_a,
float **b, int frame_offset_b,
int channels,
int num_frames, float *dot_product)
{
assert(frame_offset_a >= 0);
assert(frame_offset_b >= 0);
for (int k = 0; k < channels; ++k) {
const float* ch_a = a[k] + frame_offset_a;
const float* ch_b = b[k] + frame_offset_b;
float sum = 0.0;
for (int n = 0; n < num_frames; n++)
sum += *ch_a++ * *ch_b++;
dot_product[k] = sum;
}
}
#endif // HAVE_VECTOR
// Fit the curve f(x) = a * x^2 + b * x + c such that
// f(-1) = y[0]
// f(0) = y[1]
@@ -458,8 +389,10 @@ static int write_completed_frames_to(struct mp_scaletempo2 *p,
static bool can_perform_wsola(struct mp_scaletempo2 *p)
{
const int search_block_size = p->num_candidate_blocks
+ (p->ola_window_size - 1);
return p->target_block_index + p->ola_window_size <= p->input_buffer_frames
&& p->search_block_index + p->search_block_size <= p->input_buffer_frames;
&& p->search_block_index + search_block_size <= p->input_buffer_frames;
}
// number of frames needed until a wsola iteration can be performed
@@ -470,12 +403,6 @@ static int frames_needed(struct mp_scaletempo2 *p)
p->search_block_index + p->search_block_size - p->input_buffer_frames));
}
static void resize_input_buffer(struct mp_scaletempo2 *p, int size)
{
p->input_buffer_size = size;
p->input_buffer = realloc_2d(p->input_buffer, p->channels, size);
}
int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
uint8_t **planes, int frame_size, bool final)
{
@@ -484,9 +411,7 @@ int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
int total_fill = final ? needed : read;
if (total_fill == 0) return 0;
int required_size = total_fill + p->input_buffer_frames;
if (required_size > p->input_buffer_size)
resize_input_buffer(p, required_size);
assert(total_fill + p->input_buffer_frames <= p->input_buffer_size);
for (int i = 0; i < p->channels; ++i) {
memcpy(p->input_buffer[i] + p->input_buffer_frames,
@@ -502,9 +427,11 @@ int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
static bool target_is_within_search_region(struct mp_scaletempo2 *p)
{
const int search_block_size = p->num_candidate_blocks + (p->ola_window_size - 1);
return p->target_block_index >= p->search_block_index
&& p->target_block_index + p->ola_window_size
<= p->search_block_index + p->search_block_size;
<= p->search_block_index + search_block_size;
}
@@ -742,9 +669,9 @@ void mp_scaletempo2_init(struct mp_scaletempo2 *p, int channels, int rate)
p->channels = channels;
p->samples_per_second = rate;
p->num_candidate_blocks = (int)(p->opts->wsola_search_interval_ms
p->num_candidate_blocks = (int)(p->opts->wsola_search_interval_ms
* p->samples_per_second / 1000);
p->ola_window_size = (int)(p->opts->ola_window_size_ms
p->ola_window_size = (int)(p->opts->ola_window_size_ms
* p->samples_per_second / 1000);
// Make sure window size in an even number.
p->ola_window_size += p->ola_window_size & 1;
@@ -788,7 +715,8 @@ void mp_scaletempo2_init(struct mp_scaletempo2 *p, int channels, int rate)
p->search_block = realloc_2d(p->search_block, p->channels, p->search_block_size);
p->target_block = realloc_2d(p->target_block, p->channels, p->ola_window_size);
resize_input_buffer(p, 4 * MPMAX(p->ola_window_size, p->search_block_size));
p->input_buffer_size = 4 * MPMAX(p->ola_window_size, p->search_block_size);
p->input_buffer = realloc_2d(p->input_buffer, p->channels, p->input_buffer_size);
p->input_buffer_frames = 0;
p->energy_candidate_blocks = realloc(p->energy_candidate_blocks,
+2 -2
View File
@@ -1,4 +1,4 @@
// This filter was ported from Chromium
// This filter was ported from Chromium
// (https://chromium.googlesource.com/chromium/chromium/+/51ed77e3f37a9a9b80d6d0a8259e84a8ca635259/media/filters/audio_renderer_algorithm.cc)
//
// Copyright 2015 The Chromium Authors. All rights reserved.
@@ -118,4 +118,4 @@ int mp_scaletempo2_fill_input_buffer(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, float playback_rate);
bool mp_scaletempo2_frames_available(struct mp_scaletempo2 *p);
bool mp_scaletempo2_frames_available(struct mp_scaletempo2 *p);
+4 -4
View File
@@ -187,7 +187,7 @@ int af_format_conversion_score(int dst_format, int src_format)
return score;
}
struct mp_entry {
struct entry {
int fmt;
int score;
};
@@ -195,7 +195,7 @@ struct mp_entry {
static int cmp_entry(const void *a, const void *b)
{
#define CMP_INT(a, b) (a > b ? 1 : (a < b ? -1 : 0))
return -CMP_INT(((struct mp_entry *)a)->score, ((struct mp_entry *)b)->score);
return -CMP_INT(((struct entry *)a)->score, ((struct entry *)b)->score);
}
// Return a list of sample format compatible to src_format, sorted by order
@@ -207,11 +207,11 @@ static int cmp_entry(const void *a, const void *b)
void af_get_best_sample_formats(int src_format, int *out_formats)
{
int num = 0;
struct mp_entry e[AF_FORMAT_COUNT + 1];
struct entry e[AF_FORMAT_COUNT + 1];
for (int fmt = 1; fmt < AF_FORMAT_COUNT; fmt++) {
int score = af_format_conversion_score(fmt, src_format);
if (score > INT_MIN)
e[num++] = (struct mp_entry){fmt, score};
e[num++] = (struct entry){fmt, score};
}
qsort(e, num, sizeof(e[0]), cmp_entry);
for (int n = 0; n < num; n++)
+15 -44
View File
@@ -35,14 +35,11 @@
#include "common/common.h"
#include "common/global.h"
extern const struct ao_driver audio_out_oss;
extern const struct ao_driver audio_out_audiotrack;
extern const struct ao_driver audio_out_audiounit;
extern const struct ao_driver audio_out_coreaudio;
extern const struct ao_driver audio_out_coreaudio_exclusive;
extern const struct ao_driver audio_out_rsound;
extern const struct ao_driver audio_out_pipewire;
extern const struct ao_driver audio_out_sndio;
extern const struct ao_driver audio_out_pulse;
extern const struct ao_driver audio_out_jack;
extern const struct ao_driver audio_out_openal;
@@ -65,9 +62,6 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_COREAUDIO
&audio_out_coreaudio,
#endif
#if HAVE_PIPEWIRE
&audio_out_pipewire,
#endif
#if HAVE_PULSE
&audio_out_pulse,
#endif
@@ -76,9 +70,6 @@ static const struct ao_driver * const audio_out_drivers[] = {
#endif
#if HAVE_WASAPI
&audio_out_wasapi,
#endif
#if HAVE_OSS_AUDIO
&audio_out_oss,
#endif
// wrappers:
#if HAVE_JACK
@@ -92,9 +83,6 @@ static const struct ao_driver * const audio_out_drivers[] = {
#endif
#if HAVE_SDL2_AUDIO
&audio_out_sdl,
#endif
#if HAVE_SNDIO
&audio_out_sndio,
#endif
&audio_out_null,
#if HAVE_COREAUDIO
@@ -102,11 +90,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) {
@@ -127,6 +116,7 @@ static bool get_desc(struct m_obj_desc *dst, int index)
static const struct m_obj_list ao_obj_list = {
.get_desc = get_desc,
.description = "audio outputs",
.allow_unknown_entries = true,
.allow_trailer = true,
.disallow_positional_parameters = true,
.use_global_options = true,
@@ -312,7 +302,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;
@@ -452,9 +442,8 @@ struct ao_hotplug {
void *wakeup_ctx;
// A single AO instance is used to listen to hotplug events. It wouldn't
// make much sense to allow multiple AO drivers; all sane platforms have
// a single audio API providing all events.
// This is _not_ necessarily the same AO instance as used for playing
// audio.
// a single such audio API.
// This is _not_ the same AO instance as used for playing audio.
struct ao *ao;
// cached
struct ao_device_list *list;
@@ -494,8 +483,7 @@ bool ao_hotplug_check_update(struct ao_hotplug *hp)
}
// The return value is valid until the next call to this API.
struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp,
struct ao *playback_ao)
struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp)
{
if (hp->list && !hp->needs_update)
return hp->list;
@@ -507,20 +495,7 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp,
MP_TARRAY_APPEND(list, list->devices, list->num_devices,
(struct ao_device_desc){"auto", "Autoselect device"});
// Try to use the same AO for hotplug handling as for playback.
// Different AOs may not agree and the playback one is the only one the
// user knows about and may even have configured explicitly.
if (!hp->ao && playback_ao && playback_ao->driver->hotplug_init) {
struct ao *ao = ao_alloc(true, hp->global, hp->wakeup_cb, hp->wakeup_ctx,
(char *)playback_ao->driver->name);
if (playback_ao->driver->hotplug_init(ao) >= 0) {
hp->ao = ao;
} else {
talloc_free(ao);
}
}
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
@@ -531,13 +506,10 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp,
continue;
if (ao->driver->hotplug_init) {
if (ao->driver->hotplug_init(ao) >= 0) {
get_devices(ao, list);
if (hp->ao)
ao->driver->hotplug_uninit(ao);
else
hp->ao = ao; // keep this one
}
if (!hp->ao && ao->driver->hotplug_init(ao) >= 0)
hp->ao = ao; // keep this one
if (hp->ao && hp->ao->driver == d)
get_devices(hp->ao, list);
} else {
get_devices(ao, list);
}
@@ -586,11 +558,10 @@ static void dummy_wakeup(void *ctx)
{
}
void ao_print_devices(struct mpv_global *global, struct mp_log *log,
struct ao *playback_ao)
void ao_print_devices(struct mpv_global *global, struct mp_log *log)
{
struct ao_hotplug *hp = ao_hotplug_create(global, dummy_wakeup, NULL);
struct ao_device_list *list = ao_hotplug_get_device_list(hp, playback_ao);
struct ao_device_list *list = ao_hotplug_get_device_list(hp);
mp_info(log, "List of detected audio devices:\n");
for (int n = 0; n < list->num_devices; n++) {
struct ao_device_desc *desc = &list->devices[n];
@@ -701,7 +672,7 @@ static void convert_plane(int type, void *data, int num_samples)
break;
}
default:
MP_ASSERT_UNREACHABLE();
abort();
}
}
+8 -9
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,8 +35,6 @@ 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
@@ -60,10 +59,10 @@ enum {
AO_INIT_EXCLUSIVE = 1 << 3,
};
enum aocontrol_media_role {
AOCONTROL_MEDIA_ROLE_MUSIC,
AOCONTROL_MEDIA_ROLE_MOVIE,
};
typedef struct ao_control_vol {
float left;
float right;
} ao_control_vol_t;
struct ao_device_desc {
const char *name; // symbolic name; will be set on ao->device
@@ -120,8 +119,8 @@ struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
void *wakeup_ctx);
void ao_hotplug_destroy(struct ao_hotplug *hp);
bool ao_hotplug_check_update(struct ao_hotplug *hp);
struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp, struct ao *playback_ao);
struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp);
void ao_print_devices(struct mpv_global *global, struct mp_log *log, struct ao *playback_ao);
void ao_print_devices(struct mpv_global *global, struct mp_log *log);
#endif /* MPLAYER_AUDIO_OUT_H */
+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)
+24 -133
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;
@@ -82,7 +82,6 @@ struct JNIByteBuffer {
struct JNIAudioTrack {
jclass clazz;
jmethodID ctor;
jmethodID ctorV21;
jmethodID release;
jmethodID getState;
jmethodID getPlayState;
@@ -95,7 +94,6 @@ struct JNIAudioTrack {
jmethodID writeV23;
jmethodID writeShortV23;
jmethodID writeBufferV21;
jmethodID getBufferSizeInFramesV23;
jmethodID getPlaybackHeadPosition;
jmethodID getTimestamp;
jmethodID getLatency;
@@ -116,7 +114,6 @@ struct JNIAudioTrack {
#define OFFSET(member) offsetof(struct JNIAudioTrack, member)
{"android/media/AudioTrack", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1},
{"android/media/AudioTrack", "<init>", "(IIIIIII)V", MP_JNI_METHOD, OFFSET(ctor), 1},
{"android/media/AudioTrack", "<init>", "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;III)V", MP_JNI_METHOD, OFFSET(ctorV21), 0},
{"android/media/AudioTrack", "release", "()V", MP_JNI_METHOD, OFFSET(release), 1},
{"android/media/AudioTrack", "getState", "()I", MP_JNI_METHOD, OFFSET(getState), 1},
{"android/media/AudioTrack", "getPlayState", "()I", MP_JNI_METHOD, OFFSET(getPlayState), 1},
@@ -129,7 +126,6 @@ struct JNIAudioTrack {
{"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},
{"android/media/AudioTrack", "getTimestamp", "(Landroid/media/AudioTimestamp;)Z", MP_JNI_METHOD, OFFSET(getTimestamp), 1},
{"android/media/AudioTrack", "getPlaybackHeadPosition", "()I", MP_JNI_METHOD, OFFSET(getPlaybackHeadPosition), 1},
{"android/media/AudioTrack", "getLatency", "()I", MP_JNI_METHOD, OFFSET(getLatency), 1},
@@ -149,38 +145,6 @@ struct JNIAudioTrack {
#undef OFFSET
}};
struct JNIAudioAttributes {
jclass clazz;
jint CONTENT_TYPE_MOVIE;
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", "USAGE_MEDIA", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(USAGE_MEDIA), 0},
{0}
#undef OFFSET
}};
struct JNIAudioAttributesBuilder {
jclass clazz;
jmethodID ctor;
jmethodID setUsage;
jmethodID setContentType;
jmethodID build;
struct MPJniField mapping[];
} AudioAttributesBuilder = {.mapping = {
#define OFFSET(member) offsetof(struct JNIAudioAttributesBuilder, member)
{"android/media/AudioAttributes$Builder", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 0},
{"android/media/AudioAttributes$Builder", "<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 0},
{"android/media/AudioAttributes$Builder", "setUsage", "(I)Landroid/media/AudioAttributes$Builder;", MP_JNI_METHOD, OFFSET(setUsage), 0},
{"android/media/AudioAttributes$Builder", "setContentType", "(I)Landroid/media/AudioAttributes$Builder;", MP_JNI_METHOD, OFFSET(setContentType), 0},
{"android/media/AudioAttributes$Builder", "build", "()Landroid/media/AudioAttributes;", MP_JNI_METHOD, OFFSET(build), 0},
{0}
#undef OFFSET
}};
struct JNIAudioFormat {
jclass clazz;
jint ENCODING_PCM_8BIT;
@@ -225,27 +189,6 @@ struct JNIAudioFormat {
#undef OFFSET
}};
struct JNIAudioFormatBuilder {
jclass clazz;
jmethodID ctor;
jmethodID setEncoding;
jmethodID setSampleRate;
jmethodID setChannelMask;
jmethodID build;
struct MPJniField mapping[];
} AudioFormatBuilder = {.mapping = {
#define OFFSET(member) offsetof(struct JNIAudioFormatBuilder, member)
{"android/media/AudioFormat$Builder", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 0},
{"android/media/AudioFormat$Builder", "<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 0},
{"android/media/AudioFormat$Builder", "setEncoding", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setEncoding), 0},
{"android/media/AudioFormat$Builder", "setSampleRate", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setSampleRate), 0},
{"android/media/AudioFormat$Builder", "setChannelMask", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setChannelMask), 0},
{"android/media/AudioFormat$Builder", "build", "()Landroid/media/AudioFormat;", MP_JNI_METHOD, OFFSET(build), 0},
{0}
#undef OFFSET
}};
struct JNIAudioManager {
jclass clazz;
jint ERROR_DEAD_OBJECT;
@@ -276,64 +219,22 @@ struct JNIAudioTimestamp {
#undef OFFSET
}};
#define MP_JNI_DELETELOCAL(o) (*env)->DeleteLocalRef(env, o)
static int AudioTrack_New(struct ao *ao)
{
struct priv *p = ao->priv;
JNIEnv *env = MP_JNI_GET_ENV(ao);
jobject audiotrack = NULL;
if (AudioTrack.ctorV21) {
MP_VERBOSE(ao, "Using API21 initializer\n");
jobject tmp = NULL;
jobject format_builder = MP_JNI_NEW(AudioFormatBuilder.clazz, AudioFormatBuilder.ctor);
MP_JNI_EXCEPTION_LOG(ao);
tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setEncoding, p->format);
MP_JNI_DELETELOCAL(tmp);
tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setSampleRate, p->samplerate);
MP_JNI_DELETELOCAL(tmp);
tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setChannelMask, p->channel_config);
MP_JNI_DELETELOCAL(tmp);
jobject format = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.build);
MP_JNI_DELETELOCAL(format_builder);
jobject attr_builder = MP_JNI_NEW(AudioAttributesBuilder.clazz, AudioAttributesBuilder.ctor);
MP_JNI_EXCEPTION_LOG(ao);
tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setUsage, AudioAttributes.USAGE_MEDIA);
MP_JNI_DELETELOCAL(tmp);
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);
audiotrack = MP_JNI_NEW(
AudioTrack.clazz,
AudioTrack.ctorV21,
attr,
format,
p->size,
AudioTrack.MODE_STREAM,
p->cfg_session_id
);
MP_JNI_DELETELOCAL(format);
MP_JNI_DELETELOCAL(attr);
} else {
MP_VERBOSE(ao, "Using legacy initializer\n");
audiotrack = MP_JNI_NEW(
AudioTrack.clazz,
AudioTrack.ctor,
AudioManager.STREAM_MUSIC,
p->samplerate,
p->channel_config,
p->format,
p->size,
AudioTrack.MODE_STREAM,
p->cfg_session_id
);
}
jobject audiotrack = MP_JNI_NEW(
AudioTrack.clazz,
AudioTrack.ctor,
AudioManager.STREAM_MUSIC,
p->samplerate,
p->channel_config,
p->format,
p->size,
AudioTrack.MODE_STREAM,
p->cfg_session_id
);
if (!audiotrack || MP_JNI_EXCEPTION_LOG(ao) < 0) {
MP_FATAL(ao, "AudioTrack Init failed\n");
return -1;
@@ -347,14 +248,6 @@ static int AudioTrack_New(struct ao *ao)
return -1;
}
if (AudioTrack.getBufferSizeInFramesV23) {
int bufferSize = MP_JNI_CALL_INT(audiotrack, AudioTrack.getBufferSizeInFramesV23);
if (bufferSize > 0) {
MP_VERBOSE(ao, "AudioTrack.getBufferSizeInFrames = %d\n", bufferSize);
ao->device_buffer = bufferSize;
}
}
p->audiotrack = (*env)->NewGlobalRef(env, audiotrack);
(*env)->DeleteLocalRef(env, audiotrack);
if (!p->audiotrack)
@@ -479,12 +372,12 @@ static double AudioTrack_getLatency(struct ao *ao)
if (!p->timestamp_set &&
p->format != AudioFormat.ENCODING_IEC61937)
delay += (double)MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getLatency)/1000.0;
if (delay > 2.0) {
if (delay > 1.0) {
//MP_WARN(ao, "getLatency: written=%u playhead=%u diff=%u delay=%f\n", p->written_frames, playhead, diff, delay);
p->timestamp_fetched = 0;
return 0;
}
return MPCLAMP(delay, 0.0, 2.0);
return MPCLAMP(delay, 0.0, 1.0);
}
static int AudioTrack_write(struct ao *ao, int len)
@@ -534,9 +427,6 @@ static void uninit_jni(struct ao *ao)
mp_jni_reset_jfields(env, &AudioTimestamp, AudioTimestamp.mapping, 1, ao->log);
mp_jni_reset_jfields(env, &AudioManager, AudioManager.mapping, 1, ao->log);
mp_jni_reset_jfields(env, &AudioFormat, AudioFormat.mapping, 1, ao->log);
mp_jni_reset_jfields(env, &AudioFormatBuilder, AudioFormatBuilder.mapping, 1, ao->log);
mp_jni_reset_jfields(env, &AudioAttributes, AudioAttributes.mapping, 1, ao->log);
mp_jni_reset_jfields(env, &AudioAttributesBuilder, AudioAttributesBuilder.mapping, 1, ao->log);
mp_jni_reset_jfields(env, &ByteBuffer, ByteBuffer.mapping, 1, ao->log);
}
@@ -547,9 +437,6 @@ static int init_jni(struct ao *ao)
mp_jni_init_jfields(env, &ByteBuffer, ByteBuffer.mapping, 1, ao->log) < 0 ||
mp_jni_init_jfields(env, &AudioTimestamp, AudioTimestamp.mapping, 1, ao->log) < 0 ||
mp_jni_init_jfields(env, &AudioManager, AudioManager.mapping, 1, ao->log) < 0 ||
mp_jni_init_jfields(env, &AudioAttributes, AudioAttributes.mapping, 1, ao->log) < 0 ||
mp_jni_init_jfields(env, &AudioAttributesBuilder, AudioAttributesBuilder.mapping, 1, ao->log) < 0 ||
mp_jni_init_jfields(env, &AudioFormatBuilder, AudioFormatBuilder.mapping, 1, ao->log) < 0 ||
mp_jni_init_jfields(env, &AudioFormat, AudioFormat.mapping, 1, ao->log) < 0) {
uninit_jni(ao);
return -1;
@@ -648,6 +535,11 @@ static void uninit(struct ao *ao)
p->timestamp = NULL;
}
if (p->chunk) {
free(p->chunk);
p->chunk = NULL;
}
pthread_cond_destroy(&p->wakeup);
pthread_mutex_destroy(&p->lock);
@@ -715,16 +607,15 @@ static int init(struct ao *ao)
MP_FATAL(ao, "AudioTrack.getMinBufferSize returned an invalid size: %d", buffer_size);
return -1;
}
p->chunksize = buffer_size;
p->chunk = malloc(buffer_size);
int min = 0.075 * p->samplerate * af_fmt_to_bytes(ao->format) * ao->channels.num;
int max = min * 2;
int min = 0.200 * p->samplerate * af_fmt_to_bytes(ao->format);
int max = min * 3 / 2;
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);
ao->device_buffer = p->size / af_fmt_to_bytes(ao->format);
p->chunksize = p->size;
p->chunk = talloc_size(ao, p->size);
jobject timestamp = MP_JNI_NEW(AudioTimestamp.clazz, AudioTimestamp.ctor);
if (!timestamp || MP_JNI_EXCEPTION_LOG(ao) < 0) {
MP_FATAL(ao, "AudioTimestamp could not be created\n");
@@ -818,7 +709,7 @@ const struct ao_driver audio_out_audiotrack = {
.start = start,
.priv_size = sizeof(struct priv),
.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}
},
+15 -78
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"
@@ -35,54 +36,6 @@ struct priv {
double device_latency;
};
static OSStatus au_get_ary(AudioUnit unit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void **data, UInt32 *outDataSize)
{
OSStatus err;
err = AudioUnitGetPropertyInfo(unit, inID, inScope, inElement, outDataSize, NULL);
CHECK_CA_ERROR_SILENT_L(coreaudio_error);
*data = talloc_zero_size(NULL, *outDataSize);
err = AudioUnitGetProperty(unit, inID, inScope, inElement, *data, outDataSize);
CHECK_CA_ERROR_SILENT_L(coreaudio_error_free);
return err;
coreaudio_error_free:
talloc_free(*data);
coreaudio_error:
return err;
}
static AudioChannelLayout *convert_layout(AudioChannelLayout *layout, UInt32* size)
{
AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
AudioChannelLayout *new_layout;
if (tag == kAudioChannelLayoutTag_UseChannelDescriptions)
return layout;
else if (tag == kAudioChannelLayoutTag_UseChannelBitmap)
AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(UInt32), &layout->mChannelBitmap, size);
else
AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForTag,
sizeof(AudioChannelLayoutTag), &tag, size);
new_layout = talloc_zero_size(NULL, *size);
if (!new_layout) {
talloc_free(layout);
return NULL;
}
if (tag == kAudioChannelLayoutTag_UseChannelBitmap)
AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(UInt32), &layout->mChannelBitmap, size, new_layout);
else
AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
sizeof(AudioChannelLayoutTag), &tag, size, new_layout);
new_layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
talloc_free(layout);
return new_layout;
}
static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
const AudioTimeStamp *ts, UInt32 bus,
UInt32 frames, AudioBufferList *buffer_list)
@@ -106,15 +59,12 @@ static bool init_audiounit(struct ao *ao)
AudioStreamBasicDescription asbd;
OSStatus err;
uint32_t size;
AudioChannelLayout *layout = NULL;
struct priv *p = ao->priv;
AVAudioSession *instance = AVAudioSession.sharedInstance;
AVAudioSessionPortDescription *port = nil;
NSInteger maxChannels = instance.maximumOutputNumberOfChannels;
NSInteger prefChannels = MIN(maxChannels, ao->channels.num);
MP_VERBOSE(ao, "max channels: %ld, requested: %d\n", maxChannels, (int)ao->channels.num);
[instance setCategory:AVAudioSessionCategoryPlayback error:nil];
[instance setMode:AVAudioSessionModeMoviePlayback error:nil];
[instance setActive:YES error:nil];
@@ -141,33 +91,23 @@ static bool init_audiounit(struct ao *ao)
CHECK_CA_ERROR_L(coreaudio_error_component,
"unable to initialize audio unit");
err = au_get_ary(p->audio_unit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output,
0, (void**)&layout, &size);
CHECK_CA_ERROR_L(coreaudio_error_audiounit,
"unable to retrieve audio unit channel layout");
MP_VERBOSE(ao, "AU channel layout tag: %x (%x)\n", layout->mChannelLayoutTag, layout->mChannelBitmap);
layout = convert_layout(layout, &size);
if (!layout) {
MP_ERR(ao, "unable to convert channel layout to list format\n");
goto coreaudio_error_audiounit;
}
for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; i++) {
MP_VERBOSE(ao, "channel map: %i: %u\n", i, layout->mChannelDescriptions[i].mChannelLabel);
}
if (af_fmt_is_spdif(ao->format) || instance.outputNumberOfChannels <= 2) {
ao->channels = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
MP_VERBOSE(ao, "using stereo output\n");
} else {
ao->channels.num = (uint8_t)layout->mNumberChannelDescriptions;
for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; i++) {
ao->channels.speaker[i] =
ca_label_to_mp_speaker_id(layout->mChannelDescriptions[i].mChannelLabel);
port = instance.currentRoute.outputs.firstObject;
if (port.channels.count == 2 &&
port.channels[0].channelLabel == kAudioChannelLabel_Unknown) {
// Special case when using an HDMI adapter. The iOS device will
// perform SPDIF conversion for us, so send all available channels
// using the AC3 mapping.
ao->channels = (struct mp_chmap)MP_CHMAP6(FL, FC, FR, SL, SR, LFE);
} else {
ao->channels.num = (uint8_t)port.channels.count;
for (AVAudioSessionChannelDescription *ch in port.channels) {
ao->channels.speaker[ch.channelNumber - 1] =
ca_label_to_mp_speaker_id(ch.channelLabel);
}
}
MP_VERBOSE(ao, "using standard channel mapping\n");
}
ca_fill_asbd(ao, &asbd);
@@ -192,8 +132,6 @@ static bool init_audiounit(struct ao *ao)
CHECK_CA_ERROR_L(coreaudio_error_audiounit,
"unable to set render callback on audio unit");
talloc_free(layout);
return true;
coreaudio_error_audiounit:
@@ -201,7 +139,6 @@ coreaudio_error_audiounit:
coreaudio_error_component:
AudioComponentInstanceDispose(p->audio_unit);
coreaudio_error:
talloc_free(layout);
return false;
}
@@ -217,7 +154,7 @@ static void start(struct ao *ao)
struct priv *p = ao->priv;
AVAudioSession *instance = AVAudioSession.sharedInstance;
p->device_latency = [instance outputLatency];
p->device_latency = [instance outputLatency] + [instance IOBufferDuration];
OSStatus err = AudioOutputUnitStart(p->audio_unit);
CHECK_CA_WARN("can't start audio unit");
+11 -10
View File
@@ -17,6 +17,7 @@
#include <CoreAudio/HostTime.h>
#include "config.h"
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
@@ -36,7 +37,7 @@ struct priv {
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) {
@@ -82,7 +83,7 @@ static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
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 =
@@ -90,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);
@@ -313,11 +314,11 @@ coreaudio_error:
return false;
}
static void reset(struct ao *ao)
static void stop(struct ao *ao)
{
struct priv *p = ao->priv;
OSStatus err = AudioUnitReset(p->audio_unit, kAudioUnitScope_Global, 0);
CHECK_CA_WARN("can't reset audio unit");
OSStatus err = AudioOutputUnitStop(p->audio_unit);
CHECK_CA_WARN("can't stop audio unit");
}
static void start(struct ao *ao)
@@ -414,14 +415,14 @@ const struct ao_driver audio_out_coreaudio = {
.uninit = uninit,
.init = init,
.control = control,
.reset = reset,
.reset = stop,
.start = start,
.hotplug_init = hotplug_init,
.hotplug_uninit = hotplug_uninit,
.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",
+3 -2
View File
@@ -39,6 +39,7 @@
#include <libavutil/intreadwrite.h>
#include <libavutil/intfloat.h>
#include "config.h"
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
@@ -72,7 +73,7 @@ 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;
@@ -464,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",
+1 -1
View File
@@ -16,7 +16,7 @@
*/
/*
* Abstractions on the CoreAudio API to make property setting/getting suck less
* Abstractions on the CoreAudio API to make property setting/getting suck les
*/
#include "audio/out/ao_coreaudio_properties.h"
+16 -9
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)
{
@@ -441,15 +456,7 @@ int64_t ca_get_device_latency_us(struct ao *ao, AudioDeviceID device)
}
}
double sample_rate = ao->samplerate;
OSStatus err = CA_GET_O(device, kAudioDevicePropertyNominalSampleRate,
&sample_rate);
CHECK_CA_WARN("cannot get device sample rate, falling back to AO sample rate!");
if (err == noErr) {
MP_VERBOSE(ao, "Device sample rate: %f\n", sample_rate);
}
return latency_frames / sample_rate * 1e6;
return ca_frames_to_us(ao, latency_frames);
}
static OSStatus ca_change_format_listener(
+5 -1
View File
@@ -25,7 +25,11 @@
#include "common/msg.h"
#include "audio/out/ao.h"
#include "internal.h"
#include "osdep/apple_utils.h"
#define CA_CFSTR_ENCODING kCFStringEncodingASCII
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);
+5 -5
View File
@@ -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),
};
+12 -7
View File
@@ -31,7 +31,6 @@
#include "options/options.h"
#include "common/common.h"
#include "audio/aframe.h"
#include "audio/chmap_avchannel.h"
#include "audio/format.h"
#include "audio/fmt-conversion.h"
#include "filters/filter_internal.h"
@@ -125,13 +124,8 @@ static int init(struct ao *ao)
if (!ao_chmap_sel_adjust2(ao, &sel, &ao->channels, false))
goto fail;
mp_chmap_reorder_to_lavc(&ao->channels);
#if !HAVE_AV_CHANNEL_LAYOUT
encoder->channels = ao->channels.num;
encoder->channel_layout = mp_chmap_to_lavc(&ao->channels);
#else
mp_chmap_to_av_layout(&encoder->ch_layout, &ao->channels);
#endif
encoder->sample_fmt = AV_SAMPLE_FMT_NONE;
@@ -182,8 +176,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 +204,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)));
+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}
+10 -14
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:
@@ -197,14 +199,8 @@ static int init(struct ao *ao)
alListenerfv(AL_ORIENTATION, direction);
alGenSources(1, &source);
if (p->direct_channels) {
if (alIsExtensionPresent("AL_SOFT_direct_channels_remix")) {
alSourcei(source,
alGetEnumValue((ALchar*)"AL_DIRECT_CHANNELS_SOFT"),
alcGetEnumValue(dev, "AL_REMIX_UNMATCHED_SOFT"));
} else {
MP_WARN(ao, "Direct channels aren't supported by this version of OpenAL\n");
}
if (p->direct_channels && alGetEnumValue((ALchar*)"AL_DIRECT_CHANNELS_SOFT")) {
alSourcei(source, alGetEnumValue((ALchar*)"AL_DIRECT_CHANNELS_SOFT"), AL_TRUE);
}
cur_buf = 0;
@@ -389,12 +385,12 @@ const struct ao_driver audio_out_openal = {
.priv_defaults = &(const struct priv) {
.num_buffers = 4,
.num_samples = 8192,
.direct_channels = true,
.direct_channels = 0,
},
.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",
-2
View File
@@ -111,8 +111,6 @@ static int init(struct ao *ao)
// This AO only supports two channels at the moment
mp_chmap_from_channels(&ao->channels, 2);
// Upstream "Wilhelm" supports only 8000 <= rate <= 192000
ao->samplerate = MPCLAMP(ao->samplerate, 8000, 192000);
CHK(slCreateEngine(&p->sl, 0, NULL, 0, NULL, NULL));
CHK((*p->sl)->Realize(p->sl, SL_BOOLEAN_FALSE));
-400
View File
@@ -1,400 +0,0 @@
/*
* OSS audio output driver
*
* Original author: A'rpi
* Support for >2 output channels added 2001-11-25
* - Steve Davies <steve@daviesfam.org>
* Rozhuk Ivan <rozhuk.im@gmail.com> 2020-2023
*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/stat.h>
#if defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif
#include <sys/types.h>
#include "audio/format.h"
#include "common/msg.h"
#include "options/options.h"
#include "osdep/endian.h"
#include "osdep/io.h"
#include "ao.h"
#include "internal.h"
#ifndef AFMT_AC3
#define AFMT_AC3 -1
#endif
#define PATH_DEV_DSP "/dev/dsp"
#define PATH_DEV_MIXER "/dev/mixer"
struct priv {
int dsp_fd;
double bps; /* Bytes per second. */
};
/* like alsa except for 6.1 and 7.1, from pcm/matrix_map.h */
static const struct mp_chmap oss_layouts[MP_NUM_CHANNELS + 1] = {
{0}, /* empty */
MP_CHMAP_INIT_MONO, /* mono */
MP_CHMAP2(FL, FR), /* stereo */
MP_CHMAP3(FL, FR, LFE), /* 2.1 */
MP_CHMAP4(FL, FR, BL, BR), /* 4.0 */
MP_CHMAP5(FL, FR, BL, BR, FC), /* 5.0 */
MP_CHMAP6(FL, FR, BL, BR, FC, LFE), /* 5.1 */
MP_CHMAP7(FL, FR, BL, BR, FC, LFE, BC), /* 6.1 */
MP_CHMAP8(FL, FR, BL, BR, FC, LFE, SL, SR), /* 7.1 */
};
#if !defined(AFMT_S32_NE) && defined(AFMT_S32_LE) && defined(AFMT_S32_BE)
#define AFMT_S32_NE AFMT_S32MP_SELECT_LE_BE(AFMT_S32_LE, AFMT_S32_BE)
#endif
static const int format_table[][2] = {
{AFMT_U8, AF_FORMAT_U8},
{AFMT_S16_NE, AF_FORMAT_S16},
#ifdef AFMT_S32_NE
{AFMT_S32_NE, AF_FORMAT_S32},
#endif
#ifdef AFMT_FLOAT
{AFMT_FLOAT, AF_FORMAT_FLOAT},
#endif
#ifdef AFMT_MPEG
{AFMT_MPEG, AF_FORMAT_S_MP3},
#endif
{-1, -1}
};
#define MP_WARN_IOCTL_ERR(__ao) \
MP_WARN((__ao), "%s: ioctl() fail, err = %i: %s\n", \
__FUNCTION__, errno, strerror(errno))
static void uninit(struct ao *ao);
static void device_descr_get(size_t dev_idx, char *buf, size_t buf_size)
{
#if defined(__DragonFly__) || defined(__FreeBSD__)
char dev_path[32];
size_t tmp = (buf_size - 1);
snprintf(dev_path, sizeof(dev_path), "dev.pcm.%zu.%%desc", dev_idx);
if (sysctlbyname(dev_path, buf, &tmp, NULL, 0) != 0) {
tmp = 0;
}
buf[tmp] = 0x00;
#elif defined(SOUND_MIXER_INFO)
size_t tmp = 0;
char dev_path[32];
mixer_info mi;
snprintf(dev_path, sizeof(dev_path), PATH_DEV_MIXER"%zu", dev_idx);
int fd = open(dev_path, O_RDONLY);
if (ioctl(fd, SOUND_MIXER_INFO, &mi) == 0) {
strncpy(buf, mi.name, buf_size - 1);
tmp = (buf_size - 1);
}
close(fd);
buf[tmp] = 0x00;
#else
buf[0] = 0x00;
#endif
}
static int format2oss(int format)
{
for (size_t i = 0; format_table[i][0] != -1; i++) {
if (format_table[i][1] == format)
return format_table[i][0];
}
return -1;
}
static bool try_format(struct ao *ao, int *format)
{
struct priv *p = ao->priv;
int oss_format = format2oss(*format);
if (oss_format == -1 && af_fmt_is_spdif(*format))
oss_format = AFMT_AC3;
if (oss_format == -1) {
MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n",
af_fmt_to_str(*format));
*format = 0;
return false;
}
return (ioctl(p->dsp_fd, SNDCTL_DSP_SETFMT, &oss_format) != -1);
}
static int init(struct ao *ao)
{
struct priv *p = ao->priv;
struct mp_chmap channels = ao->channels;
audio_buf_info info;
size_t i;
int format, samplerate, nchannels, reqchannels, trig = 0;
int best_sample_formats[AF_FORMAT_COUNT + 1];
const char *device = ((ao->device) ? ao->device : PATH_DEV_DSP);
/* Opening device. */
MP_VERBOSE(ao, "Using '%s' audio device.\n", device);
p->dsp_fd = open(device, (O_WRONLY | O_CLOEXEC));
if (p->dsp_fd < 0) {
MP_ERR(ao, "Can't open audio device %s: %s.\n",
device, mp_strerror(errno));
goto err_out;
}
/* Selecting sound format. */
format = af_fmt_from_planar(ao->format);
af_get_best_sample_formats(format, best_sample_formats);
for (i = 0; best_sample_formats[i]; i++) {
format = best_sample_formats[i];
if (try_format(ao, &format))
break;
}
if (!format) {
MP_ERR(ao, "Can't set sample format.\n");
goto err_out;
}
MP_VERBOSE(ao, "Sample format: %s\n", af_fmt_to_str(format));
/* Channels count. */
if (af_fmt_is_spdif(format)) {
/* Probably could be fixed by setting number of channels;
* needs testing. */
if (channels.num != 2) {
MP_ERR(ao, "Format %s not implemented.\n", af_fmt_to_str(format));
goto err_out;
}
} else {
struct mp_chmap_sel sel = {0};
for (i = 0; i < MP_ARRAY_SIZE(oss_layouts); i++) {
mp_chmap_sel_add_map(&sel, &oss_layouts[i]);
}
if (!ao_chmap_sel_adjust(ao, &sel, &channels))
goto err_out;
nchannels = reqchannels = channels.num;
if (ioctl(p->dsp_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1) {
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
reqchannels);
goto err_out_ioctl;
}
if (nchannels != reqchannels) {
/* Update number of channels to OSS suggested value. */
if (!ao_chmap_sel_get_def(ao, &sel, &channels, nchannels))
goto err_out;
}
MP_VERBOSE(ao, "Using %d channels (requested: %d).\n",
channels.num, reqchannels);
}
/* Sample rate. */
samplerate = ao->samplerate;
if (ioctl(p->dsp_fd, SNDCTL_DSP_SPEED, &samplerate) == -1)
goto err_out_ioctl;
MP_VERBOSE(ao, "Using %d Hz samplerate.\n", samplerate);
/* Get buffer size. */
if (ioctl(p->dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
goto err_out_ioctl;
/* See ao.c ao->sstride initializations and get_state(). */
ao->device_buffer = ((info.fragstotal * info.fragsize) /
af_fmt_to_bytes(format));
if (!af_fmt_is_planar(format)) {
ao->device_buffer /= channels.num;
}
/* Do not start playback after data written. */
if (ioctl(p->dsp_fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1)
goto err_out_ioctl;
/* Update sound params. */
ao->format = format;
ao->samplerate = samplerate;
ao->channels = channels;
p->bps = (channels.num * samplerate * af_fmt_to_bytes(format));
return 0;
err_out_ioctl:
MP_WARN_IOCTL_ERR(ao);
err_out:
uninit(ao);
return -1;
}
static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->dsp_fd == -1)
return;
ioctl(p->dsp_fd, SNDCTL_DSP_HALT, NULL);
close(p->dsp_fd);
p->dsp_fd = -1;
}
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct priv *p = ao->priv;
float *vol = arg;
int v;
if (p->dsp_fd < 0)
return CONTROL_ERROR;
switch (cmd) {
case AOCONTROL_GET_VOLUME:
if (ioctl(p->dsp_fd, SNDCTL_DSP_GETPLAYVOL, &v) == -1) {
MP_WARN_IOCTL_ERR(ao);
return CONTROL_ERROR;
}
*vol = ((v & 0x00ff) + ((v & 0xff00) >> 8)) / 2.0;
return CONTROL_OK;
case AOCONTROL_SET_VOLUME:
v = ((int)*vol << 8) | (int)*vol;
if (ioctl(p->dsp_fd, SNDCTL_DSP_SETPLAYVOL, &v) == -1) {
MP_WARN_IOCTL_ERR(ao);
return CONTROL_ERROR;
}
return CONTROL_OK;
}
return CONTROL_UNKNOWN;
}
static void reset(struct ao *ao)
{
struct priv *p = ao->priv;
int trig = 0;
/* Clear buf and do not start playback after data written. */
if (ioctl(p->dsp_fd, SNDCTL_DSP_HALT, NULL) == -1 ||
ioctl(p->dsp_fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1)
{
MP_WARN_IOCTL_ERR(ao);
MP_WARN(ao, "Force reinitialize audio device.\n");
uninit(ao);
init(ao);
}
}
static void start(struct ao *ao)
{
struct priv *p = ao->priv;
int trig = PCM_ENABLE_OUTPUT;
if (ioctl(p->dsp_fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) {
MP_WARN_IOCTL_ERR(ao);
return;
}
}
static bool audio_write(struct ao *ao, void **data, int samples)
{
struct priv *p = ao->priv;
ssize_t rc;
const size_t size = (samples * ao->sstride);
if (size == 0)
return true;
while ((rc = write(p->dsp_fd, data[0], size)) == -1) {
if (errno == EINTR)
continue;
MP_WARN(ao, "audio_write: write() fail, err = %i: %s.\n",
errno, strerror(errno));
return false;
}
if ((size_t)rc != size) {
MP_WARN(ao, "audio_write: unexpected partial write: required: %zu, written: %zu.\n",
size, (size_t)rc);
return false;
}
return true;
}
static void get_state(struct ao *ao, struct mp_pcm_state *state)
{
struct priv *p = ao->priv;
audio_buf_info info;
int odelay;
if (ioctl(p->dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1 ||
ioctl(p->dsp_fd, SNDCTL_DSP_GETODELAY, &odelay) == -1)
{
MP_WARN_IOCTL_ERR(ao);
memset(state, 0x00, sizeof(struct mp_pcm_state));
state->delay = 0.0;
return;
}
state->free_samples = (info.bytes / ao->sstride);
state->queued_samples = (ao->device_buffer - state->free_samples);
state->delay = (odelay / p->bps);
state->playing = (state->queued_samples != 0);
}
static void list_devs(struct ao *ao, struct ao_device_list *list)
{
struct stat st;
char dev_path[32] = PATH_DEV_DSP, dev_descr[256] = "Default";
struct ao_device_desc dev = {.name = dev_path, .desc = dev_descr};
if (stat(PATH_DEV_DSP, &st) == 0) {
ao_device_list_add(list, ao, &dev);
}
/* Auto detect. */
for (size_t i = 0, fail_cnt = 0; fail_cnt < 8; i ++, fail_cnt ++) {
snprintf(dev_path, sizeof(dev_path), PATH_DEV_DSP"%zu", i);
if (stat(dev_path, &st) != 0)
continue;
device_descr_get(i, dev_descr, sizeof(dev_descr));
ao_device_list_add(list, ao, &dev);
fail_cnt = 0; /* Reset fail counter. */
}
}
const struct ao_driver audio_out_oss = {
.name = "oss",
.description = "OSS/ioctl audio output",
.init = init,
.uninit = uninit,
.control = control,
.reset = reset,
.start = start,
.write = audio_write,
.get_state = get_state,
.list_devs = list_devs,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.dsp_fd = -1,
},
};
+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",
-898
View File
@@ -1,898 +0,0 @@
/*
* PipeWire audio output driver.
* Copyright (C) 2021 Thomas Weißschuh <thomas@t-8ch.de>
* Copyright (C) 2021 Oschowa <oschowa@web.de>
* Copyright (C) 2020 Andreas Kempf <aakempf@gmail.com>
*
* 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/>.
*/
#include <pipewire/pipewire.h>
#include <pipewire/global.h>
#include <spa/param/audio/format-utils.h>
#include <spa/param/props.h>
#include <spa/utils/result.h>
#include <math.h>
#include "common/msg.h"
#include "options/m_config.h"
#include "options/m_option.h"
#include "ao.h"
#include "audio/format.h"
#include "internal.h"
#include "osdep/timer.h"
#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;
struct {
int buffer_msec;
char *remote;
int volume_mode;
} options;
struct {
struct pw_registry *registry;
struct spa_hook registry_listener;
struct spa_list sinks;
} hotplug;
};
struct id_list {
uint32_t id;
struct spa_list node;
};
static enum spa_audio_format af_fmt_to_pw(struct ao *ao, enum af_format format)
{
switch (format) {
case AF_FORMAT_U8: return SPA_AUDIO_FORMAT_U8;
case AF_FORMAT_S16: return SPA_AUDIO_FORMAT_S16;
case AF_FORMAT_S32: return SPA_AUDIO_FORMAT_S32;
case AF_FORMAT_FLOAT: return SPA_AUDIO_FORMAT_F32;
case AF_FORMAT_DOUBLE: return SPA_AUDIO_FORMAT_F64;
case AF_FORMAT_U8P: return SPA_AUDIO_FORMAT_U8P;
case AF_FORMAT_S16P: return SPA_AUDIO_FORMAT_S16P;
case AF_FORMAT_S32P: return SPA_AUDIO_FORMAT_S32P;
case AF_FORMAT_FLOATP: return SPA_AUDIO_FORMAT_F32P;
case AF_FORMAT_DOUBLEP: return SPA_AUDIO_FORMAT_F64P;
default:
MP_WARN(ao, "Unhandled format %d\n", format);
return SPA_AUDIO_FORMAT_UNKNOWN;
}
}
static enum spa_audio_channel mp_speaker_id_to_spa(struct ao *ao, enum mp_speaker_id mp_speaker_id)
{
switch (mp_speaker_id) {
case MP_SPEAKER_ID_FL: return SPA_AUDIO_CHANNEL_FL;
case MP_SPEAKER_ID_FR: return SPA_AUDIO_CHANNEL_FR;
case MP_SPEAKER_ID_FC: return SPA_AUDIO_CHANNEL_FC;
case MP_SPEAKER_ID_LFE: return SPA_AUDIO_CHANNEL_LFE;
case MP_SPEAKER_ID_BL: return SPA_AUDIO_CHANNEL_RL;
case MP_SPEAKER_ID_BR: return SPA_AUDIO_CHANNEL_RR;
case MP_SPEAKER_ID_FLC: return SPA_AUDIO_CHANNEL_FLC;
case MP_SPEAKER_ID_FRC: return SPA_AUDIO_CHANNEL_FRC;
case MP_SPEAKER_ID_BC: return SPA_AUDIO_CHANNEL_RC;
case MP_SPEAKER_ID_SL: return SPA_AUDIO_CHANNEL_SL;
case MP_SPEAKER_ID_SR: return SPA_AUDIO_CHANNEL_SR;
case MP_SPEAKER_ID_TC: return SPA_AUDIO_CHANNEL_TC;
case MP_SPEAKER_ID_TFL: return SPA_AUDIO_CHANNEL_TFL;
case MP_SPEAKER_ID_TFC: return SPA_AUDIO_CHANNEL_TFC;
case MP_SPEAKER_ID_TFR: return SPA_AUDIO_CHANNEL_TFR;
case MP_SPEAKER_ID_TBL: return SPA_AUDIO_CHANNEL_TRL;
case MP_SPEAKER_ID_TBC: return SPA_AUDIO_CHANNEL_TRC;
case MP_SPEAKER_ID_TBR: return SPA_AUDIO_CHANNEL_TRR;
case MP_SPEAKER_ID_DL: return SPA_AUDIO_CHANNEL_FL;
case MP_SPEAKER_ID_DR: return SPA_AUDIO_CHANNEL_FR;
case MP_SPEAKER_ID_WL: return SPA_AUDIO_CHANNEL_FL;
case MP_SPEAKER_ID_WR: return SPA_AUDIO_CHANNEL_FR;
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_NA: return SPA_AUDIO_CHANNEL_NA;
default:
MP_WARN(ao, "Unhandled channel %d\n", mp_speaker_id);
return SPA_AUDIO_CHANNEL_UNKNOWN;
};
}
static void on_process(void *userdata)
{
struct ao *ao = userdata;
struct priv *p = ao->priv;
struct pw_time time;
struct pw_buffer *b;
void *data[MP_NUM_CHANNELS];
if ((b = pw_stream_dequeue_buffer(p->stream)) == NULL) {
MP_WARN(ao, "out of buffers: %s\n", strerror(errno));
return;
}
struct spa_buffer *buf = b->buffer;
int bytes_per_channel = buf->datas[0].maxsize / ao->channels.num;
int nframes = bytes_per_channel / ao->sstride;
#if PW_CHECK_VERSION(0, 3, 49)
if (b->requested != 0)
nframes = MPMIN(b->requested, nframes);
#endif
for (int i = 0; i < buf->n_datas; i++)
data[i] = buf->datas[i].data;
pw_stream_get_time_n(p->stream, &time, sizeof(time));
if (time.rate.denom == 0)
time.rate.denom = ao->samplerate;
if (time.rate.num == 0)
time.rate.num = 1;
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 * 1e6 / ao->samplerate) +
((float) time.delay * SPA_USEC_PER_SEC * time.rate.num / time.rate.denom);
int samples = ao_read_data(ao, data, nframes, end_time);
b->size = samples;
for (int i = 0; i < buf->n_datas; i++) {
buf->datas[i].chunk->size = samples * ao->sstride;
buf->datas[i].chunk->offset = 0;
buf->datas[i].chunk->stride = ao->sstride;
}
pw_stream_queue_buffer(p->stream, b);
MP_TRACE(ao, "queued %d of %d samples\n", samples, nframes);
}
static void on_param_changed(void *userdata, uint32_t id, const struct spa_pod *param)
{
struct ao *ao = userdata;
struct priv *p = ao->priv;
const struct spa_pod *params[1];
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;
int buffer_size = ao->device_buffer * af_fmt_to_bytes(ao->format) * ao->channels.num;
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_stride, SPA_POD_Int(ao->sstride));
if (!params[0]) {
MP_ERR(ao, "Could not build parameter pod\n");
return;
}
if (pw_stream_update_params(p->stream, params, 1) < 0) {
MP_ERR(ao, "Could not update stream parameters\n");
return;
}
}
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);
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);
}
if (state == PW_STREAM_STATE_UNCONNECTED && old != PW_STREAM_STATE_UNCONNECTED) {
MP_WARN(ao, "Stream disconnected, trying to reload...\n");
ao_request_reload(ao);
}
}
static float spa_volume_to_mp_volume(float vol)
{
return vol * 100;
}
static float mp_volume_to_spa_volume(float vol)
{
return vol / 100;
}
static float volume_avg(float* vols, uint32_t n)
{
float sum = 0.0;
for (int i = 0; i < n; i++)
sum += vols[i];
return sum / n;
}
static void on_control_info(void *userdata, uint32_t id,
const struct pw_stream_control *control)
{
struct ao *ao = userdata;
struct priv *p = ao->priv;
switch (id) {
case SPA_PROP_mute:
if (control->n_values == 1)
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];
break;
}
}
static const struct pw_stream_events stream_events = {
.version = PW_VERSION_STREAM_EVENTS,
.param_changed = on_param_changed,
.process = on_process,
.state_changed = on_state_changed,
.control_info = on_control_info,
};
static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->loop)
pw_thread_loop_stop(p->loop);
spa_hook_remove(&p->stream_listener);
spa_zero(p->stream_listener);
if (p->stream)
pw_stream_destroy(p->stream);
p->stream = NULL;
if (p->core)
pw_context_destroy(pw_core_get_context(p->core));
p->core = NULL;
if (p->loop)
pw_thread_loop_destroy(p->loop);
p->loop = NULL;
pw_deinit();
}
struct registry_event_global_ctx {
struct ao *ao;
void (*sink_cb) (struct ao *ao, uint32_t id, const struct spa_dict *props, void *sink_cb_ctx);
void *sink_cb_ctx;
};
static bool is_sink_node(const char *type, const struct spa_dict *props)
{
if (strcmp(type, PW_TYPE_INTERFACE_Node) != 0)
return false;
if (!props)
return false;
const char *class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
if (!class || strcmp(class, "Audio/Sink") != 0)
return false;
return true;
}
static void for_each_sink_registry_event_global(void *data, uint32_t id,
uint32_t permissions, const
char *type, uint32_t version,
const struct spa_dict *props)
{
struct registry_event_global_ctx *ctx = data;
if (!is_sink_node(type, props))
return;
ctx->sink_cb(ctx->ao, id, props, ctx->sink_cb_ctx);
}
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,
};
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);
}
static const struct pw_core_events for_each_sink_core_events = {
.version = PW_VERSION_CORE_EVENTS,
.done = for_each_sink_done,
};
static int for_each_sink(struct ao *ao, void (cb) (struct ao *ao, uint32_t id,
const struct spa_dict *props, void *ctx), void *cb_ctx)
{
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)
goto unlock_loop;
registry = pw_core_get_registry(priv->core, PW_VERSION_REGISTRY, 0);
if (!registry)
goto remove_core_listener;
pw_core_sync(priv->core, 0, 0);
struct spa_hook registry_listener;
struct registry_event_global_ctx revents_ctx = {
.ao = ao,
.sink_cb = cb,
.sink_cb_ctx = cb_ctx,
};
spa_zero(registry_listener);
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);
spa_hook_remove(&registry_listener);
ret = 0;
destroy_registry:
pw_proxy_destroy((struct pw_proxy *)registry);
remove_core_listener:
spa_hook_remove(&core_listener);
unlock_loop:
pw_thread_loop_unlock(priv->loop);
return ret;
}
static void have_sink(struct ao *ao, uint32_t id, const struct spa_dict *props, void *ctx)
{
bool *b = ctx;
*b = true;
}
static bool session_has_sinks(struct ao *ao)
{
bool b = false;
if (for_each_sink(ao, have_sink, &b) < 0)
MP_WARN(ao, "Could not list devices, sink detection may be wrong\n");
return b;
}
static void on_error(void *data, uint32_t id, int seq, int res, const char *message)
{
struct ao *ao = data;
MP_WARN(ao, "Error during playback: %s, %s\n", spa_strerror(res), message);
}
static void on_core_info(void *data, const struct pw_core_info *info)
{
struct ao *ao = data;
MP_VERBOSE(ao, "Core user: %s\n", info->user_name);
MP_VERBOSE(ao, "Core host: %s\n", info->host_name);
MP_VERBOSE(ao, "Core version: %s\n", info->version);
MP_VERBOSE(ao, "Core name: %s\n", info->name);
}
static const struct pw_core_events core_events = {
.version = PW_VERSION_CORE_EVENTS,
.error = on_error,
.info = on_core_info,
};
static int pipewire_init_boilerplate(struct ao *ao)
{
struct priv *p = ao->priv;
struct pw_context *context;
pw_init(NULL, NULL);
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);
if (p->loop == NULL)
return -1;
pw_thread_loop_lock(p->loop);
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);
if (!context)
goto error;
p->core = pw_context_connect(
context,
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));
pw_context_destroy(context);
goto error;
}
if (pw_core_add_listener(p->core, &p->core_listener, &core_events, ao) < 0)
goto error;
pw_thread_loop_unlock(p->loop);
if (!session_has_sinks(ao)) {
MP_VERBOSE(ao, "PipeWire does not have any audio sinks, skipping\n");
return -1;
}
return 0;
error:
pw_thread_loop_unlock(p->loop);
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)
{
struct priv *p = ao->priv;
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
const struct spa_pod *params[1];
struct pw_properties *props = pw_properties_new(
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_NODE_NAME, ao->client_name,
PW_KEY_NODE_DESCRIPTION, ao->client_name,
PW_KEY_APP_NAME, ao->client_name,
PW_KEY_APP_ID, ao->client_name,
PW_KEY_APP_ICON_NAME, ao->client_name,
PW_KEY_NODE_ALWAYS_PROCESS, "true",
PW_KEY_TARGET_OBJECT, ao->device,
NULL
);
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);
}
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);
if (spa_format == SPA_AUDIO_FORMAT_UNKNOWN) {
ao->format = AF_FORMAT_FLOATP;
spa_format = SPA_AUDIO_FORMAT_F32P;
}
struct spa_audio_info_raw audio_info = {
.format = spa_format,
.rate = ao->samplerate,
.channels = ao->channels.num,
};
for (int i = 0; i < ao->channels.num; i++)
audio_info.position[i] = mp_speaker_id_to_spa(ao, ao->channels.speaker[i]);
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &audio_info);
if (!params[0])
goto error_props;
if (af_fmt_is_planar(ao->format)) {
ao->num_planes = ao->channels.num;
ao->sstride = af_fmt_to_bytes(ao->format);
} else {
ao->num_planes = 1;
ao->sstride = ao->channels.num * af_fmt_to_bytes(ao->format);
}
pw_thread_loop_lock(p->loop);
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;
if (pw_stream_connect(p->stream,
PW_DIRECTION_OUTPUT, PW_ID_ANY, flags, 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:
pw_properties_free(props);
error:
uninit(ao);
return -1;
}
static void reset(struct ao *ao)
{
struct priv *p = ao->priv;
pw_thread_loop_lock(p->loop);
pw_stream_set_active(p->stream, false);
pw_stream_flush(p->stream, false);
pw_thread_loop_unlock(p->loop);
}
static void start(struct ao *ao)
{
struct priv *p = ao->priv;
pw_thread_loop_lock(p->loop);
pw_stream_set_active(p->stream, true);
pw_thread_loop_unlock(p->loop);
}
#define CONTROL_RET(r) (!r ? CONTROL_OK : CONTROL_ERROR)
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct priv *p = ao->priv;
switch (cmd) {
case AOCONTROL_GET_VOLUME: {
float *vol = arg;
*vol = spa_volume_to_mp_volume(p->volume);
return CONTROL_OK;
}
case AOCONTROL_GET_MUTE: {
bool *muted = arg;
*muted = p->muted;
return CONTROL_OK;
}
case AOCONTROL_SET_VOLUME:
case AOCONTROL_SET_MUTE:
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;
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));
} else {
float value = mp_volume_to_spa_volume(*vol);
ret = CONTROL_RET(pw_stream_set_control(
p->stream, SPA_PROP_volume, 1, &value, 0));
}
break;
}
case AOCONTROL_SET_MUTE: {
bool *muted = arg;
float value = *muted ? 1.f : 0.f;
ret = CONTROL_RET(pw_stream_set_control(p->stream, SPA_PROP_mute, 1, &value, 0));
break;
}
case AOCONTROL_UPDATE_STREAM_TITLE: {
char *title = arg;
struct spa_dict_item items[1];
items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_NAME, title);
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;
}
pw_thread_loop_unlock(p->loop);
return ret;
}
default:
return CONTROL_UNKNOWN;
}
}
static void add_device_to_list(struct ao *ao, uint32_t id, const struct spa_dict *props, void *ctx)
{
struct ao_device_list *list = ctx;
const char *name = spa_dict_lookup(props, PW_KEY_NODE_NAME);
if (!name)
return;
const char *description = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION);
ao_device_list_add(list, ao, &(struct ao_device_desc){name, description});
}
static void hotplug_registry_global_cb(void *data, uint32_t id,
uint32_t permissions, const char *type,
uint32_t version, const struct spa_dict *props)
{
struct ao *ao = data;
struct priv *priv = ao->priv;
if (!is_sink_node(type, props))
return;
pw_thread_loop_lock(priv->loop);
struct id_list *item = talloc(ao, struct id_list);
item->id = id;
spa_list_init(&item->node);
spa_list_append(&priv->hotplug.sinks, &item->node);
pw_thread_loop_unlock(priv->loop);
ao_hotplug_event(ao);
}
static void hotplug_registry_global_remove_cb(void *data, uint32_t id)
{
struct ao *ao = data;
struct priv *priv = ao->priv;
bool removed_sink = false;
struct id_list *e;
pw_thread_loop_lock(priv->loop);
spa_list_for_each(e, &priv->hotplug.sinks, node) {
if (e->id == id) {
removed_sink = true;
spa_list_remove(&e->node);
talloc_free(e);
break;
}
}
pw_thread_loop_unlock(priv->loop);
if (removed_sink)
ao_hotplug_event(ao);
}
static const struct pw_registry_events hotplug_registry_events = {
.version = PW_VERSION_REGISTRY_EVENTS,
.global = hotplug_registry_global_cb,
.global_remove = hotplug_registry_global_remove_cb,
};
static int hotplug_init(struct ao *ao)
{
struct priv *priv = ao->priv;
int res = pipewire_init_boilerplate(ao);
if (res)
goto error_no_unlock;
pw_thread_loop_lock(priv->loop);
spa_zero(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)
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);
goto error;
}
pw_thread_loop_unlock(priv->loop);
return res;
error:
pw_thread_loop_unlock(priv->loop);
error_no_unlock:
uninit(ao);
return -1;
}
static void hotplug_uninit(struct ao *ao)
{
struct priv *priv = ao->priv;
pw_thread_loop_lock(priv->loop);
spa_hook_remove(&priv->hotplug.registry_listener);
pw_proxy_destroy((struct pw_proxy *)priv->hotplug.registry);
pw_thread_loop_unlock(priv->loop);
uninit(ao);
}
static void list_devs(struct ao *ao, struct ao_device_list *list)
{
ao_device_list_add(list, ao, &(struct ao_device_desc){});
if (for_each_sink(ao, add_device_to_list, list) < 0)
MP_WARN(ao, "Could not list devices, list may be incomplete\n");
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_pipewire = {
.description = "PipeWire audio output",
.name = "pipewire",
.init = init,
.uninit = uninit,
.reset = reset,
.start = start,
.control = control,
.hotplug_init = hotplug_init,
.hotplug_uninit = hotplug_uninit,
.list_devs = list_devs,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv)
{
.loop = NULL,
.stream = NULL,
.init_state = INIT_STATE_NONE,
.options.buffer_msec = 0,
.options.volume_mode = VOLUME_MODE_CHANNEL,
},
.options_prefix = "pipewire",
.options = (const struct m_option[]) {
{"buffer", OPT_CHOICE(options.buffer_msec, {"native", 0}),
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}
},
};
+35 -23
View File
@@ -28,6 +28,7 @@
#include <pulse/pulseaudio.h>
#include "config.h"
#include "audio/format.h"
#include "common/msg.h"
#include "options/m_option.h"
@@ -56,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) \
@@ -534,7 +535,6 @@ static void reset(struct ao *ao)
cork(ao, true);
struct priv *priv = ao->priv;
pa_threaded_mainloop_lock(priv->mainloop);
priv->playing = false;
priv->retval = 0;
if (!waitop(priv, pa_stream_flush(priv->stream, success_cb, ao)) ||
!priv->retval)
@@ -679,8 +679,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;
@@ -690,36 +696,42 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
case AOCONTROL_SET_MUTE:
case AOCONTROL_SET_VOLUME: {
pa_operation *o;
pa_threaded_mainloop_lock(priv->mainloop);
priv->retval = 0;
uint32_t stream_index = pa_stream_get_index(priv->stream);
if (cmd == AOCONTROL_SET_VOLUME) {
const 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 (!waitop(priv, pa_context_set_sink_input_volume(priv->context,
stream_index,
&volume,
context_success_cb, ao)) ||
!priv->retval) {
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);
}
o = pa_context_set_sink_input_volume(priv->context, stream_index,
&volume, NULL, NULL);
if (!o) {
pa_threaded_mainloop_unlock(priv->mainloop);
GENERIC_ERR_MSG("pa_context_set_sink_input_volume() failed");
return CONTROL_ERROR;
}
} else if (cmd == AOCONTROL_SET_MUTE) {
const bool *mute = arg;
if (!waitop(priv, pa_context_set_sink_input_mute(priv->context,
stream_index,
*mute,
context_success_cb, ao)) ||
!priv->retval) {
o = pa_context_set_sink_input_mute(priv->context, stream_index,
*mute, NULL, NULL);
if (!o) {
pa_threaded_mainloop_unlock(priv->mainloop);
GENERIC_ERR_MSG("pa_context_set_sink_input_mute() failed");
return CONTROL_ERROR;
}
} else {
MP_ASSERT_UNREACHABLE();
}
} else
abort();
/* We don't wait for completion here */
pa_operation_unref(o);
pa_threaded_mainloop_unlock(priv->mainloop);
return CONTROL_OK;
}
@@ -810,8 +822,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",
+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"
-316
View File
@@ -1,316 +0,0 @@
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
* Copyright (c) 2013 Christian Neukirchen <chneukirchen@gmail.com>
* Copyright (c) 2020 Rozhuk Ivan <rozhuk.im@gmail.com>
* Copyright (c) 2021 Andrew Krasavin <noiseless-ak@yandex.ru>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <poll.h>
#include <errno.h>
#include <sndio.h>
#include "options/m_option.h"
#include "common/msg.h"
#include "audio/format.h"
#include "ao.h"
#include "internal.h"
struct priv {
struct sio_hdl *hdl;
struct sio_par par;
int delay;
bool playing;
int vol;
int havevol;
struct pollfd *pfd;
};
static const struct mp_chmap sndio_layouts[] = {
{0}, /* empty */
{1, {MP_SPEAKER_ID_FL}}, /* mono */
MP_CHMAP2(FL, FR), /* stereo */
{0}, /* 2.1 */
MP_CHMAP4(FL, FR, BL, BR), /* 4.0 */
{0}, /* 5.0 */
MP_CHMAP6(FL, FR, BL, BR, FC, LFE), /* 5.1 */
{0}, /* 6.1 */
MP_CHMAP8(FL, FR, BL, BR, FC, LFE, SL, SR), /* 7.1 */
/* Above is the fixed channel assignment for sndio, since we need to
* fill all channels and cannot insert silence, not all layouts are
* supported.
* NOTE: MP_SPEAKER_ID_NA could be used to add padding channels. */
};
static void uninit(struct ao *ao);
/* Make libsndio call movecb(). */
static void process_events(struct ao *ao)
{
struct priv *p = ao->priv;
int n = sio_pollfd(p->hdl, p->pfd, POLLOUT);
while (poll(p->pfd, n, 0) < 0 && errno == EINTR);
sio_revents(p->hdl, p->pfd);
}
/* Call-back invoked to notify of the hardware position. */
static void movecb(void *addr, int delta)
{
struct ao *ao = addr;
struct priv *p = ao->priv;
p->delay -= delta;
}
/* Call-back invoked to notify about volume changes. */
static void volcb(void *addr, unsigned newvol)
{
struct ao *ao = addr;
struct priv *p = ao->priv;
p->vol = newvol;
}
static int init(struct ao *ao)
{
struct priv *p = ao->priv;
struct mp_chmap_sel sel = {0};
size_t i;
struct af_to_par {
int format, bits, sig;
};
static const struct af_to_par af_to_par[] = {
{AF_FORMAT_U8, 8, 0},
{AF_FORMAT_S16, 16, 1},
{AF_FORMAT_S32, 32, 1},
};
const struct af_to_par *ap;
const char *device = ((ao->device) ? ao->device : SIO_DEVANY);
/* Opening device. */
MP_VERBOSE(ao, "Using '%s' audio device.\n", device);
p->hdl = sio_open(device, SIO_PLAY, 0);
if (p->hdl == NULL) {
MP_ERR(ao, "Can't open audio device %s.\n", device);
goto err_out;
}
sio_initpar(&p->par);
/* Selecting sound format. */
ao->format = af_fmt_from_planar(ao->format);
p->par.bits = 16;
p->par.sig = 1;
p->par.le = SIO_LE_NATIVE;
for (i = 0; i < MP_ARRAY_SIZE(af_to_par); i++) {
ap = &af_to_par[i];
if (ap->format == ao->format) {
p->par.bits = ap->bits;
p->par.sig = ap->sig;
break;
}
}
p->par.rate = ao->samplerate;
/* Channels count. */
for (i = 0; i < MP_ARRAY_SIZE(sndio_layouts); i++) {
mp_chmap_sel_add_map(&sel, &sndio_layouts[i]);
}
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
goto err_out;
p->par.pchan = ao->channels.num;
p->par.appbufsz = p->par.rate * 250 / 1000; /* 250ms buffer */
p->par.round = p->par.rate * 10 / 1000; /* 10ms block size */
if (!sio_setpar(p->hdl, &p->par)) {
MP_ERR(ao, "couldn't set params\n");
goto err_out;
}
/* Get current sound params. */
if (!sio_getpar(p->hdl, &p->par)) {
MP_ERR(ao, "couldn't get params\n");
goto err_out;
}
if (p->par.bps > 1 && p->par.le != SIO_LE_NATIVE) {
MP_ERR(ao, "swapped endian output not supported\n");
goto err_out;
}
/* Update sound params. */
if (p->par.bits == 8 && p->par.bps == 1 && !p->par.sig) {
ao->format = AF_FORMAT_U8;
} else if (p->par.bits == 16 && p->par.bps == 2 && p->par.sig) {
ao->format = AF_FORMAT_S16;
} else if ((p->par.bits == 32 || p->par.msb) && p->par.bps == 4 && p->par.sig) {
ao->format = AF_FORMAT_S32;
} else {
MP_ERR(ao, "couldn't set format\n");
goto err_out;
}
p->havevol = sio_onvol(p->hdl, volcb, ao);
sio_onmove(p->hdl, movecb, ao);
p->pfd = talloc_array_ptrtype(p, p->pfd, sio_nfds(p->hdl));
if (!p->pfd)
goto err_out;
ao->device_buffer = p->par.bufsz;
MP_VERBOSE(ao, "bufsz = %i, appbufsz = %i, round = %i\n",
p->par.bufsz, p->par.appbufsz, p->par.round);
p->delay = 0;
p->playing = false;
if (!sio_start(p->hdl)) {
MP_ERR(ao, "start: sio_start() fail.\n");
goto err_out;
}
return 0;
err_out:
uninit(ao);
return -1;
}
static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->hdl) {
sio_close(p->hdl);
p->hdl = NULL;
}
p->pfd = NULL;
p->playing = false;
}
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct priv *p = ao->priv;
float *vol = arg;
switch (cmd) {
case AOCONTROL_GET_VOLUME:
if (!p->havevol)
return CONTROL_FALSE;
*vol = p->vol * 100 / SIO_MAXVOL;
break;
case AOCONTROL_SET_VOLUME:
if (!p->havevol)
return CONTROL_FALSE;
sio_setvol(p->hdl, *vol * SIO_MAXVOL / 100);
break;
default:
return CONTROL_UNKNOWN;
}
return CONTROL_OK;
}
static void reset(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->playing) {
p->playing = false;
if (!sio_stop(p->hdl)) {
MP_ERR(ao, "reset: couldn't sio_stop()\n");
}
p->delay = 0;
if (!sio_start(p->hdl)) {
MP_ERR(ao, "reset: sio_start() fail.\n");
}
}
}
static void start(struct ao *ao)
{
struct priv *p = ao->priv;
p->playing = true;
process_events(ao);
}
static bool audio_write(struct ao *ao, void **data, int samples)
{
struct priv *p = ao->priv;
const size_t size = (samples * ao->sstride);
size_t rc;
rc = sio_write(p->hdl, data[0], size);
if (rc != size) {
MP_WARN(ao, "audio_write: unexpected partial write: required: %zu, written: %zu.\n",
size, rc);
reset(ao);
p->playing = false;
return false;
}
p->delay += samples;
return true;
}
static void get_state(struct ao *ao, struct mp_pcm_state *state)
{
struct priv *p = ao->priv;
process_events(ao);
/* how many samples we can play without blocking */
state->free_samples = ao->device_buffer - p->delay;
state->free_samples = state->free_samples / p->par.round * p->par.round;
/* how many samples are already in the buffer to be played */
state->queued_samples = p->delay;
/* delay in seconds between first and last sample in buffer */
state->delay = p->delay / (double)p->par.rate;
/* report unexpected EOF / underrun */
if ((state->queued_samples && state->queued_samples &&
(state->queued_samples < state->free_samples) &&
p->playing) || sio_eof(p->hdl))
{
MP_VERBOSE(ao, "get_state: EOF/underrun detected.\n");
MP_VERBOSE(ao, "get_state: free: %d, queued: %d, delay: %lf\n", \
state->free_samples, state->queued_samples, state->delay);
p->playing = false;
state->playing = p->playing;
ao_wakeup_playthread(ao);
} else {
state->playing = p->playing;
}
}
const struct ao_driver audio_out_sndio = {
.name = "sndio",
.description = "sndio audio output",
.init = init,
.uninit = uninit,
.control = control,
.reset = reset,
.start = start,
.write = audio_write,
.get_state = get_state,
.priv_size = sizeof(struct priv),
};
+23 -25
View File
@@ -348,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;
@@ -378,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:
@@ -406,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 ?
+7 -11
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);
@@ -543,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);
@@ -558,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;
}
@@ -672,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
+12 -16
View File
@@ -162,8 +162,7 @@ static int read_buffer(struct ao *ao, void **data, int samples, bool *eof)
// pad with silence (underflow/paused/eof)
for (int n = 0; n < ao->num_planes; n++) {
af_fill_silence((char *)data[n] + pos * ao->sstride,
(samples - pos) * ao->sstride,
af_fill_silence((char *)data[n] + pos, (samples - pos) * ao->sstride,
ao->format);
}
@@ -193,7 +192,6 @@ int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us)
if (pos < samples && p->playing && !p->paused) {
p->playing = false;
ao->wakeup_cb(ao->wakeup_ctx);
// For ao_drain().
pthread_cond_broadcast(&p->wakeup);
}
@@ -357,7 +355,7 @@ void ao_set_paused(struct ao *ao, bool paused)
pthread_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)
@@ -459,7 +457,7 @@ void ao_uninit(struct ao *ao)
{
struct buffer_state *p = ao->buffer_state;
if (p && p->thread_valid) {
if (p->thread_valid) {
pthread_mutex_lock(&p->pt_lock);
p->terminate = true;
pthread_cond_broadcast(&p->pt_wakeup);
@@ -472,19 +470,17 @@ void ao_uninit(struct ao *ao)
if (ao->driver_initialized)
ao->driver->uninit(ao);
if (p) {
talloc_free(p->filter_root);
talloc_free(p->queue);
talloc_free(p->pending);
talloc_free(p->convert_buffer);
talloc_free(p->temp_buf);
talloc_free(p->filter_root);
talloc_free(p->queue);
talloc_free(p->pending);
talloc_free(p->convert_buffer);
talloc_free(p->temp_buf);
pthread_cond_destroy(&p->wakeup);
pthread_mutex_destroy(&p->lock);
pthread_cond_destroy(&p->wakeup);
pthread_mutex_destroy(&p->lock);
pthread_cond_destroy(&p->pt_wakeup);
pthread_mutex_destroy(&p->pt_lock);
}
pthread_cond_destroy(&p->pt_wakeup);
pthread_mutex_destroy(&p->pt_lock);
talloc_free(ao);
}
+11 -6
View File
@@ -2,13 +2,13 @@
# This script simply downloads waf to the current directory
from __future__ import print_function
import os, sys, stat, hashlib, subprocess
from urllib.request import urlopen, URLError
WAFRELEASE = "waf-2.0.25"
WAFRELEASE = "waf-2.0.20"
WAFURLS = ["https://waf.io/" + WAFRELEASE,
"https://www.freehackers.org/~tnagy/release/" + WAFRELEASE]
SHA256HASH = "21199cd220ccf60434133e1fd2ab8c8e5217c3799199c82722543970dc8e38d5"
"http://www.freehackers.org/~tnagy/release/" + WAFRELEASE]
SHA256HASH = "bf971e98edc2414968a262c6aa6b88541a26c3cd248689c89f4c57370955ee7f"
if os.path.exists("waf"):
wafver = subprocess.check_output([sys.executable, './waf', '--version']).decode()
@@ -20,6 +20,11 @@ if "--no-download" in sys.argv[1:]:
print("Did not find {} and no download was requested.".format(WAFRELEASE))
sys.exit(1)
try:
from urllib.request import urlopen, URLError
except:
from urllib2 import urlopen, URLError
waf = None
for WAFURL in WAFURLS:
@@ -27,8 +32,8 @@ for WAFURL in WAFURLS:
print("Downloading {}...".format(WAFURL))
waf = urlopen(WAFURL).read()
break
except URLError as err:
print("Download failed! ({})".format(err))
except URLError:
print("Download failed.")
if not waf:
print("Could not download {}.".format(WAFRELEASE))
+1 -25
View File
@@ -5,46 +5,22 @@ export CFLAGS="$CFLAGS -isystem/usr/local/include"
export CXXFLAGS="$CXXFLAGS -isystem/usr/local/include"
export LDFLAGS="$LDFLAGS -L/usr/local/lib"
meson setup build \
-Dlibmpv=true \
-Dlua=enabled \
-Degl-drm=enabled \
-Dopenal=enabled \
-Dsdl2=enabled \
-Dsndio=enabled \
-Dtests=true \
-Dvdpau=enabled \
-Dvulkan=enabled \
-Doss-audio=enabled \
$(pkg info -q v4l_compat && echo -Ddvbin=enabled) \
$(pkg info -q libdvdnav && echo -Ddvdnav=enabled) \
$(pkg info -q libcdio-paranoia && echo -Dcdda=enabled) \
$(pkg info -q pipewire && echo -Dpipewire=enabled) \
$NULL
meson compile -C build
./build/mpv -v --no-config
if [ ! -e "./waf" ] ; then
python3 ./bootstrap.py
fi
python3 ./waf configure \
--out=build_waf \
--enable-libmpv-shared \
--enable-lua \
--enable-egl-drm \
--enable-openal \
--enable-sdl2 \
--enable-sndio \
--enable-vaapi-wayland \
--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
./build_waf/mpv -v --no-config
Loaded 100 of 502 files, more files were not shown because too many files have changed in this diff. Show more