Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5d3e43198 | ||
|
|
cb3fa04bcb |
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -36,5 +36,3 @@
|
||||
|
||||
/old_build
|
||||
/Makefile
|
||||
|
||||
/subprojects
|
||||
+177
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
======================
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
+8
-52
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
Executable
+24
@@ -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
|
||||
Executable
+72
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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 } \
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
@@ -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))
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
Executable
+139
@@ -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])
|
||||
Executable
+29
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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)},
|
||||
|
||||
@@ -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}
|
||||
},
|
||||
|
||||
@@ -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)},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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, ®istry_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(®istry_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
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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 ?
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
Reference in new issue
Block a user