Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1d6257731 | ||
|
|
ba9b2f1e49 | ||
|
|
e1993d5ad2 | ||
|
|
0607dcebff | ||
|
|
a33a44cf4e | ||
|
|
273607c1c6 | ||
|
|
5b7d0fbab3 | ||
|
|
ec567d8a77 | ||
|
|
f8c57433b9 | ||
|
|
ead2933792 | ||
|
|
181e1377b2 |
No files matched your search
@@ -1,23 +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
|
||||
max_line_length = 80
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[.git/COMMIT*]
|
||||
max_line_length = 72
|
||||
|
||||
[{osdep/dirent-win.h,TOOLS/osxbundle/mpv.app/Contents/PkgInfo,TOOLS/osxbundle/mpv.app/Contents/Resources/Assets.car}]
|
||||
charset = unset
|
||||
insert_final_newline = unset
|
||||
indent_style = unset
|
||||
indent_size = unset
|
||||
max_line_length = unset
|
||||
trim_trailing_whitespace = unset
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"Format": "github-actions",
|
||||
"Disable": {
|
||||
"IndentSize": true,
|
||||
"MaxLineLength": true
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
name: "Report a Linux Issue"
|
||||
description: "Create a report for a runtime related Linux Issue"
|
||||
labels: ["os:linux"]
|
||||
type: Bug
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "mpv Information"
|
||||
placeholder: |
|
||||
mpv v0.38.0-96-gf776be6af5 Copyright © 2000-2024 mpv/MPlayer/mplayer2 projects
|
||||
built on May 5 2024 18:37:43
|
||||
libplacebo version: v7.349.0 (v6.338.0-120-g7b294350)
|
||||
FFmpeg version: 6.1.1
|
||||
FFmpeg library versions:
|
||||
libavutil 58.29.100
|
||||
libavcodec 60.31.102
|
||||
libavformat 60.16.100
|
||||
libswscale 7.5.100
|
||||
libavfilter 9.12.100
|
||||
libswresample 4.12.100
|
||||
description: |
|
||||
Provide the output of `mpv --version`.
|
||||
render: bash
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Other Information"
|
||||
description: >
|
||||
Provide following information if applicable.
|
||||
- Linux Version: `cat /etc/os-release | grep "NAME"`
|
||||
- Kernel Version: `uname -a`
|
||||
- GPU Model: `lspci -nn | grep VGA` or `lshw -C display -numeric`
|
||||
- Mesa/GPU Driver Version: `glxinfo -B | grep "OpenGL version string"` or `vulkaninfo | grep driverInfo`
|
||||
- Window Manager and Version
|
||||
- Source of the mpv binary
|
||||
- Latest version of mpv you know without the problem
|
||||
- The time when the problem started to happen (after updating mpv, system, driver, etc.)
|
||||
- Possible screenshot or video of visual glitches
|
||||
|
||||
If you're not using git master or the latest release, update.
|
||||
Packages and builds can be found [here](https://mpv.io/installation/).
|
||||
value: |-
|
||||
- Linux version:
|
||||
- Kernel Version:
|
||||
- GPU Model:
|
||||
- Mesa/GPU Driver Version:
|
||||
- Window Manager and Version:
|
||||
- Source of mpv:
|
||||
- Latest known working version:
|
||||
- Issue started after the following happened:
|
||||
render: text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Reproduction Steps"
|
||||
description: >
|
||||
Try to reproduce your issue with `--no-config` first. If it isn't reproducible
|
||||
with `--no-config` try to find out which option or script causes your issue.
|
||||
|
||||
|
||||
If the issue is render performance-related, try to reproduce it with `--no-config
|
||||
--profile=fast`, which is designed to work on lower-end mobile devices.
|
||||
|
||||
|
||||
For decoding performance-related issues, additionally try to enable hardware
|
||||
decoding with `--hwdec=yes` or the `Ctrl+h` shortcut.
|
||||
|
||||
|
||||
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.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Expected Behavior"
|
||||
placeholder: "What were you expecting?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Actual Behavior"
|
||||
placeholder: "What happened instead?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Log File"
|
||||
placeholder: "Drag and drop log file here (Don't paste content directly)"
|
||||
description: >
|
||||
Make a log file with `--gpu-debug --log-file=output.txt`, which is our preferred way, and
|
||||
attach it to the issue.
|
||||
|
||||
|
||||
If you have trouble producing a log file, you can alternatively use `--gpu-debug --msg-time -v -v`,
|
||||
save the terminal output to a file, and attach it to the issue.
|
||||
|
||||
|
||||
In the case of a crash, please provide a [backtrace](https://wiki.archlinux.org/title/Debugging/Getting_traces).
|
||||
|
||||
|
||||
Without the log file, this issue will be closed for ignoring the issue template.
|
||||
|
||||
|
||||
Disclaimer: The log file may contain identifiable information, such as your username.
|
||||
Review the log file before sharing and redact any sensitive data if necessary.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Sample Files"
|
||||
placeholder: "Drag and drop sample files here"
|
||||
description: >
|
||||
Please provide sample files for a minimal reproduction case, if the issue can't be
|
||||
reproduced without it or it is helpful to demonstrate the issue.
|
||||
|
||||
|
||||
Preferably files should be attached directly within the text field below. Due to GitHub's
|
||||
upload restriction, you may need to archive the sample file first and upload it as `.zip`
|
||||
file. If the sample file is too big to attach directly, use a solution that is easy to
|
||||
access with possible long-term retention. Possible services include, but are not limited
|
||||
to, the following:
|
||||
|
||||
- [0x0.st](https://0x0.st/) (no long-term retention)
|
||||
- [Dropbox](https://www.dropbox.com)
|
||||
- [Google Drive](https://www.google.com/drive/)
|
||||
- [iCLoud Drive](https://www.icloud.com/iclouddrive/)
|
||||
- [MediaFire](https://www.mediafire.com/)
|
||||
- [OneDrive](https://onedrive.live.com/)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: "I carefully read all instruction and confirm that I did the following:"
|
||||
options:
|
||||
- label: "I tested with the latest mpv version to validate that the issue is not already fixed."
|
||||
required: true
|
||||
- label: "I provided all required information including system and mpv version."
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file with the exact same set of files, parameters, and conditions
|
||||
used in "Reproduction Steps", with the addition of `--log-file=output.txt`.
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file while the behaviors described in "Actual Behavior" were
|
||||
actively observed.
|
||||
required: true
|
||||
- label: "I attached the full, untruncated log file."
|
||||
required: true
|
||||
- label: "I attached the backtrace in the case of a crash."
|
||||
required: true
|
||||
@@ -1,146 +0,0 @@
|
||||
name: "Report a macOS Issue"
|
||||
description: "Create a report for a runtime related macOS Issue"
|
||||
labels: ["os:mac"]
|
||||
type: Bug
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "mpv Information"
|
||||
placeholder: |
|
||||
mpv v0.38.0-96-gf776be6af5 Copyright © 2000-2024 mpv/MPlayer/mplayer2 projects
|
||||
built on May 5 2024 18:37:43
|
||||
libplacebo version: v7.349.0 (v6.338.0-120-g7b294350)
|
||||
FFmpeg version: 6.1.1
|
||||
FFmpeg library versions:
|
||||
libavutil 58.29.100
|
||||
libavcodec 60.31.102
|
||||
libavformat 60.16.100
|
||||
libswscale 7.5.100
|
||||
libavfilter 9.12.100
|
||||
libswresample 4.12.100
|
||||
description: |
|
||||
Provide the output of `mpv --version`.
|
||||
render: bash
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Other Information"
|
||||
description: >
|
||||
- macOS Version: `system_profiler SPSoftwareDataType | sed -rn 's/^ *System Version: (.*$)/\1/p'`
|
||||
- Source of the mpv binary or bundle
|
||||
- Latest version of mpv you know without the problem
|
||||
- The time when the problem started to happen (after updating mpv, system, driver, etc.)
|
||||
- Possible screenshot or video of visual glitches
|
||||
|
||||
If you're not using git master or the latest release, update.
|
||||
Nightly builds from master can be found [here](https://github.com/mpv-player/mpv/actions/workflows/build.yml?query=branch%3Amaster+event%3Apush),
|
||||
click on the latest workflow run, scroll down to the Artifacts section and download the
|
||||
appropriate build. Packages and builds can be found [here](https://mpv.io/installation/).
|
||||
value: |-
|
||||
- macOS version:
|
||||
- Source of mpv:
|
||||
- Latest known working version:
|
||||
- Issue started after the following happened:
|
||||
render: text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Reproduction Steps"
|
||||
description: >
|
||||
Try to reproduce your issue with `--no-config` first. If it isn't reproducible
|
||||
with `--no-config` try to find out which option or script causes your issue.
|
||||
|
||||
|
||||
If the issue is render performance-related, try to reproduce it with `--no-config
|
||||
--profile=fast`, which is designed to work on lower-end mobile devices.
|
||||
|
||||
|
||||
For decoding performance-related issues, additionally try to enable hardware
|
||||
decoding with `--hwdec=yes` or the `Ctrl+h` shortcut.
|
||||
|
||||
|
||||
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.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Expected Behavior"
|
||||
placeholder: "What were you expecting?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Actual Behavior"
|
||||
placeholder: "What happened instead?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Log File"
|
||||
placeholder: "Drag and drop log file here (Don't paste content directly)"
|
||||
description: >
|
||||
Make a log file with `--gpu-debug --log-file=output.txt`, which is our preferred way, and
|
||||
attach it to the issue. If you use the Bundle, a default log is created for your last run
|
||||
at `~/Library/Logs/mpv.log`. You can jump to that file via the `Help > Show log File…` menu.
|
||||
|
||||
|
||||
If you have trouble producing a log file, you can alternatively use `--gpu-debug --msg-time -v -v`,
|
||||
save the terminal output to a file, and attach it to the issue.
|
||||
|
||||
|
||||
In the case of a crash, please provide the macOS Crash Report or create a [backtrace](https://developer.apple.com/library/archive/documentation/General/Conceptual/lldb-guide/chapters/C5-Examining-The-Call-Stack.html)
|
||||
yourself.
|
||||
|
||||
|
||||
Without the log file, this issue will be closed for ignoring the issue template.
|
||||
|
||||
|
||||
Disclaimer: The log file may contain identifiable information, such as your username.
|
||||
Review the log file before sharing and redact any sensitive data if necessary.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Sample Files"
|
||||
placeholder: "Drag and drop sample files here"
|
||||
description: >
|
||||
Please provide sample files for a minimal reproduction case, if the issue can't be
|
||||
reproduced without it or it is helpful to demonstrate the issue.
|
||||
|
||||
|
||||
Preferably files should be attached directly within the text field below. Due to GitHub's
|
||||
upload restriction, you may need to archive the sample file first and upload it as `.zip`
|
||||
file. If the sample file is too big to attach directly, use a solution that is easy to
|
||||
access with possible long-term retention. Possible services include, but are not limited
|
||||
to, the following:
|
||||
|
||||
- [0x0.st](https://0x0.st/) (no long-term retention)
|
||||
- [Dropbox](https://www.dropbox.com)
|
||||
- [Google Drive](https://www.google.com/drive/)
|
||||
- [iCLoud Drive](https://www.icloud.com/iclouddrive/)
|
||||
- [MediaFire](https://www.mediafire.com/)
|
||||
- [OneDrive](https://onedrive.live.com/)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: "I carefully read all instruction and confirm that I did the following:"
|
||||
options:
|
||||
- label: "I tested with the latest mpv version to validate that the issue is not already fixed."
|
||||
required: true
|
||||
- label: "I provided all required information including system and mpv version."
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file with the exact same set of files, parameters, and conditions
|
||||
used in "Reproduction Steps", with the addition of `--log-file=output.txt`.
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file while the behaviors described in "Actual Behavior" were
|
||||
actively observed.
|
||||
required: true
|
||||
- label: "I attached the full, untruncated log file."
|
||||
required: true
|
||||
- label: "I attached the backtrace in the case of a crash."
|
||||
required: true
|
||||
@@ -1,147 +0,0 @@
|
||||
name: "Report a Windows Issue"
|
||||
description: "Create a report for a runtime related Windows Issue"
|
||||
labels: ["os:win"]
|
||||
type: Bug
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "mpv Information"
|
||||
placeholder: |
|
||||
mpv v0.38.0-96-gf776be6af5 Copyright © 2000-2024 mpv/MPlayer/mplayer2 projects
|
||||
built on May 5 2024 18:37:43
|
||||
libplacebo version: v7.349.0 (v6.338.0-120-g7b294350)
|
||||
FFmpeg version: 6.1.1
|
||||
FFmpeg library versions:
|
||||
libavutil 58.29.100
|
||||
libavcodec 60.31.102
|
||||
libavformat 60.16.100
|
||||
libswscale 7.5.100
|
||||
libavfilter 9.12.100
|
||||
libswresample 4.12.100
|
||||
description: |
|
||||
Provide the output of `mpv --version`.
|
||||
render: bash
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Other Information"
|
||||
description: >
|
||||
- Windows Version: `cmd /c ver`
|
||||
- GPU model, driver and version
|
||||
- Source of the mpv binary
|
||||
- Latest version of mpv you know without the problem
|
||||
- The time when the problem started to happen (after updating mpv, system, driver, etc.)
|
||||
- Possible screenshot or video of visual glitches
|
||||
|
||||
If you're not using git master or the latest release, update.
|
||||
Nightly builds from master can be found [here](https://github.com/mpv-player/mpv/actions/workflows/build.yml?query=branch%3Amaster+event%3Apush),
|
||||
click on the latest workflow run, scroll down to the Artifacts section and download the
|
||||
appropriate build. Packages and builds can be found [here](https://mpv.io/installation/).
|
||||
value: |-
|
||||
- Windows version:
|
||||
- GPU model, driver and version:
|
||||
- Source of mpv:
|
||||
- Latest known working version:
|
||||
- Issue started after the following happened:
|
||||
render: text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Reproduction Steps"
|
||||
description: >
|
||||
Try to reproduce your issue with `--no-config` first. If it isn't reproducible
|
||||
with `--no-config` try to find out which option or script causes your issue.
|
||||
|
||||
|
||||
If the issue is render performance-related, try to reproduce it with `--no-config
|
||||
--profile=fast`, which is designed to work on lower-end mobile devices.
|
||||
|
||||
|
||||
For decoding performance-related issues, additionally try to enable hardware
|
||||
decoding with `--hwdec=yes` or the `Ctrl+h` shortcut.
|
||||
|
||||
|
||||
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.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Expected Behavior"
|
||||
placeholder: "What were you expecting?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Actual Behavior"
|
||||
placeholder: "What happened instead?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Log File"
|
||||
placeholder: "Drag and drop log file here (Don't paste content directly)"
|
||||
description: >
|
||||
Make a log file with `--gpu-debug --log-file=output.txt`, which is our preferred way, and
|
||||
attach it to the issue.
|
||||
|
||||
|
||||
If you have trouble producing a log file, you can alternatively use `--gpu-debug --msg-time -v -v`,
|
||||
save the terminal output to a file, and attach it to the issue.
|
||||
|
||||
|
||||
In the case of a crash, please provide a backtrace, attach the appropriate `.dmp` file
|
||||
from `%LOCALAPPDATA%\CrashDumps`.
|
||||
|
||||
|
||||
Without the log file, this issue will be closed for ignoring the issue template.
|
||||
|
||||
|
||||
Disclaimer: The log file may contain identifiable information, such as your username.
|
||||
Review the log file before sharing and redact any sensitive data if necessary.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Sample Files"
|
||||
placeholder: "Drag and drop sample files here"
|
||||
description: >
|
||||
Please provide sample files for a minimal reproduction case, if the issue can't be
|
||||
reproduced without it or it is helpful to demonstrate the issue.
|
||||
|
||||
|
||||
Preferably files should be attached directly within the text field below. Due to GitHub's
|
||||
upload restriction, you may need to archive the sample file first and upload it as `.zip`
|
||||
file. If the sample file is too big to attach directly, use a solution that is easy to
|
||||
access with possible long-term retention. Possible services include, but are not limited
|
||||
to, the following:
|
||||
|
||||
- [0x0.st](https://0x0.st/) (no long-term retention)
|
||||
- [Dropbox](https://www.dropbox.com)
|
||||
- [Google Drive](https://www.google.com/drive/)
|
||||
- [iCLoud Drive](https://www.icloud.com/iclouddrive/)
|
||||
- [MediaFire](https://www.mediafire.com/)
|
||||
- [OneDrive](https://onedrive.live.com/)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: "I carefully read all instruction and confirm that I did the following:"
|
||||
options:
|
||||
- label: "I tested with the latest mpv version to validate that the issue is not already fixed."
|
||||
required: true
|
||||
- label: "I provided all required information including system and mpv version."
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file with the exact same set of files, parameters, and conditions
|
||||
used in "Reproduction Steps", with the addition of `--log-file=output.txt`.
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file while the behaviors described in "Actual Behavior" were
|
||||
actively observed.
|
||||
required: true
|
||||
- label: "I attached the full, untruncated log file."
|
||||
required: true
|
||||
- label: "I attached the backtrace in the case of a crash."
|
||||
required: true
|
||||
@@ -1,144 +0,0 @@
|
||||
name: "Report an Issue on other platforms"
|
||||
description: "Create a report for a runtime related Issue on other platforms"
|
||||
labels: []
|
||||
type: Bug
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "mpv Information"
|
||||
placeholder: |
|
||||
mpv v0.38.0-96-gf776be6af5 Copyright © 2000-2024 mpv/MPlayer/mplayer2 projects
|
||||
built on May 5 2024 18:37:43
|
||||
libplacebo version: v7.349.0 (v6.338.0-120-g7b294350)
|
||||
FFmpeg version: 6.1.1
|
||||
FFmpeg library versions:
|
||||
libavutil 58.29.100
|
||||
libavcodec 60.31.102
|
||||
libavformat 60.16.100
|
||||
libswscale 7.5.100
|
||||
libavfilter 9.12.100
|
||||
libswresample 4.12.100
|
||||
description: |
|
||||
Provide the output of `mpv --version`.
|
||||
render: bash
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Important Information"
|
||||
description: >
|
||||
- Platform and Version
|
||||
- GPU model, driver and version
|
||||
- Source of the mpv binary
|
||||
- Latest version of mpv you know without the problem
|
||||
- The time when the problem started to happen (after updating mpv, system, driver, etc.)
|
||||
- Possible screenshot or video of visual glitches
|
||||
|
||||
If you're not using git master or the latest release, update.
|
||||
Packages and builds can be found [here](https://mpv.io/installation/).
|
||||
value: |-
|
||||
- Platform version:
|
||||
- GPU model, driver and version:
|
||||
- Source of mpv:
|
||||
- Latest known working version:
|
||||
- Issue started after the following happened:
|
||||
render: text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Reproduction Steps"
|
||||
description: >
|
||||
Try to reproduce your issue with `--no-config` first. If it isn't reproducible
|
||||
with `--no-config` try to find out which option or script causes your issue.
|
||||
|
||||
|
||||
If the issue is render performance-related, try to reproduce it with `--no-config
|
||||
--profile=fast`, which is designed to work on lower-end mobile devices.
|
||||
|
||||
|
||||
For decoding performance-related issues, additionally try to enable hardware
|
||||
decoding with `--hwdec=yes` or the `Ctrl+h` shortcut.
|
||||
|
||||
|
||||
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.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Expected Behavior"
|
||||
placeholder: "What were you expecting?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Actual Behavior"
|
||||
placeholder: "What happened instead?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Log File"
|
||||
placeholder: "Drag and drop log file here (Don't paste content directly)"
|
||||
description: >
|
||||
Make a log file with `--gpu-debug --log-file=output.txt`, which is our preferred way, and
|
||||
attach it to the issue.
|
||||
|
||||
|
||||
If you have trouble producing a log file, you can alternatively use `--gpu-debug --msg-time -v -v`,
|
||||
save the terminal output to a file, and attach it to the issue.
|
||||
|
||||
|
||||
In the case of a crash, please provide a backtrace.
|
||||
|
||||
|
||||
Without the log file, this issue will be closed for ignoring the issue template.
|
||||
|
||||
|
||||
Disclaimer: The log file may contain identifiable information, such as your username.
|
||||
Review the log file before sharing and redact any sensitive data if necessary.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Sample Files"
|
||||
placeholder: "Drag and drop sample files here"
|
||||
description: >
|
||||
Please provide sample files for a minimal reproduction case, if the issue can't be
|
||||
reproduced without it or it is helpful to demonstrate the issue.
|
||||
|
||||
|
||||
Preferably files should be attached directly within the text field below. Due to GitHub's
|
||||
upload restriction, you may need to archive the sample file first and upload it as `.zip`
|
||||
file. If the sample file is too big to attach directly, use a solution that is easy to
|
||||
access with possible long-term retention. Possible services include, but are not limited
|
||||
to, the following:
|
||||
|
||||
- [0x0.st](https://0x0.st/) (no long-term retention)
|
||||
- [Dropbox](https://www.dropbox.com)
|
||||
- [Google Drive](https://www.google.com/drive/)
|
||||
- [iCLoud Drive](https://www.icloud.com/iclouddrive/)
|
||||
- [MediaFire](https://www.mediafire.com/)
|
||||
- [OneDrive](https://onedrive.live.com/)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: "I carefully read all instruction and confirm that I did the following:"
|
||||
options:
|
||||
- label: "I tested with the latest mpv version to validate that the issue is not already fixed."
|
||||
required: true
|
||||
- label: "I provided all required information including system and mpv version."
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file with the exact same set of files, parameters, and conditions
|
||||
used in "Reproduction Steps", with the addition of `--log-file=output.txt`.
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file while the behaviors described in "Actual Behavior" were
|
||||
actively observed.
|
||||
required: true
|
||||
- label: "I attached the full, untruncated log file."
|
||||
required: true
|
||||
- label: "I attached the backtrace in the case of a crash."
|
||||
required: true
|
||||
@@ -1,71 +0,0 @@
|
||||
name: "Report a build Problem"
|
||||
description: "Create a report for a build related Issue"
|
||||
labels: ["core:meson"]
|
||||
type: Bug
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Important Information"
|
||||
description: >
|
||||
- mpv version
|
||||
- Platform and Version
|
||||
- Meson version
|
||||
|
||||
If you're not using git master or the latest release, update.
|
||||
Releases are listed [here](https://github.com/mpv-player/mpv/releases).
|
||||
value: |-
|
||||
- mpv version:
|
||||
- Platform version:
|
||||
- Meson version:
|
||||
render: text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Reproduction Steps"
|
||||
description: >
|
||||
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.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Expected Behavior"
|
||||
placeholder: "What were you expecting?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Actual Behavior"
|
||||
placeholder: "What happened instead?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Log File"
|
||||
placeholder: "Drag and drop log file here (Don't paste content directly)"
|
||||
description: >
|
||||
Attach meson-logs/meson-log.txt or meson setup build output to the issue.
|
||||
|
||||
Without the log file, this issue will be closed for ignoring the issue template.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: "I carefully read all instruction and confirm that I did the following:"
|
||||
options:
|
||||
- label: "I tested with the latest mpv version to validate that the issue is not already fixed."
|
||||
required: true
|
||||
- label: "I provided all required information including system and mpv version."
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file with the exact same set of parameters, and conditions used in
|
||||
"Reproduction Steps".
|
||||
required: true
|
||||
- label: >-
|
||||
I produced the log file while the behaviors described in "Actual Behavior" were
|
||||
actively observed.
|
||||
required: true
|
||||
- label: "I attached the full, untruncated log file."
|
||||
required: true
|
||||
@@ -1,56 +0,0 @@
|
||||
name: "Request a new Feature"
|
||||
description: "Create a request for a new feature"
|
||||
labels: ["meta:feature-request"]
|
||||
type: Feature
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Expected behavior of the wanted feature"
|
||||
description: >
|
||||
Before requesting a new feature, make sure it hasn't been
|
||||
[requested yet](https://github.com/mpv-player/mpv/labels/meta%3Afeature-request),
|
||||
and isn't already supported by [mpv](https://mpv.io/manual/master/),
|
||||
[ffmpeg](https://ffmpeg.org/documentation.html), or existing
|
||||
[mpv scripts](https://github.com/mpv-player/mpv/wiki/User-Scripts).
|
||||
|
||||
|
||||
If you are not sure about this, ask on
|
||||
[mpv Discussions](https://github.com/mpv-player/mpv/discussions) and
|
||||
[mpv IRC channel](https://mpv.io/community/) first before opening a feature request.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Alternative behavior of the wanted feature"
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Log File"
|
||||
description: >
|
||||
Even if you think it's not necessary at first, it might help us later to find
|
||||
possible issues. Make a log file with `--log-file=output.txt`, which is our preferred way,
|
||||
and attach it to the issue.
|
||||
|
||||
|
||||
If you have trouble producing a log file, you can alternatively use `-v -v`, save the
|
||||
terminal output to a file, and attach it to the issue.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Sample Files"
|
||||
placeholder: "Drag and drop sample files here"
|
||||
description: >
|
||||
Please provide sample files in the case you request a format/codec/container dependent
|
||||
feature, and it could help us test and implement it.
|
||||
|
||||
|
||||
Preferably files should be attached directly within the text field below. Due to GitHub's
|
||||
upload restriction, you may need to archive the sample file first and upload it as `.zip`
|
||||
file. If the sample file is too big to attach directly, use a solution that is easy to
|
||||
access with possible long-term retention. Possible services include, but are not limited
|
||||
to, the following:
|
||||
|
||||
- [0x0.st](https://0x0.st/) (no long-term retention)
|
||||
- [Dropbox](https://www.dropbox.com)
|
||||
- [Google Drive](https://www.google.com/drive/)
|
||||
- [iCLoud Drive](https://www.icloud.com/iclouddrive/)
|
||||
- [MediaFire](https://www.mediafire.com/)
|
||||
- [OneDrive](https://onedrive.live.com/)
|
||||
@@ -1,12 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: mpv Discussions
|
||||
url: https://github.com/mpv-player/mpv/discussions
|
||||
about: If you have questions or want to discuss things use our GitHub Discussions
|
||||
- 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
|
||||
url: https://mpv.io/community/
|
||||
about: Ask questions related to the development of mpv here
|
||||
irc://irc.libera.chat/mpv-devel
|
||||
@@ -1,5 +0,0 @@
|
||||
Read this before you submit this pull request:
|
||||
https://github.com/mpv-player/mpv/blob/master/DOCS/contribute.md
|
||||
|
||||
Reading this link and following the rules will get your pull request reviewed
|
||||
and merged faster. Nobody wants lazy pull requests.
|
||||
@@ -1,601 +0,0 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- ci
|
||||
- 'release/**'
|
||||
paths-ignore:
|
||||
- 'DOCS/**'
|
||||
- 'TOOLS/lua/**'
|
||||
- '.editorconfig'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.github/PULL_REQUEST_TEMPLATE'
|
||||
- '.gitignore'
|
||||
- 'Copyright'
|
||||
- 'README.md'
|
||||
- 'RELEASE_NOTES'
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths-ignore:
|
||||
- 'DOCS/**'
|
||||
- 'TOOLS/lua/**'
|
||||
- '.editorconfig'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.github/PULL_REQUEST_TEMPLATE'
|
||||
- '.gitignore'
|
||||
- 'Copyright'
|
||||
- 'README.md'
|
||||
- 'RELEASE_NOTES'
|
||||
|
||||
jobs:
|
||||
mingw:
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
CCACHE_BASEDIR: ${{ github.workspace }}
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_MAXSIZE: 500M
|
||||
TARGET: ${{ matrix.arch }}-w64-mingw32
|
||||
RUST_TARGET: ${{ matrix.arch }}-pc-windows-gnu
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: i686
|
||||
wine: wine32
|
||||
- arch: x86_64
|
||||
wine: wine64
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Get time
|
||||
id: get_time
|
||||
run: echo "timestamp=`date +%s%N`" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Restore cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.TARGET }}-${{ steps.get_time.outputs.timestamp }}
|
||||
restore-keys: ${{ env.TARGET }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rustup target add ${{ env.RUST_TARGET }}
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y ccache g++-mingw-w64 gcc-multilib python3-pip nasm ninja-build pkgconf ${{ matrix.wine }} wine
|
||||
python -m pip install meson
|
||||
|
||||
- name: Install Meson Wraps
|
||||
run: |
|
||||
mkdir subprojects -p
|
||||
meson wrap install mujs
|
||||
meson subprojects download
|
||||
|
||||
- name: Build libraries
|
||||
run: |
|
||||
./ci/build-mingw64.sh
|
||||
|
||||
- name: Build with meson
|
||||
id: build
|
||||
run: |
|
||||
./ci/build-mingw64.sh meson pack
|
||||
|
||||
- name: Print meson log
|
||||
if: ${{ failure() && steps.build.outcome == 'failure' }}
|
||||
run: |
|
||||
cat ./mingw_build/meson-logs/meson-log.txt
|
||||
|
||||
- name: Functional test
|
||||
run: |
|
||||
cd artifact && wine ./mpv.com -v --no-config
|
||||
env:
|
||||
WINEDEBUG: '+loaddll'
|
||||
|
||||
- name: Run meson tests
|
||||
id: tests
|
||||
run: |
|
||||
meson test -C mingw_build
|
||||
|
||||
- name: Print meson test log
|
||||
if: ${{ failure() && steps.tests.outcome == 'failure' }}
|
||||
run: |
|
||||
cat ./mingw_build/meson-logs/testlog.txt
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: mpv-${{ env.TARGET }}
|
||||
path: mpv-git-*.zip
|
||||
|
||||
- name: Save Cache
|
||||
uses: actions/cache/save@v4
|
||||
if: always()
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.TARGET }}-${{ steps.get_time.outputs.timestamp }}
|
||||
|
||||
win32:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-pc-windows-msvc
|
||||
arch: "x64"
|
||||
os: "windows-latest"
|
||||
- target: aarch64-pc-windows-msvc
|
||||
arch: "arm64"
|
||||
os: "windows-11-arm"
|
||||
env:
|
||||
VS: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise"
|
||||
CC: "ccache clang"
|
||||
CXX: "ccache clang++"
|
||||
CC_LD: "lld-link"
|
||||
CXX_LD: "lld-link"
|
||||
WINDRES: "llvm-rc"
|
||||
RUST_LD: "lld-link"
|
||||
RUSTC: "rustc --target ${{ matrix.target }}"
|
||||
CCACHE_BASEDIR: ${{ github.workspace }}
|
||||
CCACHE_DIR: "${{ github.workspace }}\\.ccache"
|
||||
CCACHE_MAXSIZE: 500M
|
||||
steps:
|
||||
- name: Disable autocrlf
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Get time
|
||||
id: get_time
|
||||
run: |
|
||||
"timestamp=$((Get-Date).Ticks)" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Restore cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ matrix.target }}-${{ steps.get_time.outputs.timestamp }}
|
||||
restore-keys: ${{ matrix.target }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install meson==1.9.2
|
||||
choco install ccache nasm
|
||||
./ci/install-cmake.ps1 -arch ${{ matrix.arch }}
|
||||
|
||||
- name: Update Meson WrapDB
|
||||
run: |
|
||||
meson wrap update-db
|
||||
# Explicitly download wraps, as nested projects may have older versions of them.
|
||||
meson wrap install expat
|
||||
meson wrap install harfbuzz
|
||||
meson wrap install libpng
|
||||
meson wrap install zlib
|
||||
|
||||
- name: Build
|
||||
id: build
|
||||
run: |
|
||||
$env:PATH = ($env:PATH -split ';' | Where-Object { $_ -ne 'C:\Program Files\LLVM\bin' -and `
|
||||
$_ -ne 'C:\Program Files\CMake\bin' -and `
|
||||
$_ -ne 'C:\Strawberry\c\bin' }) -join ';'
|
||||
$env:PATH = "$pwd\cmake\bin;" + $env:PATH + ';C:\Program Files\NASM'
|
||||
Import-Module "$env:VS\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
||||
Enter-VsDevShell -VsInstallPath $env:VS -SkipAutomaticLocation -DevCmdArguments "-arch=${{ matrix.arch }} -host_arch=${{ matrix.arch }}"
|
||||
./ci/build-win32.ps1
|
||||
|
||||
- name: Print build log
|
||||
if: ${{ failure() && steps.build.outcome == 'failure' }}
|
||||
run: |
|
||||
cat ./build/meson-logs/meson-log.txt
|
||||
|
||||
- name: Run meson tests
|
||||
id: tests
|
||||
run: |
|
||||
$env:PATH = ($env:PATH -split ';' | Where-Object { $_ -ne 'C:\Program Files\LLVM\bin' -and `
|
||||
$_ -ne 'C:\Program Files\CMake\bin' -and `
|
||||
$_ -ne 'C:\Strawberry\c\bin' }) -join ';'
|
||||
$env:PATH = "$pwd\cmake\bin;" + $env:PATH + ';C:\Program Files\NASM'
|
||||
Import-Module "$env:VS\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
||||
Enter-VsDevShell -VsInstallPath $env:VS -SkipAutomaticLocation -DevCmdArguments "-arch=${{ matrix.arch }} -host_arch=${{ matrix.arch }}"
|
||||
meson test -C build -t 2
|
||||
|
||||
- name: Print meson test log
|
||||
if: ${{ failure() && steps.tests.outcome == 'failure' }}
|
||||
run: |
|
||||
cat ./build/meson-logs/testlog.txt
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: mpv-${{ matrix.target }}
|
||||
path: |
|
||||
build/mpv.???
|
||||
build/mpv-*.bat
|
||||
build/vulkan-*.dll
|
||||
!build/mpv.lib
|
||||
|
||||
- name: Save Cache
|
||||
uses: actions/cache/save@v4
|
||||
if: always()
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ matrix.target }}-${{ steps.get_time.outputs.timestamp }}
|
||||
|
||||
macos:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: "macos-14"
|
||||
arch: "arm"
|
||||
xcode: "Xcode_15.0"
|
||||
- os: "macos-15"
|
||||
arch: "arm"
|
||||
- os: "macos-15-intel"
|
||||
- os: "macos-26"
|
||||
arch: "arm"
|
||||
- os: "macos-26"
|
||||
arch: "test"
|
||||
swift: "-target arm64-apple-macosx10.15"
|
||||
target: "10.15"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- 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: Unlink pkg-config if the CI runner happens to have it already installed
|
||||
run: |
|
||||
find "$(brew config | grep HOMEBREW_PREFIX | cut -f 2- -d ' ')/bin" -lname '*/pkg-config@0*/*' -print -exec brew unlink pkg-config@0.29.2 \; -quit
|
||||
|
||||
- name: Change Xcode version
|
||||
if: ${{ matrix.xcode != '' }}
|
||||
run: |
|
||||
sudo xcode-select -s /Applications/${{ matrix.xcode }}.app
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [[ -n "${{ matrix.homebrew }}" ]]; then
|
||||
export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
cd "$(brew --repo homebrew/core)"
|
||||
git checkout --force ${{ matrix.homebrew }}
|
||||
else
|
||||
brew update
|
||||
fi
|
||||
brew install -q autoconf automake pkgconf libtool python freetype fribidi little-cms2 \
|
||||
luajit libass ffmpeg meson rust uchardet mujs libplacebo molten-vk vulkan-loader vulkan-headers
|
||||
|
||||
- name: Build with meson
|
||||
id: build
|
||||
run: |
|
||||
./ci/build-macos.sh
|
||||
env:
|
||||
TRAVIS_OS_NAME: "${{ matrix.os }}"
|
||||
SWIFT_FLAGS: "${{ matrix.swift }}"
|
||||
MACOSX_DEPLOYMENT_TARGET: "${{ matrix.target }}"
|
||||
|
||||
- name: Create App Bundle
|
||||
if: ${{ matrix.arch != 'test' }}
|
||||
run: |
|
||||
meson compile -C build macos-bundle
|
||||
tar -czvf mpv.tar.gz -C build mpv.app
|
||||
|
||||
- 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
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
if: ${{ matrix.arch != 'test' }}
|
||||
with:
|
||||
name: mpv-${{ matrix.os }}${{ matrix.arch != '' && format('-{0}', matrix.arch) || '' }}
|
||||
path: mpv.tar.gz
|
||||
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: "registry.opensuse.org/home/mia/images/images/mpv-ci:stable-deps"
|
||||
env:
|
||||
CC: "${{ matrix.config.cc }}"
|
||||
CXX: "${{ matrix.config.cxx }}"
|
||||
CC_LD: "${{ matrix.config.ld }}"
|
||||
CXX_LD: "${{ matrix.config.ld }}"
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- cc: "gcc"
|
||||
cxx: "g++"
|
||||
args: "-Db_ndebug=true"
|
||||
- cc: "clang"
|
||||
cxx: "clang++"
|
||||
ld: "lld"
|
||||
args: "-Db_lto=true -Db_lto_mode=thin"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Build with meson
|
||||
id: build
|
||||
run: |
|
||||
./ci/build-tumbleweed.sh ${{ matrix.config.args }}
|
||||
|
||||
- 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
|
||||
|
||||
linux-fuzz:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: "registry.opensuse.org/home/mia/images/images/mpv-ci:stable-deps"
|
||||
env:
|
||||
CC: "clang"
|
||||
CXX: "clang++"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Build with meson
|
||||
id: build
|
||||
run: |
|
||||
meson setup build \
|
||||
--werror \
|
||||
-Dfuzzers=true -Dlibmpv=true -Dcplayer=false
|
||||
meson compile -C build fuzzers
|
||||
|
||||
- name: Print meson log
|
||||
if: ${{ failure() && steps.build.outcome == 'failure' }}
|
||||
run: |
|
||||
cat ./build/meson-logs/meson-log.txt
|
||||
|
||||
linux-ffmpeg-6-1:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
# We want to test ffmpeg 6.1.1 which alpine 3.19 has exactly.
|
||||
# Furthermore, this is a very minimal build of mpv without commonly
|
||||
# used features to test build compatibility. Musl is a nice bonus as well
|
||||
image: alpine:3.19
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apk update
|
||||
apk add \
|
||||
binutils \
|
||||
ffmpeg-dev \
|
||||
gcc \
|
||||
git \
|
||||
g++ \
|
||||
libass-dev \
|
||||
lua5.2-dev \
|
||||
pulseaudio-dev \
|
||||
libx11-dev \
|
||||
libxfixes-dev \
|
||||
libxkbcommon-dev \
|
||||
libxrandr-dev \
|
||||
libxpresent-dev \
|
||||
libxscrnsaver-dev \
|
||||
meson \
|
||||
musl-dev \
|
||||
rust \
|
||||
samurai
|
||||
|
||||
- name: Build with meson
|
||||
id: build
|
||||
run: |
|
||||
./ci/build-linux-old.sh
|
||||
|
||||
- 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
|
||||
|
||||
openbsd:
|
||||
runs-on: ubuntu-latest # until https://github.com/actions/runner/issues/385
|
||||
timeout-minutes: 15 # avoid any weirdness with the VM
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Test in OpenBSD VM
|
||||
uses: cross-platform-actions/action@v0.30.0
|
||||
with:
|
||||
operating_system: openbsd
|
||||
version: '7.8'
|
||||
run: |
|
||||
sudo pkg_add -U \
|
||||
cmake \
|
||||
ffmpeg \
|
||||
git \
|
||||
libarchive \
|
||||
libbluray \
|
||||
libcaca \
|
||||
libcdio-paranoia \
|
||||
libdvdnav \
|
||||
libiconv \
|
||||
libplacebo \
|
||||
libv4l \
|
||||
libxkbcommon \
|
||||
luajit \
|
||||
meson \
|
||||
openal \
|
||||
pkgconf \
|
||||
pulseaudio \
|
||||
python3 \
|
||||
rubberband \
|
||||
rust \
|
||||
sdl2 \
|
||||
shaderc \
|
||||
spirv-cross \
|
||||
spirv-headers \
|
||||
uchardet \
|
||||
vulkan-loader \
|
||||
vulkan-headers \
|
||||
zimg
|
||||
./ci/build-openbsd.sh
|
||||
if ! meson test -C build; then
|
||||
cat ./build/meson-logs/testlog.txt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
freebsd:
|
||||
runs-on: ubuntu-latest # until https://github.com/actions/runner/issues/385
|
||||
timeout-minutes: 15 # avoid any weirdness with the VM
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Test in FreeBSD VM
|
||||
uses: cross-platform-actions/action@v0.30.0
|
||||
with:
|
||||
operating_system: freebsd
|
||||
version: '14.3'
|
||||
run: |
|
||||
sudo pkg update
|
||||
sudo pkg install -y \
|
||||
alsa-lib \
|
||||
cmake \
|
||||
evdev-proto \
|
||||
ffmpeg \
|
||||
git \
|
||||
iconv \
|
||||
jackit \
|
||||
libarchive \
|
||||
libbluray \
|
||||
libcaca \
|
||||
libcdio-paranoia \
|
||||
libdvdnav \
|
||||
libdisplay-info \
|
||||
libplacebo \
|
||||
libXinerama \
|
||||
libxkbcommon \
|
||||
libXpresent \
|
||||
libXv \
|
||||
luajit \
|
||||
meson \
|
||||
mujs \
|
||||
openal-soft \
|
||||
pipewire \
|
||||
pkgconf \
|
||||
pulseaudio \
|
||||
python3 \
|
||||
rubberband \
|
||||
rust \
|
||||
sekrit-twc-zimg \
|
||||
sdl2 \
|
||||
sndio \
|
||||
uchardet \
|
||||
v4l_compat \
|
||||
vulkan-headers \
|
||||
wayland-protocols
|
||||
./ci/build-freebsd.sh
|
||||
if ! meson test -C build; then
|
||||
cat ./build/meson-logs/testlog.txt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msys2:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- sys: "clang64"
|
||||
os: "windows-latest"
|
||||
- sys: "mingw64"
|
||||
os: "windows-latest"
|
||||
- sys: "ucrt64"
|
||||
os: "windows-latest"
|
||||
- sys: "clangarm64"
|
||||
os: "windows-11-arm"
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: Disable autocrlf
|
||||
shell: pwsh
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
- uses: actions/checkout@v6
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.sys }}
|
||||
update: true
|
||||
install: git
|
||||
pacboy: >-
|
||||
angleproject:p
|
||||
ca-certificates:p
|
||||
cc:p
|
||||
cppwinrt:p
|
||||
ffmpeg:p
|
||||
lcms2:p
|
||||
libarchive:p
|
||||
libass:p
|
||||
libcdio-paranoia:p
|
||||
libdvdnav:p
|
||||
libjpeg-turbo:p
|
||||
libplacebo:p
|
||||
lua51:p
|
||||
meson:p
|
||||
ninja:p
|
||||
pkgconf:p
|
||||
python:p
|
||||
rst2pdf:p
|
||||
rubberband:p
|
||||
rust:p
|
||||
shaderc:p
|
||||
spirv-cross:p
|
||||
uchardet:p
|
||||
vapoursynth:p
|
||||
vulkan-devel:p
|
||||
|
||||
- name: Build with meson
|
||||
id: build
|
||||
run: |
|
||||
./ci/build-msys2.sh meson
|
||||
env:
|
||||
SYS: ${{ matrix.sys }}
|
||||
|
||||
- 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
|
||||
@@ -1,41 +0,0 @@
|
||||
name: Cleanup caches
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [build]
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
cache:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
gh cache list -L 100 --json id,key,ref -S last_accessed_at -O desc --jq '
|
||||
map(select(.key | startswith("x86_64-w64-mingw32-") or
|
||||
startswith("i686-w64-mingw32-") or
|
||||
startswith("aarch64-pc-windows-msvc-") or
|
||||
startswith("x86_64-pc-windows-msvc-"))) |
|
||||
group_by(.ref) |
|
||||
map({
|
||||
ref: .[0].ref,
|
||||
caches: map({
|
||||
key: .key,
|
||||
prefix: (.key | capture("^(?<prefix>[\\w_-]+-)\\d+$").prefix)
|
||||
}) | group_by(.prefix) | map({keys: map(.key)})
|
||||
}) |
|
||||
.[]
|
||||
' |
|
||||
while read -r group; do
|
||||
pr=$(echo "$group" | jq -r '.ref | capture("refs/pull/(?<num>[0-9]+)/merge").num')
|
||||
if [[ -n "$pr" ]] && [ "$(gh pr view $pr --json state --jq '.state')" != "OPEN" ]; then
|
||||
keys=$(echo "$group" | jq -c '.caches | map(.keys) | .[]')
|
||||
else
|
||||
keys=$(echo "$group" | jq -c '.caches | map(.keys[1:]) | .[]')
|
||||
fi
|
||||
for key in $(echo "$keys" | jq -r '.[]'); do
|
||||
gh cache delete "$key"
|
||||
done
|
||||
done
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,46 +0,0 @@
|
||||
name: comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [build]
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
pr_comment:
|
||||
if: github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
pr=$(gh pr list --state open --json headRefOid,number \
|
||||
--jq '.[] | select(.headRefOid == "${{ github.event.workflow_run.head_sha }}") | .number')
|
||||
if [ -z "$pr" ]; then
|
||||
echo "No matching open pull request found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
artifacts=$(gh api repos/{owner}/{repo}/actions/runs/${{ github.event.workflow_run.id }}/artifacts --jq '.artifacts')
|
||||
if [ "$(echo "$artifacts" | jq 'length')" -eq 0 ]; then
|
||||
echo "No artifacts found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
body=$(echo "$artifacts" | jq -r '
|
||||
def link: "https://nightly.link/${{ github.repository }}/actions/artifacts/\(.id).zip";
|
||||
def entry: "* [\(.name)](\(link))";
|
||||
"Download the artifacts for this pull request:\n<details><summary>Windows</summary>\n\n" +
|
||||
(sort_by(.name) | map(select(.name | test("w64|msvc")) | entry) | join("\n")) +
|
||||
"\n</details>\n<details><summary>macOS</summary>\n\n" +
|
||||
(sort_by(.name) | map(select(.name | test("macos")) | entry) | join("\n")) +
|
||||
"\n</details>"
|
||||
')
|
||||
|
||||
comment_id=$(gh issue view $pr --json comments \
|
||||
--jq '.comments[] | select(.author.login == "github-actions") | .id')
|
||||
if [ -n "$comment_id" ]; then
|
||||
edit=--edit-last
|
||||
fi
|
||||
gh pr comment $pr --body "$body" $edit
|
||||
@@ -1,26 +0,0 @@
|
||||
name: docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- ci
|
||||
- 'release/**'
|
||||
paths:
|
||||
- 'DOCS/**'
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'DOCS/**'
|
||||
|
||||
jobs:
|
||||
check-docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Docs
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends -y python3-docutils rst2pdf
|
||||
./TOOLS/docutils-wrapper.py rst2man --strip-elements-with-class=contents --halt=2 ./DOCS/man/mpv.rst mpv.1
|
||||
./TOOLS/docutils-wrapper.py rst2html --halt=2 ./DOCS/man/mpv.rst mpv.html
|
||||
./TOOLS/docutils-wrapper.py rst2pdf -c -b 1 --repeat-table-rows ./DOCS/man/mpv.rst -o mpv.pdf
|
||||
@@ -1,54 +0,0 @@
|
||||
name: Pull Request Fuzzing
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths-ignore:
|
||||
- 'DOCS/**'
|
||||
- 'TOOLS/lua/**'
|
||||
- '.editorconfig'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.github/PULL_REQUEST_TEMPLATE'
|
||||
- '.gitignore'
|
||||
- 'Copyright'
|
||||
- 'README.md'
|
||||
- 'RELEASE_NOTES'
|
||||
permissions: {}
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sanitizer: [address, undefined, memory]
|
||||
steps:
|
||||
- name: Build Fuzzers (${{ matrix.sanitizer }})
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'mpv'
|
||||
language: c
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
- name: Run Fuzzers (${{ matrix.sanitizer }})
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'mpv'
|
||||
language: c
|
||||
fuzz-seconds: 1200
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
report-ooms: true
|
||||
report-timeouts: true
|
||||
output-sarif: true
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v5
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: ${{ matrix.sanitizer }}-artifacts
|
||||
path: ./out/artifacts
|
||||
- name: Upload Sarif
|
||||
if: always() && steps.build.outcome == 'success'
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
sarif_file: cifuzz-sarif/results.sarif
|
||||
checkout_path: cifuzz-sarif
|
||||
@@ -1,71 +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@v6
|
||||
with:
|
||||
fetch-depth: 50
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
./ci/lint-commit-msg.py
|
||||
|
||||
swift:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: norio-nomura/action-swiftlint@3.2.1
|
||||
|
||||
lua:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: lunarmodules/luacheck@v1
|
||||
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Check
|
||||
id: check
|
||||
uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
src: ". TOOLS/umpv"
|
||||
- name: Suggested changes
|
||||
if: ${{ failure() && steps.check.outcome == 'failure' }}
|
||||
run: ruff check . TOOLS/umpv --diff
|
||||
|
||||
mypy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: python/mypy@master
|
||||
with:
|
||||
install_project_dependencies: no
|
||||
options: '--check-untyped-defs'
|
||||
paths: ". TOOLS/umpv"
|
||||
|
||||
editorconfig:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: editorconfig-checker/action-editorconfig-checker@main
|
||||
- run: editorconfig-checker -config .editorconfig-checker.json
|
||||
|
||||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pre-commit/action@v3.0.1
|
||||
+38
-3
@@ -1,3 +1,38 @@
|
||||
/subprojects/*
|
||||
!/subprojects/packagefiles
|
||||
!/subprojects/*.wrap
|
||||
*.o
|
||||
*.a
|
||||
*.d
|
||||
*.exe
|
||||
*.pyc
|
||||
.depend
|
||||
|
||||
/config.h
|
||||
/config.mak
|
||||
/config.log
|
||||
/mpv
|
||||
/mpv.app
|
||||
/version.h
|
||||
/input/input_conf.h
|
||||
/tags
|
||||
/TAGS
|
||||
/video/out/x11_icon.inc
|
||||
/video/out/opengl/nnedi3_weights.inc
|
||||
/demux/ebml_defs.c
|
||||
/demux/ebml_types.h
|
||||
/sub/osd_font.h
|
||||
/player/lua/*.inc
|
||||
/DOCS/man/mpv.1
|
||||
/DOCS/man/mpv.aux
|
||||
/DOCS/man/mpv.log
|
||||
/DOCS/man/mpv.out
|
||||
/DOCS/man/mpv.pdf
|
||||
/DOCS/man/mpv.toc
|
||||
|
||||
/waf
|
||||
/waf-*
|
||||
/waf3-*
|
||||
/build
|
||||
/.waf*
|
||||
/.lock-waf_*
|
||||
|
||||
/old_build
|
||||
/Makefile
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
local mp_globals = {
|
||||
mp = {
|
||||
fields = {
|
||||
command = {},
|
||||
commandv = {},
|
||||
command_native = {},
|
||||
command_native_async = {},
|
||||
add_hook = {},
|
||||
abort_async_command = {},
|
||||
del_property = {},
|
||||
get_property = {},
|
||||
get_property_osd = {},
|
||||
get_property_bool = {},
|
||||
get_property_number = {},
|
||||
get_property_native = {},
|
||||
set_property = {},
|
||||
set_property_bool = {},
|
||||
set_property_number = {},
|
||||
set_property_native = {},
|
||||
get_time = {},
|
||||
add_key_binding = {},
|
||||
add_forced_key_binding = {},
|
||||
remove_key_binding = {},
|
||||
register_event = {},
|
||||
unregister_event = {},
|
||||
observe_property = {},
|
||||
unobserve_property = {},
|
||||
add_timeout = {},
|
||||
add_periodic_timer = {},
|
||||
get_opt = {},
|
||||
get_script_name = {},
|
||||
get_script_directory = {},
|
||||
osd_message = {},
|
||||
get_wakeup_pipe = {},
|
||||
get_next_timeout = {},
|
||||
dispatch_events = {},
|
||||
register_idle = {},
|
||||
unregister_idle = {},
|
||||
enable_messages = {},
|
||||
register_script_message = {},
|
||||
unregister_script_message = {},
|
||||
create_osd_overlay = {},
|
||||
get_osd_size = {},
|
||||
msg = {
|
||||
fields = {
|
||||
fatal = {},
|
||||
error = {},
|
||||
warn = {},
|
||||
info = {},
|
||||
verbose = {},
|
||||
debug = {},
|
||||
trace = {},
|
||||
}
|
||||
},
|
||||
-- Not documented
|
||||
-- TODO: Document or remove them
|
||||
disable_key_bindings = {},
|
||||
enable_key_bindings = {},
|
||||
find_config_file = {},
|
||||
format_time = {},
|
||||
get_mouse_pos = {},
|
||||
set_key_bindings = {},
|
||||
set_mouse_area = {},
|
||||
set_osd_ass = {},
|
||||
}
|
||||
},
|
||||
exit = {},
|
||||
unpack = {},
|
||||
}
|
||||
|
||||
local mp_internal = {
|
||||
mp = {
|
||||
fields = {
|
||||
-- Internal
|
||||
-- TODO: Move to mp_internal module
|
||||
ARRAY = { fields = { info = {}, type = {} }},
|
||||
MAP = { fields = { info = {}, type = {} }},
|
||||
UNKNOWN_TYPE = { fields = { info = {}, type = {} }},
|
||||
_legacy_overlay = { fields = { res_x = {}, res_y = {}, data = {}, update = {} }},
|
||||
cancel_timer = {},
|
||||
flush_keybindings = {},
|
||||
get_osd_margins = {},
|
||||
input_define_section = {},
|
||||
input_disable_section = {},
|
||||
input_enable_section = {},
|
||||
input_set_section_mouse_area = {},
|
||||
keep_running = {},
|
||||
log = {},
|
||||
raw_abort_async_command = {},
|
||||
raw_command_native_async = {},
|
||||
raw_hook_add = {},
|
||||
raw_hook_continue = {},
|
||||
raw_observe_property = {},
|
||||
raw_unobserve_property = {},
|
||||
raw_wait_event = {},
|
||||
request_event = {},
|
||||
script_name = {},
|
||||
use_suspend = {},
|
||||
wait_event = {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std = "min+mp"
|
||||
stds = { mp = { read_globals = mp_globals } }
|
||||
-- mp_internal seems to be merged with mp for other files too...
|
||||
files["player/lua/defaults.lua"] = { globals = mp_internal }
|
||||
files["player/lua/auto_profiles.lua"] = { globals = { "p", "get" } }
|
||||
max_line_length = 100
|
||||
@@ -1,23 +0,0 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: check-case-conflict
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-illegal-windows-names
|
||||
- id: check-json
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- id: check-xml
|
||||
exclude: ^TOOLS/mpv-osd-symbols.sfdir/font.props$
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
exclude: ^TOOLS/osxbundle/mpv.app/Contents/PkgInfo$
|
||||
- id: fix-byte-order-marker
|
||||
- id: mixed-line-ending
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: codespell
|
||||
args: ["--ignore-words-list", "ALS,als,datas,DNE,enew,HDA,numer,ontop,optionA,Paeth,pathc,inout,requestor"]
|
||||
exclude: ^misc/language.c$
|
||||
@@ -1,10 +0,0 @@
|
||||
disabled_rules:
|
||||
- identifier_name
|
||||
- line_length
|
||||
- large_tuple
|
||||
- void_function_in_ternary
|
||||
- function_body_length
|
||||
- cyclomatic_complexity
|
||||
- file_length
|
||||
- type_body_length
|
||||
strict: true
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
language: c
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
env:
|
||||
matrix:
|
||||
- LIBAV=libav-stable
|
||||
- LIBAV=libav-git
|
||||
- LIBAV=ffmpeg-stable
|
||||
- LIBAV=ffmpeg-git
|
||||
global:
|
||||
# Coverity token
|
||||
- secure: "H21mSRlMhk4BKS0xHZvCFGJxteCP0hRVUxTuNfM2Z9HBsyutuLEYMtViLO86VtM+Tqla3xXPzUdS4ozLwI72Ax/5ZUDXACROj73yW6QhFB5D6rLut12+FjqC7M33Qv2hl0xwgNBmR5dsm1ToP37+Wn+ecJQNvN8fkTXF+HVzOEw="
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- ci
|
||||
- coverity_scan
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: LIBAV=libav-stable
|
||||
- os: osx
|
||||
env: LIBAV=libav-git
|
||||
- os: osx
|
||||
env: LIBAV=ffmpeg-git
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
- os: linux
|
||||
env: LIBAV=ffmpeg-stable
|
||||
- os: linux
|
||||
compiler: clang
|
||||
|
||||
before_install: ./travis-deps libass-stable $LIBAV
|
||||
script:
|
||||
- ./bootstrap.py
|
||||
- ./waf configure
|
||||
- ./waf build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
irc:
|
||||
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
|
||||
@@ -0,0 +1,30 @@
|
||||
Reporting bugs
|
||||
==============
|
||||
|
||||
- State your platform and mpv version.
|
||||
- Make sure you're actually using the latest mpv release. Linux distributions
|
||||
in particular love redistributing ancient and unmaintained mpv releases.
|
||||
- Attach a log file made with ``-v`` or ``--log-file=output.txt``.
|
||||
- Avoid attaching text files packed in archive files (zip/rar/...), unless
|
||||
explicitly asked so. There are better means, such as using http://sprunge.us
|
||||
- If you think this is file specific, upload a sample file and link it.
|
||||
- Don't report multiple unrelated/barely related bugs in one issue.
|
||||
- Read: https://mpv.io/bug-reports/
|
||||
|
||||
Asking questions
|
||||
================
|
||||
|
||||
Preferably, asking questions should be done via IRC (#mpv on irc.freenode.net),
|
||||
but asking questions by opening a github issue is also barely tolerated.
|
||||
|
||||
When asking on IRC (#mpv on irc.freenode.net), don't ask to ask, just state
|
||||
your problem. Stay long enough until someone gets a chance to reply. Sometimes
|
||||
it can take hours.
|
||||
|
||||
Sending patches
|
||||
===============
|
||||
|
||||
See: https://github.com/mpv-player/mpv/blob/master/DOCS/contribute.md
|
||||
|
||||
In particular, be aware that all changes to GPL code must come with the
|
||||
implicit agreement that they might be LGPLv2.1+ relicensed at a later point.
|
||||
@@ -1,78 +1,30 @@
|
||||
mpv is a fork of mplayer2, which is a fork of MPlayer.
|
||||
|
||||
mpv as a whole is licensed under the GNU General Public License GPL version 2
|
||||
or later (called GPLv2+ in this document, see LICENSE.GPL for full license
|
||||
text) by default. The mpv program is licensed the GNU Lesser General Public
|
||||
License LGPL version 2 or later (LGPLv2.1+ in this document, see LICENSE.LGPL
|
||||
for full license text) if built without using any GPL only files.
|
||||
The -Dgpl=false configure switch is provided as a convenience for excluding
|
||||
the GPL only files listed below from the build process.
|
||||
mpv as a whole is licensed as GPL version 2 or later (see LICENSE). Most source
|
||||
files are GPLv2+, but some files are available under a more liberal license,
|
||||
such as LGPLv2.1+, BSD, MIT, ISC, and possibly others. Look at the copyright
|
||||
header of each source file, and grep the sources for "Copyright" if you need
|
||||
to know details. Files without Copyright notice are licensed as LGPLv2.1+.
|
||||
|
||||
However, do note that the build system is provided "as is" and using the
|
||||
-Dgpl=false configure switch does not in itself create a LGPLv2.1+ license
|
||||
grant.
|
||||
All new contributions must be LGPLv2.1+ licensed, or if the changes are done on
|
||||
GPL code, must come with the implicit agreement that the project can relicense
|
||||
the code to LGPLv2.1+ at a later point without asking the contributor. (This
|
||||
is a safeguard for making potential relicensing of the project to LGPLv2.1+
|
||||
easier.) Using a more liberal license compatible to LGPLv2.1+ is also ok.
|
||||
|
||||
Additionally, some non-program parts, such as the documentation and some config
|
||||
files listed below, are still built and licensed under their respective licenses
|
||||
even when -Dgpl=false is used. Please consult GPLv2+ and LGPLv2.1+ license texts
|
||||
for further details on how to distribute these files.
|
||||
|
||||
Most source files are LGPLv2.1+ or GPLv2+, but some files are available under
|
||||
more liberal licenses, such as BSD, MIT, ISC, and possibly others. Look at the
|
||||
copyright header of each source file, and grep the sources for "Copyright" if
|
||||
you need to know details. C source files without Copyright notice are usually
|
||||
licensed as LGPLv2.1+. Also see the list of files with specific licenses below
|
||||
(not all files can have a standard license header).
|
||||
|
||||
All new contributions must be LGPLv2.1+ licensed. Using a more liberal license
|
||||
compatible to LGPLv2.1+ is also ok.
|
||||
|
||||
Changes done to GPL code must come with the implicit/explicit agreement that the
|
||||
project can relicense the changes to LGPLv2.1+ at a later point without asking
|
||||
the contributor. This is a safeguard for making potential relicensing of
|
||||
remaining GPL code to LGPLv2.1+ easier.
|
||||
|
||||
See DOCS/contribute.md for binding rules wrt. licensing for contributions.
|
||||
Some GPLv3 or LGPLv3 code in mpv can be enabled by building with --enable-gpl3,
|
||||
and in this case, the resulting binaries must be distributed under GPLv3.
|
||||
|
||||
For information about authors and contributors, consult the git log, which
|
||||
contains the complete SVN and CVS history as well.
|
||||
|
||||
mpv can be built as LGPLv2.1+ without using any GPL only files. To add a
|
||||
LGPL mode to mpv, MPlayer code had to be relicensed from GPLv2+ to LGPLv2.1+ by
|
||||
asking the MPlayer authors for permission. Since permission could not be
|
||||
obtained from everyone, LGPL mode disables the following features, some of them
|
||||
quite central:
|
||||
- Linux X11 video output
|
||||
- BSD audio output via OSS
|
||||
- NVIDIA/Linux hardware decoding (vdpau, although nvdec usually works)
|
||||
- minor features: jack, DVD, CDDA, DVB, CACA, legacy direct3d VO
|
||||
Some of these will be fixed in the future. The intended use for LGPL mode is
|
||||
with libmpv, and currently it's not recommended to build mpv CLI in LGPL mode
|
||||
at all.
|
||||
Note that mplayer2 as a whole is licensed under GPLv3+. This is because it uses
|
||||
a copy of talloc (part of Samba), which is LGPLv3+, and the next compatible
|
||||
license for this mix is GPLv3+.
|
||||
|
||||
The following files are still GPL only (-Dgpl=false disables them):
|
||||
MPlayer as a whole is licensed under GPLv2 (incompatible to GPLv3!), because
|
||||
some files are licensed to GPLv2 (and _not_ any later version of the license).
|
||||
In particular, this affects the file libmpdemux/demux_ty_osd.c. It is disabled
|
||||
under mplayer2, and has been removed from mpv.
|
||||
|
||||
audio/out/ao_jack.c will stay GPL
|
||||
audio/out/ao_oss.c will stay GPL
|
||||
stream/dvb* must stay GPL
|
||||
stream/stream_cdda.c unknown
|
||||
stream/stream_dvb.* must stay GPL
|
||||
stream/stream_dvdnav.c unknown
|
||||
video/out/vo_caca.c unknown
|
||||
video/out/vo_direct3d.c unknown
|
||||
video/out/vo_vaapi.c probably impossible (some company's code)
|
||||
video/out/vo_vdpau.c probably impossible (nVidia's code)
|
||||
video/out/vo_x11.c probably impossible
|
||||
video/out/vo_xv.c probably impossible
|
||||
video/out/x11_common.* probably impossible
|
||||
video/vdpau.c hard (GPL-only parts must be ifdefed)
|
||||
video/vdpau.h unknown
|
||||
video/vdpau_mixer.* actual code must be rewritten
|
||||
DOCS/man/ GPLv2+
|
||||
etc/mplayer-input.conf unknown license, probably GPLv2+
|
||||
etc/mpv.desktop unknown license, probably GPLv2+
|
||||
etc/restore-old-bindings.conf unknown license, probably GPLv2+
|
||||
|
||||
None of the cases listed above affect the final binary if it's built as
|
||||
LGPL. Linked libraries still can affect the final license (for example if
|
||||
FFmpeg was built as GPL).
|
||||
"v2.1+" in this context means "version 2.1 or later".
|
||||
+3
-177
@@ -16,7 +16,7 @@ The version number is the same as used for MPV_CLIENT_API_VERSION (see
|
||||
``client.h`` how to convert between major/minor version numbers and the flat
|
||||
32 bit integer).
|
||||
|
||||
Also, read the section ``Compatibility`` in ``client.h``, and compatibility.rst.
|
||||
Also, read the section ``Compatibility`` in ``client.h``.
|
||||
|
||||
Options, commands, properties
|
||||
=============================
|
||||
@@ -32,181 +32,6 @@ API changes
|
||||
|
||||
::
|
||||
|
||||
--- mpv 0.40.0 ---
|
||||
2.5 - Deprecate MPV_RENDER_PARAM_AMBIENT_LIGHT. no replacement.
|
||||
--- mpv 0.39.0 ---
|
||||
2.4 - mpv_render_param with the MPV_RENDER_PARAM_ICC_PROFILE argument no
|
||||
longer has incorrect assumptions about memory allocation and can be
|
||||
correctly used.
|
||||
--- mpv 0.38.0 ---
|
||||
2.3 - partially revert the changes from API version 1.27, remove libmpv as
|
||||
the default VO and move it to the bottom of the auto-probing order.
|
||||
This restores the prior behavior on all platforms other than macOS,
|
||||
but still auto selects libmpv/cocoa-cb on macOS if it was built with
|
||||
support for cocoa-cb.
|
||||
--- mpv 0.37.0 ---
|
||||
2.2 - add mpv_time_ns()
|
||||
--- mpv 0.36.0 ---
|
||||
2.1 - add mpv_del_property()
|
||||
--- mpv 0.35.0 ---
|
||||
2.0 - remove headers/functions of the obsolete opengl_cb API
|
||||
- remove mpv_opengl_init_params.extra_exts field
|
||||
- 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)
|
||||
- deactivate the opengl_cb API (always fails to initialize now)
|
||||
The opengl_cb API was deprecated over 2 years ago. Use the render API
|
||||
instead.
|
||||
1.108 - Deprecate MPV_EVENT_IDLE
|
||||
- add mpv_event_start_file
|
||||
- add the following fields to mpv_event_end_file: playlist_entry_id,
|
||||
playlist_insert_id, playlist_insert_num_entries
|
||||
- add mpv_event_to_node()
|
||||
- add mpv_client_id()
|
||||
1.107 - Remove the deprecated qthelper.hpp. This was obviously not part of the
|
||||
libmpv API, only an "additionally" provided helper, thus this is not
|
||||
considered an API change. If you are maintaining a project that relies
|
||||
on this header, you can simply download this file and adjust the
|
||||
include statement to use it instead:
|
||||
|
||||
https://raw.githubusercontent.com/mpv-player/mpv/v0.32.0/libmpv/qthelper.hpp
|
||||
|
||||
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
|
||||
the "vd-lavc-dr" option is enabled (which it is by default).
|
||||
There were no actual API changes.
|
||||
API users on older API versions and mpv releases should set
|
||||
"vd-lavc-dr" to "no" to avoid these issues.
|
||||
API users must still adhere to the tricky rules documented in render.h
|
||||
to avoid other deadlocks.
|
||||
1.104 - Deprecate struct mpv_opengl_drm_params. Replaced by mpv_opengl_drm_params_v2
|
||||
- Deprecate MPV_RENDER_PARAM_DRM_DISPLAY. Replaced by MPV_RENDER_PARAM_DRM_DISPLAY_V2.
|
||||
1.103 - redo handling of async commands
|
||||
- add mpv_event_command and make it possible to return values from
|
||||
commands issued with mpv_command_async() or mpv_command_node_async()
|
||||
- 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
|
||||
- add MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME
|
||||
- add MPV_RENDER_PARAM_SKIP_RENDERING
|
||||
- add mpv_render_context_get_info()
|
||||
1.100 - bump API number to avoid confusion with mpv release versions
|
||||
- actually apply the GL_MP_MPGetNativeDisplay change for the new render
|
||||
API. This also means compatibility for anything but x11 and wayland
|
||||
through the old opengl-cb GL_MP_MPGetNativeDisplay method is now
|
||||
unsupported.
|
||||
- deprecate mpv_get_wakeup_pipe(). It's complex, but easy to replace
|
||||
using normal API (just set a wakeup callback to a function which
|
||||
writes to a pipe).
|
||||
- add a 1st class hook API, which replaces the hacky mpv_command()
|
||||
based one. The old API is deprecated and will be removed soon. The
|
||||
old API was never meant to be stable, while the new API is.
|
||||
1.29 - the behavior of mpv_terminate_destroy() and mpv_detach_destroy()
|
||||
changes subtly (see documentation in the header file). In particular,
|
||||
mpv_detach_destroy() will not leave the player running in all
|
||||
situations anymore (it gets closer to refcounting).
|
||||
- rename mpv_detach_destroy() to mpv_destroy() (the old function will
|
||||
remain valid as deprecated alias)
|
||||
- add mpv_create_weak_client(), which makes use of above changes
|
||||
- MPV_EVENT_SHUTDOWN is now returned exactly once if a mpv_handle
|
||||
should terminate, instead of spamming the event queue with this event
|
||||
1.28 - deprecate the render opengl_cb API, and replace it with render.h
|
||||
and render_gl.h. The goal is allowing support for APIs other than
|
||||
OpenGL. The old API is emulated with the new API.
|
||||
Likewise, the "opengl-cb" VO is renamed to "libmpv".
|
||||
mpv_get_sub_api() is deprecated along the opengl_cb API.
|
||||
The new API is relatively similar, but not the same. The rough
|
||||
equivalents are:
|
||||
mpv_opengl_cb_init_gl => mpv_render_context_create
|
||||
mpv_opengl_cb_set_update_callback => mpv_render_context_set_update_callback
|
||||
mpv_opengl_cb_draw => mpv_render_context_render
|
||||
mpv_opengl_cb_report_flip => mpv_render_context_report_swap
|
||||
mpv_opengl_cb_uninit_gl => mpv_render_context_free
|
||||
The VO opengl-cb is also renamed to "libmpv".
|
||||
Also, the GL_MP_MPGetNativeDisplay pseudo extension is not used by the
|
||||
render API anymore, and the old opengl-cb API only handles the "x11"
|
||||
and "wl" names anymore. Support for everything else has been removed.
|
||||
The new render API uses proper API parameters, e.g. for X11 you pass
|
||||
MPV_RENDER_PARAM_X11_DISPLAY directly.
|
||||
- deprecate the qthelper.hpp header file. This provided some C++ helper
|
||||
utility functions for Qt with use of libmpv. There is no reason to
|
||||
keep this in the mpv git repository, nor to make it part of the libmpv
|
||||
API. If you're using this header, you can safely copy it into your
|
||||
project - it uses only libmpv public API. Alternatively, it could be
|
||||
maintained in a separate repository by interested parties.
|
||||
1.27 - make opengl-cb the default VO. This causes a subtle behavior change
|
||||
if the API user called mpv_opengl_cb_init_gl(), but does not set
|
||||
the "vo" option. Before, it would still have used another VO (like
|
||||
on the CLI, e.g. vo=gpu). Now it'll behave as if vo=opengl-cb was
|
||||
used.
|
||||
--- mpv 0.28.0 ---
|
||||
1.26 - remove glMPGetNativeDisplay("drm") support
|
||||
- add mpv_opengl_cb_window_pos and mpv_opengl_cb_drm_params and
|
||||
support via glMPGetNativeDisplay() for using it
|
||||
- make --stop-playback-on-init-failure=no the default in libmpv (just
|
||||
like in mpv CLI)
|
||||
--- mpv 0.27.0 ---
|
||||
1.25 - remove setting "no-" options via mpv_set_option*(). (See corresponding
|
||||
deprecation in 0.23.0.)
|
||||
--- mpv 0.25.0 ---
|
||||
1.24 - add a MPV_ENABLE_DEPRECATED preprocessor symbol, which can be defined
|
||||
by the user to exclude deprecated API symbols from the C headers
|
||||
--- mpv 0.23.0 ---
|
||||
1.24 - the deprecated mpv_suspend() and mpv_resume() APIs now do nothing.
|
||||
--- mpv 0.22.0 ---
|
||||
1.23 - deprecate setting "no-" options via mpv_set_option*(). For example,
|
||||
instead of "no-video=" you should set "video=no".
|
||||
- do not override the SIGPIPE signal handler anymore. This was done as
|
||||
workaround for the FFmpeg TLS code, which has been fixed long ago.
|
||||
- deprecate mpv_suspend() and mpv_resume(). They will be stubbed out
|
||||
in mpv 0.23.0.
|
||||
- make mpv_set_property() work to some degree before mpv_initialize().
|
||||
It can now be used instead of mpv_set_option().
|
||||
- semi-deprecate mpv_set_option()/mpv_set_option_string(). You should
|
||||
use mpv_set_property() instead. There are some deprecated properties
|
||||
which conflict with some options (see client.h remarks on
|
||||
mpv_set_option()), for which mpv_set_option() might still be required.
|
||||
In future mpv releases, the conflicting deprecated options/properties
|
||||
will be removed, and mpv_set_option() will internally translate API
|
||||
calls to mpv_set_property().
|
||||
- qthelper.hpp: deprecate get_property_variant, set_property_variant,
|
||||
set_option_variant, command_variant, and replace them with
|
||||
get_property, set_property, command.
|
||||
--- mpv 0.19.0 ---
|
||||
1.22 - add stream_cb API for custom protocols
|
||||
--- mpv 0.18.1 ---
|
||||
---- - remove "status" log level from mpv_request_log_messages() docs. This
|
||||
is 100% equivalent to "v". The behavior is still the same, thus no
|
||||
actual API change.
|
||||
--- mpv 0.18.0 ---
|
||||
1.21 - mpv_set_property() changes behavior with MPV_FORMAT_NODE. Before this
|
||||
change it rejected mpv_nodes with format==MPV_FORMAT_STRING if the
|
||||
property was not a string or did not have special mechanisms in place
|
||||
the function failed. Now it always invokes the option string parser,
|
||||
and mpv_node with a basic data type works exactly as if the function
|
||||
is invoked with that type directly. This new behavior is equivalent
|
||||
to mpv_set_option().
|
||||
This also affects the mp.set_property_native() Lua function.
|
||||
- generally, setting choice options/properties with "yes"/"no" options
|
||||
can now be set as MPV_FORMAT_FLAG
|
||||
- reading a choice property as MPV_FORMAT_NODE will now return a
|
||||
MPV_FORMAT_FLAG value if the choice is "yes" (true) or "no" (false)
|
||||
This implicitly affects Lua and JSON IPC interfaces as well.
|
||||
- big changes to vo-cmdline on vo_opengl and vo_opengl_hq (but not
|
||||
vo_opengl_cb): options are now normally not reset, but applied on top
|
||||
of the current options. The special undocumented value "-" still
|
||||
works, but now resets all options to before any vo-cmdline command
|
||||
has been called.
|
||||
--- mpv 0.12.0 ---
|
||||
1.20 - deprecate "GL_MP_D3D_interfaces"/"glMPGetD3DInterface", and introduce
|
||||
"GL_MP_MPGetNativeDisplay"/"glMPGetNativeDisplay" (this is a
|
||||
@@ -285,6 +110,7 @@ API changes
|
||||
- extend the "--start" option; a leading "+", which was previously
|
||||
insignificant is now significant
|
||||
- add "cache-free" and "cache-used" properties
|
||||
- macOS: the "coreaudio" AO spdif code is split into a separate AO
|
||||
- OSX: the "coreaudio" AO spdif code is split into a separate AO
|
||||
--- mpv 0.4.0 ---
|
||||
1.0 - the API is declared stable
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
All examples in this directory and its sub-directories are licensed
|
||||
under one of the following licenses:
|
||||
|
||||
WTFPL, ISC, Ms-PL, AGPLv3, BSD (any)
|
||||
|
||||
Pick any license of your liking, and disregard the others.
|
||||
|
||||
(The full text of each license is available on this website:
|
||||
http://opensource.org/licenses/alphabetical )
|
||||
|
||||
Additionally, you may consider the example code to be public domain.
|
||||
You are free to use any of the example code without further
|
||||
requirements or need for attribution.
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# Client API examples
|
||||
|
||||
All these examples use the mpv client API through libmpv.
|
||||
|
||||
## Where are the docs?
|
||||
|
||||
The libmpv C API is documented directly in the header files (on normal Unix
|
||||
systems, this is in `/usr/include/mpv/client.h`.
|
||||
|
||||
libmpv merely gives you access to mpv's command interface, which is documented
|
||||
here:
|
||||
* Options (`mpv_set_option()` and friends): http://mpv.io/manual/master/#options
|
||||
* Commands (`mpv_command()` and friends): http://mpv.io/manual/master/#list-of-input-commands
|
||||
* Properties (`mpv_set_property()` and friends): http://mpv.io/manual/master/#properties
|
||||
|
||||
Essentially everything is done with them, including loading a file, retrieving
|
||||
playback progress, and so on.
|
||||
|
||||
## Methods of embedding the video window
|
||||
|
||||
All of these examples concentrate on how to integrate mpv's video renderers
|
||||
with your own GUI. This is generally the hardest part. libmpv enforces a
|
||||
somewhat efficient video output method, rather than e.g. returning a RGBA
|
||||
surface in memory for each frame. The latter would be prohibitively inefficient,
|
||||
because it would require conversion on the CPU. The goal is also not requiring
|
||||
the API users to reinvent their own video rendering/scaling/presentation
|
||||
mechanisms.
|
||||
|
||||
There are currently 2 methods of embedding video.
|
||||
|
||||
### Native window embedding
|
||||
|
||||
This uses the platform's native method of nesting multiple windows. For example,
|
||||
Linux/X11 can nest a window from a completely different process. The nested
|
||||
window can redraw contents on its own, and receive user input if the user
|
||||
interacts with this window.
|
||||
|
||||
libmpv lets you specify a parent window for its own video window via the `wid`
|
||||
option. Then libmpv will create its window with your window as parent, and
|
||||
render its video inside of it.
|
||||
|
||||
This method is highly OS-dependent. Some behavior is OS-specific. There are
|
||||
problems with focusing on X11 (the ancient X11 focus policy mismatches with
|
||||
that of modern UI toolkits - it's normally worked around, but this is not
|
||||
easily possible with raw window embedding). It seems to have stability problems
|
||||
on OSX when using the Qt toolkit.
|
||||
|
||||
### OpenGL embedding
|
||||
|
||||
This method lets you use libmpv's OpenGL renderer directly. You create an
|
||||
OpenGL context, and then use `mpv_opengl_cb_draw()` to render the video on
|
||||
each frame.
|
||||
|
||||
This is more complicated, because libmpv will work directly on your own OpenGL
|
||||
state. It's also not possible to have mpv automatically receive user input.
|
||||
You will have to simulate this with the `mouse`/`keypress`/`keydown`/`keyup`
|
||||
commands.
|
||||
|
||||
You also get much more flexibility. For example, you can actually render your
|
||||
own OSD on top of the video, something that is not possible with raw window
|
||||
embedding.
|
||||
|
||||
### Which one to use?
|
||||
|
||||
Due to the various platform-specific behavior and problems (in particular on
|
||||
OSX), OpenGL embedding is recommended. If you're not comfortable with requiring
|
||||
OpenGL, or want to support "direct" video output such as vdpau (which might
|
||||
win when it comes to performance and energy-saving), you should probably
|
||||
support both methods if possible.
|
||||
|
||||
## List of examples
|
||||
|
||||
### simple
|
||||
|
||||
Very primitive terminal-only example. Shows some most basic API usage.
|
||||
|
||||
### cocoa
|
||||
|
||||
Shows how to embed the mpv video window in Objective-C/Cocoa.
|
||||
|
||||
### cocoa-openglcb
|
||||
|
||||
Similar to the cocoa sample, but shows how to integrate mpv's OpenGL renderer
|
||||
using libmpv's opengl-cb API. Since it does not require complicated interaction
|
||||
with Cocoa elements from different libraries, it's more robust.
|
||||
|
||||
### qt
|
||||
|
||||
Shows how to embed the mpv video window in Qt (using normal desktop widgets).
|
||||
|
||||
### qt_opengl
|
||||
|
||||
Shows how to use mpv's OpenGL video renderer in Qt. This uses the opengl-cb API
|
||||
for video. Since it does not rely on embedding "foreign" native Windows, it's
|
||||
usually more robust, potentially faster, and it's easier to control how your
|
||||
GUI interacts with the video. You can do your own OpenGL rendering on top of
|
||||
the video as well.
|
||||
|
||||
### qml
|
||||
|
||||
Shows how to use mpv's OpenGL video renderer in QtQuick2 with QML. Uses the
|
||||
opengl-cb API for video. Since the video is a normal QML element, it's trivial
|
||||
to create OSD overlays with QML-native graphical elements as well.
|
||||
|
||||
### qml_direct
|
||||
|
||||
Alternative example, which typically avoids a FBO indirection. Might be
|
||||
slightly faster, but is less flexible and harder to use. In particular, the
|
||||
video is not a normal QML element. Uses the opengl-cb API for video.
|
||||
|
||||
### sdl
|
||||
|
||||
Show how to embed the mpv OpenGL renderer in SDL. Uses the opengl-cb API for
|
||||
video.
|
||||
@@ -0,0 +1,304 @@
|
||||
// Plays a video from the command line in an opengl view in its own window.
|
||||
|
||||
// Build with: clang -o cocoa-openglcb cocoa-openglcb.m `pkg-config --libs --cflags mpv` -framework Cocoa -framework OpenGL
|
||||
|
||||
#import <mpv/client.h>
|
||||
#import <mpv/opengl_cb.h>
|
||||
|
||||
#import <stdio.h>
|
||||
#import <stdlib.h>
|
||||
#import <OpenGL/gl.h>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
static inline void check_error(int status)
|
||||
{
|
||||
if (status < 0) {
|
||||
printf("mpv API error: %s\n", mpv_error_string(status));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void *get_proc_address(void *ctx, const char *name)
|
||||
{
|
||||
CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
|
||||
void *addr = CFBundleGetFunctionPointerForName(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")), symbolName);
|
||||
CFRelease(symbolName);
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void glupdate(void *ctx);
|
||||
|
||||
@interface MpvClientOGLView : NSOpenGLView
|
||||
@property mpv_opengl_cb_context *mpvGL;
|
||||
- (instancetype)initWithFrame:(NSRect)frame;
|
||||
- (void)drawRect;
|
||||
- (void)fillBlack;
|
||||
@end
|
||||
|
||||
@implementation MpvClientOGLView
|
||||
- (instancetype)initWithFrame:(NSRect)frame
|
||||
{
|
||||
// make sure the pixel format is double buffered so we can use
|
||||
// [[self openGLContext] flushBuffer].
|
||||
NSOpenGLPixelFormatAttribute attributes[] = {
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
0
|
||||
};
|
||||
self = [super initWithFrame:frame
|
||||
pixelFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]];
|
||||
|
||||
if (self) {
|
||||
[self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
|
||||
// swap on vsyncs
|
||||
GLint swapInt = 1;
|
||||
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||
[[self openGLContext] makeCurrentContext];
|
||||
self.mpvGL = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)fillBlack
|
||||
{
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
- (void)drawRect
|
||||
{
|
||||
if (self.mpvGL)
|
||||
mpv_opengl_cb_draw(self.mpvGL, 0, self.bounds.size.width, -self.bounds.size.height);
|
||||
else
|
||||
[self fillBlack];
|
||||
[[self openGLContext] flushBuffer];
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
[self drawRect];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface CocoaWindow : NSWindow
|
||||
@property(retain, readonly) MpvClientOGLView *glView;
|
||||
@property(retain, readonly) NSButton *pauseButton;
|
||||
@end
|
||||
|
||||
@implementation CocoaWindow
|
||||
- (BOOL)canBecomeMainWindow { return YES; }
|
||||
- (BOOL)canBecomeKeyWindow { return YES; }
|
||||
- (void)initOGLView {
|
||||
NSRect bounds = [[self contentView] bounds];
|
||||
// window coordinate origin is bottom left
|
||||
NSRect glFrame = NSMakeRect(bounds.origin.x, bounds.origin.y + 30, bounds.size.width, bounds.size.height - 30);
|
||||
_glView = [[MpvClientOGLView alloc] initWithFrame:glFrame];
|
||||
[self.contentView addSubview:_glView];
|
||||
|
||||
NSRect buttonFrame = NSMakeRect(bounds.origin.x, bounds.origin.y, 60, 30);
|
||||
_pauseButton = [[NSButton alloc] initWithFrame:buttonFrame];
|
||||
_pauseButton.buttonType = NSToggleButton;
|
||||
// button target has to be the delegate (it holds the mpv context
|
||||
// pointer), so that's set later.
|
||||
_pauseButton.action = @selector(togglePause:);
|
||||
_pauseButton.title = @"Pause";
|
||||
_pauseButton.alternateTitle = @"Play";
|
||||
[self.contentView addSubview:_pauseButton];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
{
|
||||
mpv_handle *mpv;
|
||||
dispatch_queue_t queue;
|
||||
CocoaWindow *window;
|
||||
}
|
||||
@end
|
||||
|
||||
static void wakeup(void *);
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void)createWindow {
|
||||
|
||||
int mask = NSTitledWindowMask|NSClosableWindowMask|
|
||||
NSMiniaturizableWindowMask|NSResizableWindowMask;
|
||||
|
||||
window = [[CocoaWindow alloc]
|
||||
initWithContentRect:NSMakeRect(0, 0, 1280, 720)
|
||||
styleMask:mask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
|
||||
// force a minimum size to stop opengl from exploding.
|
||||
[window setMinSize:NSMakeSize(200, 200)];
|
||||
[window initOGLView];
|
||||
[window setTitle:@"cocoa-openglcb example"];
|
||||
[window makeMainWindow];
|
||||
[window makeKeyAndOrderFront:nil];
|
||||
|
||||
NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"];
|
||||
NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""];
|
||||
NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"];
|
||||
[m setSubmenu:sm forItem:item];
|
||||
[sm addItemWithTitle: @"quit" action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
[NSApp setMenu:m];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
atexit_b(^{
|
||||
// Because activation policy has just been set to behave like a real
|
||||
// application, that policy must be reset on exit to prevent, among
|
||||
// other things, the menubar created here from remaining on screen.
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
|
||||
});
|
||||
|
||||
// Read filename
|
||||
NSArray *args = [NSProcessInfo processInfo].arguments;
|
||||
if (args.count < 2) {
|
||||
NSLog(@"Expected filename on command line");
|
||||
exit(1);
|
||||
}
|
||||
NSString *filename = args[1];
|
||||
|
||||
[self createWindow];
|
||||
window.pauseButton.target = self;
|
||||
|
||||
mpv = mpv_create();
|
||||
if (!mpv) {
|
||||
printf("failed creating context\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
check_error(mpv_set_option_string(mpv, "input-media-keys", "yes"));
|
||||
// request important errors
|
||||
check_error(mpv_request_log_messages(mpv, "warn"));
|
||||
|
||||
check_error(mpv_initialize(mpv));
|
||||
check_error(mpv_set_option_string(mpv, "vo", "opengl-cb"));
|
||||
mpv_opengl_cb_context *mpvGL = mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
|
||||
if (!mpvGL) {
|
||||
puts("libmpv does not have the opengl-cb sub-API.");
|
||||
exit(1);
|
||||
}
|
||||
// pass the mpvGL context to our view
|
||||
window.glView.mpvGL = mpvGL;
|
||||
int r = mpv_opengl_cb_init_gl(mpvGL, NULL, get_proc_address, NULL);
|
||||
if (r < 0) {
|
||||
puts("gl init has failed.");
|
||||
exit(1);
|
||||
}
|
||||
mpv_opengl_cb_set_update_callback(mpvGL, glupdate, (__bridge void *)window.glView);
|
||||
|
||||
// Deal with MPV in the background.
|
||||
queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_async(queue, ^{
|
||||
// Register to be woken up whenever mpv generates new events.
|
||||
mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *)self);
|
||||
// Load the indicated file
|
||||
const char *cmd[] = {"loadfile", filename.UTF8String, NULL};
|
||||
check_error(mpv_command(mpv, cmd));
|
||||
});
|
||||
}
|
||||
|
||||
static void glupdate(void *ctx)
|
||||
{
|
||||
MpvClientOGLView *glView = (__bridge MpvClientOGLView *)ctx;
|
||||
// I'm still not sure what the best way to handle this is, but this
|
||||
// works.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[glView drawRect];
|
||||
});
|
||||
}
|
||||
|
||||
- (void) handleEvent:(mpv_event *)event
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_SHUTDOWN: {
|
||||
mpv_detach_destroy(mpv);
|
||||
mpv_opengl_cb_uninit_gl(window.glView.mpvGL);
|
||||
mpv = NULL;
|
||||
printf("event: shutdown\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case MPV_EVENT_LOG_MESSAGE: {
|
||||
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
|
||||
printf("[%s] %s: %s", msg->prefix, msg->level, msg->text);
|
||||
}
|
||||
|
||||
default:
|
||||
printf("event: %s\n", mpv_event_name(event->event_id));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)togglePause:(NSButton *)button
|
||||
{
|
||||
if (mpv) {
|
||||
switch (button.state) {
|
||||
case NSOffState:
|
||||
{
|
||||
int pause = 0;
|
||||
mpv_set_property(mpv, "pause", MPV_FORMAT_FLAG, &pause);
|
||||
}
|
||||
break;
|
||||
case NSOnState:
|
||||
{
|
||||
int pause = 1;
|
||||
mpv_set_property(mpv, "pause", MPV_FORMAT_FLAG, &pause);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NSLog(@"This should never happen.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) readEvents
|
||||
{
|
||||
dispatch_async(queue, ^{
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE)
|
||||
break;
|
||||
[self handleEvent:event];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void wakeup(void *context)
|
||||
{
|
||||
AppDelegate *a = (__bridge AppDelegate *) context;
|
||||
[a readEvents];
|
||||
}
|
||||
|
||||
// quit when the window is closed.
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
NSLog(@"Terminating.");
|
||||
const char *args[] = {"quit", NULL};
|
||||
mpv_command(mpv, args);
|
||||
[window.glView clearGLContext];
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Delete this if you already have a main.m.
|
||||
int main(int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
AppDelegate *delegate = [AppDelegate new];
|
||||
app.delegate = delegate;
|
||||
[app run];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// Plays a video from the command line in a view provided by the client
|
||||
// application.
|
||||
|
||||
// Build with: clang -o cocoabasic cocoabasic.m `pkg-config --libs --cflags mpv` -framework cocoa
|
||||
|
||||
#include <mpv/client.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline void check_error(int status)
|
||||
{
|
||||
if (status < 0) {
|
||||
printf("mpv API error: %s\n", mpv_error_string(status));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface CocoaWindow : NSWindow
|
||||
@end
|
||||
|
||||
@implementation CocoaWindow
|
||||
- (BOOL)canBecomeMainWindow { return YES; }
|
||||
- (BOOL)canBecomeKeyWindow { return YES; }
|
||||
@end
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
{
|
||||
mpv_handle *mpv;
|
||||
dispatch_queue_t queue;
|
||||
NSWindow *w;
|
||||
NSView *wrapper;
|
||||
}
|
||||
@end
|
||||
|
||||
static void wakeup(void *);
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void)createWindow {
|
||||
|
||||
int mask = NSTitledWindowMask|NSClosableWindowMask|
|
||||
NSMiniaturizableWindowMask|NSResizableWindowMask;
|
||||
|
||||
self->w = [[CocoaWindow alloc]
|
||||
initWithContentRect:NSMakeRect(0,0, 1280, 720)
|
||||
styleMask:mask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
|
||||
[self->w setTitle:@"cocoabasic example"];
|
||||
[self->w makeMainWindow];
|
||||
[self->w makeKeyAndOrderFront:nil];
|
||||
|
||||
NSRect frame = [[self->w contentView] bounds];
|
||||
self->wrapper = [[NSView alloc] initWithFrame:frame];
|
||||
[self->wrapper setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
|
||||
[[self->w contentView] addSubview:self->wrapper];
|
||||
[self->wrapper release];
|
||||
|
||||
NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"];
|
||||
NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""];
|
||||
NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"];
|
||||
[m setSubmenu:sm forItem:item];
|
||||
[sm addItemWithTitle: @"mpv_command('stop')" action:@selector(mpv_stop) keyEquivalent:@""];
|
||||
[sm addItemWithTitle: @"mpv_command('quit')" action:@selector(mpv_quit) keyEquivalent:@""];
|
||||
[sm addItemWithTitle: @"quit" action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
[NSApp setMenu:m];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
atexit_b(^{
|
||||
// Because activation policy has just been set to behave like a real
|
||||
// application, that policy must be reset on exit to prevent, among
|
||||
// other things, the menubar created here from remaining on screen.
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
|
||||
});
|
||||
|
||||
// Read filename
|
||||
NSArray *args = [NSProcessInfo processInfo].arguments;
|
||||
if (args.count < 2) {
|
||||
NSLog(@"Expected filename on command line");
|
||||
exit(1);
|
||||
}
|
||||
NSString *filename = args[1];
|
||||
|
||||
[self createWindow];
|
||||
|
||||
// Deal with MPV in the background.
|
||||
queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_async(queue, ^{
|
||||
|
||||
mpv = mpv_create();
|
||||
if (!mpv) {
|
||||
printf("failed creating context\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int64_t wid = (intptr_t) self->wrapper;
|
||||
check_error(mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid));
|
||||
|
||||
// Maybe set some options here, like default key bindings.
|
||||
// NOTE: Interaction with the window seems to be broken for now.
|
||||
check_error(mpv_set_option_string(mpv, "input-default-bindings", "yes"));
|
||||
|
||||
// for testing!
|
||||
check_error(mpv_set_option_string(mpv, "input-media-keys", "yes"));
|
||||
check_error(mpv_set_option_string(mpv, "input-cursor", "no"));
|
||||
check_error(mpv_set_option_string(mpv, "input-vo-keyboard", "yes"));
|
||||
|
||||
// request important errors
|
||||
check_error(mpv_request_log_messages(mpv, "warn"));
|
||||
|
||||
check_error(mpv_initialize(mpv));
|
||||
|
||||
// Register to be woken up whenever mpv generates new events.
|
||||
mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *) self);
|
||||
|
||||
// Load the indicated file
|
||||
const char *cmd[] = {"loadfile", filename.UTF8String, NULL};
|
||||
check_error(mpv_command(mpv, cmd));
|
||||
});
|
||||
}
|
||||
|
||||
- (void) handleEvent:(mpv_event *)event
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_SHUTDOWN: {
|
||||
mpv_detach_destroy(mpv);
|
||||
mpv = NULL;
|
||||
printf("event: shutdown\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case MPV_EVENT_LOG_MESSAGE: {
|
||||
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
|
||||
printf("[%s] %s: %s", msg->prefix, msg->level, msg->text);
|
||||
}
|
||||
|
||||
case MPV_EVENT_VIDEO_RECONFIG: {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSArray *subviews = [self->wrapper subviews];
|
||||
if ([subviews count] > 0) {
|
||||
// mpv's events view
|
||||
NSView *eview = [self->wrapper subviews][0];
|
||||
[self->w makeFirstResponder:eview];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
printf("event: %s\n", mpv_event_name(event->event_id));
|
||||
}
|
||||
}
|
||||
|
||||
- (void) readEvents
|
||||
{
|
||||
dispatch_async(queue, ^{
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE)
|
||||
break;
|
||||
[self handleEvent:event];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void wakeup(void *context) {
|
||||
AppDelegate *a = (__bridge AppDelegate *) context;
|
||||
[a readEvents];
|
||||
}
|
||||
|
||||
// Ostensibly, mpv's window would be hooked up to this.
|
||||
- (BOOL) windowShouldClose:(id)sender
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) mpv_stop
|
||||
{
|
||||
if (mpv) {
|
||||
const char *args[] = {"stop", NULL};
|
||||
mpv_command(mpv, args);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) mpv_quit
|
||||
{
|
||||
if (mpv) {
|
||||
const char *args[] = {"quit", NULL};
|
||||
mpv_command(mpv, args);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
// Delete this if you already have a main.m.
|
||||
int main(int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
AppDelegate *delegate = [AppDelegate new];
|
||||
app.delegate = delegate;
|
||||
[app run];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <clocale>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include <QOpenGLContext>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <QtGui/QOpenGLFramebufferObject>
|
||||
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQuick/QQuickView>
|
||||
|
||||
class MpvRenderer : public QQuickFramebufferObject::Renderer
|
||||
{
|
||||
static void *get_proc_address(void *ctx, const char *name) {
|
||||
(void)ctx;
|
||||
QOpenGLContext *glctx = QOpenGLContext::currentContext();
|
||||
if (!glctx)
|
||||
return NULL;
|
||||
return (void *)glctx->getProcAddress(QByteArray(name));
|
||||
}
|
||||
|
||||
mpv::qt::Handle mpv;
|
||||
QQuickWindow *window;
|
||||
mpv_opengl_cb_context *mpv_gl;
|
||||
public:
|
||||
MpvRenderer(const MpvObject *obj)
|
||||
: mpv(obj->mpv), window(obj->window()), mpv_gl(obj->mpv_gl)
|
||||
{
|
||||
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
|
||||
if (r < 0)
|
||||
throw std::runtime_error("could not initialize OpenGL");
|
||||
}
|
||||
|
||||
virtual ~MpvRenderer()
|
||||
{
|
||||
// Until this call is done, we need to make sure the player remains
|
||||
// alive. This is done implicitly with the mpv::qt::Handle instance
|
||||
// in this class.
|
||||
mpv_opengl_cb_uninit_gl(mpv_gl);
|
||||
}
|
||||
|
||||
void render()
|
||||
{
|
||||
QOpenGLFramebufferObject *fbo = framebufferObject();
|
||||
window->resetOpenGLState();
|
||||
mpv_opengl_cb_draw(mpv_gl, fbo->handle(), fbo->width(), fbo->height());
|
||||
window->resetOpenGLState();
|
||||
}
|
||||
};
|
||||
|
||||
MpvObject::MpvObject(QQuickItem * parent)
|
||||
: QQuickFramebufferObject(parent), mpv_gl(0)
|
||||
{
|
||||
mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
|
||||
if (!mpv)
|
||||
throw std::runtime_error("could not create mpv context");
|
||||
|
||||
mpv_set_option_string(mpv, "terminal", "yes");
|
||||
mpv_set_option_string(mpv, "msg-level", "all=v");
|
||||
|
||||
if (mpv_initialize(mpv) < 0)
|
||||
throw std::runtime_error("could not initialize mpv context");
|
||||
|
||||
// Make use of the MPV_SUB_API_OPENGL_CB API.
|
||||
mpv::qt::set_option_variant(mpv, "vo", "opengl-cb");
|
||||
|
||||
// Request hw decoding, just for testing.
|
||||
mpv::qt::set_option_variant(mpv, "hwdec", "auto");
|
||||
|
||||
// Setup the callback that will make QtQuick update and redraw if there
|
||||
// is a new video frame. Use a queued connection: this makes sure the
|
||||
// doUpdate() function is run on the GUI thread.
|
||||
mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
|
||||
if (!mpv_gl)
|
||||
throw std::runtime_error("OpenGL not compiled in");
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, MpvObject::on_update, (void *)this);
|
||||
connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
MpvObject::~MpvObject()
|
||||
{
|
||||
if (mpv_gl)
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
|
||||
}
|
||||
|
||||
void MpvObject::on_update(void *ctx)
|
||||
{
|
||||
MpvObject *self = (MpvObject *)ctx;
|
||||
emit self->onUpdate();
|
||||
}
|
||||
|
||||
// connected to onUpdate(); signal makes sure it runs on the GUI thread
|
||||
void MpvObject::doUpdate()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void MpvObject::command(const QVariant& params)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, params);
|
||||
}
|
||||
|
||||
void MpvObject::setProperty(const QString& name, const QVariant& value)
|
||||
{
|
||||
mpv::qt::set_property_variant(mpv, name, value);
|
||||
}
|
||||
|
||||
QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const
|
||||
{
|
||||
window()->setPersistentOpenGLContext(true);
|
||||
window()->setPersistentSceneGraph(true);
|
||||
return new MpvRenderer(this);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
// Qt sets the locale in the QGuiApplication constructor, but libmpv
|
||||
// requires the LC_NUMERIC category to be set to "C", so change it back.
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
|
||||
qmlRegisterType<MpvObject>("mpvtest", 1, 0, "MpvObject");
|
||||
|
||||
QQuickView view;
|
||||
view.setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
view.setSource(QUrl("qrc:///mpvtest/main.qml"));
|
||||
view.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef MPVRENDERER_H_
|
||||
#define MPVRENDERER_H_
|
||||
|
||||
#include <QtQuick/QQuickFramebufferObject>
|
||||
|
||||
#include <mpv/client.h>
|
||||
#include <mpv/opengl_cb.h>
|
||||
#include <mpv/qthelper.hpp>
|
||||
|
||||
class MpvRenderer;
|
||||
|
||||
class MpvObject : public QQuickFramebufferObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
mpv::qt::Handle mpv;
|
||||
mpv_opengl_cb_context *mpv_gl;
|
||||
|
||||
friend class MpvRenderer;
|
||||
|
||||
public:
|
||||
MpvObject(QQuickItem * parent = 0);
|
||||
virtual ~MpvObject();
|
||||
virtual Renderer *createRenderer() const;
|
||||
public slots:
|
||||
void command(const QVariant& params);
|
||||
void setProperty(const QString& name, const QVariant& value);
|
||||
signals:
|
||||
void onUpdate();
|
||||
private slots:
|
||||
void doUpdate();
|
||||
private:
|
||||
static void on_update(void *ctx);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0
|
||||
|
||||
import mpvtest 1.0
|
||||
|
||||
Item {
|
||||
width: 1280
|
||||
height: 720
|
||||
|
||||
MpvObject {
|
||||
id: renderer
|
||||
anchors.fill: parent
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: renderer.command(["loadfile", "../../../test.mkv"])
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: labelFrame
|
||||
anchors.margins: -50
|
||||
radius: 5
|
||||
color: "white"
|
||||
border.color: "black"
|
||||
opacity: 0.8
|
||||
anchors.fill: box
|
||||
}
|
||||
|
||||
Row {
|
||||
id: box
|
||||
anchors.bottom: renderer.bottom
|
||||
anchors.left: renderer.left
|
||||
anchors.right: renderer.right
|
||||
anchors.margins: 100
|
||||
|
||||
Text {
|
||||
anchors.margins: 10
|
||||
wrapMode: Text.WordWrap
|
||||
text: "QtQuick and mpv are both rendering stuff.\n
|
||||
Click to load ../../../test.mkv"
|
||||
}
|
||||
|
||||
// Don't take these controls too seriously. They're for testing.
|
||||
Column {
|
||||
CheckBox {
|
||||
id: checkbox
|
||||
anchors.margins: 10
|
||||
// Heavily filtered means good, right?
|
||||
text: "Make video look like on a Smart TV"
|
||||
onClicked: {
|
||||
if (checkbox.checked) {
|
||||
renderer.command(["vo_cmdline", "sharpen=5.0"])
|
||||
} else {
|
||||
renderer.command(["vo_cmdline", ""])
|
||||
}
|
||||
}
|
||||
}
|
||||
Slider {
|
||||
id: slider
|
||||
anchors.margins: 10
|
||||
anchors.left: checkbox.left
|
||||
anchors.right: checkbox.right
|
||||
minimumValue: -100
|
||||
maximumValue: 100
|
||||
value: 0
|
||||
onValueChanged: renderer.setProperty("gamma", slider.value | 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
QT += qml quick
|
||||
|
||||
HEADERS += main.h
|
||||
SOURCES += main.cpp
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG += mpv
|
||||
|
||||
RESOURCES += mpvtest.qrc
|
||||
|
||||
OTHER_FILES += main.qml
|
||||
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/mpvtest">
|
||||
<file>main.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -0,0 +1,159 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <clocale>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include <QOpenGLContext>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQuick/QQuickView>
|
||||
|
||||
static void *get_proc_address(void *ctx, const char *name) {
|
||||
(void)ctx;
|
||||
QOpenGLContext *glctx = QOpenGLContext::currentContext();
|
||||
if (!glctx)
|
||||
return NULL;
|
||||
return (void *)glctx->getProcAddress(QByteArray(name));
|
||||
}
|
||||
|
||||
MpvRenderer::MpvRenderer(mpv::qt::Handle a_mpv, mpv_opengl_cb_context *a_mpv_gl)
|
||||
: mpv(a_mpv), mpv_gl(a_mpv_gl), window(0), size()
|
||||
{
|
||||
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
|
||||
if (r < 0)
|
||||
throw std::runtime_error("could not initialize OpenGL");
|
||||
}
|
||||
|
||||
MpvRenderer::~MpvRenderer()
|
||||
{
|
||||
// Until this call is done, we need to make sure the player remains
|
||||
// alive. This is done implicitly with the mpv::qt::Handle instance
|
||||
// in this class.
|
||||
mpv_opengl_cb_uninit_gl(mpv_gl);
|
||||
}
|
||||
|
||||
void MpvRenderer::paint()
|
||||
{
|
||||
window->resetOpenGLState();
|
||||
|
||||
// This uses 0 as framebuffer, which indicates that mpv will render directly
|
||||
// to the frontbuffer. Note that mpv will always switch framebuffers
|
||||
// explicitly. Some QWindow setups (such as using QQuickWidget) actually
|
||||
// want you to render into a FBO in the beforeRendering() signal, and this
|
||||
// code won't work there.
|
||||
// The negation is used for rendering with OpenGL's flipped coordinates.
|
||||
mpv_opengl_cb_draw(mpv_gl, 0, size.width(), -size.height());
|
||||
|
||||
window->resetOpenGLState();
|
||||
}
|
||||
|
||||
MpvObject::MpvObject(QQuickItem * parent)
|
||||
: QQuickItem(parent), mpv_gl(0), renderer(0)
|
||||
{
|
||||
mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
|
||||
if (!mpv)
|
||||
throw std::runtime_error("could not create mpv context");
|
||||
|
||||
mpv_set_option_string(mpv, "terminal", "yes");
|
||||
mpv_set_option_string(mpv, "msg-level", "all=v");
|
||||
|
||||
if (mpv_initialize(mpv) < 0)
|
||||
throw std::runtime_error("could not initialize mpv context");
|
||||
|
||||
// Make use of the MPV_SUB_API_OPENGL_CB API.
|
||||
mpv::qt::set_option_variant(mpv, "vo", "opengl-cb");
|
||||
|
||||
// Setup the callback that will make QtQuick update and redraw if there
|
||||
// is a new video frame. Use a queued connection: this makes sure the
|
||||
// doUpdate() function is run on the GUI thread.
|
||||
mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
|
||||
if (!mpv_gl)
|
||||
throw std::runtime_error("OpenGL not compiled in");
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, MpvObject::on_update, (void *)this);
|
||||
connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(this, &QQuickItem::windowChanged,
|
||||
this, &MpvObject::handleWindowChanged);
|
||||
}
|
||||
|
||||
MpvObject::~MpvObject()
|
||||
{
|
||||
if (mpv_gl)
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
|
||||
}
|
||||
|
||||
void MpvObject::handleWindowChanged(QQuickWindow *win)
|
||||
{
|
||||
if (!win)
|
||||
return;
|
||||
connect(win, &QQuickWindow::beforeSynchronizing,
|
||||
this, &MpvObject::sync, Qt::DirectConnection);
|
||||
connect(win, &QQuickWindow::sceneGraphInvalidated,
|
||||
this, &MpvObject::cleanup, Qt::DirectConnection);
|
||||
connect(win, &QQuickWindow::frameSwapped,
|
||||
this, &MpvObject::swapped, Qt::DirectConnection);
|
||||
win->setClearBeforeRendering(false);
|
||||
}
|
||||
|
||||
void MpvObject::sync()
|
||||
{
|
||||
if (!renderer) {
|
||||
renderer = new MpvRenderer(mpv, mpv_gl);
|
||||
connect(window(), &QQuickWindow::beforeRendering,
|
||||
renderer, &MpvRenderer::paint, Qt::DirectConnection);
|
||||
}
|
||||
renderer->window = window();
|
||||
renderer->size = window()->size() * window()->devicePixelRatio();
|
||||
}
|
||||
|
||||
void MpvObject::swapped()
|
||||
{
|
||||
mpv_opengl_cb_report_flip(mpv_gl, 0);
|
||||
}
|
||||
|
||||
void MpvObject::cleanup()
|
||||
{
|
||||
if (renderer) {
|
||||
delete renderer;
|
||||
renderer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MpvObject::on_update(void *ctx)
|
||||
{
|
||||
MpvObject *self = (MpvObject *)ctx;
|
||||
emit self->onUpdate();
|
||||
}
|
||||
|
||||
// connected to onUpdate(); signal makes sure it runs on the GUI thread
|
||||
void MpvObject::doUpdate()
|
||||
{
|
||||
window()->update();
|
||||
}
|
||||
|
||||
void MpvObject::command(const QVariant& params)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, params);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
// Qt sets the locale in the QGuiApplication constructor, but libmpv
|
||||
// requires the LC_NUMERIC category to be set to "C", so change it back.
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
|
||||
qmlRegisterType<MpvObject>("mpvtest", 1, 0, "MpvObject");
|
||||
|
||||
QQuickView view;
|
||||
view.setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
view.setSource(QUrl("qrc:///mpvtest/main.qml"));
|
||||
view.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef MPVRENDERER_H_
|
||||
#define MPVRENDERER_H_
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
|
||||
#include <mpv/client.h>
|
||||
#include <mpv/opengl_cb.h>
|
||||
#include <mpv/qthelper.hpp>
|
||||
|
||||
class MpvRenderer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
mpv::qt::Handle mpv;
|
||||
mpv_opengl_cb_context *mpv_gl;
|
||||
QQuickWindow *window;
|
||||
QSize size;
|
||||
|
||||
friend class MpvObject;
|
||||
public:
|
||||
MpvRenderer(mpv::qt::Handle a_mpv, mpv_opengl_cb_context *a_mpv_gl);
|
||||
virtual ~MpvRenderer();
|
||||
public slots:
|
||||
void paint();
|
||||
};
|
||||
|
||||
class MpvObject : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
mpv::qt::Handle mpv;
|
||||
mpv_opengl_cb_context *mpv_gl;
|
||||
MpvRenderer *renderer;
|
||||
|
||||
public:
|
||||
MpvObject(QQuickItem * parent = 0);
|
||||
virtual ~MpvObject();
|
||||
public slots:
|
||||
void command(const QVariant& params);
|
||||
void sync();
|
||||
void swapped();
|
||||
void cleanup();
|
||||
signals:
|
||||
void onUpdate();
|
||||
private slots:
|
||||
void doUpdate();
|
||||
void handleWindowChanged(QQuickWindow *win);
|
||||
private:
|
||||
static void on_update(void *ctx);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0
|
||||
|
||||
import mpvtest 1.0
|
||||
|
||||
Item {
|
||||
width: 1280
|
||||
height: 720
|
||||
|
||||
MpvObject {
|
||||
id: renderer
|
||||
|
||||
// This object isn't real and not visible; it just renders into the
|
||||
// background of the containing Window.
|
||||
width: 0
|
||||
height: 0
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: renderer.command(["loadfile", "../../../test.mkv"])
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: labelFrame
|
||||
anchors.margins: -50
|
||||
radius: 5
|
||||
color: "white"
|
||||
border.color: "black"
|
||||
opacity: 0.8
|
||||
anchors.fill: box
|
||||
}
|
||||
|
||||
Row {
|
||||
id: box
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 100
|
||||
|
||||
Text {
|
||||
anchors.margins: 10
|
||||
wrapMode: Text.WordWrap
|
||||
text: "QtQuick and mpv are both rendering stuff.\n
|
||||
In this example, mpv is always in the background.\n
|
||||
Click to load ../../../test.mkv"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
QT += qml quick
|
||||
|
||||
HEADERS += main.h
|
||||
SOURCES += main.cpp
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG += mpv
|
||||
|
||||
RESOURCES += mpvtest.qrc
|
||||
|
||||
OTHER_FILES += main.qml
|
||||
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/mpvtest">
|
||||
<file>main.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -0,0 +1,225 @@
|
||||
// This example can be built with: qmake && make
|
||||
|
||||
#include <clocale>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QFileDialog>
|
||||
#include <QStatusBar>
|
||||
#include <QMenuBar>
|
||||
#include <QMenu>
|
||||
#include <QGridLayout>
|
||||
#include <QApplication>
|
||||
#include <QTextEdit>
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QJsonDocument>
|
||||
#endif
|
||||
|
||||
#include <mpv/qthelper.hpp>
|
||||
|
||||
#include "qtexample.h"
|
||||
|
||||
static void wakeup(void *ctx)
|
||||
{
|
||||
// This callback is invoked from any mpv thread (but possibly also
|
||||
// recursively from a thread that is calling the mpv API). Just notify
|
||||
// the Qt GUI thread to wake up (so that it can process events with
|
||||
// mpv_wait_event()), and return as quickly as possible.
|
||||
MainWindow *mainwindow = (MainWindow *)ctx;
|
||||
emit mainwindow->mpv_events();
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent)
|
||||
{
|
||||
setWindowTitle("Qt embedding demo");
|
||||
setMinimumSize(640, 480);
|
||||
|
||||
QMenu *menu = menuBar()->addMenu(tr("&File"));
|
||||
QAction *on_open = new QAction(tr("&Open"), this);
|
||||
on_open->setShortcuts(QKeySequence::Open);
|
||||
on_open->setStatusTip(tr("Open a file"));
|
||||
connect(on_open, &QAction::triggered, this, &MainWindow::on_file_open);
|
||||
menu->addAction(on_open);
|
||||
|
||||
QAction *on_new = new QAction(tr("&New window"), this);
|
||||
connect(on_new, &QAction::triggered, this, &MainWindow::on_new_window);
|
||||
menu->addAction(on_new);
|
||||
|
||||
statusBar();
|
||||
|
||||
QMainWindow *log_window = new QMainWindow(this);
|
||||
log = new QTextEdit(log_window);
|
||||
log->setReadOnly(true);
|
||||
log_window->setCentralWidget(log);
|
||||
log_window->setWindowTitle("mpv log window");
|
||||
log_window->setMinimumSize(500, 50);
|
||||
log_window->show();
|
||||
|
||||
mpv = mpv_create();
|
||||
if (!mpv)
|
||||
throw std::runtime_error("can't create mpv instance");
|
||||
|
||||
// Create a video child window. Force Qt to create a native window, and
|
||||
// pass the window ID to the mpv wid option. Works on: X11, win32, Cocoa
|
||||
mpv_container = new QWidget(this);
|
||||
setCentralWidget(mpv_container);
|
||||
mpv_container->setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
mpv_container->setAttribute(Qt::WA_NativeWindow);
|
||||
// If you have a HWND, use: int64_t wid = (intptr_t)hwnd;
|
||||
int64_t wid = mpv_container->winId();
|
||||
mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);
|
||||
|
||||
// Enable default bindings, because we're lazy. Normally, a player using
|
||||
// mpv as backend would implement its own key bindings.
|
||||
mpv_set_option_string(mpv, "input-default-bindings", "yes");
|
||||
|
||||
// Enable keyboard input on the X11 window. For the messy details, see
|
||||
// --input-vo-keyboard on the manpage.
|
||||
mpv_set_option_string(mpv, "input-vo-keyboard", "yes");
|
||||
|
||||
// Let us receive property change events with MPV_EVENT_PROPERTY_CHANGE if
|
||||
// this property changes.
|
||||
mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
||||
|
||||
mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE);
|
||||
mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE);
|
||||
|
||||
// Request log messages with level "info" or higher.
|
||||
// They are received as MPV_EVENT_LOG_MESSAGE.
|
||||
mpv_request_log_messages(mpv, "info");
|
||||
|
||||
// From this point on, the wakeup function will be called. The callback
|
||||
// can come from any thread, so we use the QueuedConnection mechanism to
|
||||
// relay the wakeup in a thread-safe way.
|
||||
connect(this, &MainWindow::mpv_events, this, &MainWindow::on_mpv_events,
|
||||
Qt::QueuedConnection);
|
||||
mpv_set_wakeup_callback(mpv, wakeup, this);
|
||||
|
||||
if (mpv_initialize(mpv) < 0)
|
||||
throw std::runtime_error("mpv failed to initialize");
|
||||
}
|
||||
|
||||
void MainWindow::handle_mpv_event(mpv_event *event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||
mpv_event_property *prop = (mpv_event_property *)event->data;
|
||||
if (strcmp(prop->name, "time-pos") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double *)prop->data;
|
||||
std::stringstream ss;
|
||||
ss << "At: " << time;
|
||||
statusBar()->showMessage(QString::fromStdString(ss.str()));
|
||||
} else if (prop->format == MPV_FORMAT_NONE) {
|
||||
// The property is unavailable, which probably means playback
|
||||
// was stopped.
|
||||
statusBar()->showMessage("");
|
||||
}
|
||||
} else if (strcmp(prop->name, "chapter-list") == 0 ||
|
||||
strcmp(prop->name, "track-list") == 0)
|
||||
{
|
||||
// Dump the properties as JSON for demo purposes.
|
||||
#if QT_VERSION >= 0x050000
|
||||
if (prop->format == MPV_FORMAT_NODE) {
|
||||
QVariant v = mpv::qt::node_to_variant((mpv_node *)prop->data);
|
||||
// Abuse JSON support for easily printing the mpv_node contents.
|
||||
QJsonDocument d = QJsonDocument::fromVariant(v);
|
||||
append_log("Change property " + QString(prop->name) + ":\n");
|
||||
append_log(d.toJson().data());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_VIDEO_RECONFIG: {
|
||||
// Retrieve the new video size.
|
||||
int64_t w, h;
|
||||
if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 &&
|
||||
mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 &&
|
||||
w > 0 && h > 0)
|
||||
{
|
||||
// Note that the MPV_EVENT_VIDEO_RECONFIG event doesn't necessarily
|
||||
// imply a resize, and you should check yourself if the video
|
||||
// dimensions really changed.
|
||||
// mpv itself will scale/letter box the video to the container size
|
||||
// if the video doesn't fit.
|
||||
std::stringstream ss;
|
||||
ss << "Reconfig: " << w << " " << h;
|
||||
statusBar()->showMessage(QString::fromStdString(ss.str()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_LOG_MESSAGE: {
|
||||
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
|
||||
std::stringstream ss;
|
||||
ss << "[" << msg->prefix << "] " << msg->level << ": " << msg->text;
|
||||
append_log(QString::fromStdString(ss.str()));
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_SHUTDOWN: {
|
||||
mpv_terminate_destroy(mpv);
|
||||
mpv = NULL;
|
||||
break;
|
||||
}
|
||||
default: ;
|
||||
// Ignore uninteresting or unknown events.
|
||||
}
|
||||
}
|
||||
|
||||
// This slot is invoked by wakeup() (through the mpv_events signal).
|
||||
void MainWindow::on_mpv_events()
|
||||
{
|
||||
// Process all events, until the event queue is empty.
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE)
|
||||
break;
|
||||
handle_mpv_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_file_open()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Open file");
|
||||
if (mpv) {
|
||||
const QByteArray c_filename = filename.toUtf8();
|
||||
const char *args[] = {"loadfile", c_filename.data(), NULL};
|
||||
mpv_command_async(mpv, 0, args);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_new_window()
|
||||
{
|
||||
(new MainWindow())->show();
|
||||
}
|
||||
|
||||
void MainWindow::append_log(const QString &text)
|
||||
{
|
||||
QTextCursor cursor = log->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.insertText(text);
|
||||
log->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
if (mpv)
|
||||
mpv_terminate_destroy(mpv);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
// Qt sets the locale in the QApplication constructor, but libmpv requires
|
||||
// the LC_NUMERIC category to be set to "C", so change it back.
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef QTEXAMPLE_H
|
||||
#define QTEXAMPLE_H
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
#include <mpv/client.h>
|
||||
|
||||
class QTextEdit;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
private slots:
|
||||
void on_file_open();
|
||||
void on_new_window();
|
||||
void on_mpv_events();
|
||||
|
||||
signals:
|
||||
void mpv_events();
|
||||
|
||||
private:
|
||||
QWidget *mpv_container;
|
||||
mpv_handle *mpv;
|
||||
QTextEdit *log;
|
||||
|
||||
void append_log(const QString &text);
|
||||
|
||||
void create_player();
|
||||
void handle_mpv_event(mpv_event *event);
|
||||
};
|
||||
|
||||
#endif // QTEXAMPLE_H
|
||||
@@ -0,0 +1,13 @@
|
||||
QT += core gui
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = qtexample
|
||||
TEMPLATE = app
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG += mpv
|
||||
|
||||
SOURCES += qtexample.cpp
|
||||
HEADERS += qtexample.h
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <QApplication>
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
// Qt sets the locale in the QApplication constructor, but libmpv requires
|
||||
// the LC_NUMERIC category to be set to "C", so change it back.
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "mainwindow.h"
|
||||
#include "mpvwidget.h"
|
||||
#include <QPushButton>
|
||||
#include <QSlider>
|
||||
#include <QLayout>
|
||||
#include <QFileDialog>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
m_mpv = new MpvWidget(this);
|
||||
m_slider = new QSlider();
|
||||
m_slider->setOrientation(Qt::Horizontal);
|
||||
m_openBtn = new QPushButton("Open");
|
||||
m_playBtn = new QPushButton("Pause");
|
||||
QHBoxLayout *hb = new QHBoxLayout();
|
||||
hb->addWidget(m_openBtn);
|
||||
hb->addWidget(m_playBtn);
|
||||
QVBoxLayout *vl = new QVBoxLayout();
|
||||
vl->addWidget(m_mpv);
|
||||
vl->addWidget(m_slider);
|
||||
vl->addLayout(hb);
|
||||
setLayout(vl);
|
||||
connect(m_slider, SIGNAL(sliderMoved(int)), SLOT(seek(int)));
|
||||
connect(m_openBtn, SIGNAL(clicked()), SLOT(openMedia()));
|
||||
connect(m_playBtn, SIGNAL(clicked()), SLOT(pauseResume()));
|
||||
connect(m_mpv, SIGNAL(positionChanged(int)), m_slider, SLOT(setValue(int)));
|
||||
connect(m_mpv, SIGNAL(durationChanged(int)), this, SLOT(setSliderRange(int)));
|
||||
}
|
||||
|
||||
void MainWindow::openMedia()
|
||||
{
|
||||
QString file = QFileDialog::getOpenFileName(0, "Open a video");
|
||||
if (file.isEmpty())
|
||||
return;
|
||||
m_mpv->command(QStringList() << "loadfile" << file);
|
||||
}
|
||||
|
||||
void MainWindow::seek(int pos)
|
||||
{
|
||||
m_mpv->command(QVariantList() << "seek" << pos << "absolute");
|
||||
}
|
||||
|
||||
void MainWindow::pauseResume()
|
||||
{
|
||||
const bool paused = m_mpv->getProperty("pause").toBool();
|
||||
m_mpv->setProperty("pause", !paused);
|
||||
}
|
||||
|
||||
void MainWindow::setSliderRange(int duration)
|
||||
{
|
||||
m_slider->setRange(0, duration);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef MainWindow_H
|
||||
#define MainWindow_H
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
class MpvWidget;
|
||||
class QSlider;
|
||||
class QPushButton;
|
||||
class MainWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
public Q_SLOTS:
|
||||
void openMedia();
|
||||
void seek(int pos);
|
||||
void pauseResume();
|
||||
private Q_SLOTS:
|
||||
void setSliderRange(int duration);
|
||||
private:
|
||||
MpvWidget *m_mpv;
|
||||
QSlider *m_slider;
|
||||
QPushButton *m_openBtn;
|
||||
QPushButton *m_playBtn;
|
||||
};
|
||||
|
||||
#endif // MainWindow_H
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "mpvwidget.h"
|
||||
#include <stdexcept>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtCore/QMetaObject>
|
||||
|
||||
static void wakeup(void *ctx)
|
||||
{
|
||||
QMetaObject::invokeMethod((MpvWidget*)ctx, "on_mpv_events", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
static void *get_proc_address(void *ctx, const char *name) {
|
||||
Q_UNUSED(ctx);
|
||||
QOpenGLContext *glctx = QOpenGLContext::currentContext();
|
||||
if (!glctx)
|
||||
return NULL;
|
||||
return (void *)glctx->getProcAddress(QByteArray(name));
|
||||
}
|
||||
|
||||
MpvWidget::MpvWidget(QWidget *parent, Qt::WindowFlags f)
|
||||
: QOpenGLWidget(parent, f)
|
||||
{
|
||||
mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
|
||||
if (!mpv)
|
||||
throw std::runtime_error("could not create mpv context");
|
||||
|
||||
mpv_set_option_string(mpv, "terminal", "yes");
|
||||
mpv_set_option_string(mpv, "msg-level", "all=v");
|
||||
if (mpv_initialize(mpv) < 0)
|
||||
throw std::runtime_error("could not initialize mpv context");
|
||||
|
||||
// Make use of the MPV_SUB_API_OPENGL_CB API.
|
||||
mpv::qt::set_option_variant(mpv, "vo", "opengl-cb");
|
||||
|
||||
// Request hw decoding, just for testing.
|
||||
mpv::qt::set_option_variant(mpv, "hwdec", "auto");
|
||||
|
||||
mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
|
||||
if (!mpv_gl)
|
||||
throw std::runtime_error("OpenGL not compiled in");
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, MpvWidget::on_update, (void *)this);
|
||||
connect(this, SIGNAL(frameSwapped()), SLOT(swapped()));
|
||||
|
||||
mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);
|
||||
mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
||||
mpv_set_wakeup_callback(mpv, wakeup, this);
|
||||
}
|
||||
|
||||
MpvWidget::~MpvWidget()
|
||||
{
|
||||
makeCurrent();
|
||||
if (mpv_gl)
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
|
||||
// Until this call is done, we need to make sure the player remains
|
||||
// alive. This is done implicitly with the mpv::qt::Handle instance
|
||||
// in this class.
|
||||
mpv_opengl_cb_uninit_gl(mpv_gl);
|
||||
}
|
||||
|
||||
void MpvWidget::command(const QVariant& params)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, params);
|
||||
}
|
||||
|
||||
void MpvWidget::setProperty(const QString& name, const QVariant& value)
|
||||
{
|
||||
mpv::qt::set_property_variant(mpv, name, value);
|
||||
}
|
||||
|
||||
QVariant MpvWidget::getProperty(const QString &name) const
|
||||
{
|
||||
return mpv::qt::get_property_variant(mpv, name);
|
||||
}
|
||||
|
||||
void MpvWidget::initializeGL()
|
||||
{
|
||||
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
|
||||
if (r < 0)
|
||||
throw std::runtime_error("could not initialize OpenGL");
|
||||
}
|
||||
|
||||
void MpvWidget::paintGL()
|
||||
{
|
||||
mpv_opengl_cb_draw(mpv_gl, defaultFramebufferObject(), width(), -height());
|
||||
}
|
||||
|
||||
void MpvWidget::swapped()
|
||||
{
|
||||
mpv_opengl_cb_report_flip(mpv_gl, 0);
|
||||
}
|
||||
|
||||
void MpvWidget::on_mpv_events()
|
||||
{
|
||||
// Process all events, until the event queue is empty.
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE) {
|
||||
break;
|
||||
}
|
||||
handle_mpv_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
void MpvWidget::handle_mpv_event(mpv_event *event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||
mpv_event_property *prop = (mpv_event_property *)event->data;
|
||||
if (strcmp(prop->name, "time-pos") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double *)prop->data;
|
||||
Q_EMIT positionChanged(time);
|
||||
}
|
||||
} else if (strcmp(prop->name, "duration") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double *)prop->data;
|
||||
Q_EMIT durationChanged(time);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: ;
|
||||
// Ignore uninteresting or unknown events.
|
||||
}
|
||||
}
|
||||
|
||||
void MpvWidget::on_update(void *ctx)
|
||||
{
|
||||
QMetaObject::invokeMethod((MpvWidget*)ctx, "update");
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef PLAYERWINDOW_H
|
||||
#define PLAYERWINDOW_H
|
||||
|
||||
#include <QtWidgets/QOpenGLWidget>
|
||||
#include <mpv/client.h>
|
||||
#include <mpv/opengl_cb.h>
|
||||
#include <mpv/qthelper.hpp>
|
||||
|
||||
class MpvWidget Q_DECL_FINAL: public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MpvWidget(QWidget *parent = 0, Qt::WindowFlags f = 0);
|
||||
~MpvWidget();
|
||||
void command(const QVariant& params);
|
||||
void setProperty(const QString& name, const QVariant& value);
|
||||
QVariant getProperty(const QString& name) const;
|
||||
QSize sizeHint() const { return QSize(480, 270);}
|
||||
Q_SIGNALS:
|
||||
void durationChanged(int value);
|
||||
void positionChanged(int value);
|
||||
protected:
|
||||
void initializeGL() Q_DECL_OVERRIDE;
|
||||
void paintGL() Q_DECL_OVERRIDE;
|
||||
private Q_SLOTS:
|
||||
void swapped();
|
||||
void on_mpv_events();
|
||||
private:
|
||||
void handle_mpv_event(mpv_event *event);
|
||||
static void on_update(void *ctx);
|
||||
|
||||
mpv::qt::Handle mpv;
|
||||
mpv_opengl_cb_context *mpv_gl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // PLAYERWINDOW_H
|
||||
@@ -0,0 +1,13 @@
|
||||
CONFIG -= app_bundle
|
||||
QT += widgets
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG += mpv
|
||||
|
||||
HEADERS = \
|
||||
mpvwidget.h \
|
||||
mainwindow.h
|
||||
SOURCES = main.cpp \
|
||||
mpvwidget.cpp \
|
||||
mainwindow.cpp
|
||||
@@ -0,0 +1,158 @@
|
||||
// Build with: gcc -o main main.c `pkg-config --libs --cflags mpv sdl2`
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <mpv/client.h>
|
||||
#include <mpv/opengl_cb.h>
|
||||
|
||||
static Uint32 wakeup_on_mpv_redraw, wakeup_on_mpv_events;
|
||||
|
||||
static void die(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void *get_proc_address_mpv(void *fn_ctx, const char *name)
|
||||
{
|
||||
return SDL_GL_GetProcAddress(name);
|
||||
}
|
||||
|
||||
static void on_mpv_events(void *ctx)
|
||||
{
|
||||
SDL_Event event = {.type = wakeup_on_mpv_events};
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
static void on_mpv_redraw(void *ctx)
|
||||
{
|
||||
SDL_Event event = {.type = wakeup_on_mpv_redraw};
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
die("pass a single media file as argument");
|
||||
|
||||
mpv_handle *mpv = mpv_create();
|
||||
if (!mpv)
|
||||
die("context init failed");
|
||||
|
||||
// Some minor options can only be set before mpv_initialize().
|
||||
if (mpv_initialize(mpv) < 0)
|
||||
die("mpv init failed");
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
die("SDL init failed");
|
||||
|
||||
SDL_Window *window =
|
||||
SDL_CreateWindow("hi", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
1000, 500, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN |
|
||||
SDL_WINDOW_RESIZABLE);
|
||||
if (!window)
|
||||
die("failed to create SDL window");
|
||||
|
||||
// The OpenGL API is somewhat separate from the normal mpv API. This only
|
||||
// returns NULL if no OpenGL support is compiled.
|
||||
mpv_opengl_cb_context *mpv_gl = mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
|
||||
if (!mpv_gl)
|
||||
die("failed to create mpv GL API handle");
|
||||
|
||||
SDL_GLContext glcontext = SDL_GL_CreateContext(window);
|
||||
if (!glcontext)
|
||||
die("failed to create SDL GL context");
|
||||
|
||||
// This makes mpv use the currently set GL context. It will use the callback
|
||||
// to resolve GL builtin functions, as well as extensions.
|
||||
if (mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address_mpv, NULL) < 0)
|
||||
die("failed to initialize mpv GL context");
|
||||
|
||||
// Actually using the opengl_cb state has to be explicitly requested.
|
||||
// Otherwise, mpv will create a separate platform window.
|
||||
if (mpv_set_option_string(mpv, "vo", "opengl-cb") < 0)
|
||||
die("failed to set VO");
|
||||
|
||||
// We use events for thread-safe notification of the SDL main loop.
|
||||
// Generally, the wakeup callbacks (set further below) should do as least
|
||||
// work as possible, and merely wake up another thread to do actual work.
|
||||
// On SDL, waking up the mainloop is the ideal course of action. SDL's
|
||||
// SDL_PushEvent() is thread-safe, so we use that.
|
||||
wakeup_on_mpv_redraw = SDL_RegisterEvents(1);
|
||||
wakeup_on_mpv_events = SDL_RegisterEvents(1);
|
||||
if (wakeup_on_mpv_redraw == (Uint32)-1 || wakeup_on_mpv_events == (Uint32)-1)
|
||||
die("could not register events");
|
||||
|
||||
// When normal mpv events are available.
|
||||
mpv_set_wakeup_callback(mpv, on_mpv_events, NULL);
|
||||
|
||||
// When a new frame should be drawn with mpv_opengl_cb_draw().
|
||||
// (Separate from the normal event handling mechanism for the sake of
|
||||
// users which run OpenGL on a different thread.)
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, on_mpv_redraw, NULL);
|
||||
|
||||
// Play this file. Note that this starts playback asynchronously.
|
||||
const char *cmd[] = {"loadfile", argv[1], NULL};
|
||||
mpv_command(mpv, cmd);
|
||||
|
||||
while (1) {
|
||||
SDL_Event event;
|
||||
if (SDL_WaitEvent(&event) != 1)
|
||||
die("event loop error");
|
||||
int redraw = 0;
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
goto done;
|
||||
case SDL_WINDOWEVENT:
|
||||
if (event.window.event == SDL_WINDOWEVENT_EXPOSED)
|
||||
redraw = 1;
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
if (event.key.keysym.sym == SDLK_SPACE)
|
||||
mpv_command_string(mpv, "cycle pause");
|
||||
break;
|
||||
default:
|
||||
// Happens when a new video frame should be rendered, or if the
|
||||
// current frame has to be redrawn e.g. due to OSD changes.
|
||||
if (event.type == wakeup_on_mpv_redraw)
|
||||
redraw = 1;
|
||||
// Happens when at least 1 new event is in the mpv event queue.
|
||||
if (event.type == wakeup_on_mpv_events) {
|
||||
// Handle all remaining mpv events.
|
||||
while (1) {
|
||||
mpv_event *mp_event = mpv_wait_event(mpv, 0);
|
||||
if (mp_event->event_id == MPV_EVENT_NONE)
|
||||
break;
|
||||
printf("event: %s\n", mpv_event_name(mp_event->event_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (redraw) {
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
// Note:
|
||||
// - The 0 is the FBO to use; 0 is the default framebuffer (i.e.
|
||||
// render to the window directly.
|
||||
// - The negative height tells mpv to flip the coordinate system.
|
||||
// - If you do not want the video to cover the whole screen, or want
|
||||
// to apply any form of fancy transformation, you will have to
|
||||
// render to a FBO.
|
||||
// - See opengl_cb.h on what OpenGL environment mpv expects, and
|
||||
// other API details.
|
||||
mpv_opengl_cb_draw(mpv_gl, 0, w, -h);
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
}
|
||||
done:
|
||||
|
||||
// Destroy the GL renderer and all of the GL objects it allocated. If video
|
||||
// is still running, the video track will be deselected.
|
||||
mpv_opengl_cb_uninit_gl(mpv_gl);
|
||||
|
||||
mpv_terminate_destroy(mpv);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Build with: gcc -o simple simple.c `pkg-config --libs --cflags mpv`
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <mpv/client.h>
|
||||
|
||||
static inline void check_error(int status)
|
||||
{
|
||||
if (status < 0) {
|
||||
printf("mpv API error: %s\n", mpv_error_string(status));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf("pass a single media file as argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mpv_handle *ctx = mpv_create();
|
||||
if (!ctx) {
|
||||
printf("failed creating context\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Enable default key bindings, so the user can actually interact with
|
||||
// the player (and e.g. close the window).
|
||||
check_error(mpv_set_option_string(ctx, "input-default-bindings", "yes"));
|
||||
mpv_set_option_string(ctx, "input-vo-keyboard", "yes");
|
||||
int val = 1;
|
||||
check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val));
|
||||
|
||||
// Done setting up options.
|
||||
check_error(mpv_initialize(ctx));
|
||||
|
||||
// Play this file.
|
||||
const char *cmd[] = {"loadfile", argv[1], NULL};
|
||||
check_error(mpv_command(ctx, cmd));
|
||||
|
||||
// Let it play, and wait until the user quits.
|
||||
while (1) {
|
||||
mpv_event *event = mpv_wait_event(ctx, 10000);
|
||||
printf("event: %s\n", mpv_event_name(event->event_id));
|
||||
if (event->event_id == MPV_EVENT_SHUTDOWN)
|
||||
break;
|
||||
}
|
||||
|
||||
mpv_terminate_destroy(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
CLI and API compatibility policy
|
||||
================================
|
||||
|
||||
Human users and API users rely on the mpv/libmpv/scripting/IPC interface not
|
||||
breaking their use case. On the other hand, active development occasionally
|
||||
requires breaking things, such as removing old options or changing options in
|
||||
a way that may break.
|
||||
|
||||
This document lists rules when, what, and how incompatible changes can be made.
|
||||
It's interesting both for mpv developers, who want to change user-visible parts,
|
||||
and mpv users, who want to know what is guaranteed to remain stable.
|
||||
|
||||
Any of the rules below may be overridden by statements in more specific
|
||||
documentation (for example, if the manpage says that a particular option may be
|
||||
removed any time, it means that, and the option probably won't even go through
|
||||
deprecation).
|
||||
|
||||
Additions
|
||||
---------
|
||||
|
||||
Additions are basically always allowed. API users etc. are supposed to deal with
|
||||
the possibility that for example new API functions are added. Some parts of the
|
||||
API may document how they are extended.
|
||||
|
||||
Options, commands, properties, events, hooks (command interface)
|
||||
----------------------------------------------------------------
|
||||
|
||||
All of these are important for interfacing both with end users and API users
|
||||
(which include Lua scripts, libmpv, and the JSON IPC). As such, they constitute
|
||||
a large part of the user interface and APIs.
|
||||
|
||||
Certain options and commands may have documented default values. These default
|
||||
values are considered a part of the API since scripts may already rely on these
|
||||
documented behaviors. Changing these defaults are considered incompatible
|
||||
changes and must be documented. Undocumented default values do not subject to
|
||||
this requirement, and it is recommended to discourage such usage in the related
|
||||
documentations if there is a need to frequently change such defaults.
|
||||
|
||||
All incompatible changes to this must be declared in interface-changes.rst,
|
||||
which include the types of changes, the impact of these changes, and suggested
|
||||
actions to address such impact so that the incompatibility is alleviated.
|
||||
(This may also list compatible additions, but it's not a requirement.)
|
||||
|
||||
Degrees of importance and compatibility preservation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Critical and central parts of the command interface have the strictest
|
||||
requirements. It may not be reasonable to break them, and other means to achieve
|
||||
some change have to be found. For example, the "seek" command is a bit of a
|
||||
mess, but since changing it would likely affect almost every user, it may be
|
||||
impossible to break at least the commonly used syntax. If changed anyway, there
|
||||
should be a deprecation period of at least 1 year, during which the command
|
||||
still works, and possibly a warning should remain even after this.
|
||||
|
||||
Important/often used parts must be deprecated for at least 2 releases before
|
||||
they can be broken. There must be at least 1 release where the deprecated
|
||||
functionality still works, and a replacement is available (if technically
|
||||
reasonable). For example, a feature deprecated in mpv 0.30.0 may be removed in
|
||||
mpv 0.32.0. Minor releases do not count towards this.
|
||||
|
||||
Under extraordinary circumstances, such as missed incompatible changes that are
|
||||
already included in a release, critical security fixes, or a severe shortage of
|
||||
developer time to address the known incompatible changes, important/often used
|
||||
parts may be broken immediately, but the change must be extensively documented:
|
||||
all of the related documentations (including manpage, interface-changes.rst,
|
||||
etc., retrospectively modified if applicable) must clearly state the following:
|
||||
the fact that the change is a breaking change; the version when the breaking
|
||||
change happened; and the reason, impact, and suggested remedy actions.
|
||||
|
||||
Less useful parts can be broken immediately, but must come with some sort of
|
||||
removal warning.
|
||||
|
||||
Parts for debugging and testing may be removed any time, potentially even
|
||||
without any sort of documentation.
|
||||
|
||||
Currently, the importance of a part is not documented and not even well-defined,
|
||||
which is probably a mistake.
|
||||
|
||||
Renaming or removing options
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Typically, renaming an option can be done in a compatible way with OPT_REPLACED.
|
||||
You may need to check whether the corresponding properties still work (including
|
||||
messy details like observing properties for changes).
|
||||
|
||||
OPT_REMOVED can be used to inform the user of alternatives or reasons for the
|
||||
removal, which is better than an option not found error. Likewise,
|
||||
m_option.deprecation_message should be set to something helpful.
|
||||
|
||||
Both OPT_REPLACED and OPT_REMOVED can remain in the code for a long time, since
|
||||
they're unintrusive and hopefully make incompatible changes less painful for
|
||||
users.
|
||||
|
||||
Scripting APIs
|
||||
--------------
|
||||
|
||||
This affects internal scripting APIs (currently Lua and JavaScript).
|
||||
|
||||
Vaguely the same rules as with the command interface apply. Since there is a
|
||||
large number of scripts, an effort should be made to provide compatibility
|
||||
for old scripts, but it does not need to be stronger than that of the command
|
||||
interface.
|
||||
|
||||
Undocumented parts of the scripting APIs are _not_ guaranteed for compatibility.
|
||||
This applies especially for internals. Languages like Lua do not have strict
|
||||
access control (nor does the mpv code try to emulate any), so if a script
|
||||
accesses private parts, and breaks on the next mpv release, it's not mpv's
|
||||
problem.
|
||||
|
||||
JSON IPC
|
||||
--------
|
||||
|
||||
The JSON IPC is a thin protocol wrapping the libmpv API and the command
|
||||
interface. Compatibility-wise, it's about the same as the scripting APIs.
|
||||
The JSON protocol commands should remain as compatible as possible, and it
|
||||
should probably accept the current way commands are delimited (line breaks)
|
||||
forever.
|
||||
|
||||
The protocol may accept non-standard JSON extensions, but only standard JSON
|
||||
(possibly with restrictions) is guaranteed for compatibility. Clients which want
|
||||
to remain compatible should not use any extensions.
|
||||
|
||||
CLI
|
||||
---
|
||||
|
||||
Things such as default key bindings do not necessarily require compatibility.
|
||||
However, the release notes should be extremely clear on changes to "important"
|
||||
key bindings. Bindings which restore the old behavior should be added to
|
||||
restore-old-bindings.conf and restore-osc-bindings.conf.
|
||||
|
||||
Some option parsing is CLI-only and not available from libmpv or scripting. No
|
||||
compatibility guarantees come with them. However, the rules which mpv uses to
|
||||
distinguish between options and filenames must remain consistent (if the
|
||||
non-deprecated options syntax is used).
|
||||
|
||||
Terminal and log output
|
||||
-----------------------
|
||||
|
||||
There are no compatibility guarantees for the terminal output, or the text
|
||||
logged via ``MPV_EVENT_LOG_MESSAGE`` and similar APIs. In particular, scripts
|
||||
invoking mpv CLI are extremely discouraged from trying to parse text output,
|
||||
and should use other mechanisms such as the JSON IPC.
|
||||
|
||||
Protocols, filters, demuxers, etc.
|
||||
----------------------------------
|
||||
|
||||
Which of these are present is generally not guaranteed, and can even depend
|
||||
on compile time settings.
|
||||
|
||||
The filter list and their sub-options are considered part of the
|
||||
command-interface.
|
||||
|
||||
libmpv C API
|
||||
------------
|
||||
|
||||
The libmpv client API (such as ``<mpv/client.h>``) mostly gives access to
|
||||
the command interface. The API itself (if looked at as a component separate
|
||||
from the command interface) is intended to be extremely stable.
|
||||
|
||||
All API changes are documented in client-api-changes.rst.
|
||||
|
||||
API compatibility
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API is *always* compatible. Incompatible changes are only allowed on major
|
||||
API version changes (see ``MPV_CLIENT_API_VERSION``). A major version change is
|
||||
an extremely rare event, which means usually no API symbols are ever removed.
|
||||
|
||||
Essentially removing API functions by making them always return an error, or
|
||||
making it do nothing is allowed in cases where it is unlikely to break most
|
||||
clients, but requires a deprecation period of 2 releases. (This has happened to
|
||||
``mpv_suspend()`` for example.)
|
||||
|
||||
API symbols can be deprecated. This should be clearly marked in the doxygen
|
||||
with ``@deprecated``, and if possible, the affected API symbols should not be
|
||||
visible if the API user defines ``MPV_ENABLE_DEPRECATED`` to 0.
|
||||
|
||||
ABI compatibility
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ABI must never be broken, except on major API version changes. For example,
|
||||
constants don't change their values.
|
||||
|
||||
Structs are tricky. If a struct can be allocated by a user (such as ``mpv_node``),
|
||||
no fields can be added. (Unless it's an union, and the addition does not change
|
||||
the offset or alignment of any of the fields or the struct itself. This has
|
||||
happened to ``mpv_node`` in the past.) If a struct is allocated by libmpv only,
|
||||
new fields can be appended to the end (for example ``mpv_event``).
|
||||
|
||||
The ABI is only backward compatible. This means if a host application is linked
|
||||
to an older libmpv, and libmpv is updated to a newer version, it will still
|
||||
work (as in not causing any undefined behavior).
|
||||
|
||||
Forward compatibility (an application would work with an older libmpv than it
|
||||
was linked to) is not required.
|
||||
+140
-227
@@ -1,262 +1,175 @@
|
||||
# Compiling for Windows
|
||||
Compiling for Windows
|
||||
=====================
|
||||
|
||||
Compiling for Windows is supported using GNU-like compilers (GCC/Clang). Clang
|
||||
is compatible with both the ``w64-windows-gnu`` [MinGW-w64](https://www.mingw-w64.org/)
|
||||
and ``pc-windows-msvc`` [Windows SDK](https://developer.microsoft.com/windows/downloads/windows-sdk)
|
||||
targets. It supports the production of both 32-bit and 64-bit binaries and is
|
||||
suitable for building on Windows as well as cross-compiling from Linux and Cygwin.
|
||||
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:
|
||||
http://mingw-w64.sourceforge.net.
|
||||
|
||||
Although it is possible to build a complete MinGW-w64 toolchain yourself, there
|
||||
are build environments and scripts available to simplify the process, such as
|
||||
MSYS2 on Windows or a packaged toolchain provided by your favorite Linux
|
||||
distribution. Note that MinGW-w64 environments included in Linux distributions
|
||||
can vary in versions. As a general guideline, mpv only supports the MinGW-w64
|
||||
toolchain version included in the latest Ubuntu LTS release.
|
||||
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.
|
||||
|
||||
mpv employs Meson for building, and the process is the same as any standard Meson
|
||||
compilation.
|
||||
**Warning**: the original MinGW (http://www.mingw.org) is unsupported.
|
||||
|
||||
For the most up-to-date reference on build scripts, you can refer to
|
||||
[build.yml](https://github.com/mpv-player/mpv/blob/master/.github/workflows/build.yml),
|
||||
which builds and tests all supported configurations: ``MinGW-w64``, ``Windows SDK``,
|
||||
and ``MSYS2`` builds.
|
||||
Cross-compilation
|
||||
=================
|
||||
|
||||
## Cross-compilation
|
||||
When cross-compiling, you have to run mpv's configure with these arguments:
|
||||
|
||||
When cross-compiling, it is recommended to use a Meson ``--cross-file`` to set up the
|
||||
cross-compiling environment. For a basic example, please refer to
|
||||
[Cross-compilation](https://mesonbuild.com/Cross-compilation.html).
|
||||
|
||||
Alternatively, consider using [mpv-winbuild-cmake](https://github.com/shinchiro/mpv-winbuild-cmake),
|
||||
which bootstraps a MinGW-w64 toolchain and builds mpv along with its dependencies.
|
||||
|
||||
### Example with Meson
|
||||
|
||||
1. Create ``cross-file.txt`` with definitions for your toolchain and target platform.
|
||||
Refer to [x86_64-w64-mingw32.txt](https://mesonbuild.com/Cross-compilation.html)
|
||||
as a directly usable example.
|
||||
- **Important**: Beware of pkg-config usage. By default, it uses build machine
|
||||
files for dependency detection, even when ``--cross-file`` is used. It must
|
||||
be configured correctly. Refer to ``pkg_config_libdir`` and ``sys_root``
|
||||
in the [documentation](https://mesonbuild.com/Cross-compilation.html#defining-the-environment)
|
||||
for proper setup. **In this example pkg-config is not used/required.**
|
||||
2. Initialize subprojects. This step is optional; other methods are also
|
||||
available to provide the necessary dependencies.
|
||||
|
||||
``` bash
|
||||
# Update the subprojects database from Meson's WrapDB.
|
||||
meson wrap update-db
|
||||
|
||||
# Explicitly download wraps as nested projects may have older versions of them.
|
||||
meson wrap install expat
|
||||
meson wrap install harfbuzz
|
||||
meson wrap install libpng
|
||||
meson wrap install zlib
|
||||
|
||||
# Add wraps for mpv's required dependencies
|
||||
mkdir -p subprojects
|
||||
|
||||
cat <<EOF > subprojects/libplacebo.wrap
|
||||
[wrap-git]
|
||||
url = https://github.com/haasn/libplacebo
|
||||
revision = master
|
||||
depth = 1
|
||||
clone-recursive = true
|
||||
EOF
|
||||
|
||||
cat <<EOF > subprojects/libass.wrap
|
||||
[wrap-git]
|
||||
revision = master
|
||||
url = https://github.com/libass/libass
|
||||
depth = 1
|
||||
EOF
|
||||
|
||||
# For FFmpeg, use Meson's build system port; alternatively, you can compile
|
||||
# the upstream version yourself. See https://trac.ffmpeg.org/wiki/CompilationGuide
|
||||
cat <<EOF > subprojects/ffmpeg.wrap
|
||||
[wrap-git]
|
||||
url = https://gitlab.freedesktop.org/gstreamer/meson-ports/ffmpeg.git
|
||||
revision = meson-8.0
|
||||
depth = 1
|
||||
[provide]
|
||||
libavcodec = libavcodec_dep
|
||||
libavdevice = libavdevice_dep
|
||||
libavfilter = libavfilter_dep
|
||||
libavformat = libavformat_dep
|
||||
libavutil = libavutil_dep
|
||||
libswresample = libswresample_dep
|
||||
libswscale = libswscale_dep
|
||||
EOF
|
||||
```
|
||||
|
||||
3. Build
|
||||
|
||||
``` bash
|
||||
meson setup -Ddefault_library=static -Dprefer_static=true \
|
||||
-Dc_link_args='-static' -Dcpp_link_args='-static' \
|
||||
--cross-file cross-file.txt build
|
||||
|
||||
ninja -C build mpv.exe mpv.com
|
||||
```
|
||||
|
||||
This will produce fully portabiel, statically linked, ``mpv.exe`` and ``mpv.com``
|
||||
binaries.
|
||||
|
||||
#### Building libmpv
|
||||
|
||||
- To also build ``libmpv``, execute the following commands:
|
||||
|
||||
``` bash
|
||||
# For a static library
|
||||
meson configure build -Dlibmpv=true -Ddefault_library=static
|
||||
ninja -C build libmpv.a
|
||||
|
||||
# For a shared library
|
||||
meson configure build -Dlibmpv=true -Ddefault_library=shared
|
||||
ninja -C build libmpv-2.dll
|
||||
```
|
||||
|
||||
- Depending on the use case, you might want to use ``-Dgpl=false`` and review
|
||||
similar options for subprojects.
|
||||
- If any of Meson's subprojects are not linked statically, you might need to
|
||||
specify options like the following example for ffmpeg:
|
||||
``-Dffmpeg:default_library=static``.
|
||||
|
||||
#### Enabling more mpv features
|
||||
|
||||
The process above produces a basic mpv build. You can enable additional features.
|
||||
Check the Meson
|
||||
[WrapDB packages](https://mesonbuild.com/Wrapdb-projects.html) for available
|
||||
dependencies or by providing them through other means.
|
||||
|
||||
Enhancing mpv with more features is as simple as adding more arguments to the
|
||||
Meson setup command, for example:
|
||||
|
||||
``` bash
|
||||
-Dlua=enabled -Djavascript=enabled -Dlcms2=enabled -Dlibplacebo:lcms=enabled
|
||||
```bash
|
||||
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
|
||||
```
|
||||
|
||||
they will be automatically downloaded and built by Meson.
|
||||
[MXE](http://mxe.cc) makes it very easy to bootstrap a complete MingGW-w64
|
||||
environment from a Linux machine. See a working example below.
|
||||
|
||||
## Native Compilation with Clang (Windows SDK Build)
|
||||
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.
|
||||
|
||||
1. Install [Build Tools for Visual Studio](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022)
|
||||
or the full [Visual Studio](https://visualstudio.microsoft.com/downloads/#visual-studio-community-2022):
|
||||
- From the installer, select the necessary components:
|
||||
- Clang compiler for Windows
|
||||
- C++ CMake tools for Windows
|
||||
- Windows SDK
|
||||
- Activate the developer shell:
|
||||
```
|
||||
& "<Visual Studio Path>\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64 -HostArch amd64 -SkipAutomaticLocation | Out-Null
|
||||
```
|
||||
2. Install Meson, as outlined in [Getting Meson](https://mesonbuild.com/Getting-meson.html):
|
||||
3. The following build script utilizes the Meson subprojects system to build mpv and its dependencies.
|
||||
To make sure all dependency versions are up-to-date, update the subprojects database from Meson's WrapDB.
|
||||
Also explicitly download several wraps as some nested projects may pull older versions of them.
|
||||
```
|
||||
meson wrap update-db
|
||||
Example with MXE
|
||||
----------------
|
||||
|
||||
meson wrap install expat
|
||||
meson wrap install harfbuzz
|
||||
meson wrap install libpng
|
||||
meson wrap install zlib
|
||||
```
|
||||
4. Set environment variables or use the `--native-file` option in Meson.
|
||||
```powershell
|
||||
$env:CC = 'clang'
|
||||
$env:CXX = 'clang++'
|
||||
$env:CC_LD = 'lld'
|
||||
$env:CXX_LD = 'lld'
|
||||
$env:WINDRES = 'llvm-rc'
|
||||
```
|
||||
Note that some dependencies (e.g. LuaJIT) may require `sed` to configure. Fortunately, it is already bundled in
|
||||
[Git for Windows](https://www.git-scm.com/download/win):
|
||||
```powershell
|
||||
$env:PATH += ';<Git Path>\usr\bin'
|
||||
```
|
||||
5. Execute [ci\build-win32.ps1](https://github.com/mpv-player/mpv/blob/master/ci/build-win32.ps1). Refer to the script for more details.
|
||||
```bash
|
||||
# Before starting, make sure you install MXE prerequisites. MXE will download
|
||||
# and build all target dependencies, but no host dependencies. For example,
|
||||
# you need a working compiler, or MXE can't build the crosscompiler.
|
||||
#
|
||||
# Refer to
|
||||
#
|
||||
# http://mxe.cc/#requirements
|
||||
#
|
||||
# Scroll down for disto/OS-specific instructions to install them.
|
||||
|
||||
This process will produce a fully static ``mpv.exe`` and ``mpv.com``, as well as
|
||||
a static ``libmpv.a``.
|
||||
# Download MXE. Note that compiling the required packages requires about 1.4 GB
|
||||
# or more!
|
||||
|
||||
## Native Compilation with MSYS2
|
||||
cd /opt
|
||||
git clone https://github.com/mxe/mxe mxe
|
||||
cd mxe
|
||||
|
||||
For Windows developers seeking a quick setup, MSYS2 is an effective tool for
|
||||
compiling mpv natively on a Windows machine. The MSYS2 repositories provide
|
||||
binary packages for most of mpv's dependencies, simplifying the process to
|
||||
primarily involve building mpv itself.
|
||||
# Set build options.
|
||||
|
||||
### Installing MSYS2
|
||||
# The JOBS environment variable controls threads to use when building. DO NOT
|
||||
# use the regular `make -j4` option with MXE as it will slow down the build.
|
||||
# Alternatively, you can set this in the make command by appending "JOBS=4"
|
||||
# to the end of command:
|
||||
echo "JOBS := 4" >> settings.mk
|
||||
|
||||
1. Follow the installation steps from [MSYS2](https://www.msys2.org/).
|
||||
2. Initiate one of the [Environments](https://www.msys2.org/docs/environments/),
|
||||
with the CLANG64 (``clang64.exe``) being the recommended option.
|
||||
**Note:** This environment is distinct from the MSYS2 shell that opens
|
||||
automatically after the final installation dialog. You must close that
|
||||
initial shell and open a new one for the appropriate environment.
|
||||
# The MXE_TARGET environment variable builds MinGW-w64 for 32 bit targets.
|
||||
# Alternatively, you can specify this in the make command by appending
|
||||
# "MXE_TARGETS=i686-w64-mingw32" to the end of command:
|
||||
echo "MXE_TARGETS := i686-w64-mingw32.static" >> settings.mk
|
||||
|
||||
### Updating MSYS2
|
||||
# If you want to build 64 bit version, use this:
|
||||
# echo "MXE_TARGETS := x86_64-w64-mingw32.static" >> settings.mk
|
||||
|
||||
For guidance on updating MSYS2, please refer to the official documentation:
|
||||
[Updating MSYS2](https://www.msys2.org/docs/updating/).
|
||||
# Build required packages. The following provide a minimum required to build
|
||||
# a reasonable mpv binary (though not an absolute minimum).
|
||||
|
||||
### Installing mpv Dependencies
|
||||
make gcc ffmpeg libass jpeg lua
|
||||
|
||||
``` bash
|
||||
# Install pacboy and git
|
||||
pacman -S pactoys git
|
||||
# Add MXE binaries to $PATH
|
||||
export PATH=/opt/mxe/usr/bin/:$PATH
|
||||
|
||||
# Build mpv. The target will be used to automatically select the name of the
|
||||
# build tools involved (e.g. it will use i686-w64-mingw32.static-gcc).
|
||||
|
||||
cd ..
|
||||
git clone https://github.com/mpv-player/mpv.git
|
||||
cd mpv
|
||||
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
|
||||
=============================
|
||||
|
||||
For Windows developers looking to get started quickly, MSYS2 can be used to
|
||||
compile mpv natively on a Windows machine. The MSYS2 repositories have binary
|
||||
packages for most of mpv's dependencies, so the process should only involve
|
||||
building mpv itself.
|
||||
|
||||
To build 64-bit mpv on Windows:
|
||||
|
||||
Installing MSYS2
|
||||
----------------
|
||||
|
||||
1. Download an installer from https://msys2.github.io/
|
||||
|
||||
It doesn't matter whether the i686 or the x86_64 version is used. Both can
|
||||
build 32-bit and 64-bit binaries when running on a 64-bit version of Windows.
|
||||
|
||||
2. Add ``C:\msys64\mingw64\bin`` to your ``%PATH%``. mpv will depend on several
|
||||
DLLs in this folder.
|
||||
|
||||
3. Start a MinGW-w64 shell (``mingw64_shell.bat``). For a 32-bit build, use
|
||||
``mingw32_shell.bat``.
|
||||
|
||||
Updating MSYS2
|
||||
--------------
|
||||
|
||||
To prevent errors during post-install, ``msys2-runtime`` and ``pacman`` must be
|
||||
updated first.
|
||||
|
||||
```bash
|
||||
# Check for updates to msys2-runtime and pacman. If there were updates, restart
|
||||
# MSYS2 before continuing.
|
||||
pacman -Sy --needed msys2-runtime pacman
|
||||
|
||||
# Update everything else
|
||||
pacman -Su
|
||||
```
|
||||
|
||||
Installing mpv dependencies
|
||||
---------------------------
|
||||
|
||||
```bash
|
||||
# Install MSYS2 build dependencies and a MinGW-w64 compiler
|
||||
pacboy -S python pkgconf cc meson
|
||||
pacman -S git pkg-config python3 mingw-w64-x86_64-gcc
|
||||
|
||||
# Install key dependencies. libass and lcms2 are also included as dependencies
|
||||
# of ffmpeg.
|
||||
pacboy -S ffmpeg libjpeg-turbo libplacebo luajit vulkan-headers
|
||||
# Install the most important MinGW-w64 dependencies. libass, libbluray and
|
||||
# lcms2 are also pulled in as dependencies of ffmpeg.
|
||||
pacman -S mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-lua51
|
||||
|
||||
# Install additional (optional) dependencies
|
||||
pacman -S mingw-w64-x86_64-libdvdnav mingw-w64-x86_64-libguess
|
||||
```
|
||||
|
||||
### Building mpv
|
||||
For a 32-bit build, install ``mingw-w64-i686-*`` packages instead.
|
||||
|
||||
To compile and install mpv, execute the following commands.
|
||||
The binaries will be installed in the directory ``/$MSYSTEM_PREFIX/bin``.
|
||||
Building mpv
|
||||
------------
|
||||
|
||||
Clone the latest mpv from git and install waf:
|
||||
|
||||
```bash
|
||||
# Set up the build directory with the desired configuration
|
||||
meson setup build -Dlibmpv=true --prefix=$MSYSTEM_PREFIX
|
||||
|
||||
# Compile
|
||||
meson compile -C build
|
||||
|
||||
# Optionally, install the compiled binaries
|
||||
meson install -C build
|
||||
git clone https://github.com/mpv-player/mpv.git && cd mpv
|
||||
./bootstrap.py
|
||||
```
|
||||
|
||||
## Running mpv
|
||||
|
||||
Depending on your build configuration, ``mpv.exe`` may rely on external
|
||||
libraries. To create a portable package, you will need to include these
|
||||
dependencies as well. The quickest way to determine which libraries are needed
|
||||
is to run ``mpv.exe`` and note any error messages that list the required ``.dll``
|
||||
files. You can find these libraries in the sysroot used for compilation, such as
|
||||
``/clang64/bin/`` in MSYS2.
|
||||
|
||||
## Linking libmpv with MSVC Programs
|
||||
|
||||
Building mpv/libmpv directly with the MSVC compiler (cl.exe) is not supported
|
||||
due to differences in compiler flags. Our build system is designed specifically
|
||||
for GNU-like compiler drivers. However, you can still build programs in
|
||||
Visual Studio and link them with libmpv.
|
||||
|
||||
If the toolchain used generates a ``.lib`` file, it will be ready for use.
|
||||
Otherwise, you will need to create an import library for the mpv DLL with the
|
||||
following command:
|
||||
Finally, compile and install mpv. Binaries will be installed to
|
||||
``/mingw64/bin``.
|
||||
|
||||
```bash
|
||||
lib /name:mpv-2.dll /out:mpv.lib /MACHINE:X64
|
||||
# For a 32-bit build, use --prefix=/mingw32 instead
|
||||
./waf configure CC=gcc --prefix=/mingw64
|
||||
./waf install
|
||||
```
|
||||
|
||||
Ensure that the string in the ``/name:`` parameter matches the filename of the
|
||||
DLL, as this is the filename that the MSVC linker will reference.
|
||||
Or, compile and install both libmpv and mpv:
|
||||
|
||||
**Note:** Static linking is only feasible with matching compilers. For instance,
|
||||
if you build with Clang in Visual Studio, static linking is possible.
|
||||
```bash
|
||||
./waf configure CC=gcc --enable-libmpv-shared --prefix=/mingw64
|
||||
./waf install
|
||||
|
||||
# waf installs libmpv to the wrong directory, so fix it up
|
||||
mv -f /mingw64/bin/pkgconfig/mpv.pc /mingw64/lib/pkgconfig/
|
||||
mv -f /mingw64/bin/libmpv.dll.a /mingw64/lib/
|
||||
sed -i 's_/mingw64/bin_/mingw64/lib_' /mingw64/lib/pkgconfig/mpv.pc
|
||||
rmdir /mingw64/bin/pkgconfig
|
||||
```
|
||||
+42
-186
@@ -5,61 +5,27 @@ General
|
||||
-------
|
||||
|
||||
The main contact for mpv development is IRC, specifically #mpv
|
||||
and #mpv-devel on Libera.chat. GitHub is used for code review and
|
||||
long term discussions.
|
||||
and #mpv-devel on Freenode.
|
||||
|
||||
Sending patches
|
||||
---------------
|
||||
|
||||
- Make a GitHub pull request, or send a link to a plaintext patch created with
|
||||
``git format-patch``.
|
||||
- Plain diffs posted as pastebins are not acceptable! (Especially if the http
|
||||
link returns HTML.) They only cause extra work for everyone, because they lack
|
||||
commit message and authorship information.
|
||||
- Never send patches to any of the developers email addresses.
|
||||
- If your changes are not supposed to be merged immediately, mark them as
|
||||
"[RFC]" in the commit message or the pull request title.
|
||||
- Be sure to test your changes. If you didn't, please say so in the commit
|
||||
message and the pull request text.
|
||||
|
||||
Copyright of contributions
|
||||
--------------------------
|
||||
|
||||
- The copyright belongs to contributors. The project is a collaborative work. By
|
||||
sending your changes, you agree to license your contributions according to the
|
||||
requirements of this project.
|
||||
- Make a github pull request, or send a link to a plaintext patch created with
|
||||
``git format-patch``. diffs posted as pastebins (especially if the http link
|
||||
returns HTML) just cause extra work for everyone, because they lack commit
|
||||
message and authorship information.
|
||||
- All new code must be LGPLv2.1+ licensed, or come with the implicit agreement
|
||||
that it will be relicensed to LGPLv2.1+ later (see ``Copyright`` in the
|
||||
repository root directory).
|
||||
- 100% compatible licenses are allowed too.
|
||||
- Changes in files with more liberal licenses (such as BSD, MIT, or ISC) are
|
||||
assumed to be dual-licensed under LGPLv2.1+ and the license indicated in the
|
||||
file header.
|
||||
- You must be either the exclusive author of the patch, or acknowledge all
|
||||
authors involved in the commit message. If you take 3rd party code, authorship
|
||||
and copyright must be properly acknowledged. If you're making changes on
|
||||
behalf of your employer, and the employer owns the copyright, you must mention
|
||||
this. If the license of the code is not LGPLv2.1+, you must mention this.
|
||||
- These license statements are legally binding.
|
||||
- Don't use fake names (something that looks like an actual name, and may be
|
||||
someone else's name, but is not your legal name). Using a pseudonym is
|
||||
allowed if it can be used to identify or contact you, even if whatever
|
||||
account you used to submit the patch dies.
|
||||
- Do not add your name to the license header. This convention is not used by
|
||||
this project, and neither copyright law nor any of the used licenses require
|
||||
it.
|
||||
|
||||
Write good commit messages
|
||||
--------------------------
|
||||
|
||||
- When creating pull requests, be sure to test your changes. If you didn't,
|
||||
please say so in the pull request message.
|
||||
- Write informative commit messages. Use present tense to describe the
|
||||
situation with the patch applied, and past tense for the situation before
|
||||
the change.
|
||||
- The subject line (the first line in a commit message) must contain a
|
||||
- The subject line (the first line in a commit message) should contain a
|
||||
prefix identifying the sub system, followed by a short description what
|
||||
impact this commit has. This subject line and the commit message body
|
||||
must not be longer than 72 characters per line, because it messes up the
|
||||
output of many git tools otherwise.
|
||||
impact this commit has. This subject line shouldn't be longer than 72
|
||||
characters, because it messes up the output of many git tools otherwise.
|
||||
|
||||
For example, you fixed a crash in af_volume.c:
|
||||
|
||||
@@ -68,16 +34,7 @@ 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.
|
||||
If this is missing, it will break display in common git tools.
|
||||
- The body of the commit message (everything else after the subject line) must
|
||||
- The body of the commit message (everything else after the subject line) should
|
||||
be as informative as possible and contain everything that isn't obvious. Don't
|
||||
hesitate to dump as much information as you can - it doesn't cost you
|
||||
anything. Put some effort into it. If someone finds a bug months or years
|
||||
@@ -86,81 +43,32 @@ Write good commit messages
|
||||
information to test the original bug. The old bug might be reintroduced while
|
||||
fixing the new bug.
|
||||
|
||||
The commit message must be wrapped on 72 characters per line, because git
|
||||
The commit message should be wrapped on 72 characters per line, because git
|
||||
tools usually do not break text automatically. On the other hand, you do not
|
||||
need to break text that would be unnatural to break (like data for test cases,
|
||||
or long URLs).
|
||||
- Another summary of good conventions: https://chris.beams.io/posts/git-commit/
|
||||
|
||||
Split changes into multiple commits
|
||||
-----------------------------------
|
||||
|
||||
- Follow git good practices, and split independent changes into several commits.
|
||||
It's usually OK to put them into a single pull request.
|
||||
Important: put an empty line between the subject line and the commit message.
|
||||
If this is missing, it will break display in common git tools.
|
||||
- Try to separate cosmetic and functional changes. It's ok to make a few
|
||||
additional cosmetic changes in the same file you're working on. But don't do
|
||||
something like reformatting a whole file, and hiding an actual functional
|
||||
change in the same commit.
|
||||
- Splitting changes does _not_ mean that you should make them as fine-grained
|
||||
as possible. Commits should form logical steps in development. The way you
|
||||
split changes is important for code review and analyzing bugs.
|
||||
|
||||
Always squash fixup commits when making changes to pull requests
|
||||
----------------------------------------------------------------
|
||||
|
||||
- If you make fixup commits to your pull request, you should generally squash
|
||||
them with "git rebase -i". We prefer to have pull requests in a merge
|
||||
ready state.
|
||||
- We don't squash-merge (nor do we use GitHub's feature that does this) because
|
||||
pull requests with multiple commits are perfectly legitimate, and the only
|
||||
thing that makes sense in non-trivial cases.
|
||||
- With complex pull requests, it *may* make sense to keep them separate, but
|
||||
they should be clearly marked as such. Reviewing commits is generally easier
|
||||
with fixups squashed.
|
||||
- Reviewers are encouraged to look at individual commits instead of GitHub's
|
||||
"changes from all commits" view (which just encourages bad git and review
|
||||
practices).
|
||||
|
||||
Touching user-visible parts may require updating the mpv docs
|
||||
-------------------------------------------------------------
|
||||
|
||||
- Most user-visible things are normally documented in DOCS/man/. If your commit
|
||||
touches documented behavior, list of sub-options, etc., you need to adjust the
|
||||
documentation.
|
||||
- These changes usually go into the same commit that changes the code.
|
||||
- Changes to command line options (addition/modification/removal) must be
|
||||
documented in options.rst.
|
||||
- Changes to input properties or input commands must be documented in input.rst.
|
||||
- Changes to the libmpv API must be reflected in the libmpv's headers doxygen,
|
||||
and in client-api-changes.rst.
|
||||
|
||||
Interface change policy
|
||||
-----------------------
|
||||
|
||||
- All incompatible changes to the user interface (options, properties, commands)
|
||||
must be documented by making a new text file with a txt extension containing a
|
||||
small note in the DOCS/interface-changes directory.
|
||||
- The name of the file should be brief and related to the commit that makes the
|
||||
change. The content of the file should begin with the type of the change
|
||||
(add, remove, deprecate, change, rename, etc.). If the file contains multiple
|
||||
changes, the change which causes the most serious compatibility issues should
|
||||
be placed first.
|
||||
- Grouping multiple related changes in the same file is also OK. Just be sure to
|
||||
put each separate change on a different line.
|
||||
- Documenting additions in DOCS/interface-changes is optional but encouraged.
|
||||
- interface-changes.rst is never directly updated except when making new major
|
||||
releases.
|
||||
- See DOCS/interface-changes/example.txt for an example.
|
||||
- If you add a new command line option, document it in options.rst. If you
|
||||
add a new input property, document it in input.rst. Changes to the user
|
||||
interface (options, properties, commands) should be documented in
|
||||
interface-changes.rst. Changes to libmpv should be documented in
|
||||
client-api-changes.rst.
|
||||
|
||||
Code formatting
|
||||
---------------
|
||||
|
||||
mpv uses C11 with K&R formatting, with some exceptions.
|
||||
mpv uses C99 with K&R formatting, with some exceptions.
|
||||
|
||||
- Use the K&R indent style.
|
||||
- Use 4 spaces of indentation, never use tabs (except in Makefiles).
|
||||
- Add a single space between keywords and binary operators. There are some other
|
||||
cases where spaces must be added. Example:
|
||||
cases where spaces should be added. Example:
|
||||
|
||||
```C
|
||||
if ((a * b) > c) {
|
||||
@@ -168,9 +76,9 @@ mpv uses C11 with K&R formatting, with some exceptions.
|
||||
some_function(a, b, c);
|
||||
}
|
||||
```
|
||||
- Break lines on 80 columns. There is a hard limit of 100 columns. You may ignore
|
||||
- Break lines on 80 columns. There is a hard limit of 85 columns. You may ignore
|
||||
this limit if there's a strong case that not breaking the line will increase
|
||||
readability. Going over 100 columns might provoke endless discussions about
|
||||
readability. Going over 85 columns might provoke endless discussions about
|
||||
whether such a limit is needed or not, so avoid it.
|
||||
- If the body of an if/for/while statement has more than 1 physical lines, then
|
||||
always add braces, even if they're technically redundant.
|
||||
@@ -193,16 +101,18 @@ mpv uses C11 with K&R formatting, with some exceptions.
|
||||
do_something();
|
||||
}
|
||||
```
|
||||
- If the if has an else branch, both branches must use braces, even if they're
|
||||
technically redundant.
|
||||
- If the body of an if statement uses braces, the else branch should also
|
||||
use braces (and reverse).
|
||||
|
||||
Example:
|
||||
|
||||
```C
|
||||
if (a) {
|
||||
one_line();
|
||||
// do something
|
||||
something();
|
||||
something_else();
|
||||
} else {
|
||||
one_other_line();
|
||||
one_line();
|
||||
}
|
||||
```
|
||||
- If an if condition spans multiple physical lines, then put the opening brace
|
||||
@@ -220,82 +130,28 @@ mpv uses C11 with K&R formatting, with some exceptions.
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
(If the if body is simple enough, this rule can be skipped.)
|
||||
- Remove any trailing whitespace.
|
||||
- Do not make stray whitespaces changes.
|
||||
|
||||
Header #include statement order
|
||||
-------------------------------
|
||||
|
||||
The order of ``#include`` statements in the source code is not very consistent.
|
||||
New code must follow the following conventions:
|
||||
|
||||
- Put standard includes (``#include <stdlib.h>`` etc.) on the top,
|
||||
- then after a blank line, add library includes (``#include <zlib.h>`` etc.)
|
||||
- then after a blank line, add internal includes (``#include "player/core.h"``)
|
||||
- sort them alphabetically within these sections
|
||||
- If the file you're editing uses formatting different from from what is
|
||||
described here, it's probably an old file from times when nobody followed a
|
||||
consistent style. You're free to use the existing style, or the new style, or
|
||||
to send a patch to reformat the file to the new style before making functional
|
||||
changes.
|
||||
|
||||
General coding
|
||||
--------------
|
||||
|
||||
- Use C11. Also freely make use of C11 features if it's appropriate, but do not
|
||||
use VLA and complex number types.
|
||||
- Don't use non-standard language (such as GNU C-only features). In some cases
|
||||
they may be warranted, if they are optional (such as attributes enabling
|
||||
printf-like format string checks). "#pragma once" is allowed as an exception.
|
||||
But in general, standard C11 must be used.
|
||||
- Use C99. Also freely make use of C99 features if it's appropriate, such as
|
||||
stdbool.h.
|
||||
- Don't use GNU-only features. In some cases they may be warranted, if they
|
||||
are optional (such as attributes enabling printf-like format string checks).
|
||||
But in general, standard C99 should be used.
|
||||
- The same applies to libc functions. We have to be Windows-compatible too. Use
|
||||
functions guaranteed by C11 or POSIX only, unless your use is guarded by a
|
||||
configure check. Be mindful of MinGW-specifics since C11 support is not always
|
||||
guaranteed.
|
||||
functions guaranteed by C99 or POSIX only, unless your use is guarded by a
|
||||
configure check.
|
||||
- Prefer fusing declaration and initialization, rather than putting declarations
|
||||
on the top of a block. Obvious data flow is more important than avoiding
|
||||
mixing declarations and statements, which is just a C90 artifact.
|
||||
- tech-overview.txt might help to get an overview how mpv is structured.
|
||||
- If you add features that require intrusive changes, discuss them on the dev
|
||||
channel first. There might be a better way to add a feature and it can avoid
|
||||
wasted work.
|
||||
- Newly added code for any Apple Platform (macOS, iOS, etc), that uses Cocoa or
|
||||
any other object-oriented Framework or API, should be written in Swift instead
|
||||
of Objective-C. This also includes complete rewrites/refactors of existing
|
||||
code. Plain C API usages may stay as C, but can be written in Swift. Existing
|
||||
Objective-C code can stay as is, though Swift rewrites are welcome.
|
||||
|
||||
Code of Conduct
|
||||
---------------
|
||||
|
||||
Please note that this project is released with a Contributor Code of Conduct.
|
||||
By participating in this project you agree to abide by its terms.
|
||||
The Contributor Code of Conduct can be found here:
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct/
|
||||
|
||||
Rules for git push access
|
||||
-------------------------
|
||||
|
||||
Push access to the main git repository is handed out on an arbitrary basis. If
|
||||
you got access, the following rules must be followed:
|
||||
|
||||
- You are expected to follow the general development rules as outlined in this
|
||||
whole document.
|
||||
- You must be present on the IRC dev channel when you push something.
|
||||
- Anyone can push small fixes: typo corrections, small/obvious/uncontroversial
|
||||
bug fixes, edits to the user documentation or code comments, and so on.
|
||||
- You can freely make changes to parts of the code which you maintain. For
|
||||
larger changes, it's recommended to let others review the changes first.
|
||||
- You automatically maintain code if you wrote or modified most of it before
|
||||
(e.g. you made larger changes to it before, did partial or full rewrites, did
|
||||
major bug fixes, or you're the original author of the code). If there is more
|
||||
than one maintainer, you may need to come to an agreement with the others how
|
||||
to handle this to avoid conflict.
|
||||
- If you make a pull requests (especially if it's to code you maintain), and you
|
||||
want reviews, explicitly ping the people from which you expect reviews.
|
||||
- As a maintainer, you can approve pull requests by others to "your" code.
|
||||
- If you approve or merge 3rd party changes, make sure they follow the general
|
||||
development rules.
|
||||
- Changes to user interface and public API must always be approved by the
|
||||
project leader.
|
||||
- Seasoned project members are allowed to revert commits that broke the build,
|
||||
or broke basic functionality in a catastrophic way, and the developer who
|
||||
broke it is unavailable. (Depending on severity.)
|
||||
- Adhere to the CoC.
|
||||
- The project leader is not bound by these rules.
|
||||
+7
-282
@@ -31,8 +31,9 @@ estimated remaining duration of the source file is used.
|
||||
|
||||
Note::
|
||||
|
||||
Usage of relative or absolute paths as well as any protocol prefixes may be
|
||||
prevented for security reasons.
|
||||
mpv can't use ordered chapter files in EDL entries. Usage of relative or
|
||||
absolute paths as well as any protocol prefixes is prevented for security
|
||||
reasons.
|
||||
|
||||
|
||||
Syntax of mpv EDL files
|
||||
@@ -51,8 +52,7 @@ The rest of the lines belong to one of these classes:
|
||||
1) An empty or commented line. A comment starts with ``#``, which must be the
|
||||
first character in the line. The rest of the line (up until the next line
|
||||
break) is ignored. An empty line has 0 bytes between two line feed bytes.
|
||||
2) A header entry if the line starts with ``!``.
|
||||
3) A segment entry in all other cases.
|
||||
2) A segment entry in all other cases.
|
||||
|
||||
Each segment entry consists of a list of named or unnamed parameters.
|
||||
Parameters are separated with ``,``. Named parameters consist of a name,
|
||||
@@ -64,8 +64,8 @@ Syntax::
|
||||
segment_entry ::= <param> ( <param> ',' )*
|
||||
param ::= [ <name> '=' ] ( <value> | '%' <number> '%' <valuebytes> )
|
||||
|
||||
The ``name`` string can consist of any characters, except ``=%,;\n!``. The
|
||||
``value`` string can consist of any characters except of ``,;\n!``.
|
||||
The ``name`` string can consist of any characters, except ``=%,;\n``. The
|
||||
``value`` string can consist of any characters except of ``,;\n``.
|
||||
|
||||
The construct starting with ``%`` allows defining any value with arbitrary
|
||||
contents inline, where ``number`` is an integer giving the number of bytes in
|
||||
@@ -95,232 +95,6 @@ to ``20``, ``param3`` to ``value,escaped``, ``param4`` to ``value2``.
|
||||
Instead of line breaks, the character ``;`` can be used. Line feed bytes and
|
||||
``;`` are treated equally.
|
||||
|
||||
Header entries start with ``!`` as first character after a line break. Header
|
||||
entries affect all other file entries in the EDL file. Their format is highly
|
||||
implementation specific. They should generally follow the file header, and come
|
||||
before any file entries.
|
||||
|
||||
Disabling chapter generation and copying
|
||||
========================================
|
||||
|
||||
By default, chapters from the source ranges are copied to the virtual file's
|
||||
chapters. Also, a chapter is inserted after each range. This can be disabled
|
||||
with the ``no_chapters`` header.
|
||||
|
||||
Example::
|
||||
|
||||
!no_chapters
|
||||
|
||||
|
||||
MP4 DASH
|
||||
========
|
||||
|
||||
This is a header that helps implementing DASH, although it only provides a low
|
||||
level mechanism.
|
||||
|
||||
If this header is set, the given url designates an mp4 init fragment. It's
|
||||
downloaded, and every URL in the EDL is prefixed with the init fragment on the
|
||||
byte stream level. This is mostly for use by mpv's internal ytdl support. The
|
||||
ytdl script will call youtube-dl, which in turn actually processes DASH
|
||||
manifests. It may work only for this very specific purpose and fail to be
|
||||
useful in other scenarios. It can be removed or changed in incompatible ways
|
||||
at any times.
|
||||
|
||||
Example::
|
||||
|
||||
!mp4_dash,init=url
|
||||
|
||||
The ``url`` is encoded as parameter value as defined in the general EDL syntax.
|
||||
It's expected to point to an "initialization fragment", which will be prefixed
|
||||
to every entry in the EDL on the byte stream level.
|
||||
|
||||
The current implementation will
|
||||
|
||||
- ignore stream start times
|
||||
- use durations as hint for seeking only
|
||||
- not adjust source timestamps
|
||||
- open and close segments (i.e. fragments) as needed
|
||||
- not add segment boundaries as chapter points
|
||||
- require full compatibility between all segments (same codec etc.)
|
||||
|
||||
Another header part of this mechanism is ``no_clip``. This header is similar
|
||||
to ``mp4_dash``, but does not include on-demand opening/closing of segments,
|
||||
and does not support init segments. It also exists solely to support internal
|
||||
ytdl requirements. Using ``no_clip`` with segments is not recommended and
|
||||
probably breaks. ``mp4_dash`` already implicitly does a variant of ``no_clip``.
|
||||
|
||||
The ``mp4_dash`` and ``no_clip`` headers are not part of the core EDL format.
|
||||
They may be changed or removed at any time, depending on mpv's internal
|
||||
requirements.
|
||||
|
||||
Separate files for tracks
|
||||
=========================
|
||||
|
||||
The special ``new_stream`` header lets you specify separate parts and time
|
||||
offsets for separate tracks. This can for example be used to source audio and
|
||||
video track from separate files.
|
||||
|
||||
Example::
|
||||
|
||||
# mpv EDL v0
|
||||
video.mkv
|
||||
!new_stream
|
||||
audio.mkv
|
||||
|
||||
This adds all tracks from both files to the virtual track list. Upon playback,
|
||||
the tracks will be played at the same time, instead of appending them. The files
|
||||
can contain more than 1 stream; the apparent effect is the same as if the second
|
||||
part after the ``!new_stream`` part were in a separate ``.edl`` file and added
|
||||
with ``--external-file``.
|
||||
|
||||
Note that all metadata between the stream sets created by ``new_stream`` is
|
||||
disjoint. Global metadata is taken from the first part only.
|
||||
|
||||
In context of mpv, this is redundant to the ``--audio-file`` and
|
||||
``--external-file`` options, but (as of this writing) has the advantage that
|
||||
this will use a unified cache for all streams.
|
||||
|
||||
The ``new_stream`` header is not part of the core EDL format. It may be changed
|
||||
or removed at any time, depending on mpv's internal requirements.
|
||||
|
||||
If the first ``!new_stream`` is redundant, it is ignored. This is the same
|
||||
example as above::
|
||||
|
||||
# mpv EDL v0
|
||||
!new_stream
|
||||
video.mkv
|
||||
!new_stream
|
||||
audio.mkv
|
||||
|
||||
Note that ``!new_stream`` must be the first header. Whether the parser accepts
|
||||
(i.e. ignores) or rejects other headers before that is implementation specific.
|
||||
|
||||
Track metadata
|
||||
==============
|
||||
|
||||
The special ``track_meta`` header can set some specific metadata fields of the
|
||||
current ``!new_stream`` partition. The tags are applied to all tracks within
|
||||
the partition. It is not possible to set the metadata for individual tracks (the
|
||||
feature was needed only for single-track media).
|
||||
|
||||
It provides following parameters change track metadata:
|
||||
|
||||
``lang``
|
||||
Set the language tag.
|
||||
|
||||
``title``
|
||||
Set the title tag.
|
||||
|
||||
``byterate``
|
||||
Number of bytes per second this stream uses. (Purely informational.)
|
||||
|
||||
``index``
|
||||
The numeric index of the track this should map to (default: -1). This is
|
||||
the 0-based index of the virtual stream as seen by the player, enumerating
|
||||
all audio/video/subtitle streams. If nothing matches, this is silently
|
||||
discarded. The special index -1 (the default) has two meanings: if there
|
||||
was a previous meta data entry (either ``!track_meta`` or ``!delay_open``
|
||||
element since the last ``!new_stream``), then this element manipulates
|
||||
the previous meta data entry. If there was no previous entry, a new meta
|
||||
data entry that matches all streams is created.
|
||||
|
||||
Example::
|
||||
|
||||
# mpv EDL v0
|
||||
!track_meta,lang=bla,title=blabla
|
||||
file.mkv
|
||||
!new_stream
|
||||
!track_meta,title=ducks
|
||||
sub.srt
|
||||
|
||||
If ``file.mkv`` has an audio and a video stream, both will use ``blabla`` as
|
||||
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
|
||||
=====================
|
||||
|
||||
The special ``delay_open`` header can be used to open the media URL of the
|
||||
stream only when the track is selected for the first time. This is supposed to
|
||||
be an optimization to speed up opening of a remote stream if there are many
|
||||
tracks for whatever reasons.
|
||||
|
||||
This has various tricky restrictions, and also will defer failure to open a
|
||||
stream to "later". By design, it's supposed to be used for single-track streams.
|
||||
|
||||
Using multiple segments requires you to specify all offsets and durations (also
|
||||
it was never tested whether it works at all). Interaction with ``mp4_dash`` may
|
||||
be strange.
|
||||
|
||||
You can describe multiple sub-tracks by using multiple ``delay_open`` headers
|
||||
before the same source URL. (If there are multiple sub-tracks of the same media
|
||||
type, then the mapping to the real stream is probably rather arbitrary.) If the
|
||||
source contains tracks not described, a warning is logged when the delayed
|
||||
opening happens, and the track is hidden.
|
||||
|
||||
This has the following parameters:
|
||||
|
||||
``media_type``
|
||||
Required. Must be set to ``video``, ``audio``, or ``sub``. (Other tracks in
|
||||
the opened URL are ignored.)
|
||||
|
||||
``codec``
|
||||
The mpv codec name that is expected. Although mpv tries to initialize a
|
||||
decoder with it currently (and will fail track selection if it does not
|
||||
initialize successfully), it is not used for decoding - decoding still uses
|
||||
the information retrieved from opening the actual media information, and may
|
||||
be a different codec (you should try to avoid this, of course). Defaults to
|
||||
``null``.
|
||||
|
||||
Above also applies for similar fields such as ``w``. These fields are
|
||||
mostly to help with user track pre-selection.
|
||||
|
||||
``flags``
|
||||
A ``+`` separated list of boolean flags. Currently defined flags:
|
||||
|
||||
``default``
|
||||
Set the default track flag.
|
||||
|
||||
``forced``
|
||||
Set the forced track flag.
|
||||
|
||||
Other values are ignored after triggering a warning.
|
||||
|
||||
``w``, ``h``
|
||||
For video codecs: expected video size. See ``codec`` for details.
|
||||
|
||||
``fps``
|
||||
For video codecs: expected video framerate, as integer. (The rate is usually
|
||||
only crudely reported, and it makes no sense to expect exact values.)
|
||||
|
||||
``samplerate``
|
||||
For audio codecs: expected sample rate, as integer.
|
||||
|
||||
The ``delay_open`` header is not part of the core EDL format. It may be changed
|
||||
or removed at any time, depending on mpv's internal requirements.
|
||||
|
||||
Timestamp format
|
||||
================
|
||||
|
||||
@@ -328,8 +102,7 @@ Currently, time values are floating point values in seconds.
|
||||
|
||||
As an extension, you can set the ``timestamps=chapters`` option. If this option
|
||||
is set, timestamps have to be integers, and refer to chapter numbers, starting
|
||||
with 0. The default value for this parameter is ``seconds``, which means the
|
||||
time is as described in the previous paragraph.
|
||||
with 0.
|
||||
|
||||
Example::
|
||||
|
||||
@@ -338,54 +111,6 @@ Example::
|
||||
|
||||
Plays chapter 3 and ends with the start of chapter 7 (4 chapters later).
|
||||
|
||||
Implicit chapters
|
||||
=================
|
||||
|
||||
mpv will add one chapter per segment entry to the virtual timeline.
|
||||
|
||||
By default, the chapter's titles will match the entries' filenames.
|
||||
You can override set the ``title`` option to override the chapter title for
|
||||
that segment.
|
||||
|
||||
Example::
|
||||
|
||||
# mpv EDL v0
|
||||
cap.ts,5,240
|
||||
OP.mkv,0,90,title=Show Opening
|
||||
|
||||
The virtual timeline will have two chapters, one called "cap.ts" from 0-240s
|
||||
and a second one called "Show Opening" from 240-330s.
|
||||
|
||||
Entry which defines the track layout
|
||||
====================================
|
||||
|
||||
Normally, you're supposed to put only files with compatible layouts into an EDL
|
||||
file. However, at least the mpv implementation accepts entries that use
|
||||
different codecs, or even have a different number of audio/video/subtitle
|
||||
tracks. In this case, it's not obvious, which virtual tracks the EDL show should
|
||||
expose when being played.
|
||||
|
||||
Currently, mpv will apply an arbitrary heuristic which tracks the EDL file
|
||||
should expose. (Before mpv 0.30.0, it always used the first source file in the
|
||||
segment list.)
|
||||
|
||||
You can set the ``layout`` option to ``this`` to make a specific entry define
|
||||
the track layout.
|
||||
|
||||
Example::
|
||||
|
||||
# mpv EDL v0
|
||||
file_with_2_streams.ts,5,240
|
||||
file_with_5_streams.mkv,0,90,layout=this
|
||||
|
||||
The way the different virtual EDL tracks are associated with the per-segment
|
||||
ones is highly implementation-defined, and uses a heuristic. If a segment is
|
||||
missing a track, there will be a "hole", and bad behavior may result. Improving
|
||||
this is subject to further development (due to being fringe cases, they don't
|
||||
have a high priority).
|
||||
|
||||
If future versions of mpv change this again, this option may be ignored.
|
||||
|
||||
Syntax of EDL URIs
|
||||
==================
|
||||
|
||||
|
||||
+50
-54
@@ -3,26 +3,27 @@ General usage
|
||||
|
||||
::
|
||||
|
||||
mpv infile --o=outfile [--of=outfileformat] [--ofopts=formatoptions] [--orawts] \
|
||||
mpv infile -o outfile [-of outfileformat] [-ofopts formatoptions] \
|
||||
[-ofps outfps | -oautofps] [-oharddup] [-ocopyts | -orawts] [-oneverdrop] \
|
||||
[(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::
|
||||
@@ -30,13 +31,9 @@ section::
|
||||
[myencprofile]
|
||||
vf-add = scale=480:-2
|
||||
ovc = libx264
|
||||
ovcopts-add = preset=medium
|
||||
ovcopts-add = tune=fastdecode
|
||||
ovcopts-add = preset=medium,tune=fastdecode
|
||||
ovcopts-add = crf=23
|
||||
ovcopts-add = maxrate=1500k
|
||||
ovcopts-add = bufsize=1000k
|
||||
ovcopts-add = rc_init_occupancy=900k
|
||||
ovcopts-add = refs=2
|
||||
ovcopts-add = maxrate=1500k,bufsize=1000k,rc_init_occupancy=900k,refs=2
|
||||
ovcopts-add = profile=baseline
|
||||
oac = aac
|
||||
oacopts-add = b=96k
|
||||
@@ -48,7 +45,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,33 +59,33 @@ for.
|
||||
|
||||
Typical MPEG-4 Part 2 ("ASP", "DivX") encoding, AVI container::
|
||||
|
||||
mpv infile --o=outfile.avi \
|
||||
--vf=fps=25 \
|
||||
--ovc=mpeg4 --ovcopts=qscale=4 \
|
||||
--oac=libmp3lame --oacopts=b=128k
|
||||
mpv infile -o outfile.avi \
|
||||
-ofps 25 \
|
||||
-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
|
||||
30000/1001 for NTSC)
|
||||
Note: AVI does not support variable frame rate, so -ofps must be used. The
|
||||
frame rate should ideally match the input (25 for PAL, 24000/1001 or 30000/1001
|
||||
for NTSC)
|
||||
|
||||
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
|
||||
@@ -97,29 +94,28 @@ Device targets
|
||||
As the options for various devices can get complex, profiles can be used.
|
||||
|
||||
An example profile file for encoding is provided in
|
||||
etc/encoding-profiles.conf in the source tree. mpv will read any file
|
||||
named ``encoding-profiles.conf`` in either the local or system
|
||||
configuration directories.
|
||||
etc/encoding-profiles.conf in the source tree. This file is installed and loaded
|
||||
by default (if libavfilter is enabled at compilation). If you want to modify
|
||||
it, you can replace and it with your own copy by doing::
|
||||
|
||||
Keep in mind that the default profile is the playback one. If you want to add
|
||||
options that apply only in encoding mode, put them into a ``[encoding]``
|
||||
section.
|
||||
mkdir -p ~/.mpv
|
||||
cp /etc/mpv/encoding-profiles.conf ~/.mpv/encoding-profiles.conf
|
||||
|
||||
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.
|
||||
@@ -129,20 +125,20 @@ 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
|
||||
* Encoding at constant frame rate using -ofps framerate -oharddup
|
||||
* 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
|
||||
======================
|
||||
|
||||
+2
-1220
File diff suppressed because it is too large.
Load diff
@@ -1 +0,0 @@
|
||||
deprecate `--foo`, instead use `--bar=foo` as a replacement
|
||||
+288
-93
@@ -4,11 +4,8 @@ AUDIO FILTERS
|
||||
Audio filters allow you to modify the audio stream and its properties. The
|
||||
syntax is:
|
||||
|
||||
``--af=...``
|
||||
Setup a chain of audio filters. See ``--vf`` (`VIDEO FILTERS`_) for the
|
||||
full syntax.
|
||||
|
||||
This is an object settings list option. See `List Options`_ for details.
|
||||
``--af=<filter1[=parameter1:parameter2:...],filter2,...>``
|
||||
Setup a chain of audio filters.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -18,15 +15,66 @@ syntax is:
|
||||
wrapper, which gives you access to most of libavfilter's filters. This
|
||||
includes all filters that have been ported from MPlayer to libavfilter.
|
||||
|
||||
The ``--vf`` description describes how libavfilter can be used and how to
|
||||
workaround deprecated mpv filters.
|
||||
You can also set defaults for each filter. The defaults are applied before the
|
||||
normal filter parameters.
|
||||
|
||||
See ``--vf`` group of options for info on how ``--af-add``, ``--af-pre``,
|
||||
``--af-clr``, and possibly others work.
|
||||
``--af-defaults=<filter1[=parameter1:parameter2:...],filter2,...>``
|
||||
Set defaults for each filter.
|
||||
|
||||
Audio filters are managed in lists. There are a few commands to manage the
|
||||
filter list:
|
||||
|
||||
``--af-add=<filter1[,filter2,...]>``
|
||||
Appends the filters given as arguments to the filter list.
|
||||
|
||||
``--af-pre=<filter1[,filter2,...]>``
|
||||
Prepends the filters given as arguments to the filter list.
|
||||
|
||||
``--af-del=<index1[,index2,...]>``
|
||||
Deletes the filters at the given indexes. Index numbers start at 0,
|
||||
negative numbers address the end of the list (-1 is the last).
|
||||
|
||||
``--af-clr``
|
||||
Completely empties the filter list.
|
||||
|
||||
Available filters are:
|
||||
|
||||
``lavcac3enc[=options]``
|
||||
``lavrresample[=option1:option2:...]``
|
||||
This filter uses libavresample (or libswresample, depending on the build)
|
||||
to change sample rate, sample format, or channel layout of the audio stream.
|
||||
This filter is automatically enabled if the audio output does not support
|
||||
the audio configuration of the file being played.
|
||||
|
||||
It supports only the following sample formats: u8, s16, s32, float.
|
||||
|
||||
``filter-size=<length>``
|
||||
Length of the filter with respect to the lower sampling rate. (default:
|
||||
16)
|
||||
``phase-shift=<count>``
|
||||
Log2 of the number of polyphase entries. (..., 10->1024, 11->2048,
|
||||
12->4096, ...) (default: 10->1024)
|
||||
``cutoff=<cutoff>``
|
||||
Cutoff frequency (0.0-1.0), default set depending upon filter length.
|
||||
``linear``
|
||||
If set then filters will be linearly interpolated between polyphase
|
||||
entries. (default: no)
|
||||
``no-detach``
|
||||
Do not detach if input and output audio format/rate/channels match.
|
||||
(If you just want to set defaults for this filter that will be used
|
||||
even by automatically inserted lavrresample instances, you should
|
||||
prefer setting them with ``--af-defaults=lavrresample:...``.)
|
||||
``normalize=<yes|no>``
|
||||
Whether to normalize when remixing channel layouts (default: yes). This
|
||||
is e.g. applied when downmixing surround audio to stereo. The advantage
|
||||
is that this guarantees that no clipping can happen. Unfortunately,
|
||||
this can also lead to too low volume levels. Whether you enable or
|
||||
disable this is essentially a matter of taste, but the default uses
|
||||
the safer choice.
|
||||
``o=<string>``
|
||||
Set AVOptions on the SwrContext or AVAudioResampleContext. These should
|
||||
be documented by FFmpeg or Libav.
|
||||
|
||||
``lavcac3enc[=tospdif[:bitrate[:minch]]]``
|
||||
Encode multi-channel audio to AC-3 at runtime using libavcodec. Supports
|
||||
16-bit native-endian input format, maximum 6 channels. The output is
|
||||
big-endian when outputting a raw AC-3 stream, native-endian when
|
||||
@@ -59,11 +107,82 @@ Available filters are:
|
||||
If the input channel number is less than ``<minch>``, the filter will
|
||||
detach itself (default: 3).
|
||||
|
||||
``encoder=<name>``
|
||||
Select the libavcodec encoder used. Currently, this should be an AC-3
|
||||
encoder, and using another codec will fail horribly.
|
||||
``equalizer=g1:g2:g3:...:g10``
|
||||
10 octave band graphic equalizer, implemented using 10 IIR band-pass
|
||||
filters. This means that it works regardless of what type of audio is
|
||||
being played back. The center frequencies for the 10 bands are:
|
||||
|
||||
``format=format:srate:channels:out-srate:out-channels``
|
||||
=== ==========
|
||||
No. frequency
|
||||
=== ==========
|
||||
0 31.25 Hz
|
||||
1 62.50 Hz
|
||||
2 125.00 Hz
|
||||
3 250.00 Hz
|
||||
4 500.00 Hz
|
||||
5 1.00 kHz
|
||||
6 2.00 kHz
|
||||
7 4.00 kHz
|
||||
8 8.00 kHz
|
||||
9 16.00 kHz
|
||||
=== ==========
|
||||
|
||||
If the sample rate of the sound being played is lower than the center
|
||||
frequency for a frequency band, then that band will be disabled. A known
|
||||
bug with this filter is that the characteristics for the uppermost band
|
||||
are not completely symmetric if the sample rate is close to the center
|
||||
frequency of that band. This problem can be worked around by upsampling
|
||||
the sound using a resampling filter before it reaches this filter.
|
||||
|
||||
``<g1>:<g2>:<g3>:...:<g10>``
|
||||
floating point numbers representing the gain in dB for each frequency
|
||||
band (-12-12)
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=equalizer=11:11:10:5:0:-12:0:5:12:12 media.avi``
|
||||
Would amplify the sound in the upper and lower frequency region
|
||||
while canceling it almost completely around 1 kHz.
|
||||
|
||||
``channels=nch[:routes]``
|
||||
Can be used for adding, removing, routing and copying audio channels. If
|
||||
only ``<nch>`` is given, the default routing is used. It works as follows:
|
||||
If the number of output channels is greater than the number of input
|
||||
channels, empty channels are inserted (except when mixing from mono to
|
||||
stereo; then the mono channel is duplicated). If the number of output
|
||||
channels is less than the number of input channels, the exceeding
|
||||
channels are truncated.
|
||||
|
||||
``<nch>``
|
||||
number of output channels (1-8)
|
||||
``<routes>``
|
||||
List of ``,`` separated routes, in the form ``from1-to1,from2-to2,...``.
|
||||
Each pair defines where to route each channel. There can be at most
|
||||
8 routes. Without this argument, the default routing is used. Since
|
||||
``,`` is also used to separate filters, you must quote this argument
|
||||
with ``[...]`` or similar.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
``mpv --af=channels=4:[0-1,1-0,2-2,3-3] media.avi``
|
||||
Would change the number of channels to 4 and set up 4 routes that
|
||||
swap channel 0 and channel 1 and leave channel 2 and 3 intact.
|
||||
Observe that if media containing two channels were played back,
|
||||
channels 2 and 3 would contain silence but 0 and 1 would still be
|
||||
swapped.
|
||||
|
||||
``mpv --af=channels=6:[0-0,0-1,0-2,0-3] media.avi``
|
||||
Would change the number of channels to 6 and set up 4 routes that
|
||||
copy channel 0 to channels 0 to 3. Channel 4 and 5 will contain
|
||||
silence.
|
||||
|
||||
.. note::
|
||||
|
||||
You should probably not use this filter. If you want to change the
|
||||
output channel layout, try the ``format`` filter, which can make mpv
|
||||
automatically up- and downmix standard channel layouts.
|
||||
|
||||
``format=format:srate:channels:out-format:out-srate:out-channels``
|
||||
Does not do any format conversion itself. Rather, it may cause the
|
||||
filter system to insert necessary conversion filters before or after this
|
||||
filter if needed. It is primarily useful for controlling the audio format
|
||||
@@ -92,6 +211,8 @@ Available filters are:
|
||||
Force mixing to a specific channel layout. See ``--audio-channels`` option
|
||||
for possible values.
|
||||
|
||||
``<out-format>``
|
||||
|
||||
``<out-srate>``
|
||||
|
||||
``<out-channels>``
|
||||
@@ -100,9 +221,151 @@ Available filters are:
|
||||
used to do conversion itself, unlike this one which lets the filter system
|
||||
handle the conversion.
|
||||
|
||||
``volume[=<volumedb>[:...]]``
|
||||
Implements software volume control. Use this filter with caution since it
|
||||
can reduce the signal to noise ratio of the sound. In most cases it is
|
||||
best to use the *Master* volume control of your sound card or the volume
|
||||
knob on your amplifier.
|
||||
|
||||
*NOTE*: This filter is not reentrant and can therefore only be enabled
|
||||
once for every audio stream.
|
||||
|
||||
``<volumedb>``
|
||||
Sets the desired gain in dB for all channels in the stream from -200 dB
|
||||
to +60 dB, where -200 dB mutes the sound completely and +60 dB equals a
|
||||
gain of 1000 (default: 0).
|
||||
``replaygain-track``
|
||||
Adjust volume gain according to the track-gain replaygain value stored
|
||||
in the file metadata.
|
||||
``replaygain-album``
|
||||
Like replaygain-track, but using the album-gain value instead.
|
||||
``replaygain-preamp``
|
||||
Pre-amplification gain in dB to apply to the selected replaygain gain
|
||||
(default: 0).
|
||||
``replaygain-clip=yes|no``
|
||||
Prevent clipping caused by replaygain by automatically lowering the
|
||||
gain (default). Use ``replaygain-clip=no`` to disable this.
|
||||
``replaygain-fallback``
|
||||
Gain in dB to apply if the file has no replay gain tags. This option
|
||||
is always applied if the replaygain logic is somehow inactive. If this
|
||||
is applied, no other replaygain options are applied.
|
||||
``softclip``
|
||||
Turns soft clipping on. Soft-clipping can make the
|
||||
sound more smooth if very high volume levels are used. Enable this
|
||||
option if the dynamic range of the loudspeakers is very low.
|
||||
|
||||
*WARNING*: This feature creates distortion and should be considered a
|
||||
last resort.
|
||||
``s16``
|
||||
Force S16 sample format if set. Lower quality, but might be faster
|
||||
in some situations.
|
||||
``detach``
|
||||
Remove the filter if the volume is not changed at audio filter config
|
||||
time. Useful with replaygain: if the current file has no replaygain
|
||||
tags, then the filter will be removed if this option is enabled.
|
||||
(If ``--softvol=yes`` is used and the player volume controls are used
|
||||
during playback, a different volume filter will be inserted.)
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=volume=10.1 media.avi``
|
||||
Would amplify the sound by 10.1 dB and hard-clip if the sound level
|
||||
is too high.
|
||||
|
||||
``pan=n:[<matrix>]``
|
||||
Mixes channels arbitrarily. Basically a combination of the volume and the
|
||||
channels filter that can be used to down-mix many channels to only a few,
|
||||
e.g. stereo to mono, or vary the "width" of the center speaker in a
|
||||
surround sound system. This filter is hard to use, and will require some
|
||||
tinkering before the desired result is obtained. The number of options for
|
||||
this filter depends on the number of output channels. An example how to
|
||||
downmix a six-channel file to two channels with this filter can be found
|
||||
in the examples section near the end.
|
||||
|
||||
``<n>``
|
||||
Number of output channels (1-8).
|
||||
``<matrix>``
|
||||
A list of values ``[L00,L01,L02,...,L10,L11,L12,...,Ln0,Ln1,Ln2,...]``,
|
||||
where each element ``Lij`` means how much of input channel i is mixed
|
||||
into output channel j (range 0-1). So in principle you first have n
|
||||
numbers saying what to do with the first input channel, then n numbers
|
||||
that act on the second input channel etc. If you do not specify any
|
||||
numbers for some input channels, 0 is assumed.
|
||||
Note that the values are separated by ``,``, which is already used
|
||||
by the option parser to separate filters. This is why you must quote
|
||||
the value list with ``[...]`` or similar.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
``mpv --af=pan=1:[0.5,0.5] media.avi``
|
||||
Would downmix from stereo to mono.
|
||||
|
||||
``mpv --af=pan=3:[1,0,0.5,0,1,0.5] media.avi``
|
||||
Would give 3 channel output leaving channels 0 and 1 intact, and mix
|
||||
channels 0 and 1 into output channel 2 (which could be sent to a
|
||||
subwoofer for example).
|
||||
|
||||
.. note::
|
||||
|
||||
If you just want to force remixing to a certain output channel layout,
|
||||
it is easier to use the ``format`` filter. For example,
|
||||
``mpv '--af=format=channels=5.1' '--audio-channels=5.1'`` would always force
|
||||
remixing audio to 5.1 and output it like this.
|
||||
|
||||
``delay[=[ch1,ch2,...]]``
|
||||
Delays the sound to the loudspeakers such that the sound from the
|
||||
different channels arrives at the listening position simultaneously. It is
|
||||
only useful if you have more than 2 loudspeakers.
|
||||
|
||||
``[ch1,ch2,...]``
|
||||
The delay in ms that should be imposed on each channel (floating point
|
||||
number between 0 and 1000).
|
||||
|
||||
To calculate the required delay for the different channels, do as follows:
|
||||
|
||||
1. Measure the distance to the loudspeakers in meters in relation to your
|
||||
listening position, giving you the distances s1 to s5 (for a 5.1
|
||||
system). There is no point in compensating for the subwoofer (you will
|
||||
not hear the difference anyway).
|
||||
|
||||
2. Subtract the distances s1 to s5 from the maximum distance, i.e.
|
||||
``s[i] = max(s) - s[i]; i = 1...5``.
|
||||
|
||||
3. Calculate the required delays in ms as ``d[i] = 1000*s[i]/342; i =
|
||||
1...5``.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=delay=[10.5,10.5,0,0,7,0] media.avi``
|
||||
Would delay front left and right by 10.5 ms, the two rear channels
|
||||
and the subwoofer by 0 ms and the center channel by 7 ms.
|
||||
|
||||
``drc[=method:target]``
|
||||
Applies dynamic range compression. This maximizes the volume by compressing
|
||||
the audio signal's dynamic range. (Formerly called ``volnorm``.)
|
||||
|
||||
``<method>``
|
||||
Sets the used method.
|
||||
|
||||
1
|
||||
Use a single sample to smooth the variations via the standard
|
||||
weighted mean over past samples (default).
|
||||
2
|
||||
Use several samples to smooth the variations via the standard
|
||||
weighted mean over past samples.
|
||||
|
||||
``<target>``
|
||||
Sets the target amplitude as a fraction of the maximum for the sample
|
||||
type (default: 0.25).
|
||||
|
||||
.. note::
|
||||
|
||||
This filter can cause distortion with audio signals that have a very
|
||||
large dynamic range.
|
||||
|
||||
``scaletempo[=option1:option2:...]``
|
||||
Scales audio tempo without altering pitch, optionally synced to playback
|
||||
speed.
|
||||
speed (default).
|
||||
|
||||
This works by playing 'stride' ms of audio at normal speed then consuming
|
||||
'stride*scale' ms of input audio. It pieces the strides together by
|
||||
@@ -118,8 +381,8 @@ Available filters are:
|
||||
cause noticeable skips at high scale amounts and an echo at low scale
|
||||
amounts. Very low values will alter pitch. Increasing improves
|
||||
performance. (default: 60)
|
||||
``overlap=<factor>``
|
||||
Factor of stride to overlap. Decreasing improves performance.
|
||||
``overlap=<percent>``
|
||||
Percentage of stride to overlap. Decreasing improves performance.
|
||||
(default: .20)
|
||||
``search=<amount>``
|
||||
Length in milliseconds to search for best overlap position. Decreasing
|
||||
@@ -159,77 +422,29 @@ Available filters are:
|
||||
``mpv --af=scaletempo=stride=30:overlap=.50:search=10 media.ogg``
|
||||
Would tweak the quality and performance parameters.
|
||||
|
||||
``mpv --af=format=float,scaletempo media.ogg``
|
||||
Would make scaletempo use float code. Maybe faster on some
|
||||
platforms.
|
||||
|
||||
``mpv --af=scaletempo=scale=1.2:speed=pitch audio.ogg``
|
||||
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
|
||||
Waveform Similarity Overlap-and-add (WSOLA) method.
|
||||
It seems to achieves higher audio quality than scaletempo, and rubberband R2
|
||||
engine, or ``engine=faster``. This filter is inserted automatically if
|
||||
``audio-pitch-correction`` option is used (on by default) when the playback
|
||||
speed is changed.
|
||||
|
||||
By default, the ``search-interval`` and ``window-size`` parameters
|
||||
have the same values as in chromium.
|
||||
|
||||
``min-speed=<speed>``
|
||||
Mute audio if the playback speed is below ``<speed>``. (default: 0.25)
|
||||
|
||||
``max-speed=<speed>``
|
||||
Mute audio if the playback speed is above ``<speed>``
|
||||
and ``<speed> != 0``. (default: 8.0)
|
||||
|
||||
``search-interval=<amount>``
|
||||
Length in milliseconds to search for best overlap position. (default: 40)
|
||||
|
||||
``window-size=<amount>``
|
||||
Length in milliseconds of the overlap-and-add window. (default: 12)
|
||||
|
||||
``rubberband``
|
||||
High quality pitch correction with librubberband. This can be used in place
|
||||
of ``scaletempo`` and ``scaletempo2``, and will be used to adjust audio pitch
|
||||
when playing at speed different from normal. It can also be used to adjust
|
||||
audio pitch without changing playback speed.
|
||||
of ``scaletempo``, and will be used to adjust audio pitch when playing
|
||||
at speed different from normal.
|
||||
|
||||
``pitch-scale=<amount>``
|
||||
Sets the pitch scaling factor. Frequencies are multiplied by this value.
|
||||
(default: 1.0)
|
||||
|
||||
``engine=<faster|finer>``
|
||||
Select the core Rubberband engine to be used. There are two available:
|
||||
|
||||
:Faster: This is the Rubberband R2 engine. It uses significantly less
|
||||
CPU than the Finer (R3) engine.
|
||||
:Finer: This is the Rubberband R3 engine. This engine is only available
|
||||
with librubberband version 3 or newer. This produces significantly
|
||||
higher quality output, at the cost of higher CPU usage. (Default
|
||||
if available)
|
||||
|
||||
This filter has a number of additional sub-options. You can list them with
|
||||
This filter has a number of sub-options. You can list them with
|
||||
``mpv --af=rubberband=help``. This will also show the default values
|
||||
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
|
||||
Do note that certain options are only applicable to one of R2 (faster) and
|
||||
R3 (finer) engines.
|
||||
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``.)
|
||||
|
||||
This filter supports the following ``af-command`` commands:
|
||||
|
||||
``set-pitch``
|
||||
Set the ``<pitch-scale>`` argument dynamically. This can be used to
|
||||
change the playback pitch at runtime. Note that speed is controlled
|
||||
using the standard ``speed`` property, not ``af-command``.
|
||||
|
||||
``multiply-pitch <factor>``
|
||||
Multiply the current value of ``<pitch-scale>`` dynamically.
|
||||
|
||||
``lavfi=graph``
|
||||
Filter audio using FFmpeg's libavfilter.
|
||||
|
||||
@@ -244,23 +459,3 @@ Available filters are:
|
||||
|
||||
``o=<string>``
|
||||
AVOptions.
|
||||
|
||||
``fix-pts=<yes|no>``
|
||||
Determine PTS based on sample count (default: no). If this is enabled,
|
||||
the player won't rely on libavfilter passing through PTS accurately.
|
||||
Instead, it pass a sample count as PTS to libavfilter, and compute the
|
||||
PTS used by mpv based on that and the input PTS. This helps with filters
|
||||
which output a recomputed PTS instead of the original PTS (including
|
||||
filters which require the PTS to start at 0). mpv normally expects
|
||||
filters to not touch the PTS (or only to the extent of changing frame
|
||||
boundaries), so this is not the default, but it will be needed to use
|
||||
broken filters. In practice, these broken filters will either cause slow
|
||||
A/V desync over time (with some files), or break playback completely if
|
||||
you seek or start playback from the middle of a file.
|
||||
|
||||
``drop``
|
||||
This filter drops or repeats audio frames to adapt to playback speed. It
|
||||
always operates on full audio frames, because it was made to handle SPDIF
|
||||
(compressed audio passthrough). This is used automatically if the
|
||||
``--video-sync=display-adrop`` option is used. Do not use this filter (or
|
||||
the given option); they are extremely low quality.
|
||||
+158
-161
@@ -4,52 +4,56 @@ AUDIO OUTPUT DRIVERS
|
||||
Audio output drivers are interfaces to different audio output facilities. The
|
||||
syntax is:
|
||||
|
||||
``--ao=<driver1,driver2,...[,]>``
|
||||
``--ao=<driver1[:suboption1[=value]:...],driver2,...[,]>``
|
||||
Specify a priority list of audio output drivers to be used.
|
||||
|
||||
If the list has a trailing ',', mpv will fall back on drivers not contained
|
||||
in the list.
|
||||
in the list. Suboptions are optional and can mostly be omitted.
|
||||
|
||||
This is an object settings list option. See `List Options`_ for details.
|
||||
You can also set defaults for each driver. The defaults are applied before the
|
||||
normal driver parameters.
|
||||
|
||||
``--ao-defaults=<driver1[:parameter1:parameter2:...],driver2,...>``
|
||||
Set defaults for each driver.
|
||||
|
||||
.. note::
|
||||
|
||||
See ``--ao=help`` for a list of compiled-in audio output drivers sorted by
|
||||
autoprobe order.
|
||||
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`` or ``--ao=sndio``
|
||||
may work (the latter being experimental).
|
||||
|
||||
Note that the default audio output driver is subject to change, and must
|
||||
not be relied upon. If a certain AO needs to be used, it must be
|
||||
explicitly specified.
|
||||
.. admonition:: Examples
|
||||
|
||||
- ``--ao=alsa,oss,`` Try the ALSA driver, then the OSS driver, then others.
|
||||
- ``--ao=alsa:resample=yes:device=[plughw:0,3]`` Lets ALSA resample and
|
||||
sets the device-name as first card, fourth device.
|
||||
|
||||
Available audio output drivers are:
|
||||
|
||||
``alsa``
|
||||
ALSA audio output driver.
|
||||
``alsa`` (Linux only)
|
||||
ALSA audio output driver
|
||||
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--alsa-resample=yes``
|
||||
``device=<device>``
|
||||
Sets the device name. For ac3 output via S/PDIF, use an "iec958" or
|
||||
"spdif" device, unless you really know how to set it correctly.
|
||||
``resample=yes``
|
||||
Enable ALSA resampling plugin. (This is disabled by default, because
|
||||
some drivers report incorrect audio delay in some cases.)
|
||||
|
||||
``--alsa-mixer-device=<device>``
|
||||
Set the mixer device used with ``ao-volume`` (default: ``default``).
|
||||
|
||||
``--alsa-mixer-name=<name>``
|
||||
``mixer-device=<device>``
|
||||
Set the mixer device used with ``--no-softvol`` (default: ``default``).
|
||||
``mixer-name=<name>``
|
||||
Set the name of the mixer element (default: ``Master``). This is for
|
||||
example ``PCM`` or ``Master``.
|
||||
|
||||
``--alsa-mixer-index=<number>``
|
||||
``mixer-index=<number>``
|
||||
Set the index of the mixer channel (default: 0). Consider the output of
|
||||
"``amixer scontrols``", then the index is the number that follows the
|
||||
name of the element.
|
||||
|
||||
``--alsa-non-interleaved``
|
||||
``non-interleaved``
|
||||
Allow output of non-interleaved formats (if the audio decoder uses
|
||||
this format). Currently disabled by default, because some popular
|
||||
ALSA plugins are utterly broken with non-interleaved formats.
|
||||
|
||||
``--alsa-ignore-chmap``
|
||||
``ignore-chmap``
|
||||
Don't read or set the channel map of the ALSA device - only request the
|
||||
required number of channels, and then pass the audio as-is to it. This
|
||||
option most likely should not be used. It can be useful for debugging,
|
||||
@@ -58,29 +62,50 @@ Available audio output drivers are:
|
||||
or it will work only for files which use the layout implicit to your
|
||||
ALSA device).
|
||||
|
||||
``--alsa-buffer-time=<microseconds>``
|
||||
Set the requested buffer time in microseconds. A value of 0 skips requesting
|
||||
anything from the ALSA API. This and the ``--alsa-periods`` option uses the
|
||||
ALSA ``near`` functions to set the requested parameters. If doing so results
|
||||
in an empty configuration set, setting these parameters is skipped.
|
||||
.. note::
|
||||
|
||||
Both options control the buffer size. A low buffer size can lead to higher
|
||||
CPU usage and audio dropouts, while a high buffer size can lead to higher
|
||||
latency in volume changes and other filtering.
|
||||
MPlayer and mplayer2 required you to replace any ',' with '.' and
|
||||
any ':' with '=' in the ALSA device name. mpv does not do this anymore.
|
||||
Instead, quote the device name:
|
||||
|
||||
``--alsa-periods=<number>``
|
||||
Number of periods requested from the ALSA API. See ``--alsa-buffer-time``
|
||||
for further remarks.
|
||||
``--ao=alsa:device=[plug:surround50]``
|
||||
|
||||
Note that the ``[`` and ``]`` simply quote the device name. With some
|
||||
shells (like zsh), you have to quote the option string to prevent the
|
||||
shell from interpreting the brackets instead of passing them to mpv.
|
||||
|
||||
Actually, you should use the ``--audio-device`` option, instead of
|
||||
setting the device directly.
|
||||
|
||||
.. warning::
|
||||
|
||||
To get multichannel/surround audio, use ``--audio-channels=auto``. The
|
||||
default for this option is ``auto-safe``, which makes this audio output
|
||||
explicitly reject multichannel output, as there is no way to detect
|
||||
whether a certain channel layout is actually supported.
|
||||
Handling of multichannel/surround audio changed in mpv 0.8.0 from the
|
||||
behavior in MPlayer/mplayer2 and older versions of mpv.
|
||||
|
||||
You can also try `using the upmix plugin
|
||||
<https://github.com/mpv-player/mpv/wiki/ALSA-Surround-Sound-and-Upmixing>`_.
|
||||
The old behavior is that the player always downmixed to stereo by
|
||||
default. The ``--audio-channels`` (or ``--channels`` before that) option
|
||||
had to be set to get multichannel audio. Then playing stereo would
|
||||
use the ``default`` device (which typically allows multiple programs
|
||||
to play audio at the same time via dmix), while playing anything with
|
||||
more channels would open one of the hardware devices, e.g. via the
|
||||
``surround51`` alias (typically with exclusive access). Whether the
|
||||
player would use exclusive access or not would depend on the file
|
||||
being played.
|
||||
|
||||
The new behavior since mpv 0.8.0 always enables multichannel audio,
|
||||
i.e. ``--audio-channels=auto`` is the default. However, since ALSA
|
||||
provides no good way to play multichannel audio in a non-exclusive
|
||||
way (without blocking other applications from using audio), the player
|
||||
is restricted to the capabilities of the ``default`` device by default,
|
||||
which means it supports only stereo and mono (at least with current
|
||||
typical ALSA configurations). But if a hardware device is selected,
|
||||
then multichannel audio will typically work.
|
||||
|
||||
The short story is: if you want multichannel audio with ALSA, use
|
||||
``--audio-device`` to select the device (use ``--audio-device=help``
|
||||
to get a list of all devices and their mpv name).
|
||||
|
||||
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.
|
||||
@@ -88,24 +113,31 @@ Available audio output drivers are:
|
||||
``oss``
|
||||
OSS audio output driver
|
||||
|
||||
``<dsp-device>``
|
||||
Sets the audio output device (default: ``/dev/dsp``).
|
||||
``<mixer-device>``
|
||||
Sets the audio mixer device (default: ``/dev/mixer``).
|
||||
``<mixer-channel>``
|
||||
Sets the audio mixer channel (default: ``pcm``). Other valid values
|
||||
include **vol, pcm, line**. For a complete list of options look for
|
||||
``SOUND_DEVICE_NAMES`` in ``/usr/include/linux/soundcard.h``.
|
||||
|
||||
``jack``
|
||||
JACK (Jack Audio Connection Kit) audio output driver.
|
||||
JACK (Jack Audio Connection Kit) audio output driver
|
||||
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--jack-port=<name>``
|
||||
``port=<name>``
|
||||
Connects to the ports with the given name (default: physical ports).
|
||||
``--jack-name=<client>``
|
||||
``name=<client>``
|
||||
Client name that is passed to JACK (default: ``mpv``). Useful
|
||||
if you want to have certain connections established automatically.
|
||||
``--jack-autostart=<yes|no>``
|
||||
``(no-)autostart``
|
||||
Automatically start jackd if necessary (default: disabled). Note that
|
||||
this tends to be unreliable and will flood stdout with server messages.
|
||||
``--jack-connect=<yes|no>``
|
||||
``(no-)connect``
|
||||
Automatically create connections to output ports (default: enabled).
|
||||
When enabled, the maximum number of output channels will be limited to
|
||||
the number of available output ports.
|
||||
``--jack-std-channel-layout=<waveext|any>``
|
||||
``std-channel-layout=waveext|any``
|
||||
Select the standard channel layout (default: waveext). JACK itself has no
|
||||
notion of channel layouts (i.e. assigning which speaker a given
|
||||
channel is supposed to map to) - it just takes whatever the application
|
||||
@@ -118,16 +150,14 @@ 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
|
||||
formats.
|
||||
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--coreaudio-change-physical-format=<yes|no>``
|
||||
``change-physical-format=<yes|no>``
|
||||
Change the physical format to one similar to the requested audio format
|
||||
(default: no). This has the advantage that multichannel audio output
|
||||
will actually work. The disadvantage is that it will change the
|
||||
@@ -135,189 +165,156 @@ Available audio output drivers are:
|
||||
setting in the ``Audio Devices`` dialog in the ``Audio MIDI Setup``
|
||||
utility. Note that this does not affect the selected speaker setup.
|
||||
|
||||
``--coreaudio-spdif-hack=<yes|no>``
|
||||
Try to pass through AC3/DTS data as PCM. This is useful for drivers
|
||||
which do not report AC3 support. It converts the AC3 data to float,
|
||||
and assumes the driver will do the inverse conversion, which means
|
||||
a typical A/V receiver will pick it up as compressed IEC framed AC3
|
||||
stream, ignoring that it's marked as PCM. This disables normal AC3
|
||||
passthrough (even if the device reports it as supported). Use with
|
||||
extreme care.
|
||||
``exclusive``
|
||||
Use exclusive mode access. This merely redirects to
|
||||
``coreaudio_exclusive``, but should be preferred over using that AO
|
||||
directly.
|
||||
|
||||
``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).
|
||||
|
||||
``avfoundation`` (macOS only)
|
||||
Native macOS audio output driver using ``AVSampleBufferAudioRenderer``
|
||||
in AVFoundation, which supports `spatial audio
|
||||
<https://support.apple.com/en-us/HT211775>`_.
|
||||
|
||||
.. warning::
|
||||
|
||||
Turning on spatial audio may hang the playback
|
||||
if mpv is not started out of the bundle,
|
||||
though playback with spatial audio off always works.
|
||||
|
||||
``audiounit`` (iOS only)
|
||||
Native iOS audio output driver using ``AudioUnits`` and AudioToolbox.
|
||||
|
||||
``openal``
|
||||
OpenAL audio output driver.
|
||||
Experimental OpenAL audio output driver
|
||||
|
||||
``--openal-num-buffers=<2-128>``
|
||||
Specify the number of audio buffers to use. Lower values are better for
|
||||
lower CPU usage. Default: 4.
|
||||
|
||||
``--openal-num-samples=<256-32768>``
|
||||
Specify the number of complete samples to use for each buffer. Higher
|
||||
values are better for lower CPU usage. Default: 8192.
|
||||
|
||||
``--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.
|
||||
.. note:: This driver is not very useful. Playing multi-channel audio with
|
||||
it is slow.
|
||||
|
||||
``pulse``
|
||||
PulseAudio audio output driver
|
||||
|
||||
The following global options are supported by this audio output:
|
||||
``[<host>][:<output sink>]``
|
||||
Specify the host and optionally output sink to use. An empty <host>
|
||||
string uses a local connection, "localhost" uses network transfer
|
||||
(most likely not what you want).
|
||||
|
||||
``--pulse-host=<host>``
|
||||
Specify the host to use. An empty <host> string uses a local connection,
|
||||
"localhost" uses network transfer (most likely not what you want).
|
||||
|
||||
``--pulse-buffer=<1-2000|native>``
|
||||
``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.
|
||||
changes. Default: 250.
|
||||
|
||||
``--pulse-latency-hacks=<yes|no>``
|
||||
Enable hacks to workaround PulseAudio timing bugs (default: yes). If
|
||||
``latency-hacks=<yes|no>``
|
||||
Enable hacks to workaround PulseAudio timing bugs (default: no). If
|
||||
enabled, mpv will do elaborate latency calculations on its own. If
|
||||
disabled, it will use PulseAudio automatically updated timing
|
||||
information. Disabling this might help with e.g. networked audio or
|
||||
some plugins, while enabling it might help in some unknown situations
|
||||
(it is currently enabled due to known bugs with PulseAudio 16.0).
|
||||
(it used to be required to get good behavior on old PulseAudio versions).
|
||||
|
||||
``--pulse-allow-suspended=<yes|no>``
|
||||
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.
|
||||
If you have stuttering video when using pulse, try to enable this
|
||||
option. (Or try to update PulseAudio.)
|
||||
|
||||
``sdl``
|
||||
SDL 2.0+ audio output driver. Should work on any platform supported by SDL
|
||||
2.0, but may require the ``SDL_AUDIODRIVER`` environment variable to be set
|
||||
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
|
||||
appropriately for your system.
|
||||
|
||||
.. note:: This driver is for compatibility with extremely foreign
|
||||
environments, such as systems where none of the other drivers
|
||||
are available.
|
||||
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--sdl-buflen=<length>``
|
||||
``buflen=<length>``
|
||||
Sets the audio buffer length in seconds. Is used only as a hint by the
|
||||
sound system. Playing a file with ``-v`` will show the requested and
|
||||
obtained exact buffer size. A value of 0 selects the sound system
|
||||
default.
|
||||
|
||||
``bufcnt=<count>``
|
||||
Sets the number of extra audio buffers in mpv. Usually needs not be
|
||||
changed.
|
||||
|
||||
``null``
|
||||
Produces no audio output but maintains video playback speed. You can use
|
||||
``--ao=null --ao-null-untimed`` for benchmarking.
|
||||
Produces no audio output but maintains video playback speed. Use
|
||||
``--ao=null:untimed`` for benchmarking.
|
||||
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--ao-null-untimed``
|
||||
``untimed``
|
||||
Do not simulate timing of a perfect audio device. This means audio
|
||||
decoding will go as fast as possible, instead of timing it to the
|
||||
system clock.
|
||||
|
||||
``--ao-null-buffer``
|
||||
``buffer``
|
||||
Simulated buffer length in seconds.
|
||||
|
||||
``--ao-null-outburst``
|
||||
``outburst``
|
||||
Simulated chunk size in samples.
|
||||
|
||||
``--ao-null-speed``
|
||||
``speed``
|
||||
Simulated audio playback speed as a multiplier. Usually, a real audio
|
||||
device will not go exactly as fast as the system clock. It will deviate
|
||||
just a little, and this option helps to simulate this.
|
||||
|
||||
``--ao-null-latency``
|
||||
``latency``
|
||||
Simulated device latency. This is additional to EOF.
|
||||
|
||||
``--ao-null-broken-eof``
|
||||
``broken-eof``
|
||||
Simulate broken audio drivers, which always add the fixed device
|
||||
latency to the reported audio playback position.
|
||||
|
||||
``--ao-null-broken-delay``
|
||||
``broken-delay``
|
||||
Simulate broken audio drivers, which don't report latency correctly.
|
||||
|
||||
``--ao-null-channel-layouts``
|
||||
``channel-layouts``
|
||||
If not empty, this is a ``,`` separated list of channel layouts the
|
||||
AO allows. This can be used to test channel layout selection.
|
||||
|
||||
``--ao-null-format``
|
||||
Force the audio output format the AO will accept. If unset accepts any.
|
||||
|
||||
``pcm``
|
||||
Raw PCM/WAVE file writer audio output
|
||||
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--ao-pcm-waveheader=<yes|no>``
|
||||
``(no-)waveheader``
|
||||
Include or do not include the WAVE header (default: included). When
|
||||
not included, raw PCM will be generated.
|
||||
``--ao-pcm-file=<filename>``
|
||||
``file=<filename>``
|
||||
Write the sound to ``<filename>`` instead of the default
|
||||
``audiodump.wav``. If ``no-waveheader`` is specified, the default is
|
||||
``audiodump.pcm``.
|
||||
``--ao-pcm-append=<yes|no>``
|
||||
``(no-)append``
|
||||
Append to the file, instead of overwriting it. Always use this with the
|
||||
``no-waveheader`` option - with ``waveheader`` it's broken, because
|
||||
it will write a WAVE header every time the file is opened.
|
||||
|
||||
``rsound``
|
||||
Audio output to an RSound daemon
|
||||
|
||||
.. note:: Completely useless, unless you intend to run RSound. Not to be
|
||||
confused with RoarAudio, which is something completely
|
||||
different.
|
||||
|
||||
``host=<name/path>``
|
||||
Set the address of the server (default: localhost). Can be either a
|
||||
network hostname for TCP connections or a Unix domain socket path
|
||||
starting with '/'.
|
||||
``port=<number>``
|
||||
Set the TCP port used for connecting to the server (default: 12345).
|
||||
Not used if connecting to a Unix domain socket.
|
||||
|
||||
``sndio``
|
||||
Audio output to the OpenBSD sndio sound system
|
||||
|
||||
.. note:: Experimental. There are known bugs and issues.
|
||||
|
||||
(Note: only supports mono, stereo, 4.0, 5.1 and 7.1 channel
|
||||
layouts.)
|
||||
|
||||
``device=<device>``
|
||||
sndio device to use (default: ``$AUDIODEVICE``, resp. ``snd0``).
|
||||
|
||||
``wasapi``
|
||||
Audio output to the Windows Audio Session API.
|
||||
|
||||
The following global options are supported by this audio output:
|
||||
``exclusive``
|
||||
Requests exclusive, direct hardware access. By definition prevents
|
||||
sound playback of any other program until mpv exits.
|
||||
``device=<id>``
|
||||
Uses the requested endpoint instead of the system's default audio
|
||||
endpoint. Both an ordinal number (0,1,2,...) and the GUID
|
||||
String are valid; the GUID string is guaranteed to not change
|
||||
unless the driver is uninstalled.
|
||||
|
||||
``--wasapi-exclusive-buffer=<default|min|1-2000000>``
|
||||
Set buffer duration in exclusive mode (i.e., with
|
||||
``--audio-exclusive=yes``). ``default`` and ``min`` use the default and
|
||||
minimum device period reported by WASAPI, respectively. You can also
|
||||
directly specify the buffer duration in microseconds, in which case a
|
||||
duration shorter than the minimum device period will be rounded up to
|
||||
the minimum period.
|
||||
Also supports searching active devices by human-readable name. If more
|
||||
than one device matches the name, refuses loading it.
|
||||
|
||||
The default buffer duration should provide robust playback in most
|
||||
cases, but reportedly on some devices there are glitches following
|
||||
stream resets under the default setting. In such cases, specifying a
|
||||
shorter duration might help.
|
||||
This option is mostly deprecated in favour of the more general
|
||||
``--audio-device`` option. That said, ``--audio-device=help`` will give
|
||||
a list of valid device GUIDs (prefixed with ``wasapi/``), as well as
|
||||
their human readable names, which should work here.
|
||||
@@ -8,12 +8,6 @@ There is no real changelog, but you can look at the following things:
|
||||
|
||||
https://github.com/mpv-player/mpv/releases
|
||||
* The git log, which is the "real" changelog
|
||||
* The file https://github.com/mpv-player/mpv/blob/master/DOCS/interface-changes.rst
|
||||
documents changes to the command and user interface, such as options and
|
||||
properties.
|
||||
* C API changes are listed in
|
||||
https://github.com/mpv-player/mpv/blob/master/DOCS/client-api-changes.rst
|
||||
* The file ``mplayer-changes.rst`` in the ``DOCS`` sub directory on the git
|
||||
repository, which used to be in place of this section. It documents some
|
||||
changes that happened since mplayer2 forked off MPlayer. (Not updated
|
||||
anymore.)
|
||||
changes that happened since mplayer2 forked off MPlayer.
|
||||
@@ -1,58 +0,0 @@
|
||||
COMMANDS
|
||||
========
|
||||
|
||||
This script allows running and completing input commands in the console
|
||||
interactively, and also adds mpv's log to the console's log.
|
||||
|
||||
Keybindings
|
||||
-----------
|
||||
|
||||
\`
|
||||
Open the console to enter commands.
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
``script-binding commands/open``
|
||||
|
||||
Open the console to enter commands.
|
||||
|
||||
``script-message-to commands type <text> [<cursor_pos>]``
|
||||
Show the console and pre-fill it with the provided text, optionally
|
||||
specifying the initial cursor position as a positive integer starting from
|
||||
1. The console is automatically closed after running the command.
|
||||
|
||||
.. admonition:: Examples for input.conf
|
||||
|
||||
``% script-message-to commands type "seek absolute-percent" 6``
|
||||
Enter a percent position to seek to.
|
||||
|
||||
``Ctrl+o script-message-to console type "loadfile ''" 11``
|
||||
Enter a file or URL to play, with autocompletion of paths in the
|
||||
filesystem.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This script can be customized through a config file ``script-opts/commands.conf``
|
||||
placed in mpv's user directory and through the ``--script-opts`` command-line
|
||||
option. The configuration syntax is described in `mp.options functions`_.
|
||||
|
||||
Configurable Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``persist_history``
|
||||
Default: no
|
||||
|
||||
Whether to save the command history to a file and load it.
|
||||
|
||||
``history_path``
|
||||
Default: ``~~state/command_history.txt``
|
||||
|
||||
The file path for ``persist_history`` (see `FILES`_).
|
||||
|
||||
``remember_input``
|
||||
Default: yes
|
||||
|
||||
Whether to remember the input line and cursor position when closing the
|
||||
console, and prefill it the next time it is opened.
|
||||
@@ -1,231 +0,0 @@
|
||||
CONSOLE
|
||||
=======
|
||||
|
||||
This script provides the ability to process the user's textual input to other
|
||||
scripts through the ``mp.input`` API. It can be displayed on both the video
|
||||
window and the terminal. It can be disabled entirely using the
|
||||
``--load-console=no`` option.
|
||||
|
||||
Console can either process free-form text or select from a predefined list of
|
||||
items.
|
||||
|
||||
Free-form text mode keybindings
|
||||
-------------------------------
|
||||
|
||||
ESC and Ctrl+[
|
||||
Hide the console.
|
||||
|
||||
ENTER, Ctrl+j and Ctrl+m
|
||||
Select the first completion if one wasn't already manually selected, and run
|
||||
the typed command.
|
||||
|
||||
Shift+ENTER
|
||||
Type a literal newline character.
|
||||
|
||||
LEFT and Ctrl+b
|
||||
Move the cursor to the previous character.
|
||||
|
||||
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.
|
||||
|
||||
PGUP
|
||||
Go to the first command in the history.
|
||||
|
||||
PGDN
|
||||
Stop navigating the command history.
|
||||
|
||||
Ctrl+r
|
||||
Search the command history. See `SELECT`_ for the key bindings in this mode.
|
||||
|
||||
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).
|
||||
|
||||
Ctrl+y
|
||||
Copy the current line to the clipboard.
|
||||
|
||||
TAB and Ctrl+i
|
||||
Cycle through completions.
|
||||
|
||||
Shift+TAB
|
||||
Cycle through the completions backwards.
|
||||
|
||||
Ctrl+l
|
||||
Clear all log messages from the console.
|
||||
|
||||
MBTN_MID
|
||||
Paste text (uses the primary selection on X11 and Wayland).
|
||||
|
||||
WHEEL_UP
|
||||
Move back in the command history.
|
||||
|
||||
WHEEL_DOWN
|
||||
Move forward in the command history.
|
||||
|
||||
Known issues
|
||||
------------
|
||||
|
||||
- Non-ASCII keyboard input has restrictions
|
||||
- The cursor keys move between Unicode code-points, not grapheme clusters
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This script can be customized through a config file ``script-opts/console.conf``
|
||||
placed in mpv's user directory and through the ``--script-opts`` command-line
|
||||
option. The configuration syntax is described in `mp.options functions`_.
|
||||
|
||||
Configurable Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``monospace_font``
|
||||
Default: platform dependent
|
||||
|
||||
The monospace font used when there are completions to align in a grid.
|
||||
|
||||
When there are no completions, ``--osd-font`` is used.
|
||||
|
||||
``font_size``
|
||||
Default: 24
|
||||
|
||||
The font size. This will be multiplied by ``display-hidpi-scale`` when the
|
||||
console is not scaled with the window.
|
||||
|
||||
``border_size``
|
||||
Default: 1.65
|
||||
|
||||
The font border size.
|
||||
|
||||
``background_alpha``
|
||||
Default: 80
|
||||
|
||||
The transparency of the menu's background. Ranges from 0 (opaque) to 255
|
||||
(fully transparent).
|
||||
|
||||
``gap``
|
||||
Default: 0.2
|
||||
|
||||
The gap between menu items, specified as a percentage the font size.
|
||||
|
||||
``padding``
|
||||
Default: 10
|
||||
|
||||
The padding of the menu.
|
||||
|
||||
``menu_outline_size``
|
||||
Default: 0
|
||||
|
||||
The size of the menu's border.
|
||||
|
||||
``menu_outline_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
The color of the menu's border.
|
||||
|
||||
``corner_radius``
|
||||
Default: 8
|
||||
|
||||
The radius of the menu's corners.
|
||||
|
||||
``margin_x``
|
||||
Default: same as ``--osd-margin-x``
|
||||
|
||||
The margin from the left of the window.
|
||||
|
||||
``margin_y``
|
||||
Default: same as ``--osd-margin-y``
|
||||
|
||||
The margin from the bottom of the window.
|
||||
|
||||
``scale_with_window``
|
||||
Default: ``auto``
|
||||
|
||||
Whether to scale the console with the window height. Can be ``yes``, ``no``,
|
||||
or ``auto``, which follows the value of ``--osd-scale-by-window``.
|
||||
|
||||
``focused_color``
|
||||
Default: ``#222222``
|
||||
|
||||
The color of the focused item.
|
||||
|
||||
``focused_back_color``
|
||||
Default: ``#FFFFFF``
|
||||
|
||||
The background color of the focused item.
|
||||
|
||||
``match_color``
|
||||
Default: ``#0088FF``
|
||||
|
||||
The color of characters that match the searched string.
|
||||
|
||||
``exact_match``
|
||||
Default: no
|
||||
|
||||
Whether to match menu search queries exactly instead of fuzzily. Without
|
||||
this option, prefixing queries with ``'`` enables exact matching.
|
||||
|
||||
``case_sensitive``
|
||||
Default: no
|
||||
|
||||
Whether exact searches are case sensitive. Only works with ASCII characters.
|
||||
|
||||
``history_dedup``
|
||||
Default: true
|
||||
|
||||
Remove duplicate entries in history as to only keep the latest one.
|
||||
|
||||
``font_hw_ratio``
|
||||
Default: auto
|
||||
|
||||
The ratio of font height to font width.
|
||||
Adjusts grid width of completions.
|
||||
Values in the range 1.8..2.5 make sense for common monospace fonts.
|
||||
@@ -1,98 +0,0 @@
|
||||
CONTEXT MENU SCRIPT
|
||||
===================
|
||||
|
||||
This script provides a context menu for platforms without integration with a
|
||||
native context menu. On these platforms, it can be disabled entirely using the
|
||||
``--load-context-menu=no`` option. On platforms where the integration is
|
||||
implemented, it is already disabled by default.
|
||||
|
||||
Script messages
|
||||
---------------
|
||||
|
||||
``open``
|
||||
Show the context menu.
|
||||
|
||||
``select``
|
||||
Select the focused item when there is one.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This script can be customized through a config file
|
||||
``script-opts/context_menu.conf`` placed in mpv's user directory and through
|
||||
the ``--script-opts`` command-line option. The configuration syntax is
|
||||
described in `mp.options functions`_.
|
||||
|
||||
Configurable Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``font_size``
|
||||
Default: 14
|
||||
|
||||
The font size.
|
||||
|
||||
``gap``
|
||||
Default: 0.2
|
||||
|
||||
The gap between menu items, specified as a percentage the font size.
|
||||
|
||||
``padding_x``
|
||||
Default: 8
|
||||
|
||||
The horizontal padding of the menu.
|
||||
|
||||
``padding_y``
|
||||
Default: 4
|
||||
|
||||
The vertical padding of the menu.
|
||||
|
||||
``menu_outline_size``
|
||||
Default: 0
|
||||
|
||||
The size of the menu's border.
|
||||
|
||||
``menu_outline_color``
|
||||
Default: ``#FFFFFF``
|
||||
|
||||
The color of the menu's border.
|
||||
|
||||
``corner_radius``
|
||||
Default: 5
|
||||
|
||||
The radius of the menu's corners.
|
||||
|
||||
``scale_with_window``
|
||||
Default: auto
|
||||
|
||||
Whether to scale sizes with the window height. Can be ``yes``, ``no``, or
|
||||
``auto``, which follows the value of ``--osd-scale-by-window``.
|
||||
|
||||
When sizes aren't scaled with the window, they are scaled by
|
||||
``display-hidpi-scale``.
|
||||
|
||||
``focused_color``
|
||||
Default: ``#222222``
|
||||
|
||||
The color of the focused item.
|
||||
|
||||
``focused_back_color``
|
||||
Default: ``#FFFFFF``
|
||||
|
||||
The background color of the focused item.
|
||||
|
||||
``disabled_color``
|
||||
Default: ``#555555``
|
||||
|
||||
The color of disabled items.
|
||||
|
||||
``seconds_to_open_submenus``
|
||||
Default: 0.2
|
||||
|
||||
The number of seconds to open submenus after the cursor enters items
|
||||
associated with one.
|
||||
|
||||
``seconds_to_close_submenus``
|
||||
Default: 0.2
|
||||
|
||||
The number of seconds to close submenus after the cursor enters a parent
|
||||
menu.
|
||||
+99
-50
@@ -8,25 +8,64 @@ You can encode files from one format/codec to another using this facility.
|
||||
|
||||
``--of=<format>``
|
||||
Specifies the output format (overrides autodetection by the file name
|
||||
extension of the file specified by ``--o``). See ``--of=help`` for a full
|
||||
list of supported formats.
|
||||
extension of the file specified by ``-o``). This can be a comma separated
|
||||
list of possible formats to try. See ``--of=help`` for a full list of
|
||||
supported formats.
|
||||
|
||||
``--ofopts=<options>``
|
||||
Specifies the output format options for libavformat.
|
||||
See ``--ofopts=help`` for a full list of supported options.
|
||||
|
||||
This is a key/value list option. See `List Options`_ for details.
|
||||
Options are managed in lists. There are a few commands to manage the
|
||||
options list.
|
||||
|
||||
``--ofopts-add=<option>``
|
||||
Appends the option given as an argument to the options list. (Passing
|
||||
multiple options is currently still possible, but deprecated.)
|
||||
``--ofopts-add=<options1[,options2,...]>``
|
||||
Appends the options given as arguments to the options list.
|
||||
|
||||
``--ofopts=""``
|
||||
``--ofopts-pre=<options1[,options2,...]>``
|
||||
Prepends the options given as arguments to the options list.
|
||||
|
||||
``--ofopts-del=<index1[,index2,...]>``
|
||||
Deletes the options at the given indexes. Index numbers start at 0,
|
||||
negative numbers address the end of the list (-1 is the last).
|
||||
|
||||
``--ofopts-clr``
|
||||
Completely empties the options list.
|
||||
|
||||
``--ofps=<float value>``
|
||||
Specifies the output format time base (default: 24000). Low values like 25
|
||||
limit video fps by dropping frames.
|
||||
|
||||
``--oautofps``
|
||||
Sets the output format time base to the guessed frame rate of the input
|
||||
video (simulates MEncoder behavior, useful for AVI; may cause frame drops).
|
||||
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.
|
||||
|
||||
``--omaxfps=<float value>``
|
||||
Specifies the minimum distance of adjacent frames (default: 0, which means
|
||||
unset). Content of lower frame rate is not readjusted to this frame rate;
|
||||
content of higher frame rate is decimated to this frame rate.
|
||||
|
||||
``--oharddup``
|
||||
If set, the frame rate given by ``--ofps`` is attained not by skipping time
|
||||
codes, but by duplicating frames (constant frame rate mode).
|
||||
|
||||
``--oneverdrop``
|
||||
If set, frames are never dropped. Instead, time codes of video are
|
||||
readjusted to always increase. This may cause AV desync, though; to work
|
||||
around this, use a high-fps time base using ``--ofps`` and absolutely
|
||||
avoid ``--oautofps``.
|
||||
|
||||
``--oac=<codec>``
|
||||
Specifies the output audio codec. See ``--oac=help`` for a full list of
|
||||
supported codecs.
|
||||
Specifies the output audio codec. This can be a comma separated list of
|
||||
possible codecs to try. See ``--oac=help`` for a full list of supported
|
||||
codecs.
|
||||
|
||||
``--oaoffset=<value>``
|
||||
Shifts audio data by the given time (in seconds) by adding/removing
|
||||
samples at the start.
|
||||
|
||||
``--oacopts=<options>``
|
||||
Specifies the output audio codec options for libavcodec.
|
||||
@@ -37,20 +76,36 @@ You can encode files from one format/codec to another using this facility.
|
||||
"``--oac=libmp3lame --oacopts=b=128000``"
|
||||
selects 128 kbps MP3 encoding.
|
||||
|
||||
This is a key/value list option. See `List Options`_ for details.
|
||||
Options are managed in lists. There are a few commands to manage the
|
||||
options list.
|
||||
|
||||
``--oacopts-add=<option>``
|
||||
Appends the option given as an argument to the options list. (Passing
|
||||
multiple options is currently still possible, but deprecated.)
|
||||
``--oacopts-add=<options1[,options2,...]>``
|
||||
Appends the options given as arguments to the options list.
|
||||
|
||||
``--oacopts=""``
|
||||
``--oacopts-pre=<options1[,options2,...]>``
|
||||
Prepends the options given as arguments to the options list.
|
||||
|
||||
``--oacopts-del=<index1[,index2,...]>``
|
||||
Deletes the options at the given indexes. Index numbers start at 0,
|
||||
negative numbers address the end of the list (-1 is the last).
|
||||
|
||||
``--oacopts-clr``
|
||||
Completely empties the options list.
|
||||
|
||||
``--ovc=<codec>``
|
||||
Specifies the output video codec. See ``--ovc=help`` for a full list of
|
||||
supported codecs.
|
||||
``--oafirst``
|
||||
Force the audio stream to become the first stream in the output.
|
||||
By default, the order is unspecified.
|
||||
|
||||
``--ovcopts=<options>``
|
||||
``--ovc=<codec>``
|
||||
Specifies the output video codec. This can be a comma separated list of
|
||||
possible codecs to try. See ``--ovc=help`` for a full list of supported
|
||||
codecs.
|
||||
|
||||
``--ovoffset=<value>``
|
||||
Shifts video data by the given time (in seconds) by shifting the pts
|
||||
values.
|
||||
|
||||
``--ovcopts <options>``
|
||||
Specifies the output video codec options for libavcodec.
|
||||
See --ovcopts=help for a full list of supported options.
|
||||
|
||||
@@ -62,45 +117,39 @@ You can encode files from one format/codec to another using this facility.
|
||||
``"--ovc=libx264 --ovcopts=crf=23"``
|
||||
selects VBR quality factor 23 for H.264 encoding.
|
||||
|
||||
This is a key/value list option. See `List Options`_ for details.
|
||||
Options are managed in lists. There are a few commands to manage the
|
||||
options list.
|
||||
|
||||
``--ovcopts-add=<option>``
|
||||
Appends the option given as an argument to the options list. (Passing
|
||||
multiple options is currently still possible, but deprecated.)
|
||||
``--ovcopts-add=<options1[,options2,...]>``
|
||||
Appends the options given as arguments to the options list.
|
||||
|
||||
``--ovcopts=""``
|
||||
``--ovcopts-pre=<options1[,options2,...]>``
|
||||
Prepends the options given as arguments to the options list.
|
||||
|
||||
``--ovcopts-del=<index1[,index2,...]>``
|
||||
Deletes the options at the given indexes. Index numbers start at 0,
|
||||
negative numbers address the end of the list (-1 is the last).
|
||||
|
||||
``--ovcopts-clr``
|
||||
Completely empties the options list.
|
||||
|
||||
``--ovfirst``
|
||||
Force the video stream to become the first stream in the output.
|
||||
By default, the order is unspecified.
|
||||
|
||||
``--ocopyts``
|
||||
Copies input pts to the output video (not supported by some output
|
||||
container formats, e.g. AVI). Discontinuities are still fixed.
|
||||
By default, audio pts are set to playback time and video pts are
|
||||
synchronized to match audio pts, as some output formats do not support
|
||||
anything else.
|
||||
|
||||
``--orawts``
|
||||
Copies input pts to the output video (not supported by some output
|
||||
container formats, e.g. AVI). In this mode, discontinuities are not fixed
|
||||
and all pts are passed through as-is. Never seek backwards or use multiple
|
||||
input files in this mode!
|
||||
|
||||
``--ocopy-metadata=<yes|no>``
|
||||
Copy metadata from input files to output files when encoding (default: yes).
|
||||
|
||||
``--oset-metadata=<metadata-tag[,metadata-tag,...]>``
|
||||
Specifies metadata to include in the output file.
|
||||
Supported keys vary between output formats. For example, Matroska (MKV) and
|
||||
FLAC allow almost arbitrary keys, while support in MP4 and MP3 is more
|
||||
limited.
|
||||
|
||||
This is a key/value list option. See `List Options`_ for details.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
"``--oset-metadata=title="Output title",comment="Another tag"``"
|
||||
adds a title and a comment to the output file.
|
||||
|
||||
``--oremove-metadata=<metadata-tag[,metadata-tag,...]>``
|
||||
Specifies metadata to exclude from the output file when copying from the
|
||||
input file.
|
||||
|
||||
This is a string list option. See `List Options`_ for details.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
"``--oremove-metadata=comment,genre``"
|
||||
excludes copying of the the comment and genre tags to the output
|
||||
file.
|
||||
``--no-ometadata``
|
||||
Turns off copying of metadata from input files to output files when
|
||||
encoding (which is enabled by default).
|
||||
+787
-3003
File diff suppressed because it is too large.
Load diff
+29
-173
@@ -1,12 +1,10 @@
|
||||
JSON IPC
|
||||
========
|
||||
|
||||
mpv can be controlled by external programs using the JSON-based IPC protocol.
|
||||
It can be enabled by specifying the path to a unix socket or a named pipe using
|
||||
the option ``--input-ipc-server``, or the file descriptor number of a unix socket
|
||||
or a named pipe using ``--input-ipc-client``.
|
||||
Clients can connect to this socket and send commands to the player or receive
|
||||
events from it.
|
||||
mpv can be controlled by external programs using the JSON-based IPC protocol. It
|
||||
can be enabled by specifying the path to a unix socket using the option
|
||||
``--input-unix-socket``. Clients can connect to this socket and send commands to
|
||||
the player or receive events from it.
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -19,12 +17,12 @@ events from it.
|
||||
Socat example
|
||||
-------------
|
||||
|
||||
You can use the ``socat`` tool to send commands (and receive replies) from the
|
||||
You can use the ``socat`` tool to send commands (and receive reply) from the
|
||||
shell. Assuming mpv was started with:
|
||||
|
||||
::
|
||||
|
||||
mpv file.mkv --input-ipc-server=/tmp/mpvsocket
|
||||
mpv file.mkv --input-unix-socket=/tmp/mpvsocket
|
||||
|
||||
Then you can control it using socat:
|
||||
|
||||
@@ -43,48 +41,14 @@ It's also possible to send input.conf style text-only commands:
|
||||
|
||||
::
|
||||
|
||||
> echo 'show-text ${playback-time}' | socat - /tmp/mpvsocket
|
||||
> echo 'show_text ${playback-time}' | socat - /tmp/mpvsocket
|
||||
|
||||
But you won't get a reply over the socket. (This particular command shows the
|
||||
playback time on the player's OSD.)
|
||||
|
||||
Command Prompt example
|
||||
----------------------
|
||||
|
||||
Unfortunately, it's not as easy to test the IPC protocol on Windows, since
|
||||
Windows ports of socat (in Cygwin and MSYS2) don't understand named pipes. In
|
||||
the absence of a simple tool to send and receive from bidirectional pipes, the
|
||||
``echo`` command can be used to send commands, but not receive replies from the
|
||||
command prompt.
|
||||
|
||||
Assuming mpv was started with:
|
||||
|
||||
::
|
||||
|
||||
mpv file.mkv --input-ipc-server=\\.\pipe\mpvsocket
|
||||
|
||||
You can send commands from a command prompt:
|
||||
|
||||
::
|
||||
|
||||
echo show-text ${playback-time} >\\.\pipe\mpvsocket
|
||||
|
||||
To be able to simultaneously read and write from the IPC pipe, like on Linux,
|
||||
it's necessary to write an external program that uses overlapped file I/O (or
|
||||
some wrapper like .NET's NamedPipeClientStream.)
|
||||
|
||||
You can open the pipe in PuTTY as "serial" device. This is not very
|
||||
comfortable, but gives a way to test interactively without having to write code.
|
||||
|
||||
Protocol
|
||||
--------
|
||||
|
||||
The protocol uses UTF-8-only JSON as defined by RFC-8259. Unlike standard JSON,
|
||||
"\u" escape sequences are not allowed to construct surrogate pairs. To avoid
|
||||
getting conflicts, encode all text characters including and above codepoint
|
||||
U+0020 as UTF-8. mpv might output broken UTF-8 in corner cases (see "UTF-8"
|
||||
section below).
|
||||
|
||||
Clients can execute commands on the player by sending JSON messages of the
|
||||
following form:
|
||||
|
||||
@@ -118,11 +82,8 @@ 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
|
||||
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
|
||||
currently show a warning. In the future, this will raise an error.
|
||||
verbatim into the response. mpv does not intrepret the ``request_id`` in any
|
||||
way; it is solely for the use of the requester.
|
||||
|
||||
For example, this request:
|
||||
|
||||
@@ -136,85 +97,17 @@ Would generate this response:
|
||||
|
||||
{ "error": "success", "data": 1.468135, "request_id": 100 }
|
||||
|
||||
If you don't specify a ``request_id``, command replies will set it to 0.
|
||||
|
||||
All commands, replies, and events are separated from each other with a line
|
||||
break character (``\n``).
|
||||
|
||||
If the first character (after skipping whitespace) is not ``{``, the command
|
||||
will be interpreted as non-JSON text command, as they are used in input.conf
|
||||
(or ``mpv_command_string()`` in the client API). Additionally, lines starting
|
||||
(or ``mpv_command_string()`` in the client API). Additionally, line starting
|
||||
with ``#`` and empty lines are ignored.
|
||||
|
||||
Currently, embedded 0 bytes terminate the current line, but you should not
|
||||
rely on this.
|
||||
|
||||
Data flow
|
||||
---------
|
||||
|
||||
Currently, the mpv-side IPC implementation does not service the socket while a
|
||||
command is executed and the reply is written. It is for example not possible
|
||||
that other events, that happened during the execution of the command, are
|
||||
written to the socket before the reply is written.
|
||||
|
||||
This might change in the future. The only guarantee is that replies to IPC
|
||||
messages are sent in sequence.
|
||||
|
||||
Also, since socket I/O is inherently asynchronous, it is possible that you read
|
||||
unrelated event messages from the socket, before you read the reply to the
|
||||
previous command you sent. In this case, these events were queued by the mpv
|
||||
side before it read and started processing your command message.
|
||||
|
||||
If the mpv-side IPC implementation switches away from blocking writes and
|
||||
blocking command execution, it may attempt to send events at any time.
|
||||
|
||||
You can also use asynchronous commands, which can return in any order, and
|
||||
which do not block IPC protocol interaction at all while the command is
|
||||
executed in the background.
|
||||
|
||||
Asynchronous commands
|
||||
---------------------
|
||||
|
||||
Command can be run asynchronously. This behaves exactly as with normal command
|
||||
execution, except that execution is not blocking. Other commands can be sent
|
||||
while it's executing, and command completion can be arbitrarily reordered.
|
||||
|
||||
The ``async`` field controls this. If present, it must be a boolean. If missing,
|
||||
``false`` is assumed.
|
||||
|
||||
For example, this initiates an asynchronous command:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["screenshot"], "request_id": 123, "async": true }
|
||||
|
||||
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.
|
||||
|
||||
Some commands execute synchronously, but these will behave like asynchronous
|
||||
commands that finished execution immediately.
|
||||
|
||||
Cancellation of asynchronous commands is available in the libmpv API, but has
|
||||
not yet been implemented in the IPC protocol.
|
||||
|
||||
Commands with named arguments
|
||||
-----------------------------
|
||||
|
||||
If the ``command`` field is a JSON object, named arguments are expected. This
|
||||
is described in the C API ``mpv_command_node()`` documentation (the
|
||||
``MPV_FORMAT_NODE_MAP`` case). In some cases, this may make commands more
|
||||
readable, while some obscure commands basically require using named arguments.
|
||||
|
||||
Currently, only "proper" commands (as listed by `List of Input Commands`_)
|
||||
support named arguments.
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
@@ -262,7 +155,14 @@ extra commands can also be used as part of the protocol:
|
||||
{ "error": "success" }
|
||||
|
||||
``set_property_string``
|
||||
Alias for ``set_property``. Both commands accept native values and strings.
|
||||
Like ``set_property``, but the argument value must be passed as string.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ "command": ["set_property_string", "pause", "yes"] }
|
||||
{ "error": "success" }
|
||||
|
||||
``observe_property``
|
||||
Watch a property for changes. If the given property is changed, then an
|
||||
@@ -276,14 +176,6 @@ extra commands can also be used as part of the protocol:
|
||||
{ "error": "success" }
|
||||
{ "event": "property-change", "id": 1, "data": 52.0, "name": "volume" }
|
||||
|
||||
.. warning::
|
||||
|
||||
If the connection is closed, the IPC client is destroyed internally,
|
||||
and the observed properties are unregistered. This happens for example
|
||||
when sending commands to a socket with separate ``socat`` invocations.
|
||||
This can make it seem like property observation does not work. You must
|
||||
keep the IPC connection open to make it work.
|
||||
|
||||
``observe_property_string``
|
||||
Like ``observe_property``, but the resulting data will always be a string.
|
||||
|
||||
@@ -324,6 +216,16 @@ extra commands can also be used as part of the protocol:
|
||||
By default, most events are enabled, and there is not much use for this
|
||||
command.
|
||||
|
||||
``suspend``
|
||||
Suspend the mpv main loop. There is a long-winded explanation of this in
|
||||
the C API function ``mpv_suspend()``. In short, this prevents the player
|
||||
from displaying the next video frame, so that you don't get blocked when
|
||||
trying to access the player.
|
||||
|
||||
``resume``
|
||||
Undo one ``suspend`` call. ``suspend`` increments an internal counter, and
|
||||
``resume`` decrements it. When 0 is reached, the player is actually resumed.
|
||||
|
||||
``get_version``
|
||||
Returns the client API version the C API of the remote mpv instance
|
||||
provides.
|
||||
@@ -340,50 +242,4 @@ sometimes sends invalid JSON. If that is a problem for the client application's
|
||||
parser, it should filter the raw data for invalid UTF-8 sequences and perform
|
||||
the desired replacement, before feeding the data to its JSON parser.
|
||||
|
||||
mpv will not attempt to construct invalid UTF-8 with broken "\u" escape
|
||||
sequences. This includes surrogate pairs.
|
||||
|
||||
JSON extensions
|
||||
---------------
|
||||
|
||||
The following non-standard extensions are supported:
|
||||
|
||||
- a list or object item can have a trailing ","
|
||||
- object syntax accepts "=" in addition of ":"
|
||||
- object keys can be unquoted, if they start with a character in "A-Za-z\_"
|
||||
and contain only characters in "A-Za-z0-9\_"
|
||||
- byte escapes with "\xAB" are allowed (with AB being a 2 digit hex number)
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
{ objkey = "value\x0A" }
|
||||
|
||||
Is equivalent to:
|
||||
|
||||
::
|
||||
|
||||
{ "objkey": "value\n" }
|
||||
|
||||
Alternative ways of starting clients
|
||||
------------------------------------
|
||||
|
||||
You can create an anonymous IPC connection without having to set
|
||||
``--input-ipc-server``. This is achieved through a mpv pseudo scripting backend
|
||||
that starts processes.
|
||||
|
||||
You can put ``.run`` file extension in the mpv scripts directory in its config
|
||||
directory (see the `FILES`_ section for details), or load them through other
|
||||
means (see `Script location`_). These scripts are simply executed with the OS
|
||||
native mechanism (as if you ran them in the shell). They must have a proper
|
||||
shebang and have the executable bit set.
|
||||
|
||||
When executed, a socket (the IPC connection) is passed to them through file
|
||||
descriptor inheritance. The file descriptor is indicated as the special command
|
||||
line argument ``--mpv-ipc-fd=N``, where ``N`` is the numeric file descriptor.
|
||||
|
||||
The rest is the same as with a normal ``--input-ipc-server`` IPC connection. mpv
|
||||
does not attempt to observe or other interact with the started script process.
|
||||
|
||||
This does not work in Windows yet.
|
||||
mpv will not attempt to construct invalid UTF-8 with broken escape sequences.
|
||||
@@ -1,409 +0,0 @@
|
||||
JAVASCRIPT
|
||||
==========
|
||||
|
||||
JavaScript support in mpv is near identical to its Lua support. Use this section
|
||||
as reference on differences and availability of APIs, but otherwise you should
|
||||
refer to the Lua documentation for API details and general scripting in mpv.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
JavaScript code which leaves fullscreen mode when the player is paused:
|
||||
|
||||
::
|
||||
|
||||
function on_pause_change(name, value) {
|
||||
if (value == true)
|
||||
mp.set_property("fullscreen", "no");
|
||||
}
|
||||
mp.observe_property("pause", "bool", on_pause_change);
|
||||
|
||||
|
||||
Similarities with Lua
|
||||
---------------------
|
||||
|
||||
mpv tries to load a script file as JavaScript if it has a ``.js`` extension, but
|
||||
otherwise, the documented Lua options, script directories, loading, etc apply to
|
||||
JavaScript files too.
|
||||
|
||||
Script initialization and lifecycle is the same as with Lua, and most of the Lua
|
||||
functions in the modules ``mp``, ``mp.utils``, ``mp.msg``, ``mp.options`` and
|
||||
``mp.input`` are available to JavaScript with identical APIs - including running
|
||||
commands, getting/setting properties, registering events/key-bindings/hooks,
|
||||
etc.
|
||||
|
||||
Differences from Lua
|
||||
--------------------
|
||||
|
||||
No need to load modules. ``mp``, ``mp.utils``, ``mp.msg``, ``mp.options`` and
|
||||
``mp.input`` are preloaded, and you can use e.g. ``var cwd =
|
||||
mp.utils.getcwd();`` without prior setup.
|
||||
|
||||
Errors are slightly different. Where the Lua APIs return ``nil`` for error,
|
||||
the JavaScript ones return ``undefined``. Where Lua returns ``something, error``
|
||||
JavaScript returns only ``something`` - and makes ``error`` available via
|
||||
``mp.last_error()``. Note that only some of the functions have this additional
|
||||
``error`` value - typically the same ones which have it in Lua.
|
||||
|
||||
Standard APIs are preferred. For instance ``setTimeout`` and ``JSON.stringify``
|
||||
are available, but ``mp.add_timeout`` and ``mp.utils.format_json`` are not.
|
||||
|
||||
No standard library. This means that interaction with anything outside of mpv is
|
||||
limited to the available APIs, typically via ``mp.utils``. However, some file
|
||||
functions were added, and CommonJS ``require`` is available too - where the
|
||||
loaded modules have the same privileges as normal scripts.
|
||||
|
||||
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 .
|
||||
|
||||
Unsupported Lua APIs and their JS alternatives
|
||||
----------------------------------------------
|
||||
|
||||
``mp.add_timeout(seconds, fn)`` JS: ``id = setTimeout(fn, ms)``
|
||||
|
||||
``mp.add_periodic_timer(seconds, fn)`` JS: ``id = setInterval(fn, ms)``
|
||||
|
||||
``utils.parse_json(str [, trail])`` JS: ``JSON.parse(str)``
|
||||
|
||||
``utils.format_json(v)`` JS: ``JSON.stringify(v)``
|
||||
|
||||
``utils.to_string(v)`` see ``dump`` below.
|
||||
|
||||
``mp.get_next_timeout()`` see event loop below.
|
||||
|
||||
``mp.dispatch_events([allow_wait])`` see event loop below.
|
||||
|
||||
Scripting APIs - identical to Lua
|
||||
---------------------------------
|
||||
|
||||
(LE) - Last-Error, indicates that ``mp.last_error()`` can be used after the
|
||||
call to test for success (empty string) or failure (non empty reason string).
|
||||
Where the Lua APIs use ``nil`` to indicate error, JS APIs use ``undefined``.
|
||||
|
||||
``mp.command(string)`` (LE)
|
||||
|
||||
``mp.commandv(arg1, arg2, ...)`` (LE)
|
||||
|
||||
``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.
|
||||
|
||||
``mp.abort_async_command(id)``
|
||||
|
||||
``mp.del_property(name)`` (LE)
|
||||
|
||||
``mp.get_property(name [,def])`` (LE)
|
||||
|
||||
``mp.get_property_osd(name [,def])`` (LE)
|
||||
|
||||
``mp.get_property_bool(name [,def])`` (LE)
|
||||
|
||||
``mp.get_property_number(name [,def])`` (LE)
|
||||
|
||||
``mp.get_property_native(name [,def])`` (LE)
|
||||
|
||||
``mp.set_property(name, value)`` (LE)
|
||||
|
||||
``mp.set_property_bool(name, value)`` (LE)
|
||||
|
||||
``mp.set_property_number(name, value)`` (LE)
|
||||
|
||||
``mp.set_property_native(name, value)`` (LE)
|
||||
|
||||
``mp.get_time()``
|
||||
|
||||
``mp.add_key_binding(key, name|fn [,fn [,flags]])``
|
||||
|
||||
``mp.add_forced_key_binding(...)``
|
||||
|
||||
``mp.remove_key_binding(name)``
|
||||
|
||||
``mp.register_event(name, fn)``
|
||||
|
||||
``mp.unregister_event(fn)``
|
||||
|
||||
``mp.observe_property(name, type, fn)``
|
||||
|
||||
``mp.unobserve_property(fn)``
|
||||
|
||||
``mp.get_opt(key)``
|
||||
|
||||
``mp.get_script_name()``
|
||||
|
||||
``mp.get_script_directory()``
|
||||
|
||||
``mp.osd_message(text [,duration])``
|
||||
|
||||
``mp.get_wakeup_pipe()``
|
||||
|
||||
``mp.register_idle(fn)``
|
||||
|
||||
``mp.unregister_idle(fn)``
|
||||
|
||||
``mp.enable_messages(level)``
|
||||
|
||||
``mp.register_script_message(name, fn)``
|
||||
|
||||
``mp.unregister_script_message(name)``
|
||||
|
||||
``mp.create_osd_overlay(format)``
|
||||
|
||||
``mp.get_osd_size()`` (returned object has properties: width, height, aspect)
|
||||
|
||||
``mp.msg.log(level, ...)``
|
||||
|
||||
``mp.msg.fatal(...)``
|
||||
|
||||
``mp.msg.error(...)``
|
||||
|
||||
``mp.msg.warn(...)``
|
||||
|
||||
``mp.msg.info(...)``
|
||||
|
||||
``mp.msg.verbose(...)``
|
||||
|
||||
``mp.msg.debug(...)``
|
||||
|
||||
``mp.msg.trace(...)``
|
||||
|
||||
``mp.utils.getcwd()`` (LE)
|
||||
|
||||
``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.split_path(path)``
|
||||
|
||||
``mp.utils.join_path(p1, p2)``
|
||||
|
||||
``mp.utils.subprocess(t)``
|
||||
|
||||
``mp.utils.subprocess_detached(t)``
|
||||
|
||||
``mp.utils.get_env_list()``
|
||||
|
||||
``mp.utils.getpid()`` (LE)
|
||||
|
||||
``mp.add_hook(type, priority, fn(hook))``
|
||||
|
||||
``mp.options.read_options(obj [, identifier [, on_update]])`` (types:
|
||||
string/boolean/number)
|
||||
|
||||
``mp.input.get(obj)``
|
||||
|
||||
``mp.input.select(obj)``
|
||||
|
||||
``mp.input.terminate()``
|
||||
|
||||
``mp.input.log(message, style)``
|
||||
|
||||
``mp.input.log_error(message)``
|
||||
|
||||
``mp.input.set_log(log)``
|
||||
|
||||
``exit()`` (global)
|
||||
|
||||
Additional utilities
|
||||
--------------------
|
||||
|
||||
``mp.last_error()``
|
||||
If used after an API call which updates last error, returns an empty string
|
||||
if the API call succeeded, or a non-empty error reason string otherwise.
|
||||
|
||||
``Error.stack`` (string)
|
||||
When using ``try { ... } catch(e) { ... }``, then ``e.stack`` is the stack
|
||||
trace of the error - if it was created using the ``Error(...)`` constructor.
|
||||
|
||||
``print`` (global)
|
||||
A convenient alias to ``mp.msg.info``.
|
||||
|
||||
``dump`` (global)
|
||||
Like ``print`` but also expands objects and arrays recursively.
|
||||
|
||||
``mp.utils.getenv(name)``
|
||||
Returns the value of the host environment variable ``name``, or
|
||||
``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``.
|
||||
|
||||
``mp.utils.read_file(fname [,max])``
|
||||
Returns the content of file ``fname`` as string. If ``max`` is provided and
|
||||
not negative, limit the read to ``max`` bytes.
|
||||
|
||||
``mp.utils.write_file(fname, str)``
|
||||
(Over)write file ``fname`` with text content ``str``. ``fname`` must be
|
||||
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.
|
||||
|
||||
``mp.get_time_ms()``
|
||||
Same as ``mp.get_time()`` but in ms instead of seconds.
|
||||
|
||||
``mp.get_script_file()``
|
||||
Returns the file name of the current script.
|
||||
|
||||
``mp.utils.compile_js(fname, content_str)``
|
||||
Compiles the JS code ``content_str`` as file name ``fname`` (without loading
|
||||
anything from the filesystem), and returns it as a function. Very similar
|
||||
to a ``Function`` constructor, but shows at stack traces as ``fname``.
|
||||
|
||||
``mp.module_paths``
|
||||
Global modules search paths array for the ``require`` function (see below).
|
||||
|
||||
Timers (global)
|
||||
---------------
|
||||
|
||||
The standard HTML/node.js timers are available:
|
||||
|
||||
``id = setTimeout(fn [,duration [,arg1 [,arg2...]]])``
|
||||
|
||||
``id = setTimeout(code_string [,duration])``
|
||||
|
||||
``clearTimeout(id)``
|
||||
|
||||
``id = setInterval(fn [,duration [,arg1 [,arg2...]]])``
|
||||
|
||||
``id = setInterval(code_string [,duration])``
|
||||
|
||||
``clearInterval(id)``
|
||||
|
||||
``setTimeout`` and ``setInterval`` return id, and later call ``fn`` (or execute
|
||||
``code_string``) after ``duration`` ms. Interval also repeat every ``duration``.
|
||||
|
||||
``duration`` has a minimum and default value of 0, ``code_string`` is
|
||||
a plain string which is evaluated as JS code, and ``[,arg1 [,arg2..]]`` are used
|
||||
as arguments (if provided) when calling back ``fn``.
|
||||
|
||||
The ``clear...(id)`` functions cancel timer ``id``, and are irreversible.
|
||||
|
||||
Note: timers always call back asynchronously, e.g. ``setTimeout(fn)`` will never
|
||||
call ``fn`` before returning. ``fn`` will be called either at the end of this
|
||||
event loop iteration or at a later event loop iteration. This is true also for
|
||||
intervals - which also never call back twice at the same event loop iteration.
|
||||
|
||||
Additionally, timers are processed after the event queue is empty, so it's valid
|
||||
to use ``setTimeout(fn)`` as a one-time idle observer.
|
||||
|
||||
CommonJS modules and ``require(id)``
|
||||
------------------------------------
|
||||
|
||||
CommonJS Modules are a standard system where scripts can export common functions
|
||||
for use by other scripts. Specifically, a module is a script which adds
|
||||
properties (functions, etc) to its pre-existing ``exports`` object, which
|
||||
another script can access with ``require(module-id)``. This runs the module and
|
||||
returns its ``exports`` object. Further calls to ``require`` for the same module
|
||||
will return its cached ``exports`` object without running the module again.
|
||||
|
||||
Modules and ``require`` are supported, standard compliant, and generally similar
|
||||
to node.js. However, most node.js modules won't run due to missing modules such
|
||||
as ``fs``, ``process``, etc, but some node.js modules with minimal dependencies
|
||||
do work. In general, this is for mpv modules and not a node.js replacement.
|
||||
|
||||
A ``.js`` file extension is always added to ``id``, e.g. ``require("./foo")``
|
||||
will load the file ``./foo.js`` and return its ``exports`` object.
|
||||
|
||||
An id which starts with ``./`` or ``../`` is relative to the script or module
|
||||
which ``require`` it. Otherwise it's considered a top-level id (CommonJS term).
|
||||
|
||||
Top-level id is evaluated as absolute filesystem path if possible, e.g. ``/x/y``
|
||||
or ``~/x``. Otherwise it's considered a global module id and searched according
|
||||
to ``mp.module_paths`` in normal array order, e.g. ``require("x")`` tries to
|
||||
load ``x.js`` at one of the array paths, and id ``foo/x`` tries to load ``x.js``
|
||||
inside dir ``foo`` at one of the paths.
|
||||
|
||||
The ``mp.module_paths`` array is empty by default except for scripts which are
|
||||
loaded as a directory where it contains one item - ``<directory>/modules/`` .
|
||||
The array may be updated from a script (or using custom init - see below) which
|
||||
will affect future calls to ``require`` for global module id's which are not
|
||||
already loaded/cached.
|
||||
|
||||
No ``global`` variable, but a module's ``this`` at its top lexical scope is the
|
||||
global object - also in strict mode. If you have a module which needs ``global``
|
||||
as the global object, you could do ``this.global = this;`` before ``require``.
|
||||
|
||||
Functions and variables declared at a module don't pollute the global object.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
The event loop
|
||||
--------------
|
||||
|
||||
The event loop poll/dispatch mpv events as long as the queue is not empty, then
|
||||
processes the timers, then waits for the next event, and repeats this forever.
|
||||
|
||||
You could put this code at your script to replace the built-in event loop, and
|
||||
also print every event which mpv sends to your script:
|
||||
|
||||
::
|
||||
|
||||
function mp_event_loop() {
|
||||
var wait = 0;
|
||||
do {
|
||||
var e = mp.wait_event(wait);
|
||||
dump(e); // there could be a lot of prints...
|
||||
if (e.event != "none") {
|
||||
mp.dispatch_event(e);
|
||||
wait = 0;
|
||||
} else {
|
||||
wait = mp.process_timers() / 1000;
|
||||
if (wait != 0) {
|
||||
mp.notify_idle_observers();
|
||||
wait = mp.peek_timers_wait() / 1000;
|
||||
}
|
||||
}
|
||||
} while (mp.keep_running);
|
||||
}
|
||||
|
||||
|
||||
``mp_event_loop`` is a name which mpv tries to call after the script loads.
|
||||
The internal implementation is similar to this (without ``dump`` though..).
|
||||
|
||||
``e = mp.wait_event(wait)`` returns when the next mpv event arrives, or after
|
||||
``wait`` seconds if positive and no mpv events arrived. ``wait`` value of 0
|
||||
returns immediately (with ``e.event == "none"`` if the queue is empty).
|
||||
|
||||
``mp.dispatch_event(e)`` calls back the handlers registered for ``e.event``,
|
||||
if there are such (event handlers, property observers, script messages, etc).
|
||||
|
||||
``mp.process_timers()`` calls back the already-added, non-canceled due timers,
|
||||
and returns the duration in ms till the next due timer (possibly 0), or -1 if
|
||||
there are no pending timers. Must not be called recursively.
|
||||
|
||||
``mp.notify_idle_observers()`` calls back the idle observers, which we do when
|
||||
we're about to sleep (wait != 0), but the observers may add timers or take
|
||||
non-negligible duration to complete, so we re-calculate ``wait`` afterwards.
|
||||
|
||||
``mp.peek_timers_wait()`` returns the same values as ``mp.process_timers()``
|
||||
but without doing anything. Invalid result if called from a timer callback.
|
||||
|
||||
Note: ``exit()`` is also registered for the ``shutdown`` event, and its
|
||||
implementation is a simple ``mp.keep_running = false``.
|
||||
@@ -1,80 +0,0 @@
|
||||
EMBEDDING INTO OTHER PROGRAMS (LIBMPV)
|
||||
======================================
|
||||
|
||||
mpv can be embedded into other programs as video/audio playback backend. The
|
||||
recommended way to do so is using libmpv. See ``include/mpv/client.h`` in the mpv
|
||||
source code repository. This provides a C API. Bindings for other languages
|
||||
might be available (see wiki).
|
||||
|
||||
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/include/mpv/client.h
|
||||
- https://mpv.io/manual/master/#options
|
||||
- https://mpv.io/manual/master/#list-of-input-commands
|
||||
- https://mpv.io/manual/master/#properties
|
||||
- https://github.com/mpv-player/mpv-examples/tree/master/libmpv
|
||||
|
||||
C PLUGINS
|
||||
=========
|
||||
|
||||
You can write C plugins for mpv. These use the libmpv API, although they do not
|
||||
use the libmpv library itself.
|
||||
|
||||
They are enabled by default if compiler supports linking with the ``-rdynamic``
|
||||
flag on Linux/BSD platforms. On Windows the are always enabled.
|
||||
|
||||
C plugins location
|
||||
------------------
|
||||
|
||||
C plugins are put into the mpv scripts directory in its config directory
|
||||
(see the `FILES`_ section for details). They must have a ``.so`` or ``.dll``
|
||||
file extension. They can also be explicitly loaded with the ``--script`` option.
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
A C plugin must export the following function::
|
||||
|
||||
int mpv_open_cplugin(mpv_handle *handle)
|
||||
|
||||
The plugin function will be called on loading time. This function must not
|
||||
return as long as your plugin is loaded (it runs in its own thread). The
|
||||
``handle`` will be deallocated as soon as the plugin function returns.
|
||||
|
||||
The return value is interpreted as error status. A value of ``0`` is
|
||||
interpreted as success, while ``-1`` signals an error. In the latter case,
|
||||
the player prints an uninformative error message that loading failed.
|
||||
|
||||
Return values other than ``0`` and ``-1`` are reserved, and trigger undefined
|
||||
behavior.
|
||||
|
||||
Within the plugin function, you can call libmpv API functions. The ``handle``
|
||||
is created by ``mpv_create_client()`` (or actually an internal equivalent),
|
||||
and belongs to you. You can call ``mpv_wait_event()`` to wait for things
|
||||
happening, and so on. However do not call ``mpv_destroy()`` or
|
||||
``mpv_terminate_destroy()`` on this handle.
|
||||
|
||||
Note that the player might block until your plugin calls ``mpv_wait_event()``
|
||||
for the first time. This gives you a chance to install initial hooks etc.
|
||||
before playback begins.
|
||||
|
||||
The details are quite similar to Lua scripts.
|
||||
|
||||
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
|
||||
symbols from the mpv host binary.
|
||||
|
||||
On Windows to make symbols from the host binary available, you have to define
|
||||
MPV_CPLUGIN_DYNAMIC_SYM when compiling cplugin. This will load symbols
|
||||
dynamically, before calling ``mpv_open_cplugin()``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
See:
|
||||
|
||||
- https://github.com/mpv-player/mpv-examples/tree/master/cplugins
|
||||
+223
-507
@@ -1,12 +1,18 @@
|
||||
LUA SCRIPTING
|
||||
=============
|
||||
|
||||
mpv can load Lua scripts. (See `Script location`_.)
|
||||
mpv can load Lua scripts. Scripts passed to the ``--script`` option, or found in
|
||||
the ``scripts`` subdirectory of the mpv configuration directory (usually
|
||||
``~/.config/mpv/scripts/``) will be loaded on program start. mpv also appends the
|
||||
``scripts`` subdirectory to the end of Lua's path so you can import scripts from
|
||||
there too. Since it's added to the end, don't name scripts you want to import
|
||||
the same as Lua libraries because they will be overshadowed by them.
|
||||
|
||||
mpv provides the built-in module ``mp``, which contains functions to send
|
||||
commands to the mpv core and to retrieve information about playback state, user
|
||||
settings, file information, and so on.
|
||||
|
||||
These scripts can be used to control mpv in a similar way to slave mode.
|
||||
Technically, the Lua code uses the client API internally.
|
||||
|
||||
Example
|
||||
@@ -24,55 +30,6 @@ A script which leaves fullscreen mode when the player is paused:
|
||||
mp.observe_property("pause", "bool", on_pause_change)
|
||||
|
||||
|
||||
Script location
|
||||
---------------
|
||||
|
||||
Scripts can be passed to the ``--script`` option, and are automatically loaded
|
||||
from the ``scripts`` subdirectory of the mpv configuration directory (usually
|
||||
``~/.config/mpv/scripts/``).
|
||||
|
||||
A script can be a single file. The file extension is used to select the
|
||||
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``,
|
||||
or any sub-directories in the script directory, such as for example
|
||||
``~/.config/mpv/scripts/something/``), then the directory represents a single
|
||||
script. The player will try to load a file named ``main.x``, where ``x`` is
|
||||
replaced with the file extension. For example, if ``main.lua`` exists, it is
|
||||
loaded with the Lua scripting backend.
|
||||
|
||||
You must not put any other files or directories that start with ``main.`` into
|
||||
the script's top level directory. If the script directory contains for example
|
||||
both ``main.lua`` and ``main.js``, only one of them will be loaded (and which
|
||||
one depends on mpv internals that may change any time). Likewise, if there is
|
||||
for example ``main.foo``, your script will break as soon as mpv adds a backend
|
||||
that uses the ``.foo`` file extension.
|
||||
|
||||
mpv also appends the top level directory of the script to the start of Lua's
|
||||
package path so you can import scripts from there too. Be aware that this will
|
||||
shadow Lua libraries that use the same package path. (Single file scripts do not
|
||||
include mpv specific directories in the Lua package path. This was silently
|
||||
changed in mpv 0.32.0.)
|
||||
|
||||
Using a script directory is the recommended way to package a script that
|
||||
consists of multiple source files, or requires other files (you can use
|
||||
``mp.get_script_directory()`` to get the location and e.g. load data files).
|
||||
|
||||
Making a script a git repository, basically a repository which contains a
|
||||
``main.lua`` file in the root directory, makes scripts easily updateable
|
||||
(without the dangers of auto-updates). Another suggestion is to use git
|
||||
submodules to share common files or libraries.
|
||||
|
||||
Details on the script initialization and lifecycle
|
||||
--------------------------------------------------
|
||||
|
||||
@@ -82,16 +39,13 @@ option. Some scripts are loaded internally (like ``--osc``). Each script runs in
|
||||
its own thread. Your script is first run "as is", and once that is done, the event loop
|
||||
is entered. This event loop will dispatch events received by mpv and call your
|
||||
own event handlers which you have registered with ``mp.register_event``, or
|
||||
timers added with ``mp.add_timeout`` or similar. Note that since the
|
||||
script starts execution concurrently with player initialization, some properties
|
||||
may not be populated with meaningful values until the relevant subsystems have
|
||||
initialized. Rather than retrieving these properties at the top of scripts, you
|
||||
should use ``mp.observe_property`` or read them within event handlers.
|
||||
timers added with ``mp.add_timeout`` or similar.
|
||||
|
||||
When the player quits, all scripts will be asked to terminate. This happens via
|
||||
a ``shutdown`` event, which by default will make the event loop return. If your
|
||||
script got into an endless loop, mpv will probably behave fine during playback,
|
||||
but it won't terminate when quitting, because it's waiting on your script.
|
||||
script got into an endless loop, mpv will probably behave fine during playback
|
||||
(unless the player is suspended, see ``mp.suspend``), but it won't terminate
|
||||
when quitting, because it's waiting on your script.
|
||||
|
||||
Internally, the C code will call the Lua function ``mp_event_loop`` after
|
||||
loading a Lua script. This function is normally defined by the default prelude
|
||||
@@ -107,10 +61,7 @@ the player will more or less hang until the script returns from the main chunk
|
||||
(and ``mp_event_loop`` is called), or the script calls ``mp_event_loop`` or
|
||||
``mp.dispatch_events`` directly. This is done to make it possible for a script
|
||||
to fully setup event handlers etc. before playback actually starts. In older
|
||||
mpv versions, this happened asynchronously. With mpv 0.29.0, this changes
|
||||
slightly, and it merely waits for scripts to be loaded in this manner before
|
||||
starting playback as part of the player initialization phase. Scripts run though
|
||||
initialization in parallel. This might change again.
|
||||
mpv versions, this happened asynchronously.
|
||||
|
||||
mp functions
|
||||
------------
|
||||
@@ -153,58 +104,12 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
``mp.command_native(table [,def])``
|
||||
Similar to ``mp.commandv``, but pass the argument list as table. This has
|
||||
the advantage that in at least some cases, arguments can be passed as
|
||||
native types. It also allows you to use named argument.
|
||||
|
||||
If the table is an array, each array item is like an argument in
|
||||
``mp.commandv()`` (but can be a native type instead of a string).
|
||||
|
||||
If the table contains string keys, it's interpreted as command with named
|
||||
arguments. This requires at least an entry with the key ``name`` to be
|
||||
present, which must be a string, and contains the command name. The special
|
||||
entry ``_flags`` is optional, and if present, must be an array of
|
||||
`Input Command Prefixes`_ to apply. All other entries are interpreted as
|
||||
arguments.
|
||||
native types.
|
||||
|
||||
Returns a result table on success (usually empty), or ``def, error`` on
|
||||
error. ``def`` is the second parameter provided to the function, and is
|
||||
nil if it's missing.
|
||||
|
||||
``mp.command_native_async(table [,fn])``
|
||||
Like ``mp.command_native()``, but the command is ran asynchronously (as far
|
||||
as possible), and upon completion, fn is called. fn has three arguments:
|
||||
``fn(success, result, error)``:
|
||||
|
||||
``success``
|
||||
Always a Boolean and is true if the command was successful,
|
||||
otherwise false.
|
||||
|
||||
``result``
|
||||
The result value (can be nil) in case of success, nil otherwise (as
|
||||
returned by ``mp.command_native()``).
|
||||
|
||||
``error``
|
||||
The error string in case of an error, nil otherwise.
|
||||
|
||||
Returns a table with undefined contents, which can be used as argument for
|
||||
``mp.abort_async_command``.
|
||||
|
||||
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)``
|
||||
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
|
||||
@@ -270,7 +175,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
Similar to ``mp.set_property``, but set the given property using its native
|
||||
type.
|
||||
|
||||
Since there are several data types which cannot represented natively in
|
||||
Since there are several data types which can not represented natively in
|
||||
Lua, this might not always work as expected. For example, while the Lua
|
||||
wrapper can do some guesswork to decide whether a Lua table is an array
|
||||
or a map, this would fail with empty tables. Also, there are not many
|
||||
@@ -287,23 +192,10 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
Register callback to be run on a key binding. The binding will be mapped to
|
||||
the given ``key``, which is a string describing the physical key. This uses
|
||||
the same key names as in input.conf, and also allows combinations
|
||||
(e.g. ``ctrl+a``). If the key is empty or ``nil``, no physical key is
|
||||
registered, but the user still can create own bindings (see below).
|
||||
(e.g. ``ctrl+a``).
|
||||
|
||||
After calling this function, key presses will cause the function ``fn`` to
|
||||
be called (unless the user remapped the key with another binding).
|
||||
However, if the key binding is canceled , the function will not be called,
|
||||
unless ``complex`` flag is set to ``true``, where the function will be
|
||||
called with the ``canceled`` entry set to ``true``.
|
||||
|
||||
For example, a canceled key binding can happen in the following situations:
|
||||
|
||||
- If key A is pressed while key B is being held down, key B is logically
|
||||
released ("canceled" by key A), which stops the current autorepeat
|
||||
action key B has.
|
||||
- If key A is pressed while a mouse button is being held down, the mouse
|
||||
button is logically released, but the mouse button's action will not be
|
||||
called, unless ``complex`` flag is set to ``true``.
|
||||
|
||||
The ``name`` argument should be a short symbolic string. It allows the user
|
||||
to remap the key binding via input.conf using the ``script-message``
|
||||
@@ -311,59 +203,22 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
an example). The name should be unique across other bindings in the same
|
||||
script - if not, the previous binding with the same name will be
|
||||
overwritten. You can omit the name, in which case a random name is generated
|
||||
internally. (Omitting works as follows: either pass ``nil`` for ``name``,
|
||||
or pass the ``fn`` argument in place of the name. The latter is not
|
||||
recommended and is handled for compatibility only.)
|
||||
internally.
|
||||
|
||||
The ``flags`` argument is used for optional parameters. This is a table,
|
||||
which can have the following entries:
|
||||
The last argument is used for optional flags. This is a table, which can
|
||||
have the following entries:
|
||||
|
||||
``repeatable``
|
||||
If set to ``true``, enables key repeat for this specific binding.
|
||||
This option only makes sense when ``complex`` is not set to ``true``.
|
||||
|
||||
``scalable``
|
||||
If set to ``true``, enables key scaling for this specific binding.
|
||||
This option only makes sense when ``complex`` is set to ``true``.
|
||||
Note that this has no effect if the key binding is invoked by
|
||||
``script-binding`` command, where the scalability of the command
|
||||
takes precedence.
|
||||
|
||||
``complex``
|
||||
If set to ``true``, then ``fn`` is called on key down, repeat and up
|
||||
events, with the first argument being a table. This table has the
|
||||
following entries (and may contain undocumented ones):
|
||||
|
||||
``event``
|
||||
Set to one of the strings ``down``, ``repeat``, ``up`` or
|
||||
``press`` (the latter if key up/down/repeat can't be
|
||||
tracked), which indicates the key's logical state.
|
||||
|
||||
``is_mouse``
|
||||
Boolean: Whether the event was caused by a mouse button.
|
||||
|
||||
``canceled``
|
||||
Boolean: Whether the event was canceled.
|
||||
Not all types of cancellations set this flag.
|
||||
|
||||
``key_name``
|
||||
The name of they key that triggered this, or ``nil`` if
|
||||
invoked artificially. If the key name is unknown, it's an
|
||||
empty string.
|
||||
|
||||
``key_text``
|
||||
Text if triggered by a text key, otherwise ``nil``. See
|
||||
description of ``script-binding`` command for details (this
|
||||
field is equivalent to the 5th argument).
|
||||
|
||||
``scale``
|
||||
The scale of the key, such as the ones produced by ``WHEEL_*``
|
||||
keys. The scale is 1 if the key is nonscalable.
|
||||
|
||||
``arg``
|
||||
User-provided string in the ``arg`` argument in the
|
||||
``script-binding`` command if the key binding is invoked
|
||||
by that command.
|
||||
If set to ``true``, then ``fn`` is called on both key up and down
|
||||
events (as well as key repeat, if enabled), with the first
|
||||
argument being a table. This table has an ``event`` entry, which
|
||||
is set to one of the strings ``down``, ``repeat``, ``up`` or
|
||||
``press`` (the latter if key up/down can't be tracked). It further
|
||||
has an ``is_mouse`` entry, which tells whether the event was caused
|
||||
by a mouse button.
|
||||
|
||||
Internally, key bindings are dispatched via the ``script-message-to`` or
|
||||
``script-binding`` input commands and ``mp.register_script_message``.
|
||||
@@ -385,7 +240,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
This will print the message ``the key was pressed`` when ``x`` was pressed.
|
||||
|
||||
The user can remap these key bindings. Then the user has to put the
|
||||
following into their input.conf to remap the command to the ``y`` key:
|
||||
following into his input.conf to remap the command to the ``y`` key:
|
||||
|
||||
::
|
||||
|
||||
@@ -400,11 +255,11 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
|
||||
::
|
||||
|
||||
y script-binding fooscript/something
|
||||
y script-binding fooscript.something
|
||||
|
||||
``mp.add_forced_key_binding(...)``
|
||||
This works almost the same as ``mp.add_key_binding``, but registers the
|
||||
key binding in a way that will overwrite the user's custom bindings in their
|
||||
key binding in a way that will overwrite the user's custom bindings in his
|
||||
input.conf. (``mp.add_key_binding`` overwrites default key bindings only,
|
||||
but not those by the user's input.conf.)
|
||||
|
||||
@@ -445,52 +300,39 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
the property will be passed as second argument to ``fn``, using
|
||||
``mp.get_property_<type>`` to retrieve it. This means if ``type`` is for
|
||||
example ``string``, ``fn`` is roughly called as in
|
||||
``fn(name, mp.get_property(name))``.
|
||||
``fn(name, mp.get_property_string(name))``.
|
||||
|
||||
If possible, change events are coalesced. If a property is changed a bunch
|
||||
of times in a row, only the last change triggers the change function. (The
|
||||
exact behavior depends on timing and other things.)
|
||||
|
||||
If a property is unavailable, or on error, the value argument to ``fn`` is
|
||||
``nil``. (The ``observe_property()`` call always succeeds, even if a
|
||||
property does not exist.)
|
||||
|
||||
In some cases the function is not called even if the property changes.
|
||||
This depends on the property, and it's a valid feature request to ask for
|
||||
better update handling of a specific property.
|
||||
Whether this can happen depends on the property.
|
||||
|
||||
If the ``type`` is ``none`` or ``nil``, the change function ``fn`` will be
|
||||
called sporadically even if the property doesn't actually change. You should
|
||||
therefore avoid using these types.
|
||||
|
||||
You always get an initial change notification. This is meant to initialize
|
||||
the user's state to the current value of the property.
|
||||
If the ``type`` is ``none`` or ``nil``, sporadic property change events are
|
||||
possible. This means the change function ``fn`` can be called even if the
|
||||
property doesn't actually change.
|
||||
|
||||
``mp.unobserve_property(fn)``
|
||||
Undo ``mp.observe_property(..., fn)``. This removes all property handlers
|
||||
that are equal to the ``fn`` parameter. This uses normal Lua ``==``
|
||||
comparison, so be careful when dealing with closures.
|
||||
|
||||
``mp.add_timeout(seconds, fn [, disabled])``
|
||||
``mp.add_timeout(seconds, fn)``
|
||||
Call the given function fn when the given number of seconds has elapsed.
|
||||
Note that the number of seconds can be fractional. For now, the timer's
|
||||
resolution may be as low as 50 ms, although this will be improved in the
|
||||
future.
|
||||
|
||||
If the ``disabled`` argument is set to ``true`` or a truthy value, the
|
||||
timer will wait to be manually started with a call to its ``resume()``
|
||||
method.
|
||||
|
||||
This is a one-shot timer: it will be removed when it's fired.
|
||||
|
||||
Returns a timer object. See ``mp.add_periodic_timer`` for details.
|
||||
|
||||
``mp.add_periodic_timer(seconds, fn [, disabled])``
|
||||
``mp.add_periodic_timer(seconds, fn)``
|
||||
Call the given function periodically. This is like ``mp.add_timeout``, but
|
||||
the timer is re-added after the function fn is run.
|
||||
|
||||
Returns a timer object. The timer object provides the following methods:
|
||||
|
||||
``stop()``
|
||||
Disable the timer. Does nothing if the timer is already disabled.
|
||||
This will remember the current elapsed time when stopping, so that
|
||||
@@ -507,10 +349,6 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
with ``add_timeout()``), this starts the timer from the beginning,
|
||||
using the initially configured timeout.
|
||||
|
||||
``is_enabled()``
|
||||
Whether the timer is currently enabled or was previously disabled
|
||||
(e.g. by ``stop()`` or ``kill()``).
|
||||
|
||||
``timeout`` (RW)
|
||||
This field contains the current timeout period. This value is not
|
||||
updated as time progresses. It's only used to calculate when the
|
||||
@@ -525,8 +363,8 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
(``true``). This value is used when the timer expires (but before
|
||||
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 .)
|
||||
Note that these are method, and you have to call them using ``:`` instead
|
||||
of ``.`` (Refer to http://www.lua.org/manual/5.2/manual.html#3.4.9 .)
|
||||
|
||||
Example:
|
||||
|
||||
@@ -535,7 +373,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
seconds = 0
|
||||
timer = mp.add_periodic_timer(1, function()
|
||||
print("called every second")
|
||||
-- stop it after 10 seconds
|
||||
# stop it after 10 seconds
|
||||
seconds = seconds + 1
|
||||
if seconds >= 10 then
|
||||
timer:kill()
|
||||
@@ -552,16 +390,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``.
|
||||
|
||||
``mp.get_script_directory()``
|
||||
Return the directory if this is a script packaged as directory (see
|
||||
`Script location`_ for a description). Return nothing if this is a single
|
||||
file script.
|
||||
The script ``/path/to/fooscript.lua`` becomes ``fooscript``.
|
||||
|
||||
``mp.osd_message(text [,duration])``
|
||||
Show an OSD message on the screen. ``duration`` is in seconds, and is
|
||||
@@ -573,9 +406,30 @@ Advanced mp functions
|
||||
These also live in the ``mp`` module, but are documented separately as they
|
||||
are useful only in special situations.
|
||||
|
||||
``mp.suspend()``
|
||||
Suspend the mpv main loop. There is a long-winded explanation of this in
|
||||
the C API function ``mpv_suspend()``. In short, this prevents the player
|
||||
from displaying the next video frame, so that you don't get blocked when
|
||||
trying to access the player.
|
||||
|
||||
This is automatically called by the event handler.
|
||||
|
||||
``mp.resume()``
|
||||
Undo one ``mp.suspend()`` call. ``mp.suspend()`` increments an internal
|
||||
counter, and ``mp.resume()`` decrements it. When 0 is reached, the player
|
||||
is actually resumed.
|
||||
|
||||
``mp.resume_all()``
|
||||
This resets the internal suspend counter and resumes the player. (It's
|
||||
like calling ``mp.resume()`` until the player is actually resumed.)
|
||||
|
||||
You might want to call this if you're about to do something that takes a
|
||||
long time, but doesn't really need access to the player (like a network
|
||||
operation). Note that you still can access the player at any time.
|
||||
|
||||
``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.)
|
||||
pipe. (See ``client.h`` for details.)
|
||||
|
||||
``mp.get_next_timeout()``
|
||||
Return the relative time in seconds when the next timer (``mp.add_timeout``
|
||||
@@ -595,17 +449,7 @@ are useful only in special situations.
|
||||
``mp.get_wakeup_pipe()`` if you're interested in properly working
|
||||
notification of new events and working timers.
|
||||
|
||||
``mp.register_idle(fn)``
|
||||
Register an event loop idle handler. Idle handlers are called before the
|
||||
script goes to sleep after handling all new events. This can be used for
|
||||
example to delay processing of property change events: if you're observing
|
||||
multiple properties at once, you might not want to act on each property
|
||||
change, but only when all change notifications have been received.
|
||||
|
||||
``mp.unregister_idle(fn)``
|
||||
Undo ``mp.register_idle(fn)``. This removes all idle handlers that
|
||||
are equal to the ``fn`` parameter. This uses normal Lua ``==`` comparison,
|
||||
so be careful when dealing with closures.
|
||||
This function calls ``mp.suspend()`` and ``mp.resume_all()`` on its own.
|
||||
|
||||
``mp.enable_messages(level)``
|
||||
Set the minimum log level of which mpv message output to receive. These
|
||||
@@ -615,9 +459,9 @@ are useful only in special situations.
|
||||
The level is a string, see ``msg.log`` for allowed log levels.
|
||||
|
||||
``mp.register_script_message(name, fn)``
|
||||
This is a helper to dispatch ``script-message`` or ``script-message-to``
|
||||
invocations to Lua functions. ``fn`` is called if ``script-message`` or
|
||||
``script-message-to`` (with this script as destination) is run
|
||||
This is a helper to dispatch ``script_message`` or ``script_message_to``
|
||||
invocations to Lua functions. ``fn`` is called if ``script_message`` or
|
||||
``script_message_to`` (with this script as destination) is run
|
||||
with ``name`` as first parameter. The other parameters are passed to ``fn``.
|
||||
If a message with the given name is already registered, it's overwritten.
|
||||
|
||||
@@ -627,56 +471,6 @@ are useful only in special situations.
|
||||
Undo a previous registration with ``mp.register_script_message``. Does
|
||||
nothing if the ``name`` wasn't registered.
|
||||
|
||||
``mp.create_osd_overlay(format)``
|
||||
Create an OSD overlay. This is a very thin wrapper around the ``osd-overlay``
|
||||
command. The function returns a table, which mostly contains fields that
|
||||
will be passed to ``osd-overlay``. The ``format`` parameter is used to
|
||||
initialize the ``format`` field. The ``data`` field contains the text to
|
||||
be used as overlay. For details, see the ``osd-overlay`` command.
|
||||
|
||||
In addition, it provides the following methods:
|
||||
|
||||
``update()``
|
||||
Commit the OSD overlay to the screen, or in other words, run the
|
||||
``osd-overlay`` command with the current fields of the overlay table.
|
||||
Returns the result of the ``osd-overlay`` command itself.
|
||||
|
||||
``remove()``
|
||||
Remove the overlay from the screen. A ``update()`` call will add it
|
||||
again.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
ov = mp.create_osd_overlay("ass-events")
|
||||
ov.data = "{\\an5}{\\b1}hello world!"
|
||||
ov:update()
|
||||
|
||||
The advantage of using this wrapper (as opposed to running ``osd-overlay``
|
||||
directly) is that the ``id`` field is allocated automatically.
|
||||
|
||||
``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
|
||||
be "scaled" pixels). The third is the display pixel aspect ratio.
|
||||
|
||||
May return invalid/nonsense values if OSD is not initialized yet.
|
||||
|
||||
``exit()`` (global)
|
||||
Make the script exit at the end of the current event loop iteration. This
|
||||
does not terminate mpv itself or other scripts.
|
||||
|
||||
This can be polyfilled to support mpv versions older than 0.40 with:
|
||||
|
||||
::
|
||||
|
||||
if not _G.exit then
|
||||
function exit()
|
||||
mp.keep_running = false
|
||||
end
|
||||
end
|
||||
|
||||
mp.msg functions
|
||||
----------------
|
||||
|
||||
@@ -685,17 +479,16 @@ with ``require 'mp.msg'``.
|
||||
|
||||
``msg.log(level, ...)``
|
||||
The level parameter is the message priority. It's a string and one of
|
||||
``fatal``, ``error``, ``warn``, ``info``, ``v``, ``debug``, ``trace``. The
|
||||
user's settings will determine which of these messages will be
|
||||
visible. Normally, all messages are visible, except ``v``, ``debug`` and
|
||||
``trace``.
|
||||
``fatal``, ``error``, ``warn``, ``info``, ``v``, ``debug``. The user's
|
||||
settings will determine which of these messages will be visible. Normally,
|
||||
all messages are visible, except ``v`` and ``debug``.
|
||||
|
||||
The parameters after that are all converted to strings. Spaces are inserted
|
||||
to separate multiple parameters.
|
||||
|
||||
You don't need to add newlines.
|
||||
|
||||
``msg.fatal(...)``, ``msg.error(...)``, ``msg.warn(...)``, ``msg.info(...)``, ``msg.verbose(...)``, ``msg.debug(...)``, ``msg.trace(...)``
|
||||
``msg.fatal(...)``, ``msg.error(...)``, ``msg.warn(...)``, ``msg.info(...)``, ``msg.verbose(...)``, ``msg.debug(...)``
|
||||
All of these are shortcuts and equivalent to the corresponding
|
||||
``msg.log(level, ...)`` call.
|
||||
|
||||
@@ -707,42 +500,29 @@ command-line. All you have to do is to supply a table with default options to
|
||||
the read_options function. The function will overwrite the default values
|
||||
with values found in the config-file and the command-line (in that order).
|
||||
|
||||
``options.read_options(table [, identifier [, on_update]])``
|
||||
``options.read_options(table [, identifier])``
|
||||
A ``table`` with key-value pairs. The type of the default values is
|
||||
important for converting the values read from the config file or
|
||||
command-line back. Do not use ``nil`` as a default value!
|
||||
|
||||
The ``identifier`` is used to identify the config-file and the command-line
|
||||
options. These needs to unique to avoid collisions with other scripts.
|
||||
Defaults to ``mp.get_script_name()`` if the parameter is ``nil`` or missing.
|
||||
|
||||
The ``on_update`` parameter enables run-time updates of all matching option
|
||||
values via the ``script-opts`` option/property. If any of the matching
|
||||
options changes, the values in the ``table`` (which was originally passed to
|
||||
the function) are changed, and ``on_update(list)`` is called. ``list`` is
|
||||
a table where each updated option has a ``list[option_name] = true`` entry.
|
||||
There is no initial ``on_update()`` call. This never re-reads the config file.
|
||||
``script-opts`` is always applied on the original config file, ignoring
|
||||
previous ``script-opts`` values (for example, if an option is removed from
|
||||
``script-opts`` at runtime, the option will have the value in the config
|
||||
file). ``table`` entries are only written for option values whose values
|
||||
effectively change (this is important if the script changes ``table``
|
||||
entries independently).
|
||||
Defaults to ``mp.get_script_name()``.
|
||||
|
||||
|
||||
Example implementation::
|
||||
|
||||
require 'mp.options'
|
||||
local options = {
|
||||
optionA = "defaultvalueA",
|
||||
optionB = -0.5,
|
||||
optionC = true,
|
||||
}
|
||||
|
||||
require "mp.options".read_options(options, "myscript")
|
||||
read_options(options, "myscript")
|
||||
print(options.optionA)
|
||||
|
||||
|
||||
The config file will be stored in ``script-opts/identifier.conf`` in mpv's user
|
||||
The config file will be stored in ``lua-settings/identifier.conf`` in mpv's user
|
||||
folder. Comment lines can be started with # and stray spaces are not removed.
|
||||
Boolean values will be represented with yes/no.
|
||||
|
||||
@@ -762,8 +542,8 @@ Example command-line::
|
||||
--script-opts=myscript-optionA=TEST,myscript-optionB=0,myscript-optionC=yes
|
||||
|
||||
|
||||
mp.utils functions
|
||||
------------------
|
||||
mp.utils options
|
||||
----------------
|
||||
|
||||
This built-in module provides generic helper functions for Lua, and have
|
||||
strictly speaking nothing to do with mpv or video/audio playback. They are
|
||||
@@ -803,34 +583,6 @@ strictly part of the guaranteed API.
|
||||
|
||||
On error, ``nil, error`` is returned.
|
||||
|
||||
``utils.file_info(path)``
|
||||
Stats the given path for information and returns a table with the
|
||||
following entries:
|
||||
|
||||
``mode``
|
||||
protection bits (on Windows, always 755 (octal) for directories
|
||||
and 644 (octal) for files)
|
||||
``size``
|
||||
size in bytes
|
||||
``atime``
|
||||
time of last access
|
||||
``mtime``
|
||||
time of last modification
|
||||
``ctime``
|
||||
time of last metadata change
|
||||
``is_file``
|
||||
Whether ``path`` is a regular file (boolean)
|
||||
``is_dir``
|
||||
Whether ``path`` is a directory (boolean)
|
||||
|
||||
``mode`` and ``size`` are integers.
|
||||
Timestamps (``atime``, ``mtime`` and ``ctime``) are integer seconds since
|
||||
the Unix epoch (Unix time).
|
||||
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.
|
||||
|
||||
``utils.split_path(path)``
|
||||
Split a path into directory component and filename component, and return
|
||||
them. The first return value is always the directory. The second return
|
||||
@@ -838,51 +590,51 @@ strictly part of the guaranteed API.
|
||||
|
||||
``utils.join_path(p1, p2)``
|
||||
Return the concatenation of the 2 paths. Tries to be clever. For example,
|
||||
if ``p2`` is an absolute path, ``p2`` is returned without change.
|
||||
if ```p2`` is an absolute path, p2 is returned without change.
|
||||
|
||||
``utils.subprocess(t)``
|
||||
Runs an external process and waits until it exits. Returns process status
|
||||
and the captured output. This is a legacy wrapper around calling the
|
||||
``subprocess`` command with ``mp.command_native``. It does the following
|
||||
things:
|
||||
|
||||
- copy the table ``t``
|
||||
- rename ``cancellable`` field to ``playback_only``
|
||||
- rename ``max_size`` to ``capture_size``
|
||||
- set ``capture_stdout`` field to ``true`` if unset
|
||||
- set ``name`` field to ``subprocess``
|
||||
- call ``mp.command_native(copied_t)``
|
||||
- if the command failed, create a dummy result table
|
||||
- copy ``error_string`` to ``error`` field if the string is non-empty
|
||||
- return the result table
|
||||
|
||||
It is recommended to use ``mp.command_native`` or ``mp.command_native_async``
|
||||
directly, instead of calling this legacy wrapper. It is for compatibility
|
||||
only.
|
||||
|
||||
See the ``subprocess`` documentation for semantics and further parameters.
|
||||
|
||||
``utils.subprocess_detached(t)``
|
||||
Runs an external process and detaches it from mpv's control.
|
||||
and the captured output.
|
||||
|
||||
The parameter ``t`` is a table. The function reads the following entries:
|
||||
|
||||
``args``
|
||||
Array of strings of the same semantics as the ``args`` used in the
|
||||
``subprocess`` function.
|
||||
Array of strings. The first array entry is the executable. This
|
||||
can be either an absolute path, or a filename with no path
|
||||
components, in which case the ``PATH`` environment variable is
|
||||
used to resolve the executable. The other array elements are
|
||||
passed as command line arguments.
|
||||
|
||||
The function returns ``nil``.
|
||||
``cancellable``
|
||||
Optional. If set to ``true`` (default), then if the user stops
|
||||
playback or goes to the next file while the process is running,
|
||||
the process will be killed.
|
||||
|
||||
This is a legacy wrapper around calling the ``run`` command with
|
||||
``mp.commandv`` and other functions.
|
||||
``max_size``
|
||||
Optional. The maximum size in bytes of the data that can be captured
|
||||
from stdout. (Default: 16 MB.)
|
||||
|
||||
``utils.getpid()``
|
||||
Returns the process ID of the running mpv process. This can be used to identify
|
||||
the calling mpv when launching (detached) subprocesses.
|
||||
The function returns a table as result with the following entries:
|
||||
|
||||
``utils.get_env_list()``
|
||||
Returns the C environment as a list of strings. (Do not confuse this with
|
||||
the Lua "environment", which is an unrelated concept.)
|
||||
``status``
|
||||
The raw exit status of the process. It will be negative on error.
|
||||
|
||||
``stdout``
|
||||
Captured output stream as string, limited to ``max_size``.
|
||||
|
||||
``error``
|
||||
``nil`` on success. 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 ``cancellable`` being set to ``true``.
|
||||
|
||||
``killed_by_us``
|
||||
Set to ``true`` if the process has been killed by mpv as a result
|
||||
of ``cancellable`` being set to ``true``.
|
||||
|
||||
In all cases, ``mp.resume_all()`` is implicitly called.
|
||||
|
||||
``utils.parse_json(str [, trail])``
|
||||
Parses the given string argument as JSON, and returns it as a Lua table. On
|
||||
@@ -910,137 +662,6 @@ strictly part of the guaranteed API.
|
||||
Turn the given value into a string. Formats tables and their contents. This
|
||||
doesn't do anything special; it is only needed because Lua is terrible.
|
||||
|
||||
mp.input functions
|
||||
--------------------
|
||||
|
||||
This module lets scripts get textual input from the user using the console
|
||||
REPL.
|
||||
|
||||
``input.get(table)``
|
||||
Show the console to let the user enter text.
|
||||
|
||||
The following entries of ``table`` are read:
|
||||
|
||||
``prompt``
|
||||
The string to be displayed before the input field.
|
||||
|
||||
``submit``
|
||||
A callback invoked when the user presses Enter. The first argument is
|
||||
the text in the console.
|
||||
|
||||
``keep_open``
|
||||
Whether to keep the console open on submit. Defaults to ``false``.
|
||||
|
||||
``opened``
|
||||
A callback invoked when the console is shown. This can be used to
|
||||
present a list of options with ``input.set_log()``.
|
||||
|
||||
``edited``
|
||||
A callback invoked when the text changes. The first argument is the text
|
||||
in the console.
|
||||
|
||||
``complete``
|
||||
A callback invoked when the user edits the text or moves the cursor. The
|
||||
first argument is the text before the cursor. The callback should return
|
||||
a table of the string candidate completion values and the 1-based cursor
|
||||
position from which the completion starts. console will show the
|
||||
completions that fuzzily match the text between this position and the
|
||||
cursor and allow selecting them.
|
||||
|
||||
The third and optional return value is a string that will be appended to
|
||||
the input line without displaying it in the completions.
|
||||
|
||||
``autoselect_completion``
|
||||
Whether to automatically select the first completion on submit if one
|
||||
wasn't already manually selected. Defaults to ``false``.
|
||||
|
||||
``closed``
|
||||
A callback invoked when the console is hidden, either because
|
||||
``input.terminate()`` was invoked from the other callbacks, or because
|
||||
the user closed it with a key binding. The first argument is the text in
|
||||
the console, and the second argument is the cursor position.
|
||||
|
||||
``default_text``
|
||||
A string to pre-fill the input field with.
|
||||
|
||||
``cursor_position``
|
||||
The initial cursor position, starting from 1.
|
||||
|
||||
``history_path``
|
||||
If specified, the path to save and load the history of the entered
|
||||
lines.
|
||||
|
||||
``id``
|
||||
An identifier that determines which input history and log buffer to use
|
||||
among the ones stored for ``input.get()`` calls. Defaults to the calling
|
||||
script name with ``prompt`` appended.
|
||||
|
||||
``input.terminate()``
|
||||
Close the console.
|
||||
|
||||
``input.log(message, style, terminal_style)``
|
||||
Add a line to the log buffer. ``style`` can contain additional ASS tags to
|
||||
apply to ``message``, and ``terminal_style`` can contain escape sequences
|
||||
that are used when the console is displayed in the terminal.
|
||||
|
||||
``input.log_error(message)``
|
||||
Helper to add a line to the log buffer with the same color as the one used
|
||||
for commands that error. Useful when the user submits invalid input.
|
||||
|
||||
``input.set_log(log)``
|
||||
Replace the entire log buffer.
|
||||
|
||||
``log`` is a table of strings, or tables with ``text``, ``style`` and
|
||||
``terminal_style`` keys.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
input.set_log({
|
||||
"regular text",
|
||||
{
|
||||
text = "error text",
|
||||
style = "{\\c&H7a77f2&}",
|
||||
terminal_style = "\027[31m",
|
||||
}
|
||||
})
|
||||
|
||||
``input.select(table)``
|
||||
Specify a list of items that are presented to the user for selection.
|
||||
|
||||
The following entries of ``table`` are read:
|
||||
|
||||
``prompt``
|
||||
The string to be displayed before the input field.
|
||||
|
||||
``items``
|
||||
The table of the entries to choose from.
|
||||
|
||||
``default_item``
|
||||
The 1-based integer index of the preselected item.
|
||||
|
||||
``submit``
|
||||
The callback invoked when the user presses Enter. The first argument is
|
||||
the 1-based index of the selected item.
|
||||
|
||||
``keep_open``
|
||||
Whether to keep the console open on submit. Defaults to ``false``.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
input.select({
|
||||
items = {
|
||||
"First playlist entry",
|
||||
"Second playlist entry",
|
||||
},
|
||||
submit = function (id)
|
||||
mp.commandv("playlist-play-index", id - 1)
|
||||
end,
|
||||
})
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
@@ -1060,7 +681,116 @@ Example:
|
||||
|
||||
mp.register_event("file-loaded", my_fn)
|
||||
|
||||
For the existing event types, see `List of events`_.
|
||||
|
||||
|
||||
List of events
|
||||
--------------
|
||||
|
||||
``start-file``
|
||||
Happens right before a new file is loaded. When you receive this, the
|
||||
player is loading the file (or possibly already done with it).
|
||||
|
||||
``end-file``
|
||||
Happens after a file was unloaded. Typically, the player will load the
|
||||
next file right away, or quit if this was the last file.
|
||||
|
||||
The event has the ``reason`` field, which takes one of these values:
|
||||
|
||||
``eof``
|
||||
The file has ended. This can (but doesn't have to) include
|
||||
incomplete files or broken network connections under
|
||||
circumstances.
|
||||
|
||||
``stop``
|
||||
Playback was ended by a command.
|
||||
|
||||
``quit``
|
||||
Playback was ended by sending the quit command.
|
||||
|
||||
``error``
|
||||
An error happened. In this case, an ``error`` field is present with
|
||||
the error string.
|
||||
|
||||
``redirect``
|
||||
Happens with playlists and similar. Details see
|
||||
``MPV_END_FILE_REASON_REDIRECT`` in the C API.
|
||||
|
||||
``unknown``
|
||||
Unknown. Normally doesn't happen, unless the Lua API is out of sync
|
||||
with the C API. (Likewise, it could happen that your script gets
|
||||
reason strings that did not exist yet at the time your script was
|
||||
written.)
|
||||
|
||||
``file-loaded``
|
||||
Happens after a file was loaded and begins playback.
|
||||
|
||||
``seek``
|
||||
Happens on seeking. (This might include cases when the player seeks
|
||||
internally, even without user interaction. This includes e.g. segment
|
||||
changes when playing ordered chapters Matroska files.)
|
||||
|
||||
``playback-restart``
|
||||
Start of playback after seek or after file was loaded.
|
||||
|
||||
``idle``
|
||||
Idle mode is entered. This happens when playback ended, and the player was
|
||||
started with ``--idle`` or ``--force-window``. This mode is implicitly ended
|
||||
when the ``start-file`` or ``shutdown`` events happen.
|
||||
|
||||
``tick``
|
||||
Called after a video frame was displayed. This is a hack, and you should
|
||||
avoid using it. Use timers instead and maybe watch pausing/unpausing events
|
||||
to avoid wasting CPU when the player is paused.
|
||||
|
||||
``shutdown``
|
||||
Sent when the player quits, and the script should terminate. Normally
|
||||
handled automatically. See `Details on the script initialization and lifecycle`_.
|
||||
|
||||
``log-message``
|
||||
Receives messages enabled with ``mp.enable_messages``. The message data
|
||||
is contained in the table passed as first parameter to the event handler.
|
||||
The table contains, in addition to the default event fields, the following
|
||||
fields:
|
||||
|
||||
``prefix``
|
||||
The module prefix, identifies the sender of the message. This is what
|
||||
the terminal player puts in front of the message text when using the
|
||||
``--v`` option, and is also what is used for ``--msg-level``.
|
||||
|
||||
``level``
|
||||
The log level as string. See ``msg.log`` for possible log level names.
|
||||
Note that later versions of mpv might add new levels or remove
|
||||
(undocumented) existing ones.
|
||||
|
||||
``text``
|
||||
The log message. The text will end with a newline character. Sometimes
|
||||
it can contain multiple lines.
|
||||
|
||||
Keep in mind that these messages are meant to be hints for humans. You
|
||||
should not parse them, and prefix/level/text of messages might change
|
||||
any time.
|
||||
|
||||
``get-property-reply``
|
||||
Undocumented (not useful for Lua scripts).
|
||||
|
||||
``set-property-reply``
|
||||
Undocumented (not useful for Lua scripts).
|
||||
|
||||
``command-reply``
|
||||
Undocumented (not useful for Lua scripts).
|
||||
|
||||
``client-message``
|
||||
Undocumented (used internally).
|
||||
|
||||
``video-reconfig``
|
||||
Happens on video output or filter reconfig.
|
||||
|
||||
``audio-reconfig``
|
||||
Happens on audio output or filter reconfig.
|
||||
|
||||
The following events also happen, but are deprecated: ``tracks-changed``,
|
||||
``track-switched``, ``pause``, ``unpause``, ``metadata-update``,
|
||||
``chapter-change``. Use ``mp.observe_property()`` instead.
|
||||
|
||||
Extras
|
||||
------
|
||||
@@ -1074,22 +804,8 @@ guarantee a stable interface.
|
||||
their result (normally, the Lua scripting interface is asynchronous from
|
||||
the point of view of the player core). ``priority`` is an arbitrary integer
|
||||
that allows ordering among hooks of the same kind. Using the value 50 is
|
||||
recommended as neutral default value.
|
||||
|
||||
``fn(hook)`` is the function that will be called during execution of the
|
||||
hook. The parameter passed to it (``hook``) is a Lua object that can control
|
||||
further aspects about the currently invoked hook. It provides the following
|
||||
methods:
|
||||
|
||||
``defer()``
|
||||
Returning from the hook function should not automatically continue
|
||||
the hook. Instead, the API user wants to call ``hook:cont()`` on its
|
||||
own at a later point in time (before or after the function has
|
||||
returned).
|
||||
|
||||
``cont()``
|
||||
Continue the hook. Doesn't need to be called unless ``defer()`` was
|
||||
called.
|
||||
recommended as neutral default value. ``fn`` is the function that will be
|
||||
called during execution of the hook.
|
||||
|
||||
See `Hooks`_ for currently existing hooks and what they do - only the hook
|
||||
list is interesting; handling hook execution is done by the Lua script
|
||||
|
||||
+185
-1084
File diff suppressed because it is too large.
Load diff
+1245
-6108
File diff suppressed because it is too large.
Load diff
+148
-529
@@ -20,94 +20,37 @@ The Interface
|
||||
|
||||
::
|
||||
|
||||
+------+---------+---------+-----------------------------------------------+
|
||||
| menu | pl prev | pl next | title cache |
|
||||
+------+------+------+---------+-----------+------+-------+-----+-----+----+
|
||||
| play | skip | skip | time | seekbar | time | audio | sub | vol | fs |
|
||||
| | back | frwd | elapsed | | left | | | | |
|
||||
+------+------+------+---------+-----------+------+-------+-----+-----+----+
|
||||
+------------------+-----------+--------------------+
|
||||
| playlist prev | title | playlist next |
|
||||
+-------+------+---+--+------+-+----+------+--------+
|
||||
| audio | skip | seek | | seek | skip | full |
|
||||
+-------+ back | back | play | frwd | frwd | screen |
|
||||
| sub | | | | | | |
|
||||
+-------+------+------+------+------+------+--------+
|
||||
| seekbar |
|
||||
+----------------+--------------+-------------------+
|
||||
| time passed | cache status | time remaining |
|
||||
+----------------+--------------+-------------------+
|
||||
|
||||
|
||||
menu
|
||||
============= ================================================
|
||||
left-click open the menu
|
||||
============= ================================================
|
||||
|
||||
pl prev
|
||||
playlist prev
|
||||
============= ================================================
|
||||
left-click play previous file in playlist
|
||||
shift+L-click show the playlist
|
||||
middle-click show the playlist
|
||||
right-click open the playlist menu
|
||||
============= ================================================
|
||||
|
||||
pl next
|
||||
============= ================================================
|
||||
left-click play next file in playlist
|
||||
shift+L-click show the playlist
|
||||
middle-click show the playlist
|
||||
right-click open the playlist menu
|
||||
shift+L-click show playlist
|
||||
============= ================================================
|
||||
|
||||
title
|
||||
| Displays the current playlist position and media-title, filename or custom
|
||||
title, or the target chapter name while hovering the seekbar.
|
||||
| Displays current media-title or filename
|
||||
|
||||
============= ================================================
|
||||
left-click show file and track info
|
||||
shift+L-click show the path
|
||||
middle-click show the path
|
||||
right-click open the history menu
|
||||
left-click show playlist position and length and full title
|
||||
right-click show filename
|
||||
============= ================================================
|
||||
|
||||
cache
|
||||
| Shows current cache fill status
|
||||
|
||||
play
|
||||
playlist next
|
||||
============= ================================================
|
||||
left-click toggle play/pause
|
||||
shift+L-click toggle infinite looping of the playlist
|
||||
middle-click toggle infinite looping of the playlist
|
||||
right-click toggle infinite looping of the current file
|
||||
============= ================================================
|
||||
|
||||
skip back
|
||||
============= ================================================
|
||||
left-click go to beginning of chapter / previous chapter
|
||||
shift+L-click show chapters
|
||||
middle-click show chapters
|
||||
right-click open the chapter menu
|
||||
============= ================================================
|
||||
|
||||
skip frwd
|
||||
============= ================================================
|
||||
left-click go to next chapter
|
||||
shift+L-click show chapters
|
||||
middle-click show chapters
|
||||
right-click open the chapter menu
|
||||
============= ================================================
|
||||
|
||||
time elapsed
|
||||
| Shows current playback position timestamp
|
||||
|
||||
============= ================================================
|
||||
left-click toggle displaying timecodes with milliseconds
|
||||
============= ================================================
|
||||
|
||||
seekbar
|
||||
| Indicates current playback position and position of chapters
|
||||
|
||||
============= ================================================
|
||||
left-click seek to position
|
||||
right-click seek to the nearest chapter
|
||||
mouse wheel seek forward/backward
|
||||
============= ================================================
|
||||
|
||||
time left
|
||||
| Shows remaining playback time timestamp
|
||||
|
||||
============= ================================================
|
||||
left-click toggle between total and remaining time
|
||||
left-click play next file in playlist
|
||||
shift+L-click show playlist
|
||||
============= ================================================
|
||||
|
||||
audio and sub
|
||||
@@ -115,29 +58,69 @@ audio and sub
|
||||
|
||||
============= ================================================
|
||||
left-click cycle audio/sub tracks forward
|
||||
shift+L-click cycle audio/sub tracks backwards
|
||||
middle-click cycle audio/sub tracks backwards
|
||||
right-click open the audio/sub track menu
|
||||
mouse wheel cycle audio/sub tracks forward/backwards
|
||||
right-click cycle audio/sub tracks backwards
|
||||
shift+L-click show available audio/sub tracks
|
||||
============= ================================================
|
||||
|
||||
vol
|
||||
skip back
|
||||
============= ================================================
|
||||
left-click toggle mute
|
||||
right-click open the audio device menu
|
||||
mouse wheel volume up/down
|
||||
left-click go to beginning of chapter / previous chapter
|
||||
shift+L-click show chapters
|
||||
============= ================================================
|
||||
|
||||
fs
|
||||
seek back
|
||||
============= ================================================
|
||||
left-click skip back 5 seconds
|
||||
right-click skip back 30 seconds
|
||||
shift-L-click skip back 1 frame
|
||||
============= ================================================
|
||||
|
||||
play
|
||||
============= ================================================
|
||||
left-click toggle play/pause
|
||||
============= ================================================
|
||||
|
||||
seek frwd
|
||||
============= ================================================
|
||||
left-click skip forward 10 seconds
|
||||
right-click skip forward 60 seconds
|
||||
shift-L-click skip forward 1 frame
|
||||
============= ================================================
|
||||
|
||||
skip frwd
|
||||
============= ================================================
|
||||
left-click go to next chapter
|
||||
shift+L-click show chapters
|
||||
============= ================================================
|
||||
|
||||
fullscreen
|
||||
============= ================================================
|
||||
left-click toggle fullscreen
|
||||
right-click toggle whether the window is maximized
|
||||
============= ================================================
|
||||
|
||||
Since mpv 0.40.0, it is possible to configure the commands to run with mouse
|
||||
actions on some interface elements, and the default behaviors of several
|
||||
elements were changed. If you miss some older behaviors, look at
|
||||
``etc/restore-osc-bindings.conf`` in the mpv git repository.
|
||||
seekbar
|
||||
| Indicates current playback position and position of chapters
|
||||
|
||||
============= ================================================
|
||||
left-click seek to position
|
||||
============= ================================================
|
||||
|
||||
time passed
|
||||
| Shows current playback position timestamp
|
||||
|
||||
============= ================================================
|
||||
left-click toggle displaying timecodes with milliseconds
|
||||
============= ================================================
|
||||
|
||||
cache status
|
||||
| Shows current cache fill status (only visible when below 45%)
|
||||
|
||||
time remaining
|
||||
| Shows remaining playback time timestamp
|
||||
|
||||
============= ================================================
|
||||
left-click toggle between total and remaining time
|
||||
============= ================================================
|
||||
|
||||
Key Bindings
|
||||
~~~~~~~~~~~~
|
||||
@@ -147,15 +130,28 @@ these keys. In case of collision, the function needs to be bound to a
|
||||
different key. See the `Script Commands`_ section.
|
||||
|
||||
============= ================================================
|
||||
del Cycles visibility between never / auto (mouse-move) / always
|
||||
del Hide the OSC permanently until mpv is restarted.
|
||||
============= ================================================
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This script can be customized through a config file ``script-opts/osc.conf``
|
||||
placed in mpv's user directory and through the ``--script-opts`` command-line
|
||||
option. The configuration syntax is described in `mp.options functions`_.
|
||||
The OSC offers limited configuration through a config file
|
||||
``lua-settings/osc.conf`` placed in mpv's user dir and through the
|
||||
``--script-opts`` command-line option. Options provided through the command-line
|
||||
will override those from the config file.
|
||||
|
||||
Config Syntax
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The config file must exactly follow the following syntax::
|
||||
|
||||
# this is a comment
|
||||
optionA=value1
|
||||
optionB=value2
|
||||
|
||||
``#`` can only be used at the beginning of a line and there may be no
|
||||
spaces around the ``=`` or anywhere else.
|
||||
|
||||
Command-line Syntax
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
@@ -171,454 +167,81 @@ Example::
|
||||
Configurable Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``layout``
|
||||
Default: bottombar
|
||||
|
||||
The layout for the OSC. Currently available are: box, slimbox,
|
||||
bottombar, topbar, slimbottombar and slimtopbar. Default pre-0.21.0 was
|
||||
'box'.
|
||||
|
||||
``seekbarstyle``
|
||||
Default: bar
|
||||
|
||||
Sets the style of the playback position marker and overall shape
|
||||
of the seekbar: ``bar``, ``diamond`` or ``knob``.
|
||||
|
||||
``seekbarhandlesize``
|
||||
Default: 0.6
|
||||
|
||||
Size ratio of the seek handle if ``seekbarstyle`` is set to ``diamond``
|
||||
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.
|
||||
|
||||
``seekrangestyle``
|
||||
Default: inverted
|
||||
|
||||
Display seekable ranges on the seekbar. ``bar`` shows them on the full
|
||||
height of the bar, ``line`` as a thick line and ``inverted`` as a thin
|
||||
line that is inverted over playback position markers. ``none`` will hide
|
||||
them. Additionally, ``slider`` will show a permanent handle inside the seekbar
|
||||
with cached ranges marked inside. Note that these will look differently
|
||||
based on the seekbarstyle option. Also, ``slider`` does not work with
|
||||
``seekbarstyle`` set to ``bar``.
|
||||
|
||||
``seekrangeseparate``
|
||||
Default: yes
|
||||
|
||||
Controls whether to show line-style seekable ranges on top of the
|
||||
seekbar or separately if ``seekbarstyle`` is set to ``bar``.
|
||||
|
||||
``seekrangealpha``
|
||||
Default: 20
|
||||
|
||||
Alpha of the seekable ranges, 0 (opaque) to 255 (fully transparent).
|
||||
|
||||
``scrollcontrols``
|
||||
Default: yes
|
||||
|
||||
By default, going up or down with the mouse wheel can trigger certain
|
||||
actions (such as seeking) if the mouse is hovering an OSC element.
|
||||
Set to ``no`` to disable any special mouse wheel behavior.
|
||||
|
||||
``deadzonesize``
|
||||
Default: 0.5
|
||||
|
||||
Size of the deadzone. The deadzone is an area that makes the mouse act
|
||||
like leaving the window. Movement there won't make the OSC show up and
|
||||
it will hide immediately if the mouse enters it. The deadzone starts
|
||||
at the window border opposite to the OSC and the size controls how much
|
||||
of the window it will span. Values between 0.0 and 1.0, where 0 means the
|
||||
OSC will always popup with mouse movement in the window, and 1 means the
|
||||
OSC will only show up when the mouse hovers it. Default pre-0.21.0 was 0.
|
||||
|
||||
``minmousemove``
|
||||
Default: 0
|
||||
|
||||
Minimum amount of pixels the mouse has to move between ticks to make
|
||||
the OSC show up. Default pre-0.21.0 was 3.
|
||||
|
||||
``showwindowed``
|
||||
Default: yes
|
||||
|
||||
Enable the OSC when windowed
|
||||
| Default: yes
|
||||
| Enable the OSC when windowed
|
||||
|
||||
``showfullscreen``
|
||||
Default: yes
|
||||
|
||||
Enable the OSC when fullscreen
|
||||
|
||||
``idlescreen``
|
||||
Default: yes
|
||||
|
||||
Show the mpv logo and message when idle
|
||||
| Default: yes
|
||||
| Enable the OSC when fullscreen
|
||||
|
||||
``scalewindowed``
|
||||
Default: 1.0
|
||||
|
||||
Scale factor of the OSC when windowed.
|
||||
| Default: 1.0
|
||||
| Scale factor of the OSC when windowed
|
||||
|
||||
``scalefullscreen``
|
||||
Default: 1.0
|
||||
| Default: 1.0
|
||||
| Scale factor of the OSC when fullscreen
|
||||
|
||||
Scale factor of the OSC when fullscreen
|
||||
``scaleforcedwindow``
|
||||
| Default: 2.0
|
||||
| Scale factor of the OSC when rendered on a forced (dummy) window
|
||||
|
||||
``vidscale``
|
||||
Default: auto
|
||||
|
||||
Scale the OSC with the video.
|
||||
``no`` tries to keep the OSC size constant as much as the window size allows.
|
||||
``auto`` scales the OSC with the OSD, which is scaled with the window or kept at a
|
||||
constant size, depending on the ``--osd-scale-by-window`` option.
|
||||
| Default: yes
|
||||
| Scale the OSC with the video
|
||||
| ``no`` tries to keep the OSC size constant as much as the window size allows
|
||||
|
||||
``valign``
|
||||
Default: 0.8
|
||||
|
||||
Vertical alignment in box and slimbox layouts, -1 (top) to 1 (bottom).
|
||||
| Default: 0.8
|
||||
| Vertical alignment, -1 (top) to 1 (bottom)
|
||||
|
||||
``halign``
|
||||
Default: 0.0
|
||||
|
||||
Horizontal alignment in box and slimbox layouts, -1 (left) to 1 (right).
|
||||
|
||||
``barmargin``
|
||||
Default: 0
|
||||
|
||||
Margin from bottom (bottombar, slimbottombar) or top (topbar, slimtopbar),
|
||||
in pixels.
|
||||
| Default: 0.0
|
||||
| Horizontal alignment, -1 (left) to 1 (right)
|
||||
|
||||
``boxalpha``
|
||||
Default: 80
|
||||
|
||||
Alpha of the background box, 0 (opaque) to 255 (fully transparent)
|
||||
| Default: 80
|
||||
| Alpha of the background box, 0 (opaque) to 255 (fully transparent)
|
||||
|
||||
``hidetimeout``
|
||||
Default: 500
|
||||
|
||||
Duration in ms until the OSC hides if no mouse movement, must not be
|
||||
negative
|
||||
| Default: 500
|
||||
| Duration in ms until the OSC hides if no mouse movement, negative value
|
||||
disables auto-hide
|
||||
|
||||
``fadeduration``
|
||||
Default: 200
|
||||
| Default: 200
|
||||
| Duration of fade out in ms, 0 = no fade
|
||||
|
||||
Duration of fade effects in ms, 0 = no fade.
|
||||
``deadzonesize``
|
||||
| Default: 0
|
||||
| Size of the deadzone. The deadzone is an area that makes the mouse act
|
||||
like leaving the window. Movement there won't make the OSC show up and
|
||||
it will hide immediately if the mouse enters it. The deadzone starts
|
||||
at the window border opposite to the OSC and the size controls how much
|
||||
of the window it will span. Values between 0 and 1.
|
||||
|
||||
``fadein``
|
||||
Default: no
|
||||
``minmousemove``
|
||||
| Default: 3
|
||||
| Minimum amount of pixels the mouse has to move between ticks to make
|
||||
the OSC show up
|
||||
|
||||
Enable fade-in.
|
||||
``layout``
|
||||
| Default: box
|
||||
| The layout for the OSC. Currently available are: box, slimbox,
|
||||
bottombar and topbar.
|
||||
|
||||
``title``
|
||||
Default: ${!playlist-count==1:[${playlist-pos-1}/${playlist-count}] }${media-title}
|
||||
|
||||
String that supports property expansion that will be displayed as
|
||||
OSC title.
|
||||
ASS tags are escaped and newlines are converted to spaces.
|
||||
|
||||
``tooltipborder``
|
||||
Default: 1
|
||||
|
||||
Size of the tooltip outline when using bottombar or topbar layouts
|
||||
``seekbarstyle``
|
||||
| Default: slider
|
||||
| Sets the style of the seekbar, slider (diamond marker) or bar (fill)
|
||||
|
||||
``timetotal``
|
||||
Default: no
|
||||
|
||||
Show total time instead of time remaining
|
||||
|
||||
``remaining_playtime``
|
||||
Default: yes
|
||||
|
||||
Whether the time-remaining display takes speed into account.
|
||||
``yes`` - how much playback time remains at the current speed.
|
||||
``no`` - how much video-time remains.
|
||||
| Default: no
|
||||
| Show total time instead of time remaining
|
||||
|
||||
``timems``
|
||||
Default: no
|
||||
|
||||
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)
|
||||
|
||||
Also supports ``never`` and ``always``
|
||||
|
||||
``visibility_modes``
|
||||
Default: never_auto_always
|
||||
|
||||
The list of visibility modes to cycle through when calling the
|
||||
osc-visibility cycle script message. Modes are separated by ``_``.
|
||||
|
||||
``boxmaxchars``
|
||||
Default: 80
|
||||
|
||||
Max chars for the osc title at the box layout. mpv does not measure the
|
||||
text width on screen and so it needs to limit it by number of chars. The
|
||||
default is conservative to allow wide fonts to be used without overflow.
|
||||
However, with many common fonts a bigger number can be used. YMMV.
|
||||
|
||||
``boxvideo``
|
||||
Default: no
|
||||
|
||||
Whether to overlay the osc over the video (``no``), or to box the video
|
||||
within the areas not covered by the osc (``yes``). If this option is set,
|
||||
the osc may overwrite the ``--video-margin-ratio-*`` options, even if the
|
||||
user has set them. (It will not overwrite them if all of them are set to
|
||||
default values.) Additionally, ``visibility`` must be set to ``always``.
|
||||
Otherwise, this option does nothing.
|
||||
|
||||
Currently, this is supported for the ``bottombar``, ``slimbottombar``,
|
||||
``topbar`` and ``slimtopbar`` layouts only. The other layouts do not change
|
||||
if this option is set. Separately, if window controls are present (see
|
||||
below), they will be affected regardless of which osc layout is in use.
|
||||
|
||||
The border is static and appears even if the OSC is configured to appear
|
||||
only on mouse interaction. If the OSC is invisible, the border is simply
|
||||
filled with the background color (black by default).
|
||||
|
||||
This currently still makes the OSC overlap with subtitles (if the
|
||||
``--sub-use-margins`` option is set to ``yes``, the default). This may be
|
||||
fixed later.
|
||||
|
||||
This does not work correctly with video outputs like ``--vo=xv``, which
|
||||
render OSD into the unscaled video.
|
||||
|
||||
``windowcontrols``
|
||||
Default: auto (Show window controls if there is no window border)
|
||||
|
||||
Whether to show window management controls over the video, and if so,
|
||||
which side of the window to place them. This may be desirable when the
|
||||
window has no decorations, either because they have been explicitly
|
||||
disabled (``border=no``) or because the current platform doesn't support
|
||||
them (eg: gnome-shell with wayland).
|
||||
|
||||
The set of window controls is fixed, offering ``minimize``, ``maximize``,
|
||||
and ``quit``. Not all platforms implement ``minimize`` and ``maximize``,
|
||||
but ``quit`` will always work.
|
||||
|
||||
``windowcontrols_alignment``
|
||||
Default: right
|
||||
|
||||
If window controls are shown, indicates which side should they be aligned
|
||||
to.
|
||||
|
||||
Supports ``left`` and ``right`` which will place the controls on those
|
||||
respective sides.
|
||||
|
||||
``windowcontrols_title``
|
||||
Default: ${media-title}
|
||||
|
||||
String that supports property expansion that will be displayed as the
|
||||
windowcontrols title.
|
||||
ASS tags are escaped, and newlines and trailing slashes are stripped.
|
||||
|
||||
``greenandgrumpy``
|
||||
Default: no
|
||||
|
||||
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.
|
||||
|
||||
``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.
|
||||
|
||||
``background_color``
|
||||
Default: #000000
|
||||
|
||||
Sets the background color of the OSC.
|
||||
|
||||
``timecode_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
Sets the color of the timecode and seekbar, of the OSC.
|
||||
|
||||
``title_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
Sets the color of the video title. Formatted as #RRGGBB.
|
||||
|
||||
``time_pos_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
Sets the color of the timecode at hover position in the seekbar.
|
||||
|
||||
``time_pos_outline_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
Sets the color of the timecode's outline at hover position in the seekbar.
|
||||
Also affects the timecode in the slimbox layout.
|
||||
|
||||
``buttons_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
Sets the colors of the big buttons.
|
||||
|
||||
``top_buttons_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
Sets the colors of the top buttons.
|
||||
|
||||
``small_buttonsL_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
Sets the colors of the small buttons on the left in the box layout.
|
||||
|
||||
``small_buttonsR_color``
|
||||
Default: #FFFFFF
|
||||
|
||||
Sets the colors of the small buttons on the right in the box layout.
|
||||
|
||||
``held_element_color``
|
||||
Default: #999999
|
||||
|
||||
Sets the colors of the elements that are being pressed or held down.
|
||||
|
||||
``tick_delay``
|
||||
Default: 1/60
|
||||
|
||||
Sets the minimum interval between OSC redraws in seconds. This can be
|
||||
decreased on fast systems to make OSC rendering smoother.
|
||||
|
||||
Ignored if ``tick_delay_follow_display_fps`` is set to yes and the VO
|
||||
supports the ``display-fps`` property.
|
||||
|
||||
``tick_delay_follow_display_fps``
|
||||
Default: no
|
||||
|
||||
Use display fps to calculate the interval between OSC redraws.
|
||||
|
||||
The following options configure what commands are run when the buttons are
|
||||
clicked. ``mbtn_mid`` commands are also triggered with ``shift+mbtn_left``.
|
||||
|
||||
``menu_mbtn_left_command=script-binding select/menu; script-message-to osc osc-hide``
|
||||
|
||||
``menu_mbtn_mid_command=``
|
||||
|
||||
``menu_mbtn_right_command=``
|
||||
|
||||
``playlist_prev_mbtn_left_command=playlist-prev; show-text ${playlist} 3000``
|
||||
|
||||
``playlist_prev_mbtn_mid_command=show-text ${playlist} 3000``
|
||||
|
||||
``playlist_prev_mbtn_right_command=script-binding select/select-playlist; script-message-to osc osc-hide``
|
||||
|
||||
``playlist_next_mbtn_left_command=playlist-next; show-text ${playlist} 3000``
|
||||
|
||||
``playlist_next_mbtn_mid_command=show-text ${playlist} 3000``
|
||||
|
||||
``playlist_next_mbtn_right_command=script-binding select/select-playlist; script-message-to osc osc-hide``
|
||||
|
||||
``title_mbtn_left_command=script-binding stats/display-page-5``
|
||||
|
||||
``title_mbtn_mid_command=show-text ${path}``
|
||||
|
||||
``title_mbtn_right_command=script-binding select/select-watch-history; script-message-to osc osc-hide``
|
||||
|
||||
``play_pause_mbtn_left_command=cycle pause``
|
||||
|
||||
``play_pause_mbtn_mid_command=cycle-values loop-playlist inf no``
|
||||
|
||||
``play_pause_mbtn_right_command=cycle-values loop-file inf no``
|
||||
|
||||
``chapter_prev_mbtn_left_command=osd-msg add chapter -1``
|
||||
|
||||
``chapter_prev_mbtn_mid_command=show-text ${chapter-list} 3000``
|
||||
|
||||
``chapter_prev_mbtn_right_command=script-binding select/select-chapter; script-message-to osc osc-hide``
|
||||
|
||||
``chapter_next_mbtn_left_command=osd-msg add chapter 1``
|
||||
|
||||
``chapter_next_mbtn_mid_command=show-text ${chapter-list} 3000``
|
||||
|
||||
``chapter_next_mbtn_right_command=script-binding select/select-chapter; script-message-to osc osc-hide``
|
||||
|
||||
``audio_track_mbtn_left_command=cycle audio``
|
||||
|
||||
``audio_track_mbtn_mid_command=cycle audio down``
|
||||
|
||||
``audio_track_mbtn_right_command=script-binding select/select-aid; script-message-to osc osc-hide``
|
||||
|
||||
``audio_track_wheel_down_command=cycle audio``
|
||||
|
||||
``audio_track_wheel_up_command=cycle audio down``
|
||||
|
||||
``sub_track_mbtn_left_command=cycle sub``
|
||||
|
||||
``sub_track_mbtn_mid_command=cycle sub down``
|
||||
|
||||
``sub_track_mbtn_right_command=script-binding select/select-sid; script-message-to osc osc-hide``
|
||||
|
||||
``sub_track_wheel_down_command=cycle sub``
|
||||
|
||||
``sub_track_wheel_up_command=cycle sub down``
|
||||
|
||||
``volume_mbtn_left_command=no-osd cycle mute``
|
||||
|
||||
``volume_mbtn_mid_command=``
|
||||
|
||||
``volume_mbtn_right_command=script-binding select/select-audio-device; script-message-to osc osc-hide``
|
||||
|
||||
``volume_wheel_down_command=add volume -5``
|
||||
|
||||
``volume_wheel_up_command=add volume 5``
|
||||
|
||||
``fullscreen_mbtn_left_command="cycle fullscreen"``
|
||||
|
||||
``fullscreen_mbtn_mid_command=``
|
||||
|
||||
``fullscreen_mbtn_right_command="cycle window-maximized"``
|
||||
|
||||
Custom Buttons
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Additional script-opts are available to define custom buttons in ``bottombar``
|
||||
and ``topbar`` layouts.
|
||||
|
||||
.. admonition:: Example to add loop, shuffle and speed buttons
|
||||
|
||||
::
|
||||
|
||||
custom_button_1_content=🔁
|
||||
custom_button_1_mbtn_left_command=cycle-values loop-file inf no
|
||||
custom_button_1_mbtn_right_command=cycle-values loop-playlist inf no
|
||||
|
||||
custom_button_2_content=🔀
|
||||
custom_button_2_mbtn_left_command=playlist-shuffle
|
||||
|
||||
custom_button_3_content=⏱
|
||||
custom_button_3_mbtn_left_command=add speed 1
|
||||
custom_button_3_mbtn_right_command=set speed 1
|
||||
custom_button_3_wheel_up_command=add speed 0.25
|
||||
custom_button_3_wheel_down_command=add speed -0.25
|
||||
| Default: no
|
||||
| Display timecodes with milliseconds
|
||||
|
||||
Script Commands
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -626,26 +249,22 @@ Script Commands
|
||||
The OSC script listens to certain script commands. These commands can bound
|
||||
in ``input.conf``, or sent by other scripts.
|
||||
|
||||
``osc-visibility``
|
||||
Controls visibility mode ``never`` / ``auto`` (on mouse move) / ``always``
|
||||
and also ``cycle`` to cycle between the modes. If a second argument is
|
||||
passed (any value), then the output on the OSD will be silenced.
|
||||
``enable-osc``
|
||||
Undoes ``disable-osc`` or the effect of the ``del`` key.
|
||||
|
||||
``osc-show``
|
||||
Triggers the OSC to show up, just as if user moved mouse.
|
||||
``disable-osc``
|
||||
Hide the OSC permanently. This is also what the ``del`` key does.
|
||||
|
||||
``osc-message``
|
||||
Show a message on screen using the OSC. First argument is the message,
|
||||
second the duration in seconds.
|
||||
|
||||
``osc-hide``
|
||||
Hide the OSC when ``visibility`` is ``auto``.
|
||||
|
||||
Example
|
||||
|
||||
You could put this into ``input.conf`` to hide the OSC with the ``a`` key and
|
||||
to set auto mode (the default) with ``b``::
|
||||
to unhide it with ``b``::
|
||||
|
||||
a script-message osc-visibility never
|
||||
b script-message osc-visibility auto
|
||||
a script_message disable-osc
|
||||
b script_message enable-osc
|
||||
|
||||
``osc-idlescreen``
|
||||
Controls the visibility of the mpv logo on idle. Valid arguments are ``yes``,
|
||||
``no``, and ``cycle`` to toggle between yes and no. If a second argument is
|
||||
passed (any value), then the output on the OSD will be silenced.
|
||||
@@ -1,60 +0,0 @@
|
||||
POSITIONING
|
||||
===========
|
||||
|
||||
This script provides script bindings to pan videos and images. It can be
|
||||
disabled using the ``--load-positioning=no`` option.
|
||||
|
||||
Script bindings
|
||||
---------------
|
||||
|
||||
``pan-x <amount>``
|
||||
Adjust ``--video-align-x`` relatively to the OSD width, rather than
|
||||
relatively to the video width like the option. This is useful to pan large
|
||||
images consistently.
|
||||
|
||||
``amount`` is a number such that an amount of 1 scrolls as much as the OSD
|
||||
width.
|
||||
|
||||
``pan-y <amount>``
|
||||
Adjust ``--video-align-y`` relatively to the OSD height, rather than
|
||||
relatively to the video height like the option.
|
||||
|
||||
``amount`` is a number such that an amount of 1 scrolls as much as the OSD
|
||||
height.
|
||||
|
||||
``drag-to-pan``
|
||||
Pan the video while holding a mouse button, keeping the clicked part of the
|
||||
video under the cursor.
|
||||
|
||||
``align-to-cursor``
|
||||
Pan through the whole video while holding a mouse button, or after clicking
|
||||
once if ``toggle_align_to_cursor`` is ``yes``.
|
||||
|
||||
``cursor-centric-zoom <amount>``
|
||||
Increase ``--video-zoom`` by ``amount`` while keeping the part of the video
|
||||
hovered by the cursor under it, or the average position of touch points if
|
||||
known.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This script can be customized through a config file
|
||||
``script-opts/positioning.conf`` placed in mpv's user directory and through the
|
||||
``--script-opts`` command-line option. The configuration syntax is described in
|
||||
`mp.options functions`_.
|
||||
|
||||
Configurable Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``toggle_align_to_cursor``
|
||||
Default: no
|
||||
|
||||
Whether ``align-to-cursor`` requires holding down a mouse button to pan. If
|
||||
``no``, dragging pans. If ``yes``, clicking the first time makes pan follow
|
||||
the cursor, and clicking a second time disables this.
|
||||
|
||||
``suppress_osd``
|
||||
Default: no
|
||||
|
||||
Whether to not print the new value of ``--video-zoom`` when using
|
||||
``cursor-centric-zoom``.
|
||||
@@ -1,163 +0,0 @@
|
||||
SELECT
|
||||
======
|
||||
|
||||
console can present a list of items to browse and select from with the
|
||||
``mp.input.select`` API. ``select.lua`` is a builtin client of this API
|
||||
providing script bindings that gather and format the data to be selected in the
|
||||
console and do operations on the selected item. It can be disabled using the
|
||||
``--load-select=no`` option.
|
||||
|
||||
Key bindings
|
||||
------------
|
||||
|
||||
When using ``mp.input.select``, the key bindings listed in `CONSOLE`_ are
|
||||
extended with the following:
|
||||
|
||||
ENTER, Ctrl+j and Ctrl+m
|
||||
Select the focused item.
|
||||
|
||||
UP and Ctrl+p
|
||||
Focus the item above, or the last one when the first item is selected.
|
||||
|
||||
DOWN and Ctrl+n
|
||||
Focus the item below, or the first one when the last item is selected.
|
||||
|
||||
PGUP and Ctrl+b
|
||||
Scroll up one page.
|
||||
|
||||
PGDN and Ctrl+f
|
||||
Scroll down one page.
|
||||
|
||||
Ctrl+y
|
||||
Copy the focused item to the clipboard.
|
||||
|
||||
MBTN_LEFT
|
||||
Select the item under the cursor, or close the console if clicking outside
|
||||
of the menu rectangle.
|
||||
|
||||
WHEEL_UP
|
||||
Scroll up.
|
||||
|
||||
WHEEL_DOWN
|
||||
Scroll down.
|
||||
|
||||
Typing printable characters does a fuzzy search of the presented items.
|
||||
|
||||
If the query starts with ``'``, only exact matches are filtered. You can also
|
||||
specify multiple search terms delimited by spaces, and only items matching all
|
||||
terms are filtered.
|
||||
|
||||
Script bindings
|
||||
---------------
|
||||
|
||||
By default select.lua's script bindings are bound to key sequences starting with
|
||||
``g`` listed in `Keyboard Control`_. The names of the script bindings listed
|
||||
below can be used to bind them to different keys.
|
||||
|
||||
.. admonition:: Example to rebind playlist selection in input.conf
|
||||
|
||||
Ctrl+p script-binding select/select-playlist
|
||||
|
||||
Available script bindings are:
|
||||
|
||||
``select-playlist``
|
||||
Select a playlist entry. ``--osd-playlist-entry`` determines how playlist
|
||||
entries are formatted.
|
||||
|
||||
``select-sid``
|
||||
Select a subtitle track, or disable the current one.
|
||||
|
||||
``select-secondary-sid``
|
||||
Select a secondary subtitle track, or disable the current one.
|
||||
|
||||
``select-aid``
|
||||
Select an audio track, or disable the current one.
|
||||
|
||||
``select-vid``
|
||||
Select a video track, or disable the current one.
|
||||
|
||||
``select-track``
|
||||
Select a track of any type, or disable a selected track.
|
||||
|
||||
``select-chapter``
|
||||
Select a chapter.
|
||||
|
||||
``select-edition``
|
||||
Select an MKV edition or DVD/Blu-ray title.
|
||||
|
||||
``select-subtitle-line``
|
||||
Select a subtitle line to seek to. This doesn't work with image subtitles.
|
||||
|
||||
This currently requires ``ffmpeg`` in ``PATH``, or in the same folder as mpv
|
||||
on Windows.
|
||||
|
||||
``select-audio-device``
|
||||
Select an audio device.
|
||||
|
||||
``select-watch-history``
|
||||
Select a file from the watch history. Requires ``--save-watch-history``.
|
||||
|
||||
If you don't already use ``--autocreate-playlist``, it is recommended to
|
||||
enable it with this script binding to populate the playlist with the other
|
||||
files in the entry's directory.
|
||||
|
||||
.. admonition:: Example for input.conf to play files adjacent to the history entry
|
||||
|
||||
g-h script-binding select/select-watch-history; no-osd set autocreate-playlist filter
|
||||
|
||||
``select-watch-later``
|
||||
Select a file from watch later config files (see `RESUMING PLAYBACK`_) to
|
||||
resume playing. Requires ``--write-filename-in-watch-later-config``. This
|
||||
doesn't work with ``--ignore-path-in-watch-later-config``.
|
||||
|
||||
If you don't already use ``--autocreate-playlist``, it is recommended to
|
||||
enable it with this script binding to populate the playlist with the other
|
||||
files in the entry's directory.
|
||||
|
||||
.. admonition:: Example for input.conf to play files adjacent to the watch later entry
|
||||
|
||||
g-w script-binding select/select-watch-later; no-osd set autocreate-playlist filter
|
||||
|
||||
``select-binding``
|
||||
List the defined input bindings. You can also select one to run the
|
||||
associated command.
|
||||
|
||||
``show-properties``
|
||||
List the names and values of all properties. You can also select one to
|
||||
print its value on the OSD, which is useful for long values that get
|
||||
clipped.
|
||||
|
||||
``edit-config-file``
|
||||
Open ``mpv.conf`` in the system text editor, creating it if it doesn't
|
||||
already exist.
|
||||
|
||||
``edit-input-conf``
|
||||
Open ``input.conf`` in the system text editor, creating it if it doesn't
|
||||
already exist.
|
||||
|
||||
``open-docs``
|
||||
Open mpv's online documentation in the browser.
|
||||
|
||||
``menu``
|
||||
Show a menu with miscellaneous entries.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This script can be customized through a config file ``script-opts/select.conf``
|
||||
placed in mpv's user directory and through the ``--script-opts`` command-line
|
||||
option. The configuration syntax is described in `mp.options functions`_.
|
||||
|
||||
Configurable options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``history_date_format``
|
||||
Default: %Y-%m-%d %H:%M:%S
|
||||
|
||||
The format of dates of history entries. This is passed to Lua's ``os.date``,
|
||||
which uses the same formats as ``strftime(3)``.
|
||||
|
||||
``hide_history_duplicates``
|
||||
Default: yes
|
||||
|
||||
Whether to show only the last of history entries with the same path.
|
||||
@@ -1,304 +0,0 @@
|
||||
STATS
|
||||
=====
|
||||
|
||||
This builtin script displays information and statistics for the currently
|
||||
played file. It is enabled by default if mpv was compiled with Lua support.
|
||||
It can be disabled entirely using the ``--load-stats-overlay=no`` option.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The following key bindings are active by default unless something else is
|
||||
already bound to them:
|
||||
|
||||
==== ==============================================
|
||||
i Show stats for a fixed duration
|
||||
I Toggle stats (shown until toggled again)
|
||||
? Toggle displaying the key bindings
|
||||
==== ==============================================
|
||||
|
||||
While the stats are visible on screen the following key bindings are active,
|
||||
regardless of existing bindings. They allow you to switch between *pages* of
|
||||
stats:
|
||||
|
||||
==== ==================
|
||||
1 Show usual stats
|
||||
2 Show frame timings (scroll)
|
||||
3 Input cache stats
|
||||
4 Active key bindings (scroll)
|
||||
5 Selected Tracks Info (scroll)
|
||||
0 Internal stuff (scroll)
|
||||
==== ==================
|
||||
|
||||
If stats were displayed by toggling, these key bindings are also active:
|
||||
|
||||
==== ==================
|
||||
ESC Close the stats
|
||||
==== ==================
|
||||
|
||||
On pages which support scroll, these key bindings are also active:
|
||||
|
||||
==== ==================
|
||||
UP Scroll one line up
|
||||
DOWN Scroll one line down
|
||||
==== ==================
|
||||
|
||||
On page 4, these key bindings are also active:
|
||||
|
||||
==== ==================
|
||||
/ Search key bindings
|
||||
==== ==================
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This script can be customized through a config file ``script-opts/stats.conf``
|
||||
placed in mpv's user directory and through the ``--script-opts`` command-line
|
||||
option. The configuration syntax is described in `mp.options functions`_.
|
||||
|
||||
Configurable Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``key_page_1``
|
||||
Default: 1
|
||||
``key_page_2``
|
||||
Default: 2
|
||||
``key_page_3``
|
||||
Default: 3
|
||||
``key_page_4``
|
||||
Default: 4
|
||||
``key_page_5``
|
||||
Default: 5
|
||||
``key_page_0``
|
||||
Default: 0
|
||||
``key_exit``
|
||||
Default: ESC
|
||||
|
||||
Key bindings for page switching while stats are displayed.
|
||||
|
||||
``key_scroll_up``
|
||||
Default: UP
|
||||
``key_scroll_down``
|
||||
Default: DOWN
|
||||
``key_scroll_search``
|
||||
Default: /
|
||||
``scroll_lines``
|
||||
Default: 1
|
||||
|
||||
Scroll key bindings and number of lines to scroll on pages which support it.
|
||||
|
||||
``duration``
|
||||
Default: 4
|
||||
|
||||
How long the stats are shown in seconds (oneshot).
|
||||
|
||||
``redraw_delay``
|
||||
Default: 1
|
||||
|
||||
How long it takes to refresh the displayed stats in seconds (toggling).
|
||||
|
||||
``persistent_overlay``
|
||||
Default: no
|
||||
|
||||
When `no`, other scripts printing text to the screen can overwrite the
|
||||
displayed stats. When `yes`, displayed stats are persistently shown for the
|
||||
respective duration. This can result in overlapping text when multiple
|
||||
scripts decide to print text at the same time.
|
||||
|
||||
``file_tag_max_length``
|
||||
Default: 128
|
||||
|
||||
Only show file tags shorter than this length, in bytes.
|
||||
|
||||
``file_tag_max_count``
|
||||
Default: 16
|
||||
|
||||
Only show the first specified amount of file tags.
|
||||
|
||||
``term_clip``
|
||||
Default: yes
|
||||
|
||||
Whether to clip lines to the terminal width.
|
||||
|
||||
``plot_perfdata``
|
||||
Default: no
|
||||
|
||||
Show graphs for performance data (page 2).
|
||||
|
||||
``plot_vsync_ratio``
|
||||
Default: no
|
||||
|
||||
``plot_vsync_jitter``
|
||||
Default: no
|
||||
|
||||
Show graphs for vsync and jitter values (page 1). Only when toggled.
|
||||
|
||||
``plot_cache``
|
||||
Default: yes
|
||||
|
||||
Show graphs for cache values (page 3). Only when toggled.
|
||||
|
||||
``plot_tonemapping_lut``
|
||||
Default: no
|
||||
|
||||
Enable tone-mapping LUT visualization automatically. Only when toggled.
|
||||
|
||||
``flush_graph_data``
|
||||
Default: yes
|
||||
|
||||
Clear data buffers used for drawing graphs when toggling.
|
||||
|
||||
``font``
|
||||
Default: same as ``osd-font``
|
||||
|
||||
Font name. Should support as many font weights as possible for optimal
|
||||
visual experience.
|
||||
|
||||
``font_mono``
|
||||
Default: monospace
|
||||
|
||||
Font name for parts where monospaced characters are necessary to align
|
||||
text. Currently, monospaced digits are sufficient.
|
||||
|
||||
``font_size``
|
||||
Default: 20
|
||||
|
||||
Font size used to render text.
|
||||
|
||||
``font_color``
|
||||
Default: same as ``osd-color``
|
||||
|
||||
Color of the text.
|
||||
|
||||
``border_size``
|
||||
Default: 1.65
|
||||
|
||||
Size of border drawn around the font.
|
||||
|
||||
``border_color``
|
||||
Default: same as ``osd-border-color``
|
||||
|
||||
Color of the text border.
|
||||
|
||||
``shadow_x_offset``
|
||||
Default: same as ``--osd-shadow-offset``
|
||||
|
||||
The horizontal distance from the text to position the shadow at.
|
||||
|
||||
``shadow_y_offset``
|
||||
Default: same as ``--osd-shadow-offset``
|
||||
|
||||
The vertical distance from the text to position the shadow at.
|
||||
|
||||
``shadow_color``
|
||||
Default: same as ``osd-shadow-color``
|
||||
|
||||
Color of the text shadow.
|
||||
|
||||
``alpha``
|
||||
Default: 11
|
||||
|
||||
Transparency of text when ``font_color`` is specified, of text borders when
|
||||
``border_color`` is specified, and of text shadows when ``shadow_color`` is
|
||||
specified.
|
||||
|
||||
``plot_bg_border_color``
|
||||
Default: 0000FF
|
||||
|
||||
Border color used for drawing graphs.
|
||||
|
||||
``plot_bg_border_width``
|
||||
Default: 1.25
|
||||
|
||||
Border width used for drawing graphs.
|
||||
|
||||
``plot_bg_color``
|
||||
Default: 262626
|
||||
|
||||
Background color used for drawing graphs.
|
||||
|
||||
``plot_color``
|
||||
Default: FFFFFF
|
||||
|
||||
Color used for drawing graphs.
|
||||
|
||||
``vidscale``
|
||||
Default: auto
|
||||
|
||||
Scale the text and graphs with the video.
|
||||
``no`` tries to keep the sizes constant.
|
||||
``auto`` scales the text and graphs with the OSD, which is scaled with the
|
||||
window or kept at a constant size, depending on the ``--osd-scale-by-window`` option.
|
||||
|
||||
Note: colors are given as hexadecimal values and use ASS tag order: BBGGRR
|
||||
(blue green red).
|
||||
|
||||
Different key bindings
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Additional keys can be configured in ``input.conf`` to display the stats::
|
||||
|
||||
e script-binding stats/display-stats
|
||||
E script-binding stats/display-stats-toggle
|
||||
|
||||
And to display a certain page directly::
|
||||
|
||||
i script-binding stats/display-page-1
|
||||
h script-binding stats/display-page-4-toggle
|
||||
|
||||
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-opt=stats-bindlist=yes`` will print the list to
|
||||
the terminal and quit immediately. Long lines are clipped to the terminal width
|
||||
unless this is disabled with ``--script-opt=stats-term_clip=no``. Escape
|
||||
sequences can be disabled by adding ``-`` before ``yes``, i.e.
|
||||
``--script-opt=stats-bindlist=-yes``.
|
||||
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Most entries shown on this page have rather vague meaning. Likely none of this
|
||||
is useful for you. Don't attempt to use it. Forget its existence.
|
||||
|
||||
Selecting this for the first time will start collecting some internal
|
||||
performance data. That means performance will be slightly lower than normal for
|
||||
the rest of the time the player is running (even if the stats page is closed).
|
||||
Note that the stats page itself uses a lot of CPU and even GPU resources, and
|
||||
may have a heavy impact on performance.
|
||||
|
||||
The displayed information is accumulated over the redraw delay (shown as
|
||||
``poll-time`` field).
|
||||
|
||||
This adds entries for each Lua script. If there are too many scripts running,
|
||||
parts of the list will simply be out of the screen, but it can be scrolled.
|
||||
|
||||
If the underlying platform does not support pthread per thread times, the
|
||||
displayed times will be 0 or something random (I suspect that at time of this
|
||||
writing, only Linux provides the correct via pthread APIs for per thread times).
|
||||
|
||||
Most entries are added lazily and only during data collection, which is why
|
||||
entries may pop up randomly after some time. It's also why the memory usage
|
||||
entries for scripts that have been inactive since the start of data collection
|
||||
are missing.
|
||||
|
||||
Memory usage is approximate and does not reflect internal fragmentation.
|
||||
|
||||
JS scripts memory reporting is disabled by default because collecting the data
|
||||
at the JS side has an overhead and will increase memory usage. It can be
|
||||
enabled by setting the ``--js-memory-report`` option before starting mpv.
|
||||
|
||||
If entries have ``/time`` and ``/cpu`` variants, the former gives the real time
|
||||
(monotonic clock), while the latter the thread CPU time (only if the
|
||||
corresponding pthread API works and is supported).
|
||||
+484
-544
File diff suppressed because it is too large.
Load diff
+916
-488
File diff suppressed because it is too large.
Load diff
+27
-31
@@ -7,11 +7,8 @@ large part of which is incompatible or completely changes how the player
|
||||
behaves. Although there are still many similarities to its ancestors, **mpv**
|
||||
should generally be treated as a completely different program.
|
||||
|
||||
.. admonition:: Warning
|
||||
|
||||
This document is **not updated** anymore, and is **incomplete** and
|
||||
**outdated**. If you look for old option replacements, always check with
|
||||
the current mpv manpage, as the options could have changed meanwhile.
|
||||
.. note::
|
||||
These lists are incomplete.
|
||||
|
||||
General Changes from MPlayer to mpv
|
||||
-----------------------------------
|
||||
@@ -33,11 +30,11 @@ Player
|
||||
* Slave mode compatibility broken (see below).
|
||||
* Re-enable screensaver while the player is paused.
|
||||
* Allow resuming playback at a later point with ``Shift+q``, also see the
|
||||
``quit-watch-later`` input command.
|
||||
``quit_watch_later`` input command.
|
||||
* ``--keep-open`` option to stop the player from closing the window and
|
||||
exiting after playback ends.
|
||||
* A client API, that allows embedding **mpv** into applications
|
||||
(see ``mpv/client.h`` in the sources).
|
||||
(see ``libmpv/client.h`` in the sources).
|
||||
|
||||
Input
|
||||
~~~~~
|
||||
@@ -52,7 +49,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.
|
||||
@@ -76,16 +73,15 @@ Video
|
||||
|
||||
* Wayland support.
|
||||
* Native support for VAAPI and VDA. Improved VDPAU video output.
|
||||
* Improved GPU-accelerated video output (see the ``gpu-hq`` preset).
|
||||
* Make hardware decoding work with the ``gpu`` video output.
|
||||
* Improved OpenGL output (see the ``opengl-hq`` video output).
|
||||
* Make hardware decoding work with the ``opengl`` video output.
|
||||
* Support for libavfilter (for video->video and audio->audio). This allows
|
||||
using most of FFmpeg's filters, which improve greatly on the old MPlayer
|
||||
filters in features, performance, and correctness.
|
||||
* More correct color reproduction (color matrix generation), including support
|
||||
for BT.2020 (Ultra HD). linear XYZ (Digital Cinema) and SMPTE ST2084 (HDR)
|
||||
inputs.
|
||||
for BT.2020 (Ultra HD) and linear XYZ (Digital Cinema) inputs.
|
||||
* Support for color managed displays, via ICC profiles.
|
||||
* High-quality image resamplers (see the ``--scale`` suboption).
|
||||
* High-quality image resamplers (see the ``opengl`` ``scale`` suboption).
|
||||
* Support for scaling in (sigmoidized) linear light.
|
||||
* Better subtitle rendering using libass by default.
|
||||
* Improvements when playing multiple files (``-fixed-vo`` is default, do not
|
||||
@@ -135,15 +131,16 @@ Miscellaneous
|
||||
* Support for precise scrolling which scales the parameter of commands. If the
|
||||
input doesn't support precise scrolling the scale factor stays 1.
|
||||
* Allow changing/adjusting video filters at runtime. (This is also used to make
|
||||
the ``D`` key insert vf_bwdif if deinterlacing is not supported otherwise).
|
||||
the ``D`` key insert vf_yadif if deinterlacing is not supported otherwise).
|
||||
* Improved support for .cue files.
|
||||
|
||||
macOS
|
||||
Mac OS X
|
||||
~~~~~~~~
|
||||
|
||||
* Native OpenGL backend.
|
||||
* Cocoa event loop is independent from MPlayer's event loop, so user
|
||||
actions like accessing menus and live resizing do not block the playback.
|
||||
* Apple Remote support.
|
||||
* Media Keys support.
|
||||
* VDA support using libavcodec hwaccel API instead of FFmpeg's decoder with up
|
||||
to 2-2.5x reduction in CPU usage.
|
||||
@@ -177,7 +174,7 @@ Internal changes
|
||||
* General code cleanups (including refactoring or rewrites of many parts).
|
||||
* New build system.
|
||||
* Many bug fixes and removal of long-standing issues.
|
||||
* Generally preferring FFmpeg over internal demuxers, decoders, and
|
||||
* Generally preferring FFmpeg/Libav over internal demuxers, decoders, and
|
||||
filters.
|
||||
|
||||
Detailed Listing of User-visible Changes
|
||||
@@ -211,10 +208,10 @@ Command Line Switches
|
||||
``-no<opt>`` ``--no-<opt>`` (add a dash)
|
||||
``-a52drc level`` ``--ad-lavc-ac3drc=level``
|
||||
``-ac spdifac3`` ``--ad=spdif:ac3`` (see ``--ad=help``)
|
||||
``-af volnorm`` (removed; use acompressor ffmpeg filter instead)
|
||||
``-af volnorm`` ``--af=drc`` (renamed)
|
||||
``-afm hwac3`` ``--ad=spdif:ac3,spdif:dts``
|
||||
``-ao alsa:device=hw=0.3`` ``--ao=alsa:device=[hw:0,3]``
|
||||
``-aspect`` ``--video-aspect-override``
|
||||
``-aspect`` ``--video-aspect``
|
||||
``-ass-bottom-margin`` ``--vf=sub=bottom:top``
|
||||
``-ass`` ``--sub-ass``
|
||||
``-audiofile-cache`` (removed; the main cache settings are used)
|
||||
@@ -229,7 +226,7 @@ Command Line Switches
|
||||
``-endpos`` ``--length``
|
||||
``-fixed-vo`` (removed; always the default)
|
||||
``-font`` ``--osd-font``
|
||||
``-forcedsubsonly`` ``--sub-forced-events-only``
|
||||
``-forcedsubsonly`` ``--sub-forced-only``
|
||||
``-forceidx`` ``--index``
|
||||
``-format`` ``--audio-format``
|
||||
``-fsmode-dontuse`` (removed)
|
||||
@@ -247,7 +244,7 @@ Command Line Switches
|
||||
``-msglevel`` ``--msg-level`` (changed semantics)
|
||||
``-msgmodule`` ``--msg-module``
|
||||
``-name`` ``--x11-name``
|
||||
``-noar`` ``(removed; replaced by MediaPlayer framework)``
|
||||
``-noar`` ``--no-input-appleremote``
|
||||
``-noautosub`` ``--no-sub-auto``
|
||||
``-noconsolecontrols`` ``--no-input-terminal``
|
||||
``-nosound`` ``--no-audio``
|
||||
@@ -264,14 +261,13 @@ Command Line Switches
|
||||
``-ssf <sub>`` ``--sws-...``
|
||||
``-stop-xscreensaver`` ``--stop-screensaver``
|
||||
``-sub-fuzziness`` ``--sub-auto``
|
||||
``-sub-text-*`` ``--sub-*``
|
||||
``-sub`` ``--sub-file``
|
||||
``-subcp`` ``--sub-codepage``
|
||||
``-subdelay`` ``--sub-delay``
|
||||
``-subfile`` ``--sub-file``
|
||||
``-subfont-*`` ``--sub-*``, ``--osd-*``
|
||||
``-subfont-*`` ``--sub-text-*``, ``--osd-*``
|
||||
``-subfont-text-scale`` ``--sub-scale``
|
||||
``-subfont`` ``--sub-font``
|
||||
``-subfont`` ``--sub-text-font``
|
||||
``-subfps`` ``--sub-fps``
|
||||
``-subpos`` ``--sub-pos``
|
||||
``-sws`` ``--sws-scaler``
|
||||
@@ -315,15 +311,15 @@ input.conf and Slave Commands
|
||||
+--------------------------------+----------------------------------------+
|
||||
| Old | New |
|
||||
+================================+========================================+
|
||||
| ``pt_step 1 [0|1]`` | ``playlist-next [weak|force]`` |
|
||||
| ``pt_step 1 [0|1]`` | ``playlist_next [weak|force]`` |
|
||||
| | (translation layer cannot deal with |
|
||||
| | whitespace) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``pt_step -1 [0|1]`` | ``playlist-prev [weak|force] (same)`` |
|
||||
| ``pt_step -1 [0|1]`` | ``playlist_prev [weak|force] (same)`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``switch_ratio [<ratio>]`` | ``set video-aspect-override <ratio>`` |
|
||||
| ``switch_ratio [<ratio>]`` | ``set video-aspect <ratio>`` |
|
||||
| | |
|
||||
| | ``set video-aspect-override 0`` (reset)|
|
||||
| | ``set video-aspect 0`` (reset aspect) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``step_property_osd <prop>`` | ``cycle <prop> <step>`` (wraps), |
|
||||
| ``<step> <dir>`` | ``add <prop> <step>`` (clamps). |
|
||||
@@ -334,7 +330,7 @@ input.conf and Slave Commands
|
||||
| ``<step> <dir>`` | ``no-osd``: ``no-osd cycle <prop>`` |
|
||||
| | ``<step>`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``osd_show_property_text`` | ``show-text <text>`` |
|
||||
| ``osd_show_property_text`` | ``show_text <text>`` |
|
||||
| ``<text>`` | The property expansion format string |
|
||||
| | syntax slightly changed. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
@@ -343,9 +339,9 @@ input.conf and Slave Commands
|
||||
| | ``raw`` prefix to disable property |
|
||||
| | expansion. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``show_tracks`` | ``show-text ${track-list}`` |
|
||||
| ``show_tracks`` | ``show_text ${track-list}`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``show_chapters`` | ``show-text ${chapter-list}`` |
|
||||
| ``show_chapters`` | ``show_text ${chapter-list}`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| ``af_switch``, ``af_add``, ... | ``af set|add|...`` |
|
||||
+--------------------------------+----------------------------------------+
|
||||
@@ -410,7 +406,7 @@ Slave mode
|
||||
`JSON IPC`_ for more information.
|
||||
|
||||
* **mpv** also provides a client API, which can be used to embed the player
|
||||
by loading it as shared library. (See ``mpv/client.h`` in the sources.)
|
||||
by loading it as shared library. (See ``libmpv/client.h`` in the sources.)
|
||||
It might also be possible to implement a custom slave mode-like protocol
|
||||
using Lua scripting.
|
||||
|
||||
|
||||
+48
-77
@@ -1,15 +1,14 @@
|
||||
Release Policy
|
||||
==============
|
||||
|
||||
A few times a year, a new release is cut off of the master branch and
|
||||
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.
|
||||
There is only one release branch that keeps track of the latest version and
|
||||
will not be maintained separately.
|
||||
|
||||
The goal of releases is to provide Linux distributions with something to
|
||||
package. If you want the newest features, just use the master branch.
|
||||
@@ -20,123 +19,95 @@ Releases other than the latest release are unsupported and unmaintained.
|
||||
Release procedure
|
||||
-----------------
|
||||
|
||||
While on master:
|
||||
- Merge master into branch release/current.
|
||||
|
||||
- Update the `RELEASE_NOTES` file, replacing the previous release notes.
|
||||
- Create and/or update the `RELEASE_NOTES` file.
|
||||
|
||||
- Update the `MPV_VERSION` file.
|
||||
- Create and/or update the `VERSION` file.
|
||||
|
||||
- Update `DOCS/client-api-changes.rst` (in particular, update the last version
|
||||
number if necessary)
|
||||
- Update `DOCS/client-api-changes.rst` (on major releases).
|
||||
|
||||
- Run `TOOLS/gen-interface-changes.py` to refresh `DOCS/interface-changes.rst`,
|
||||
and edit manually as necessary.
|
||||
- Create tag v0.X.Y.
|
||||
|
||||
- Delete all `.txt` files in `DOCS/interface-changes/` except for `example.txt`.
|
||||
|
||||
- Create signed commit with changes.
|
||||
|
||||
- Create signed tag v0.X.Y.
|
||||
|
||||
- Push release branch (`release/0.X`) and tag to GitHub.
|
||||
- Push branch and tag to GitHub.
|
||||
|
||||
- Create a new GitHub release using the content of `RELEASE_NOTES` related to
|
||||
the new version. Check the "Create a discussion for this release" box.
|
||||
|
||||
- Re-add -UNKNOWN suffix to version in `MPV_VERSION` file and commit.
|
||||
|
||||
If necessary (e.g. to 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.
|
||||
the new version.
|
||||
|
||||
Release notes template
|
||||
----------------------
|
||||
|
||||
Here is a template that should 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
|
||||
=============
|
||||
|
||||
We are excited to announce the release of mpv 0.X.Y.
|
||||
Features
|
||||
--------
|
||||
|
||||
Key highlights:
|
||||
|
||||
* List of notable stuff
|
||||
|
||||
This release requires FFmpeg <ver> or newer and libplacebo <ver> or newer.
|
||||
|
||||
# Features
|
||||
|
||||
## New
|
||||
New
|
||||
~~~
|
||||
|
||||
- List of new features
|
||||
|
||||
|
||||
## Changed
|
||||
|
||||
- List of changed features
|
||||
|
||||
|
||||
## Removed
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- List of removed features
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
# Options and Commands
|
||||
- List of deprecated features
|
||||
|
||||
## Added
|
||||
Behavior
|
||||
--------
|
||||
|
||||
- List of user-visible changes in behavior
|
||||
|
||||
Options and Commands
|
||||
--------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- List of added options and commands
|
||||
|
||||
|
||||
## Changed
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- List of changed options and commands
|
||||
|
||||
Renamed
|
||||
~~~~~~~
|
||||
|
||||
## Deprecated
|
||||
- List of renamed options and commands
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- List of deprecated options and commands
|
||||
|
||||
|
||||
## Removed
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- List of removed options and commands
|
||||
|
||||
|
||||
# Fixes and Minor Enhancements
|
||||
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 or by visiting
|
||||
<https://github.com/mpv-player/mpv/compare/start...end>.
|
||||
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 lists all changes since the last release, including those
|
||||
that have been backported to patch releases already. Except for the
|
||||
"Key highlights" section they are ordered chronologically with older commits
|
||||
at the top.
|
||||
|
||||
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
|
||||
+86
-476
@@ -11,11 +11,11 @@ player/*.c:
|
||||
* main():
|
||||
* basic initializations (e.g. init_libav() and more)
|
||||
* pre-parse command line (verbosity level, config file locations)
|
||||
* load config files (mp_parse_cfgfiles())
|
||||
* load config files (parse_cfgfiles())
|
||||
* parse command line, add files from the command line to playlist
|
||||
(m_config_parse_mp_command_line())
|
||||
* check help options etc. (call handle_help_options()), possibly exit
|
||||
* call mp_play_files() function that works down the playlist:
|
||||
* call play_files() function that works down the playlist:
|
||||
* run idle loop (idle_loop()), until there are files in the
|
||||
playlist or an exit command was given (only if --idle it set)
|
||||
* actually load and play a file in play_current_file():
|
||||
@@ -30,7 +30,7 @@ player/*.c:
|
||||
* determine next entry on the playlist to play
|
||||
* loop, or exit if no next file or quit is requested
|
||||
(see enum stop_play_reason)
|
||||
* call mp_destroy()
|
||||
* call exit_player_with_rc()
|
||||
* run_playloop():
|
||||
* calls fill_audio_out_buffers()
|
||||
This checks whether new audio needs to be decoded, and pushes it
|
||||
@@ -44,9 +44,11 @@ player/*.c:
|
||||
|
||||
Things worth saying about the playback core:
|
||||
- most state is in MPContext (core.h), which is not available to the
|
||||
subsystems (and should not be made available)
|
||||
- the currently played tracks are in mpctx->current_track, and decoder
|
||||
state in track.dec/d_sub
|
||||
subsystems
|
||||
- the currently played tracks are in mpctx->current_tracks, and decoder
|
||||
state in d_video/d_audio/d_sub
|
||||
- the timeline stuff is used only with MKV ordered chapters (and some other
|
||||
minor features: cue, edl)
|
||||
- the other subsystems rarely call back into the frontend, and the frontend
|
||||
polls them instead (probably a good thing)
|
||||
- one exceptions are wakeup callbacks, which notify a "higher" component
|
||||
@@ -54,15 +56,23 @@ player/*.c:
|
||||
|
||||
I like to call the player/*.c files the "frontend".
|
||||
|
||||
ta.h & ta.c:
|
||||
Hierarchical memory manager inspired by talloc from Samba. It's like a
|
||||
malloc() with more features. Most importantly, each talloc allocation can
|
||||
have a parent, and if the parent is free'd, all children will be free'd as
|
||||
well. The parent is an arbitrary talloc allocation. It's either set by the
|
||||
allocation call by passing a talloc parent, usually as first argument to the
|
||||
allocation function. It can also be set or reset later by other calls (at
|
||||
least talloc_steal()). A talloc allocation that is used as parent is often
|
||||
called a talloc context.
|
||||
talloc.h & talloc.c:
|
||||
Hierarchical memory manager copied from Samba. It's like a malloc() with
|
||||
more features. Most importantly, each talloc allocation can have a parent,
|
||||
and if the parent is free'd, all children will be free'd as well. The
|
||||
parent is an arbitrary talloc allocation. It's either set by the allocation
|
||||
call by passing a talloc parent, usually as first argument to the allocation
|
||||
function. It can also be set or reset later by other calls (at least
|
||||
talloc_steal()). A talloc allocation that is used as parent is often called
|
||||
a talloc context.
|
||||
|
||||
Lots of code still uses malloc() proper, and you should be careful what
|
||||
type of allocation you're dealing with when returning or free'ing an
|
||||
allocation. (Needless to say, talloc_free() and free() are completely
|
||||
different things.)
|
||||
|
||||
The copy in mpv has been modified to abort on OOM conditions. An
|
||||
allocation call will never return NULL.
|
||||
|
||||
One very useful feature of talloc is fast tracking of memory leaks. ("Fast"
|
||||
as in it doesn't require valgrind.) You can enable it by setting the
|
||||
@@ -73,14 +83,16 @@ ta.h & ta.c:
|
||||
Documentation can be found here:
|
||||
http://git.samba.org/?p=samba.git;a=blob;f=lib/talloc/talloc.h;hb=HEAD
|
||||
|
||||
For some reason, we're still using API-compatible wrappers instead of TA
|
||||
directly. The talloc wrapper has only a subset of the functionality, and
|
||||
in particular the wrappers abort() on memory allocation failure.
|
||||
|
||||
Note: unlike tcmalloc, jemalloc, etc., talloc() is not actually a malloc
|
||||
replacement. It works on top of system malloc and provides additional
|
||||
features that are supposed to make memory management easier.
|
||||
|
||||
Warning: actually, we're not using talloc anymore. talloc in mpv has been
|
||||
replaced by a custom re-implementation (TA in ta/). It provides
|
||||
some talloc emulation (just the parts needed by mpv). We will get
|
||||
rid of the talloc emulation later and use TA natively.
|
||||
(See ta/README for details.)
|
||||
|
||||
player/command.c:
|
||||
This contains the implementation for client API commands and properties.
|
||||
Properties are essentially dynamic variables changed by certain commands.
|
||||
@@ -93,7 +105,7 @@ player/core.h:
|
||||
usually not accessed by other parts of mpv for the sake of modularization.
|
||||
|
||||
player/client.c:
|
||||
This implements the client API (mpv/client.h). For the most part, this
|
||||
This implements the client API (libmpv/client.h). For the most part, this
|
||||
just calls into other parts of the player. This also manages a ringbuffer
|
||||
of events from player to clients.
|
||||
|
||||
@@ -103,48 +115,15 @@ options/options.h, options/options.c
|
||||
options.c. Most default values for options and MPOpts are in
|
||||
mp_default_opts at the end of options.c.
|
||||
|
||||
MPOpts is unfortunately quite monolithic, but is being incrementally broken
|
||||
up into sub-structs. Many components have their own sub-option structs
|
||||
separate from MPOpts. New options should be bound to the component that uses
|
||||
them. Add a new option table/struct if needed.
|
||||
MPOpts is unfortunately quite monolithic, and virtually accessed by
|
||||
everything.But some components (like video outputs and video filters) have
|
||||
their own sub-option tables separate from MPOpts.
|
||||
|
||||
The global MPOpts still contains the sub-structs as fields, which serves to
|
||||
link them to the option parser. For example, an entry like this may be
|
||||
typical:
|
||||
|
||||
{"", OPT_SUBSTRUCT(demux_opts, demux_conf)},
|
||||
|
||||
This directs the option access code to include all options in demux_conf
|
||||
into the global option list, with no prefix (""), and as part of the
|
||||
MPOpts.demux_opts field. The MPOpts.demux_opts field is actually not
|
||||
accessed anywhere, and instead demux.c does this:
|
||||
|
||||
struct m_config_cache *opts_cache =
|
||||
m_config_cache_alloc(demuxer, global, &demux_conf);
|
||||
struct demux_opts *opts = opts_cache->opts;
|
||||
|
||||
... to get a copy of its options.
|
||||
|
||||
See m_config_core.h (below) how to access options.
|
||||
|
||||
The actual option parser is spread over m_option.c, m_config_frontend.c,
|
||||
and parse_commandline.c, and uses the option table in options.c.
|
||||
|
||||
options/m_config_*.h & m_config_*.c:
|
||||
Code for querying and managing options. m_config_frontend.h contains
|
||||
declarations for the "legacy-ish" global m_config struct, while
|
||||
m_config_core.h provides ways to access options in a threads-safe way
|
||||
anywhere, like m_config_cache_alloc().
|
||||
|
||||
m_config_cache_alloc() lets anyone read, observe, and write options in any
|
||||
thread. The only state it needs is struct mpv_global, which is an opaque
|
||||
type that can be passed "down" the component hierarchy. For safety reasons,
|
||||
you should not pass down any pointers to option structs (like MPOpts), but
|
||||
instead pass down mpv_global, and use m_config_cache_alloc() (or similar)
|
||||
to get a synchronized copy of the options.
|
||||
The actual option parser is spread over m_option.c, m_config.c, and
|
||||
parser-mpcmd.c, and uses the option table in options.c.
|
||||
|
||||
input/input.c:
|
||||
This translates keyboard input coming from VOs and other sources (such
|
||||
This translates keyboard input comming from VOs and other sources (such
|
||||
as remote control devices like Apple IR or client API commands) to the
|
||||
key bindings listed in the user's (or the builtin) input.conf and turns
|
||||
them into items of type struct mp_cmd. These commands are queued, and read
|
||||
@@ -165,37 +144,30 @@ stream/*:
|
||||
E.g. if mpv sees "http://something" on the command line, it will pick
|
||||
stream_lavf.c based on the prefix, and pass the rest of the filename to it.
|
||||
|
||||
Some stream inputs are quite special: stream_dvdnav.c turns DVDs into mpeg
|
||||
Some stream inputs are quite special: stream_dvd.c turns DVDs into mpeg
|
||||
streams (DVDs are actually a bunch of vob files etc. on a filesystem),
|
||||
stream_tv.c provides TV input including channel switching.
|
||||
|
||||
Some stream inputs are just there to invoke special demuxers, like
|
||||
stream_mf.c. (Basically to make the prefix "mf://" do something special.)
|
||||
|
||||
cache.c is a caching wrapper around streams implementations, needed for
|
||||
smooth network playback.
|
||||
|
||||
demux/:
|
||||
Demuxers split data streams into audio/video/sub streams, which in turn
|
||||
are split in packets. Packets (see packet.h) are mostly byte chunks
|
||||
are split in packets. Packets (see demux_packet.h) are mostly byte chunks
|
||||
tagged with a playback time (PTS). These packets are passed to the decoders.
|
||||
|
||||
Most demuxers have been removed from this fork, and the only important and
|
||||
"actual" demuxers left are demux_mkv.c and demux_lavf.c (uses libavformat).
|
||||
There are some pseudo demuxers like demux_cue.c.
|
||||
There are some pseudo demuxers like demux_cue.c, which exist only to invoke
|
||||
other frontend code (tl_cue.c in this case).
|
||||
|
||||
The main interface is in demux.h. The stream headers are in stheader.h.
|
||||
There is a stream header for each audio/video/sub stream, and each of them
|
||||
holds codec information about the stream and other information.
|
||||
|
||||
demux.c is a bit big, the main reason being that it contains the demuxer
|
||||
cache, which is implemented as a list of packets. The cache is complex
|
||||
because it support seeking, multiple ranges, prefetching, and so on.
|
||||
|
||||
filters/:
|
||||
Filter related code. filter.c contains the generic filtering framework
|
||||
which converts input frames to output frames (audio, video, or demux
|
||||
packet data). f_decoder_wrapper.c is a source filter which connects the
|
||||
frontend with the actual audio and video decoders. f_output_chain.c handles
|
||||
VO/AO output conversions. f_autoconvert.c automatically inserts the
|
||||
appropriate conversion filters if format conversion is needed.
|
||||
|
||||
video/:
|
||||
This contains several things related to audio/video decoding, as well as
|
||||
video filters.
|
||||
@@ -204,36 +176,41 @@ video/:
|
||||
internally.
|
||||
|
||||
video/decode/:
|
||||
vd_*.c are video decoders. (There's only vd_lavc.c left.)
|
||||
vd_*.c are video decoders. (There's only vd_lavc.c left.) dec_video.c/vd.c
|
||||
handle most of connecting the frontend with the actual decoder.
|
||||
|
||||
video/filter/:
|
||||
vf_*.c are video filters. They are fed by the video decoder, and output the
|
||||
filtered images to the VOs. By default, no video filters are used.
|
||||
vf_*.c and vf.c form the video filter chain. They are fed by the video
|
||||
decoder, and output the filtered images to the VOs though vf_vo.c. By
|
||||
default, no video filters (except vf_vo) are used. vf_scale is automatically
|
||||
inserted if the video output can't handle the video format used by the
|
||||
decoder.
|
||||
|
||||
video/out/:
|
||||
Video output. They also create GUI windows and handle user input. In most
|
||||
cases, the windowing code is shared among VOs, like x11_common.c for X11 and
|
||||
w32_common.c for Windows. The VOs stand between frontend and windowing code.
|
||||
vo_gpu and vo_gpu_next can pick a windowing system at runtime, e.g. the same
|
||||
binary can provide both X11 and Cocoa support on macOS.
|
||||
vo_opengl can pick a windowing system at runtime, e.g. the same binary can
|
||||
provide both X11 and Cocoa support on OSX.
|
||||
|
||||
VOs can be reconfigured at runtime. A vo_reconfig() call can change the video
|
||||
VOs can be reconfigured at runtime. A vo_config() call can change the video
|
||||
resolution and format, without destroying the window.
|
||||
|
||||
vo_gpu should be taken as reference.
|
||||
vo_vdpau and vo_opengl should be taken as reference.
|
||||
|
||||
audio/:
|
||||
format.h/format.c define the uncompressed audio formats. (As well as some
|
||||
compressed formats used for spdif.)
|
||||
|
||||
audio/decode/:
|
||||
ad_*.c handle audio decoding. ad_lavc.c is the decoder using ffmpeg.
|
||||
ad_spdif.c is not really a decoder, but is used for compressed audio
|
||||
passthrough.
|
||||
ad_*.c and dec_audio.c/ad.c handle audio decoding. ad_lavc.c is the
|
||||
decoder using ffmpeg. ad_spdif.c is not really a decoder, but is used for
|
||||
compressed audio passthrough.
|
||||
|
||||
audio/filter/:
|
||||
Audio filters. af_scaletempo2 is inserted by default if playback is different
|
||||
from normal speed.
|
||||
Audio filter chain. af_lavrresample is inserted if any form of conversion
|
||||
between audio formats is needed. (af_convert24.c and af_convertsignendian.c
|
||||
are also used for some formats not directly supported by FFmpeg.)
|
||||
|
||||
audio/out/:
|
||||
Audio outputs.
|
||||
@@ -241,9 +218,9 @@ audio/out/:
|
||||
Unlike VOs, AOs can't be reconfigured on a format change. On audio format
|
||||
changes, the AO will simply be closed and re-opened.
|
||||
|
||||
buffer.c is the wrapper to support for two types of audio APIs: push and
|
||||
pull. ao.c calls into that. It contains generic code to deal with the data
|
||||
flow these APIs impose.
|
||||
There are wrappers to support for two types of audio APIs: push.c and
|
||||
pull.c. ao.c calls into one of these. They contain generic code to deal
|
||||
with the data flow these APIs impose.
|
||||
|
||||
Note that mpv synchronizes the video to the audio. That's the reason
|
||||
why buggy audio drivers can have a bad influence on playback quality.
|
||||
@@ -260,397 +237,30 @@ sub/:
|
||||
in turn asks dec_sub.c for subtitle overlay bitmaps, which relays the
|
||||
request to one of the sd_*.c subtitle decoders/renderers.
|
||||
|
||||
Subtitle loading is in demux/. Normally, subtitles are loaded via demux_lavf.c.
|
||||
Subtitle loading is in demux/. The MPlayer subreader.c is mostly gone - parts
|
||||
of it survive in demux_subreader.c. It's used as last fallback, or to handle
|
||||
some text subtitle types on Libav. It should go away eventually. Normally,
|
||||
subtitles are loaded via demux_lavf.c.
|
||||
|
||||
The subtitles are passed to dec_sub.c and the subtitle decoders in sd_*.c
|
||||
as they are demuxed. All text subtitles are rendered by sd_ass.c. If text
|
||||
subtitles are not in the ASS format, the libavcodec subtitle converters are
|
||||
used (lavc_conv.c).
|
||||
subtitles are not in the ASS format, subtitle converters are inserted, for
|
||||
example sd_srt.c, which is used to convert SRT->ASS. sd_srt.c is also used
|
||||
as general converter for text->ASS (to prevent interpretation of text as
|
||||
ASS tags).
|
||||
|
||||
Text subtitles can be preloaded, in which case they are read fully as soon
|
||||
as the subtitle is selected. In this case, they are effectively stored in
|
||||
sd_ass.c's internal state.
|
||||
as the subtitle is selected, and then effectively stored in an ASS_Track.
|
||||
It's used for external text subtitles, and required to make codepage
|
||||
detection as well as timing postprocessing work. (Timing postprocessing
|
||||
removes tiny gaps or overlaps between subtitle events.)
|
||||
|
||||
player/timeline/:
|
||||
A timeline is the abstraction used by loadfile.c to combine several files
|
||||
into one seemingly linear video. It's mainly used for ordered chapters
|
||||
playback. The high level code to find and load other files containing the
|
||||
segments for playing an ordered chapters file is in tl_matroska.c.
|
||||
|
||||
etc/:
|
||||
The files input.conf and builtin.conf are actually integrated into the mpv
|
||||
binary by the build system. They contain the default configs and keybindings.
|
||||
|
||||
Best practices and Concepts within mpv
|
||||
======================================
|
||||
|
||||
General contribution etc.
|
||||
-------------------------
|
||||
|
||||
See: DOCS/contribute.md
|
||||
|
||||
Error checking
|
||||
--------------
|
||||
|
||||
If an error is relevant, it should be handled. If it's interesting, log the
|
||||
error. However, mpv often keeps errors silent and reports failures somewhat
|
||||
coarsely by propagating them upwards the caller chain. This is OK, as long as
|
||||
the errors are not very interesting, or would require a developer to debug it
|
||||
anyway (in which case using a debugger would be more convenient, and the
|
||||
developer would need to add temporary debug printfs to get extremely detailed
|
||||
information which would not be appropriate during normal operation).
|
||||
|
||||
Basically, keep a balance on error reporting. But always check them, unless you
|
||||
have a good argument not to.
|
||||
|
||||
Memory allocation errors (OOM) are a special class of errors. Normally such
|
||||
allocation failures are not handled "properly". Instead, abort() is called.
|
||||
(New code should use MP_HANDLE_OOM() for this.) This is done out of laziness and
|
||||
for convenience, and due to the fact that MPlayer/mplayer2 never handled it
|
||||
correctly. (MPlayer varied between handling it correctly, trying to do so but
|
||||
failing, and just not caring, while mplayer2 started using abort() for it.)
|
||||
|
||||
This is justifiable in a number of ways. Error handling paths are notoriously
|
||||
untested and buggy, so merely having them won't make your program more reliable.
|
||||
Having these error handling paths also complicates non-error code, due to the
|
||||
need to roll back state at any point after a memory allocation.
|
||||
|
||||
Take any larger body of code, that is supposed to handle OOM, and test whether
|
||||
the error paths actually work, for example by overriding malloc with a version
|
||||
that randomly fails. You will find bugs quickly, and often they will be very
|
||||
annoying to fix (if you can even reproduce them).
|
||||
|
||||
In addition, a clear indication that something went wrong may be missing. On
|
||||
error your program may exhibit "degraded" behavior by design. Consider a video
|
||||
encoder dropping frames somewhere in the middle of a video due to temporary
|
||||
allocation failures, instead of just exiting with an errors. In other cases, it
|
||||
may open conceptual security holes. Failing fast may be better.
|
||||
|
||||
mpv uses GPU APIs, which may be break on allocation errors (because driver
|
||||
authors will have the same issues as described here), or don't even have a real
|
||||
concept for dealing with OOM (OpenGL).
|
||||
|
||||
libmpv is often used by GUIs, which I predict always break if OOM happens.
|
||||
|
||||
Last but not least, OSes like Linux use "overcommit", which basically means that
|
||||
your program may crash any time OOM happens, even if it doesn't use malloc() at
|
||||
all!
|
||||
|
||||
But still, don't just assume malloc() always succeeds. Use MP_HANDLE_OOM(). The
|
||||
ta* APIs do this for you. The reason for this is that dereferencing a NULL
|
||||
pointer can have security relevant consequences if large offsets are involved.
|
||||
Also, a clear error message is better than a random segfault.
|
||||
|
||||
Some big memory allocations are checked anyway. For example, all code must
|
||||
assume that allocating video frames or packets can fail. (The above example
|
||||
of dropping video frames during encoding is entirely possible in mpv.)
|
||||
|
||||
Undefined behavior
|
||||
------------------
|
||||
|
||||
Undefined behavior (UB) is a concept in the C language. C is famous for being a
|
||||
language that makes it almost impossible to write working code, because
|
||||
undefined behavior is so easily triggered, compilers will happily abuse it to
|
||||
generate "faster" code, debugging tools will shout at you, and sometimes it
|
||||
even means your code doesn't work.
|
||||
|
||||
There is a lot of literature on this topic. Read it.
|
||||
|
||||
(In C's defense, UB exists in other languages too, but since they're not used
|
||||
for low level infrastructure, and/or these languages are at times not rigorously
|
||||
defined, simply nobody cares. However, the C standard committee is still guilty
|
||||
for not addressing this. I'll admit that I can't even tell from the standard's
|
||||
gibberish whether some specific behavior is UB or not. It's written like tax
|
||||
law.)
|
||||
|
||||
In mpv, we generally try to avoid undefined behavior. For one, we want portable
|
||||
and reliable operation. But more importantly, we want clean output from
|
||||
debugging tools, in order to find real bugs more quickly and effectively.
|
||||
|
||||
Avoid the "works in practice" argument. Once debugging tools come into play, or
|
||||
simply when "in practice" stops being true, this will all get back to you in a
|
||||
bad way.
|
||||
|
||||
Global state, library safety
|
||||
----------------------------
|
||||
|
||||
Mutable global state is when code uses global variables that are not read-only.
|
||||
This must be avoided in mpv. Always use context structs that the caller of
|
||||
your code needs to allocate, and whose pointers are passed to your functions.
|
||||
|
||||
Library safety means that your code (or library) can be used by a library
|
||||
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
|
||||
or other "global" resources. Typical examples include use of signals, simple
|
||||
global variables (like hsearch() in libc), or internal caches not protected by
|
||||
locks.
|
||||
|
||||
A surprisingly high number of libraries are not library safe because they need
|
||||
global initialization. Typically they provide an API function, which
|
||||
"initializes" the library, and which must be called before calling any other
|
||||
API functions. Often, you are to provide global configuration parameters, which
|
||||
can change the behavior of the library. If two libraries A and B use library C,
|
||||
but A and B initialize C with different parameters, something "bad" may happen.
|
||||
In addition, these global initialization functions are often not thread-safe. So
|
||||
if A and B try to initialize C at the same time (from different threads and
|
||||
without knowing about each other), it may cause undefined behavior. (libcurl is
|
||||
a good example of both of these issues. FFmpeg and some TLS libraries used to be
|
||||
affected, but improved.)
|
||||
|
||||
This is so bad because library A and B from the previous example most likely
|
||||
have no way to cooperate, because they're from different authors and have no
|
||||
business knowing each others. They'd need a library D, which wraps library C
|
||||
in a safe way. Unfortunately, typically something worse happens: libraries get
|
||||
"infected" by the unsafeness of its sub-libraries, and export a global init API
|
||||
just to initialize the sub-libraries. In the previous example, libraries A and B
|
||||
would export global init APIs just to init library C, even though the rest of
|
||||
A/B are clean and library safe. (Again, libcurl is an example of this, if you
|
||||
subtract other historic anti-features.)
|
||||
|
||||
The main problem with library safety is that its lack propagates to all
|
||||
libraries using the library.
|
||||
|
||||
We require libmpv to be library safe. This is not really possible, because some
|
||||
libraries are not library safe (FFmpeg, Xlib, partially ALSA). However, for
|
||||
ideological reasons, there is no global init API, and best effort is made to try
|
||||
to avoid problems.
|
||||
|
||||
libmpv has some features that are not library safe, but which are disabled by
|
||||
default (such as terminal usage aka stdout, or JSON IPC blocking SIGPIPE for
|
||||
internal convenience).
|
||||
|
||||
A notable, very disgustingly library unsafe behavior of libmpv is calling
|
||||
abort() on some memory allocation failure. See error checking section.
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
All logging and terminal output in mpv goes through the functions and macros
|
||||
provided in common/msg.h. This is in part for library safety, and in part to
|
||||
make sure users can silence all output, or to redirect the output elsewhere,
|
||||
like a log file or the internal console.lua script.
|
||||
|
||||
Locking
|
||||
-------
|
||||
|
||||
See generally available literature. In mpv, we use mp_thread for this.
|
||||
|
||||
Always keep locking clean. Don't skip locking just because it will work "in
|
||||
practice". (See undefined behavior section.) If your use case is simple, you may
|
||||
use C11 atomics, but most likely you will only hurt yourself and others.
|
||||
|
||||
Always make clear which fields in a struct are protected by which lock. If a
|
||||
field is immutable, or simply not thread-safe (e.g. state for a single worker
|
||||
thread), document it as well.
|
||||
|
||||
Internal mpv APIs are assumed to be not thread-safe by default. If they have
|
||||
special guarantees (such as being usable by more than one thread at a time),
|
||||
these should be explicitly documented.
|
||||
|
||||
All internal mpv APIs must be free of global state. Even if a component is not
|
||||
thread-safe, multiple threads can use _different_ instances of it without any
|
||||
locking.
|
||||
|
||||
On a side note, recursive locks may seem convenient at first, but introduce
|
||||
additional problems with condition variables and locking hierarchies. They
|
||||
should be avoided.
|
||||
|
||||
Locking hierarchy
|
||||
-----------------
|
||||
|
||||
A simple way to avoid deadlocks with classic locking is to define a locking
|
||||
hierarchy or lock order. If all threads acquire locks in the same order, no
|
||||
deadlocks will happen.
|
||||
|
||||
For example, a "leaf" lock is a lock that is below all other locks in the
|
||||
hierarchy. You can acquire it any time, as long as you don't acquire other
|
||||
locks while holding it.
|
||||
|
||||
Unfortunately, C has no way to declare or check the lock order, so you should at
|
||||
least document it.
|
||||
|
||||
In addition, try to avoid exposing locks to the outside. Making the declaration
|
||||
of a lock private to a specific .c file (and _not_ exporting accessors or
|
||||
lock/unlock functions that manipulate the lock) is a good idea. Your component's
|
||||
API may acquire internal locks, but should release them when returning. Keeping
|
||||
the entire locking in a single file makes it easy to check it.
|
||||
|
||||
Avoiding callback hell
|
||||
----------------------
|
||||
|
||||
mpv code is separated in components, like the "frontend" (i.e. MPContext mpctx),
|
||||
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,
|
||||
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
|
||||
queues, wakeup callbacks, and similar mechanisms.
|
||||
|
||||
Typically, a component provides an API, and does not know anything about its
|
||||
user. The API user (component higher in the hierarchy) polls the state of the
|
||||
lower component when needed.
|
||||
|
||||
This also enforces some level of modularization, and with some luck the locking
|
||||
hierarchy. (Basically, locks of lower components automatically become leaf
|
||||
locks.) Another positive effect is simpler memory management.
|
||||
|
||||
(Also see e.g.: http://250bpm.com/blog:24)
|
||||
|
||||
Wakeup callbacks
|
||||
----------------
|
||||
|
||||
This is a common concept in mpv. Even the public API uses it. It's used when an
|
||||
API has internal threads (or otherwise triggers asynchronous events), but the
|
||||
component call hierarchy needs to be kept. The wakeup callback is the only
|
||||
exception to the call hierarchy, and always calls up.
|
||||
|
||||
For example, vo spawns a thread that the API user (the mpv frontend) does not
|
||||
need to know about. vo simply provides a single-threaded API (or that looks like
|
||||
one). This API needs a way to notify the API user of new events. But the vo
|
||||
event producer is on the vo thread - it can't simply invoke a callback back into
|
||||
the API user, because then the API user has to deal with locking, despite not
|
||||
using threads. In addition, this will probably cause problems like mentioned in
|
||||
the "callback hell" section, especially lock order issues.
|
||||
|
||||
The solution is the wakeup callback. It merely unblocks the API user from
|
||||
waiting, and the API user then uses the normal vo API to examine whether or
|
||||
which state changed. As a concept, it documents what a wakeup callback is
|
||||
allowed to do and what not, to avoid the aforementioned problems.
|
||||
|
||||
Generally, you are not allowed to call any API from the wakeup callback. You
|
||||
just do whatever is needed to unblock your thread. For example, if it's waiting
|
||||
on a mutex/condition variable, acquire the mutex, set a change flag, signal
|
||||
the condition variable, unlock, return. (This mutex must not be held when
|
||||
calling the API. It must be a leaf lock.)
|
||||
|
||||
Restricting the wakeup callback like this sidesteps any reentrancy issues and
|
||||
other complexities. The API implementation can simply hold internal (and
|
||||
non-recursive) locks while invoking the wakeup callback.
|
||||
|
||||
The API user still needs to deal with locking (probably), but there's only the
|
||||
need to implement a single "receiver", that can handle the entire API of the
|
||||
used component. (Or multiple APIs - MPContext for example has only 1 wakeup
|
||||
callback that handles all AOs, VOs, input, demuxers, and more. It simple re-runs
|
||||
the playloop.)
|
||||
|
||||
You could get something more advanced by turning this into a message queue. The
|
||||
API would append a message to the queue, and the API user can read it. But then
|
||||
you still need a way to "wakeup" the API user (unless you force the API user
|
||||
to block on your API, which will make things inconvenient for the API user). You
|
||||
also need to worry about what happens if the message queue overruns (you either
|
||||
lose messages or have unbounded memory usage). In the mpv public API, the
|
||||
distinction between message queue and wakeup callback is sort of blurry, because
|
||||
it does provide a message queue, but an additional wakeup callback, so API
|
||||
users are not required to call mpv_wait_event() with a high timeout.
|
||||
|
||||
mpv itself prefers using wakeup callbacks over a generic event queue, because
|
||||
most times an event queue is not needed (or complicates things), and it is
|
||||
better to do it manually.
|
||||
|
||||
(You could still abstract the API user side of wakeup callback handling, and
|
||||
avoid reimplementing it all the time. Although mp_dispatch_queue already
|
||||
provides mechanisms for this.)
|
||||
|
||||
Condition variables
|
||||
-------------------
|
||||
|
||||
They're used whenever a thread needs to wait for something, without nonsense
|
||||
like sleep calls or busy waiting. mpv uses the mp_thread API for this.
|
||||
There's a lot of literature on condition variables, threading in general. Read it.
|
||||
|
||||
For initial understanding, it may be helpful to know that condition variables
|
||||
are not variables that signal a condition. mp_cond does not have any
|
||||
state per-se. Maybe mp_cond would better be named mp_interrupt,
|
||||
because its sole purpose is to interrupt a thread waiting via mp_cond_wait()
|
||||
(or similar). The "something" in "waiting for something" can be called
|
||||
predicate (to avoid confusing it with "condition"). Consult literature for the
|
||||
proper terms.
|
||||
|
||||
The very short version is...
|
||||
|
||||
Shared declarations:
|
||||
|
||||
mp_mutex lock;
|
||||
mp_cond cond_var;
|
||||
struct something state_var; // protected by lock, changes signaled by cond_var
|
||||
|
||||
Waiter thread:
|
||||
|
||||
mp_mutex_lock(&lock);
|
||||
|
||||
// Wait for a change in state_var. We want to wait until predicate_fulfilled()
|
||||
// returns true.
|
||||
// Must be a loop for 2 reasons:
|
||||
// 1. cond_var may be associated with other conditions too
|
||||
// 2. mp_cond_wait() can have sporadic wakeups
|
||||
while (!predicate_fulfilled(&state_var)) {
|
||||
// This unlocks, waits for cond_var to be signaled, and then locks again.
|
||||
// The _whole_ point of cond_var is that unlocking and waiting for the
|
||||
// signal happens atomically.
|
||||
mp_cond_wait(&cond_var, &lock);
|
||||
}
|
||||
|
||||
// Here you may react to the state change. The state cannot change
|
||||
// asynchronously as long as you still hold the lock (and didn't release
|
||||
// and reacquire it).
|
||||
// ...
|
||||
|
||||
mp_mutex_unlock(&lock);
|
||||
|
||||
Signaler thread:
|
||||
|
||||
mp_mutex_lock(&lock);
|
||||
|
||||
// Something changed. Update the shared variable with the new state.
|
||||
update_state(&state_var);
|
||||
|
||||
// Notify that something changed. This will wake up the waiter thread if
|
||||
// it's blocked in mp_cond_wait(). If not, nothing happens.
|
||||
mp_cond_broadcast(&cond_var);
|
||||
|
||||
// Fun fact: good implementations wake up the waiter only when the lock is
|
||||
// released, to reduce kernel scheduling overhead.
|
||||
mp_mutex_unlock(&lock);
|
||||
|
||||
Some basic rules:
|
||||
1. Always access your state under proper locking
|
||||
2. Always check your predicate before every call to mp_cond_wait()
|
||||
(And don't call mp_cond_wait() if the predicate is fulfilled.)
|
||||
3. Always call mp_cond_wait() in a loop
|
||||
(And only if your predicate failed without releasing the lock..)
|
||||
4. Always call mp_cond_broadcast()/_signal() inside of its associated
|
||||
lock
|
||||
|
||||
mpv sometimes violates rule 3, and leaves "retrying" (i.e. looping) to the
|
||||
caller.
|
||||
|
||||
Common pitfalls:
|
||||
- Thinking that mp_cond is some kind of semaphore, or holds any
|
||||
application state or the user predicate (it _only_ wakes up threads
|
||||
that are at the same time blocking on mp_cond_wait() and friends,
|
||||
nothing else)
|
||||
- Changing the predicate, but not updating all mp_cond_broadcast()/
|
||||
_signal() calls correctly
|
||||
- Forgetting that mp_cond_wait() unlocks the lock (other threads can
|
||||
and must acquire the lock)
|
||||
- Holding multiple nested locks while trying to wait (=> deadlock, violates
|
||||
the lock order anyway)
|
||||
- Waiting for a predicate correctly, but unlocking/relocking before acting
|
||||
on it (unlocking allows arbitrary state changes)
|
||||
- Confusing which lock/condition var. is used to manage a bit of state
|
||||
|
||||
Generally available literature probably has better examples and explanations.
|
||||
|
||||
Using condition variables the proper way is generally preferred over using more
|
||||
messy variants of them. (Just saying because on win32, "SetEvent" exists, and
|
||||
it's inferior to condition variables. Try to avoid the win32 primitives, even if
|
||||
you're dealing with Windows-only code.)
|
||||
|
||||
Threads
|
||||
-------
|
||||
|
||||
Threading should be conservatively used. Normally, mpv code pretends to be
|
||||
single-threaded, and provides thread-unsafe APIs. Threads are used coarsely,
|
||||
and if you can avoid messing with threads, you should. For example, VOs and AOs
|
||||
do not need to deal with threads normally, even though they run on separate
|
||||
threads. The glue code "isolates" them from any threading issues.
|
||||
The file input.conf is actually integrated into the mpv binary by the
|
||||
build system. It contains the default keybindings.
|
||||
@@ -0,0 +1,157 @@
|
||||
waf build system overview
|
||||
=========================
|
||||
|
||||
mpv's new build system is based on waf and it should completely replace the
|
||||
custom ./configure + Makefile based system inherited from MPlayer.
|
||||
|
||||
Goals and the choice of waf
|
||||
===========================
|
||||
|
||||
The new system comes with some goals, which can be summed up as: be as good as
|
||||
the old one at what it did well (customizability) and fix some of it's major
|
||||
shortcomings:
|
||||
|
||||
1) The build system must be uniform in how it handles any single feature check.
|
||||
Repetition and boilerplate have to be avoided.
|
||||
|
||||
When adding a new feature using the old configure, one had to add a fair
|
||||
amount of code to the shell script to do option parsing, detection of the
|
||||
feature and declaration of variables for the Makefile to pickup. The worst
|
||||
part is this pieces are spread apart in the configure and copy pasted for
|
||||
any single case. That brings us to..
|
||||
|
||||
2) --enable-feature has to be overridden by the user and helps them understand that
|
||||
they have libraries missing and should install them for the feature to be
|
||||
enabled.
|
||||
|
||||
3) Must be customizable, hackable, pleasant to the developer eyes and to work
|
||||
with in general.
|
||||
|
||||
4) Must have separate configuration and build steps.
|
||||
|
||||
Goal 2 comes as a given on pretty much any build system, since autotools made
|
||||
this behaviour very popular among users (and rightly so).
|
||||
|
||||
Goal 1+3 were somewhat harder to accomplish as it looks like all of the build
|
||||
systems we evaluated (waf included!) had problems with them. For reference we
|
||||
had proof of concept build systems with waf, CMake and autotools.
|
||||
|
||||
What puts waf apart from CMake and autotools, is that projects using it use
|
||||
Python to program their build system. Also while the Waf Book shows really
|
||||
simple API usages, you can write your own build system on top of waf that is
|
||||
tailored to the project's specific needs.
|
||||
|
||||
mpv's custom configure step on top of waf
|
||||
=========================================
|
||||
|
||||
To some extents mpv has a custom build system written on top of waf. This
|
||||
document will not go over the standard waf behaviour as that is documented in
|
||||
the `Waf book <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
|
||||
lists and depending on the check definition it calls into the actual waf API.
|
||||
|
||||
A simple example using pkg-config would be::
|
||||
|
||||
{
|
||||
'name': '--vdpau',
|
||||
'desc': 'VDPAU acceleration',
|
||||
'deps': [ 'x11' ],
|
||||
'func': check_pkg_config('vdpau', '>= 0.2'),
|
||||
}
|
||||
|
||||
This defines a feature called ``vdpau`` which can be enabled or disabled by
|
||||
the users with configure flags (that's the meaning of ``--``). This feature
|
||||
depends on another feature whose name is ``x11``, and the autodetection check
|
||||
consists of running ``pkg-config`` and looking for ``vdpau`` with version
|
||||
``>= 0.2``. If the check succeeds a ``#define HAVE_VDPAU 1`` will be added to
|
||||
``config.h``, if not ``#define HAVE_VDPAU 0`` will be added.
|
||||
|
||||
The defines names are automatically prepended with ``HAVE_``, capitalized and
|
||||
special characters are replaced with underscores. This happens in
|
||||
``waftools/inflectors.py``.
|
||||
|
||||
Mandatory fields:
|
||||
-----------------
|
||||
|
||||
``name``: indicates the unique identifier used by the custom dependency code
|
||||
to refer to a feature. If the unique identifier is prepended with ``--``
|
||||
the build system will also generate options for ``./waf configure`` so that
|
||||
the feature can be enabled and disabled.
|
||||
|
||||
``desc``: this is the textual representation of the feature used in the
|
||||
interactions with the users.
|
||||
|
||||
``func``: function that will perform the check. These functions are defined in
|
||||
``waftools/checks``. The reusable checks are all functions that return
|
||||
functions. The return functions will then be applied using waf's configuration
|
||||
context.
|
||||
|
||||
The source code for the reusable checks is a bit convoluted, but it should be
|
||||
easy to pick up their usage from the ``wscript``. Their signature mirrors
|
||||
the semantics of some of the shell functions used in mplayer.
|
||||
|
||||
If someone expresses some interest, I will extend this document with official
|
||||
documentation for each check function.
|
||||
|
||||
Optional fields
|
||||
---------------
|
||||
|
||||
``deps``: list of dependencies of this feature. It is a list of names of
|
||||
other features as defined in the ``name`` field (minus the eventual leading
|
||||
``--``). All of the dependencies must be satisfied. If they are not the check
|
||||
will be skipped without even running ``func``.
|
||||
|
||||
``deps_any``: like deps but it is satisfied even if only one of the dependencies
|
||||
is satisfied. You can think of ``deps`` as a 'and' condition and ``deps_any``
|
||||
as a 'or' condition.
|
||||
|
||||
``deps_neg``: like deps but it is satisfied when none of the dependencies is
|
||||
satisfied.
|
||||
|
||||
``req``: defaults to False. If set to True makes this feature a hard
|
||||
dependency of mpv (configuration will fail if autodetection fails). If set to
|
||||
True you must also provide ``fmsg``.
|
||||
|
||||
``fmsg``: string with the failure message in case a required dependency is not
|
||||
satisfied.
|
||||
|
||||
``os_specific_checks``: this takes a dictionary that has ``os-`` dependencies
|
||||
as keys (such as ``os-win32``), and by values has another dictionary that is
|
||||
merged on top of the current feature definition only for that specific OS.
|
||||
For example::
|
||||
|
||||
{
|
||||
'name': '--pthreads',
|
||||
'desc': 'POSIX threads',
|
||||
'func': check_pthreads,
|
||||
'os_specific_checks': {
|
||||
'os-win32': {
|
||||
'func': check_pthreads_w32_static.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
will override the value of ``func`` with ``check_pthreads_w32_static`` only
|
||||
if the target OS of the build is Windows.
|
||||
|
||||
``groups``: groups a dependency with another one. This can be used to disabled
|
||||
all the grouped dependencies with one ``--disable-``. At the moment this is
|
||||
only used for OpenGL backends, where you want to disable them when
|
||||
``--disable-gl`` is passed to the configure.
|
||||
|
||||
mpv's custom build step on top of waf
|
||||
=====================================
|
||||
|
||||
Build step is pretty much vanilla waf. The only difference being that the list
|
||||
of source files can contain both strings or tuples. If a tuple is found,
|
||||
the second element in the tuple will the used to match the features detected
|
||||
in the configure step (the ``name`` field described above). If this feature
|
||||
was not enabled during configure, the source file will not be compiled in.
|
||||
|
||||
All of the custom Python for this is inside the function ``filtered_sources``
|
||||
contained in the file ``waftools/dependencies.py``.
|
||||
|
||||
Also ``dependencies_use`` and ``dependencies_includes`` collect cflags and
|
||||
ldflags that were generated from the features checks in the configure step.
|
||||
+15
-13
@@ -1,8 +1,8 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
<https://fsf.org/>
|
||||
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,8 +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, see <https://www.gnu.org/licenses/>.
|
||||
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.
|
||||
|
||||
@@ -328,11 +330,11 @@ necessary. Here is a sample; alter the names:
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Moe Ghoul>, 1 April 1989
|
||||
Moe Ghoul, President of Vice
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
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.
|
||||
-501
@@ -1,501 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
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
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
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
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
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
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
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.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
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
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
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
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
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
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
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
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library 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.
|
||||
|
||||
This library 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 this library; if not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Moe Ghoul>, 1 April 1990
|
||||
Moe Ghoul, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
@@ -1 +0,0 @@
|
||||
0.41.0
|
||||
@@ -1,164 +1,142 @@
|
||||

|
||||

|
||||
|
||||
# mpv
|
||||
## mpv
|
||||
|
||||
--------------
|
||||
|
||||
|
||||
* [External links](#external-links)
|
||||
* [Overview](#overview)
|
||||
* [System requirements](#system-requirements)
|
||||
* [Downloads](#downloads)
|
||||
* [Changelog](#changelog)
|
||||
* [Compilation](#compilation)
|
||||
* [FFmpeg vs. Libav](#ffmpeg-vs-libav)
|
||||
* [Release cycle](#release-cycle)
|
||||
* [Bug reports](#bug-reports)
|
||||
* [Contributing](#contributing)
|
||||
* [License](#license)
|
||||
* [Contact](#contact)
|
||||
|
||||
|
||||
## External links
|
||||
|
||||
|
||||
* [Relation to MPlayer and mplayer2](#relation-to-mplayer-and-mplayer2)
|
||||
* [Wiki](https://github.com/mpv-player/mpv/wiki)
|
||||
* [User Scripts](https://github.com/mpv-player/mpv/wiki/User-Scripts)
|
||||
* [FAQ][FAQ]
|
||||
* [Manual](https://mpv.io/manual/master/)
|
||||
|
||||
* [Man pages](http://mpv.io/manual/master/)
|
||||
* [Contact](#contact)
|
||||
* [License](#license)
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
**mpv** is a free (as in freedom) media player for the command line. It supports
|
||||
a wide variety of media file formats, audio and video codecs, and subtitle types.
|
||||
|
||||
There is a [FAQ][FAQ].
|
||||
**mpv** is a media player based on MPlayer and mplayer2. It supports a wide
|
||||
variety of video file formats, audio and video codecs, and subtitle types.
|
||||
|
||||
Releases can be found on the [release list][releases].
|
||||
|
||||
## System requirements
|
||||
|
||||
- A not too ancient Linux (usually, only the latest releases of distributions
|
||||
are actively supported), Windows 10 1607 or later, or macOS 10.15 or later.
|
||||
- A somewhat capable CPU. Hardware decoding might help if the CPU is too slow to
|
||||
decode video in realtime, but must be explicitly enabled with the `--hwdec`
|
||||
option.
|
||||
- A not too crappy GPU. mpv's focus is not on power-efficient playback on
|
||||
embedded or integrated GPUs (for example, hardware decoding is not even
|
||||
enabled by default). Low power GPUs may cause issues like tearing, stutter,
|
||||
etc. On such GPUs, it's recommended to use `--profile=fast` for smooth playback.
|
||||
The main video output uses shaders for video rendering and scaling,
|
||||
rather than GPU fixed function hardware. On Windows, you might want to make
|
||||
sure the graphics drivers are current. In some cases, ancient fallback video
|
||||
output methods can help (such as `--vo=xv` on Linux), but this use is not
|
||||
- A not too ancient Linux, or Windows Vista or later, or OSX 10.8 or later.
|
||||
- A somewhat capable CPU. Hardware decoding might sometimes help if the CPU
|
||||
is too slow to decode video realtime, but must be explicitly enabled with
|
||||
the `--hwdec` option. On Windows, a CPU with SSE4 instruction set is required
|
||||
to get decent hardware decoding performance.
|
||||
- A not too crappy GPU. mpv is not intended to be used with bad GPUs. There are
|
||||
many caveats with drivers or system compositors causing tearing, stutter,
|
||||
etc. On Windows, you might want to make sure the graphics drivers are
|
||||
current, especially OpenGL. In some cases, ancient fallback video output
|
||||
methods can help (such as `--vo=xv` on Linux), but this use is not
|
||||
recommended or supported.
|
||||
|
||||
mpv does not go out of its way to break on older hardware or old, unsupported
|
||||
operating systems, but development is not done with them in mind. Keeping
|
||||
compatibility with such setups is not guaranteed. If things work, consider it
|
||||
a happy accident.
|
||||
|
||||
## Downloads
|
||||
|
||||
|
||||
For semi-official builds and third-party packages please see
|
||||
[mpv.io/installation](https://mpv.io/installation/).
|
||||
|
||||
## Changelog
|
||||
|
||||
|
||||
There is no complete changelog; however, changes to the player core interface
|
||||
are listed in the [interface changelog][interface-changes].
|
||||
|
||||
Changes to the C API are documented in the [client API changelog][api-changes].
|
||||
|
||||
The [release list][releases] has a summary of most of the important changes
|
||||
on every release.
|
||||
|
||||
Changes to the default key bindings are indicated in
|
||||
[restore-old-bindings.conf][restore-old-bindings].
|
||||
|
||||
Changes to the default OSC bindings are indicated in
|
||||
[restore-osc-bindings.conf][restore-osc-bindings].
|
||||
[mpv.io](http://mpv.io/installation/).
|
||||
|
||||
## Compilation
|
||||
|
||||
|
||||
Compiling with full features requires development files for several
|
||||
external libraries. Mpv requires [meson](https://mesonbuild.com/index.html)
|
||||
to build. Meson can be obtained from your distro or PyPI.
|
||||
external libraries. Below is a list of some important requirements.
|
||||
|
||||
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.
|
||||
The mpv build system uses *waf* but we don't store it in your source tree. The
|
||||
script './bootstrap.py' will download the latest version of waf that was tested
|
||||
with the build system.
|
||||
|
||||
Example:
|
||||
For a list of the available build options use `./waf configure --help`. If
|
||||
you think you have support for some feature installed but configure fails to
|
||||
detect it, the file `build/config.log` may contain information about the
|
||||
reasons for the failure.
|
||||
|
||||
meson setup build
|
||||
meson compile -C build
|
||||
meson install -C build
|
||||
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 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`.
|
||||
|
||||
For libplacebo, meson can use a git check out as a subproject for a convenient
|
||||
way to compile mpv if a sufficient libplacebo version is not easily available
|
||||
in the build environment. It will be statically linked with mpv. Example:
|
||||
|
||||
mkdir -p subprojects
|
||||
git clone https://code.videolan.org/videolan/libplacebo.git --depth=1 --recursive subprojects/libplacebo
|
||||
To build the software you can use `./waf build`: the result of the compilation
|
||||
will be located in `build/mpv`. You can use `./waf install` to install mpv
|
||||
to the *prefix* after it is compiled.
|
||||
|
||||
Essential dependencies (incomplete list):
|
||||
|
||||
- gcc or clang
|
||||
- X development headers (xlib, xrandr, xext, xscrnsaver, xpresent, libvdpau,
|
||||
libGL, GLX, EGL, xv, ...)
|
||||
- X development headers (xlib, X extensions, libvdpau, libGL, libXv, ...)
|
||||
- Audio output development headers (libasound/ALSA, pulseaudio)
|
||||
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavfilter
|
||||
and either libswresample or libavresample)
|
||||
- libplacebo
|
||||
At least FFmpeg 2.4.0 or Libav 11 is required.
|
||||
- zlib
|
||||
- iconv (normally provided by the system libc)
|
||||
- libass (OSD, OSC, text subtitles)
|
||||
- Lua (optional, required for the OSC pseudo-GUI and youtube-dl integration)
|
||||
- libjpeg (optional, used for screenshots only)
|
||||
- uchardet (optional, for subtitle charset detection)
|
||||
- nvdec and vaapi libraries for hardware decoding on Linux (optional)
|
||||
- vdpau and vaapi libraries for hardware decoding on Linux (optional)
|
||||
|
||||
Libass dependencies (when building libass):
|
||||
Libass dependencies:
|
||||
|
||||
- gcc or clang, nasm on x86 and x86_64
|
||||
- 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 macOS, 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):
|
||||
FFmpeg dependencies:
|
||||
|
||||
- gcc or clang, nasm on x86 and x86_64
|
||||
- OpenSSL or GnuTLS (have to be explicitly enabled when compiling FFmpeg)
|
||||
- libx264/libmp3lame/libfdk-aac if you want to use encoding (have to be
|
||||
explicitly enabled when compiling FFmpeg)
|
||||
- For native DASH playback, FFmpeg needs to be built with --enable-libxml2
|
||||
(although there are security implications, and DASH support has lots of bugs).
|
||||
- AV1 decoding support requires dav1d.
|
||||
- For good nvidia support on Linux, make sure nv-codec-headers is installed
|
||||
and can be found by configure.
|
||||
- gcc or clang, yasm on x86 and x86_64
|
||||
- OpenSSL (has to be explicitly enabled when compiling ffmpeg)
|
||||
- libx264/libmp3lame/libfdk-aac if you want to use encoding (has to be
|
||||
explicitly enabled when compiling ffmpeg)
|
||||
- Libav also works, but some features will not work. (See section below.)
|
||||
|
||||
Most of the above libraries are available in suitable versions on normal
|
||||
Linux distributions. For ease of compiling the latest git master of everything,
|
||||
you may wish to use the separately available build wrapper ([mpv-build][mpv-build])
|
||||
which first compiles FFmpeg libraries and libass, and then compiles the player
|
||||
statically linked against those.
|
||||
Linux distributions. However FFmpeg is an exception (distro versions may be
|
||||
too old to work at all or work well). For that reason you may want to use
|
||||
the separately available build wrapper ([mpv-build][mpv-build]) that first compiles FFmpeg
|
||||
libraries and libass, and then compiles the player statically linked against
|
||||
those.
|
||||
|
||||
If you want to build a Windows binary, see [Windows compilation][windows_compilation].
|
||||
If you want to build a Windows binary, you either have to use MSYS2 and MinGW,
|
||||
or cross-compile from Linux with MinGW. See [Windows compilation][windows_compilation].
|
||||
|
||||
|
||||
## FFmpeg vs. Libav
|
||||
|
||||
|
||||
Generally, mpv should work with the latest release as well as the git version
|
||||
of both FFmpeg and Libav. But FFmpeg is preferred, and some mpv features work
|
||||
with FFmpeg only (subtitle formats in particular).
|
||||
|
||||
|
||||
## Release cycle
|
||||
|
||||
Once or twice a year, a release is cut off from the current development state
|
||||
and is assigned a 0.X.0 version number. No further maintenance is done, except
|
||||
in the event of security issues.
|
||||
|
||||
The goal of releases is to make Linux distributions happy. Linux distributions
|
||||
are also expected to apply their own patches in case of bugs.
|
||||
Every few months, a new release is cut off of the master branch and is assigned
|
||||
a 0.X.0 version number.
|
||||
|
||||
As part of the maintenance process, minor releases are made, which are assigned
|
||||
0.X.Y version numbers. Minor releases contain bug fixes only. They never merge
|
||||
the master branch, and no features are added to it. Only the latest release is
|
||||
maintained.
|
||||
|
||||
The goal of releases is to provide stability and an unchanged base for the sake
|
||||
of Linux distributions. If you want the newest features, just use the master
|
||||
branch, which is stable most of the time, except sometimes, when it's not.
|
||||
|
||||
Releases other than the latest release are unsupported and unmaintained.
|
||||
|
||||
@@ -168,55 +146,60 @@ See the [release policy document][release-policy] for more information.
|
||||
|
||||
|
||||
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug
|
||||
reports or feature requests. Follow the template's instructions or the issue
|
||||
will likely be ignored or closed as invalid.
|
||||
|
||||
Questions can be asked in the [discussions][discussions] or on IRC (see
|
||||
[Contact](#Contact) below).
|
||||
reports or feature requests.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
Please read [contribute.md][contribute.md].
|
||||
|
||||
For small changes you can just send us pull requests through GitHub. For bigger
|
||||
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)
|
||||
for ideas on what you could contribute with.
|
||||
## Relation to MPlayer and mplayer2
|
||||
|
||||
## License
|
||||
|
||||
GPLv2 "or later" by default, LGPLv2.1 "or later" with `-Dgpl=false`.
|
||||
See [details.](https://github.com/mpv-player/mpv/blob/master/Copyright)
|
||||
mpv is based on mplayer2, which in turn is based on the original MPlayer
|
||||
(also called mplayer, mplayer-svn, mplayer1). Many changes have been made, a
|
||||
large part of which is incompatible or completely changes how the player
|
||||
behaves. Although there are still many similarities to its ancestors, mpv
|
||||
should generally be treated as a completely different program.
|
||||
|
||||
## History
|
||||
mpv was forked because we wanted to modernize MPlayer. This includes
|
||||
removing cruft (including features which stopped making sense 10 years ago),
|
||||
and of course adding modern features. Such huge and intrusive changes made it
|
||||
infeasible to work directly with MPlayer, which is mostly focused on
|
||||
preservation, so a fork had to be made. (Actually, mpv is based on mplayer2,
|
||||
which already started this process of removing cruft.)
|
||||
|
||||
This software is based on the MPlayer project. Before mpv existed as a project,
|
||||
the code base was briefly developed under the mplayer2 project. For details,
|
||||
see the [FAQ][FAQ].
|
||||
In general, mpv should be considered a completely new program, rather than a
|
||||
MPlayer drop-in replacement.
|
||||
|
||||
If you are wondering what's different from mplayer2 and MPlayer, an incomplete
|
||||
list of changes is located [here][mplayer-changes].
|
||||
|
||||
## Contact
|
||||
|
||||
|
||||
Most activity happens on the IRC channel and the GitHub issue tracker.
|
||||
Most activity happens on the IRC channel and the github issue tracker. The
|
||||
mailing lists are mostly unused.
|
||||
|
||||
- **GitHub issue tracker**: [issue tracker][issue-tracker] (report bugs here)
|
||||
- **Discussions**: [discussions][discussions]
|
||||
- **User IRC Channel**: `#mpv` on `irc.libera.chat`
|
||||
- **Developer IRC Channel**: `#mpv-devel` on `irc.libera.chat`
|
||||
- **GitHub issue tracker**: [issue tracker][issue-tracker] (report bugs here)
|
||||
- **User IRC Channel**: `#mpv` on `irc.freenode.net`
|
||||
- **Developer IRC Channel**: `#mpv-devel` on `irc.freenode.net`
|
||||
|
||||
To contact the `mpv` team in private write to `mpv-team@googlegroups.com`. Use
|
||||
only if discretion is required.
|
||||
|
||||
[FAQ]: https://github.com/mpv-player/mpv/wiki/FAQ
|
||||
[releases]: https://github.com/mpv-player/mpv/releases
|
||||
[mpv-build]: https://github.com/mpv-player/mpv-build
|
||||
[homebrew-mpv]: https://github.com/mpv-player/homebrew-mpv
|
||||
[issue-tracker]: https://github.com/mpv-player/mpv/issues
|
||||
[discussions]: https://github.com/mpv-player/mpv/discussions
|
||||
[ffmpeg_vs_libav]: https://github.com/mpv-player/mpv/wiki/FFmpeg-versus-Libav
|
||||
[release-policy]: https://github.com/mpv-player/mpv/blob/master/DOCS/release-policy.md
|
||||
[windows_compilation]: https://github.com/mpv-player/mpv/blob/master/DOCS/compile-windows.md
|
||||
[interface-changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/interface-changes.rst
|
||||
[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
|
||||
[restore-osc-bindings]: https://github.com/mpv-player/mpv/blob/master/etc/restore-osc-bindings.conf
|
||||
[contribute.md]: https://github.com/mpv-player/mpv/blob/master/DOCS/contribute.md
|
||||
[mplayer-changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/mplayer-changes.rst
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Mostly GPLv2 or later. See [details.](https://github.com/mpv-player/mpv/blob/master/Copyright)
|
||||
+1381
-125
File diff suppressed because it is too large.
Load diff
@@ -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) 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 Exception:
|
||||
remove(output)
|
||||
if depfile is not None:
|
||||
remove(depfile)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(proc.returncode)
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from functools import partial
|
||||
|
||||
sys_re = re.compile("^/System")
|
||||
usr_re = re.compile("^/usr/lib/")
|
||||
exe_re = re.compile("@executable_path")
|
||||
|
||||
def is_user_lib(objfile, libname):
|
||||
return not sys_re.match(libname) and \
|
||||
not usr_re.match(libname) and \
|
||||
not exe_re.match(libname) and \
|
||||
not "libobjc." in libname and \
|
||||
not "libSystem." in libname and \
|
||||
not "libc." in libname and \
|
||||
not "libgcc." in libname and \
|
||||
not os.path.basename(libname) == 'Python' and \
|
||||
not os.path.basename(objfile) in libname
|
||||
|
||||
def otool(objfile):
|
||||
command = "otool -L %s | grep -e '\t' | awk '{ print $1 }'" % objfile
|
||||
output = subprocess.check_output(command, shell = True)
|
||||
return filter(partial(is_user_lib, objfile), output.split())
|
||||
|
||||
def install_name_tool_change(old, new, objfile):
|
||||
subprocess.call(["install_name_tool", "-change", old, new, objfile])
|
||||
|
||||
def install_name_tool_id(name, objfile):
|
||||
subprocess.call(["install_name_tool", "-id", name, objfile])
|
||||
|
||||
def libraries(objfile, result = dict()):
|
||||
libs_list = otool(objfile)
|
||||
result[objfile] = set(libs_list)
|
||||
|
||||
for lib in libs_list:
|
||||
if lib not in result:
|
||||
libraries(lib, result)
|
||||
|
||||
return result
|
||||
|
||||
def leafs(libs_dict, processed = []):
|
||||
result = []
|
||||
processed = set(processed)
|
||||
|
||||
for objfile, libs in libs_dict.items():
|
||||
if libs <= processed:
|
||||
result.append(objfile)
|
||||
|
||||
return result
|
||||
|
||||
def lib_path(binary):
|
||||
return os.path.join(os.path.dirname(binary), 'lib')
|
||||
|
||||
def lib_name(lib):
|
||||
return os.path.join("@executable_path", "lib", os.path.basename(lib))
|
||||
|
||||
def process_libraries(libs_dict, binary, processed = []):
|
||||
ls = leafs(libs_dict, processed)
|
||||
diff = set(ls) - set(processed)
|
||||
if diff == set([binary]):
|
||||
return
|
||||
|
||||
for src in diff:
|
||||
name = lib_name(src)
|
||||
dst = os.path.join(lib_path(binary), os.path.basename(src))
|
||||
|
||||
shutil.copy(src, dst)
|
||||
os.chmod(dst, 0o755)
|
||||
install_name_tool_id(name, dst)
|
||||
|
||||
if src in libs_dict[binary]:
|
||||
install_name_tool_change(src, name, binary)
|
||||
|
||||
for p in processed:
|
||||
if p in libs_dict[src]:
|
||||
install_name_tool_change(p, lib_name(p), dst)
|
||||
|
||||
process_libraries(libs_dict, binary, ls)
|
||||
|
||||
def main():
|
||||
binary = os.path.abspath(sys.argv[1])
|
||||
if not os.path.exists(lib_path(binary)):
|
||||
os.makedirs(lib_path(binary))
|
||||
libs = libraries(binary)
|
||||
print(libs)
|
||||
process_libraries(libs, binary)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,313 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
sys_re = re.compile("^/System")
|
||||
usr_re = re.compile("^/usr/lib/")
|
||||
exe_re = re.compile("@executable_path")
|
||||
|
||||
def is_user_lib(objfile, libname):
|
||||
return not sys_re.match(libname) and \
|
||||
not usr_re.match(libname) and \
|
||||
not exe_re.match(libname) and \
|
||||
"libobjc." not in libname and \
|
||||
"libSystem." not in libname and \
|
||||
"libc." not in libname and \
|
||||
"libgcc." not in libname and \
|
||||
os.path.basename(libname) != "Python" and \
|
||||
os.path.basename(objfile) not in libname and \
|
||||
"libswift" not in libname
|
||||
|
||||
def otool(objfile, rapths):
|
||||
command = f"otool -L '{objfile}' | grep -e '\t' | awk '{{ print $1 }}'"
|
||||
output = subprocess.check_output(command, shell=True, universal_newlines=True)
|
||||
libs = set(filter(partial(is_user_lib, objfile), output.split()))
|
||||
|
||||
libs_resolved = set()
|
||||
libs_relative = set()
|
||||
for lib in libs:
|
||||
lib_path = resolve_lib_path(objfile, lib, rapths)
|
||||
libs_resolved.add(lib_path)
|
||||
if lib_path != lib:
|
||||
libs_relative.add(lib)
|
||||
|
||||
return libs_resolved, libs_relative
|
||||
|
||||
def get_rapths(objfile):
|
||||
rpaths: list[str] = []
|
||||
command = f"otool -l '{objfile}' | grep -A2 LC_RPATH | grep path"
|
||||
path_re = re.compile(r"^\s*path (.*) \(offset \d*\)$")
|
||||
|
||||
try:
|
||||
result = subprocess.check_output(command, shell=True, universal_newlines=True)
|
||||
except Exception:
|
||||
return rpaths
|
||||
|
||||
for line in result.splitlines():
|
||||
match = path_re.search(line)
|
||||
if match is None:
|
||||
continue
|
||||
line_clean = match.group(1).strip()
|
||||
# resolve @loader_path
|
||||
if line_clean.startswith("@loader_path/"):
|
||||
line_clean = line_clean[len("@loader_path/"):]
|
||||
line_clean = os.path.join(os.path.dirname(objfile), line_clean)
|
||||
line_clean = os.path.normpath(line_clean)
|
||||
rpaths.append(line_clean)
|
||||
|
||||
return rpaths
|
||||
|
||||
def get_rpaths_dev_tools(binary):
|
||||
command = (
|
||||
f"otool -l '{binary}' | grep -A2 LC_RPATH | grep path | "
|
||||
'grep "Xcode\\|CommandLineTools"'
|
||||
)
|
||||
result = subprocess.check_output(command, shell = True, universal_newlines=True)
|
||||
path_re = re.compile(r"^\s*path (.*) \(offset \d*\)$")
|
||||
return [
|
||||
match.group(1).strip()
|
||||
for line in result.splitlines()
|
||||
if (match := path_re.search(line)) is not None
|
||||
]
|
||||
|
||||
def resolve_lib_path(objfile, lib, rapths):
|
||||
if os.path.exists(lib):
|
||||
return lib
|
||||
|
||||
if lib.startswith("@rpath/"):
|
||||
lib = lib[len("@rpath/"):]
|
||||
for rpath in rapths:
|
||||
lib_path = os.path.join(rpath, lib)
|
||||
if os.path.exists(lib_path):
|
||||
return lib_path
|
||||
elif lib.startswith("@loader_path/"):
|
||||
lib = lib[len("@loader_path/"):]
|
||||
lib_path = os.path.normpath(os.path.join(objfile, lib))
|
||||
if os.path.exists(lib_path):
|
||||
return lib_path
|
||||
|
||||
raise Exception("Could not resolve library: " + lib)
|
||||
|
||||
def check_vulkan_max_version(version):
|
||||
try:
|
||||
subprocess.check_output(
|
||||
f"pkg-config vulkan --max-version={version}",
|
||||
shell=True,
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def get_homebrew_prefix():
|
||||
# set default to standard ARM path, intel path is already in the vulkan
|
||||
# loader search array
|
||||
result = "/opt/homebrew"
|
||||
try:
|
||||
result = subprocess.check_output(
|
||||
["brew", "--prefix"],
|
||||
universal_newlines=True,
|
||||
stderr=subprocess.DEVNULL,
|
||||
).strip()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
def install_name_tool_change(old, new, objfile):
|
||||
subprocess.call(
|
||||
["install_name_tool", "-change", old, new, objfile],
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
def install_name_tool_id(name, objfile):
|
||||
subprocess.call(
|
||||
["install_name_tool", "-id", name, objfile],
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
def install_name_tool_add_rpath(rpath, binary):
|
||||
subprocess.call(["install_name_tool", "-add_rpath", rpath, binary])
|
||||
|
||||
def install_name_tool_delete_rpath(rpath, binary):
|
||||
subprocess.call(["install_name_tool", "-delete_rpath", rpath, binary])
|
||||
|
||||
def libraries(objfile, result=None, result_relative=None, rapths=None):
|
||||
if result is None:
|
||||
result = {}
|
||||
|
||||
if result_relative is None:
|
||||
result_relative = set()
|
||||
|
||||
if rapths is None:
|
||||
rapths = []
|
||||
|
||||
rapths = get_rapths(objfile) + rapths
|
||||
libs_list, libs_relative = otool(objfile, rapths)
|
||||
result[objfile] = libs_list
|
||||
result_relative |= libs_relative
|
||||
|
||||
for lib in libs_list:
|
||||
if lib not in result:
|
||||
libraries(lib, result, result_relative, rapths)
|
||||
|
||||
return result, result_relative
|
||||
|
||||
def lib_path(binary):
|
||||
return os.path.join(os.path.dirname(binary), "lib")
|
||||
|
||||
def resources_path(binary):
|
||||
return os.path.join(os.path.dirname(binary), "../Resources")
|
||||
|
||||
def lib_name(lib):
|
||||
return os.path.join("@executable_path", "lib", os.path.basename(lib))
|
||||
|
||||
def process_libraries(libs_dict, libs_dyn, binary):
|
||||
libs_set = set(libs_dict)
|
||||
# Remove binary from libs_set to prevent a duplicate of the binary being
|
||||
# added to the libs directory.
|
||||
libs_set.remove(binary)
|
||||
|
||||
for src in libs_set:
|
||||
name = lib_name(src)
|
||||
dst = os.path.join(lib_path(binary), os.path.basename(src))
|
||||
|
||||
shutil.copy(src, dst)
|
||||
os.chmod(dst, 0o755)
|
||||
install_name_tool_id(name, dst)
|
||||
|
||||
if src in libs_dict[binary]:
|
||||
install_name_tool_change(src, name, binary)
|
||||
|
||||
for p in libs_set:
|
||||
if p in libs_dict[src]:
|
||||
install_name_tool_change(p, lib_name(p), dst)
|
||||
|
||||
for lib in libs_dyn:
|
||||
install_name_tool_change(lib, lib_name(lib), dst)
|
||||
|
||||
for lib in libs_dyn:
|
||||
install_name_tool_change(lib, lib_name(lib), binary)
|
||||
|
||||
def process_swift_libraries(binary):
|
||||
swift_stdlib_tool = subprocess.check_output(
|
||||
["xcrun", "--find", "swift-stdlib-tool"],
|
||||
universal_newlines=True,
|
||||
).strip()
|
||||
# from xcode11 on the dynamic swift libs reside in a separate directory from
|
||||
# the std one, might need versioned paths for future swift versions
|
||||
swift_lib_path = os.path.join(swift_stdlib_tool, "../../lib/swift-5.0/macosx")
|
||||
swift_lib_path = os.path.abspath(swift_lib_path)
|
||||
|
||||
command = [
|
||||
swift_stdlib_tool, "--copy", "--platform", "macosx",
|
||||
"--scan-executable", binary, "--destination", lib_path(binary),
|
||||
]
|
||||
|
||||
if os.path.exists(swift_lib_path):
|
||||
command.extend(["--source-libraries", swift_lib_path])
|
||||
|
||||
subprocess.check_output(command, universal_newlines=True)
|
||||
|
||||
print(">> setting additional rpath for swift libraries")
|
||||
install_name_tool_add_rpath("@executable_path/lib", binary)
|
||||
|
||||
def process_vulkan_loader(binary, loader_name, loader_relative_folder, library_node):
|
||||
# https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderDriverInterface.md#example-macos-driver-search-path
|
||||
# https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderLayerInterface.md#macos-layer-discovery
|
||||
loader_system_search_folders = [
|
||||
os.path.join(os.path.expanduser("~"), ".config", loader_relative_folder),
|
||||
os.path.join("/etc/xdg", loader_relative_folder),
|
||||
os.path.join("/usr/local/etc", loader_relative_folder),
|
||||
os.path.join("/etc", loader_relative_folder),
|
||||
os.path.join(os.path.expanduser("~"), ".local/share", loader_relative_folder),
|
||||
os.path.join("/usr/local/share", loader_relative_folder),
|
||||
os.path.join("/usr/share/vulkan", loader_relative_folder),
|
||||
os.path.join(get_homebrew_prefix(), "etc", loader_relative_folder),
|
||||
os.path.join(get_homebrew_prefix(), "share", loader_relative_folder), # old location
|
||||
]
|
||||
|
||||
loader_system_folder = ""
|
||||
for loader_system_search_folder in loader_system_search_folders:
|
||||
if os.path.exists(loader_system_search_folder):
|
||||
loader_system_folder = loader_system_search_folder
|
||||
break
|
||||
|
||||
if not loader_system_folder:
|
||||
print(">>> could not find loader folder " + loader_relative_folder)
|
||||
return
|
||||
|
||||
loader_bundle_folder = os.path.join(resources_path(binary), loader_relative_folder)
|
||||
loader_system_path = os.path.join(loader_system_folder, loader_name)
|
||||
loader_bundle_path = os.path.join(loader_bundle_folder, loader_name)
|
||||
library_relative_folder = "../../../Frameworks/"
|
||||
|
||||
if not os.path.exists(loader_system_path):
|
||||
print(">>> could not find loader " + loader_name)
|
||||
return
|
||||
|
||||
if not os.path.exists(loader_bundle_folder):
|
||||
os.makedirs(loader_bundle_folder)
|
||||
|
||||
loader_system_file = open(loader_system_path)
|
||||
loader_json_data = json.load(loader_system_file)
|
||||
library_path = loader_json_data[library_node]["library_path"]
|
||||
library_system_path = os.path.join(loader_system_folder, library_path)
|
||||
|
||||
if not os.path.exists(library_system_path):
|
||||
print(">>> could not find loader library " + library_system_path)
|
||||
return
|
||||
|
||||
print(">>> modifying and writing loader json " + loader_name)
|
||||
loader_bundle_file = open(loader_bundle_path, "w")
|
||||
loader_library_name = os.path.basename(library_system_path)
|
||||
library_path = os.path.join(library_relative_folder, loader_library_name)
|
||||
loader_json_data[library_node]["library_path"] = library_path
|
||||
json.dump(loader_json_data, loader_bundle_file, indent=4)
|
||||
|
||||
print(">>> copying loader library " + loader_library_name)
|
||||
framework_bundle_folder = os.path.join(
|
||||
loader_bundle_folder, library_relative_folder,
|
||||
)
|
||||
if not os.path.exists(framework_bundle_folder):
|
||||
os.makedirs(framework_bundle_folder)
|
||||
library_target_path = os.path.join(framework_bundle_folder, loader_library_name)
|
||||
shutil.copy(library_system_path, library_target_path)
|
||||
|
||||
def remove_dev_tools_rapths(binary):
|
||||
for path in get_rpaths_dev_tools(binary):
|
||||
install_name_tool_delete_rpath(path, binary)
|
||||
|
||||
def process(binary):
|
||||
binary = os.path.abspath(binary)
|
||||
if not os.path.exists(lib_path(binary)):
|
||||
os.makedirs(lib_path(binary))
|
||||
print(">> gathering all linked libraries")
|
||||
libs, libs_rel = libraries(binary)
|
||||
|
||||
print(">> copying and processing all linked libraries")
|
||||
process_libraries(libs, libs_rel, binary)
|
||||
|
||||
print(">> removing rpath definitions towards dev tools")
|
||||
remove_dev_tools_rapths(binary)
|
||||
|
||||
print(">> copying and processing swift libraries")
|
||||
process_swift_libraries(binary)
|
||||
|
||||
print(">> copying and processing vulkan loader")
|
||||
process_vulkan_loader(binary, "MoltenVK_icd.json", "vulkan/icd.d", "ICD")
|
||||
if check_vulkan_max_version("1.3.261.1"):
|
||||
process_vulkan_loader(
|
||||
binary,
|
||||
"VkLayer_khronos_synchronization2.json",
|
||||
"vulkan/explicit_layer.d",
|
||||
"layer",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
process(sys.argv[1])
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#! /usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# Convert the contents of a file into a C string constant.
|
||||
# Note that the compiler will implicitly add an extra 0 byte at the end
|
||||
# of every string, so code using the string may need to remove that to get
|
||||
# the exact contents of the original file.
|
||||
# FIXME: why not a char array?
|
||||
|
||||
# treat only alphanumeric and punctuations (excluding " and ?) as safe
|
||||
my $unsafe_chars = qr{[^][A-Za-z0-9!#%&'()*+,./:;<=>^_{|}~ -]};
|
||||
|
||||
for my $file (@ARGV) {
|
||||
open my $fh, '<:raw', $file or next;
|
||||
print "/* Generated from $file */\n";
|
||||
while (<$fh>) {
|
||||
# replace unsafe chars with their equivalent octal escapes
|
||||
s/($unsafe_chars)/\\@{[sprintf '%03o', ord($1)]}/gos;
|
||||
print "\"$_\"\n"
|
||||
}
|
||||
close $fh;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Convert the contents of a file into a C string constant.
|
||||
# Note that the compiler will implicitly add an extra 0 byte at the end
|
||||
# of every string, so code using the string may need to remove that to get
|
||||
# the exact contents of the original file.
|
||||
|
||||
#
|
||||
# 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 sys
|
||||
|
||||
|
||||
def file2string(infilename, infile, outfile):
|
||||
outfile.write(f"// Generated from {infilename}\n\n")
|
||||
|
||||
conv = [f"\\{c:03o}" for c in range(256)]
|
||||
safe_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" \
|
||||
"0123456789!#%&'()*+,-./:;<=>[]^_{|}~ "
|
||||
|
||||
for c in safe_chars:
|
||||
conv[ord(c)] = c
|
||||
for c, esc in [("\n", "n"), ("\t", "t"), ("\\", "\\"), ('"', '"')]:
|
||||
conv[ord(c)] = "\\" + esc
|
||||
for line in infile:
|
||||
outfile.write('"' + "".join(conv[c] for c in line) + '"\n')
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(sys.argv[1], "rb") as infile, open(sys.argv[2], "w") as outfile:
|
||||
file2string(os.path.relpath(sys.argv[1], sys.argv[3]), infile, outfile)
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Generate a new interface-changes.rst based on the entries in
|
||||
# the interface-changes directory.
|
||||
|
||||
#
|
||||
# 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 pathlib
|
||||
import sys
|
||||
import textwrap
|
||||
from shutil import which
|
||||
from subprocess import check_output
|
||||
|
||||
|
||||
def add_new_entries(docs_dir, out, git):
|
||||
changes_dir = pathlib.Path(docs_dir) / "interface-changes"
|
||||
files = []
|
||||
for f in pathlib.Path(changes_dir).glob("*.txt"):
|
||||
if f.is_file() and f.name != "example.txt":
|
||||
timestamp = check_output([git, "log", "--format=%ct", "-n", "1", "--",
|
||||
f], encoding="UTF-8")
|
||||
if timestamp:
|
||||
content = f.read_text()
|
||||
files.append(content)
|
||||
else:
|
||||
print(f"Skipping file not tracked by git: {f.name}")
|
||||
|
||||
# Sort the changes by "severity", which roughly corresponds to
|
||||
# alphabetical order by accident (e.g. remove > deprecate > change > add)
|
||||
for file in reversed(sorted(files)):
|
||||
for line in file.splitlines():
|
||||
line = textwrap.fill(line.rstrip(), width=80,
|
||||
initial_indent=" - ",
|
||||
subsequent_indent=" ")
|
||||
out.write(line + "\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: {sys.argv[0]} <version>")
|
||||
sys.exit(1)
|
||||
|
||||
git = which("git")
|
||||
if not git:
|
||||
print("Unable to find git binary")
|
||||
sys.exit(1)
|
||||
|
||||
# Accept passing only the major version number and the full 0 version.
|
||||
major_version = None
|
||||
if sys.argv[1].isdigit():
|
||||
major_version = sys.argv[1]
|
||||
else:
|
||||
ver_split = sys.argv[1].split(".")
|
||||
if len(ver_split) == 3 and ver_split[1].isdigit():
|
||||
major_version = ver_split[1]
|
||||
|
||||
if not major_version:
|
||||
print(f"Invalid version number: {sys.argv[1]}")
|
||||
sys.exit(1)
|
||||
|
||||
docs_dir = pathlib.Path(sys.argv[0]).resolve().parents[1] / "DOCS"
|
||||
interface_changes = docs_dir / "interface-changes.rst"
|
||||
with open(interface_changes) as f:
|
||||
lines = [line.rstrip() for line in f]
|
||||
|
||||
ver_line = " --- mpv 0." + major_version + ".0 ---"
|
||||
next_ver_line = " --- mpv 0." + str(int(major_version) + 1) + ".0 ---"
|
||||
found = False
|
||||
with open(interface_changes, "w", newline="\n") as f:
|
||||
for line in lines:
|
||||
if line == ver_line:
|
||||
f.write(next_ver_line + "\n")
|
||||
f.write(line + "\n")
|
||||
if line == ver_line:
|
||||
add_new_entries(docs_dir, f, git)
|
||||
found = True
|
||||
if not found:
|
||||
print(f"Nothing changed! The following line was not found:\n{ver_line}")
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Modify X-KDE-Protocols in the mpv.desktop file based on output from
|
||||
# mpv --list-protocols.
|
||||
|
||||
#
|
||||
# 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 sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(sys.argv[1], encoding="UTF-8") as f:
|
||||
next(f)
|
||||
mpv_desktop = dict([line.split("=", 1) for line in f])
|
||||
|
||||
if not mpv_desktop["X-KDE-Protocols"]:
|
||||
raise ValueError("Missing X-KDE-Protocols entry in mpv.desktop file")
|
||||
|
||||
with open(sys.argv[2], encoding="UTF-8") as mpv_protocols_f:
|
||||
mpv_protocols = {
|
||||
line.strip(" :/")
|
||||
for line in mpv_protocols_f.read().splitlines()
|
||||
if "://" in line
|
||||
}
|
||||
|
||||
if len(mpv_protocols) == 0:
|
||||
raise ValueError("Unable to parse any protocols from mpv '--list-protocols'")
|
||||
|
||||
protocol_list = set(mpv_desktop["X-KDE-Protocols"].strip().split(","))
|
||||
compatible_protocols = sorted(mpv_protocols & protocol_list)
|
||||
mpv_desktop["X-KDE-Protocols"] = ",".join(compatible_protocols) + "\n"
|
||||
|
||||
with open(sys.argv[3], "w", encoding="UTF-8") as f:
|
||||
f.write("[Desktop Entry]" + "\n")
|
||||
for key, value in mpv_desktop.items():
|
||||
f.write(f"{key}={value}")
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script is expected to be called as TOOLS/gen-osd-font.sh (it will access
|
||||
# TOOLS/mpv-osd-symbols.sfdir), and it will write sub/osd_font.otf.
|
||||
|
||||
# Needs fontforge with python scripting
|
||||
|
||||
fontforge -lang=py -c 'f=open(argv[1]); f.generate(argv[2])' \
|
||||
TOOLS/mpv-osd-symbols.sfdir sub/osd_font.otf
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script is expected to be called as TOOLS/gen-x11-icon.sh (it will access
|
||||
# etc/mpv-icon...), and it will write video/out/x11_icon.bin.
|
||||
|
||||
conv() {
|
||||
echo
|
||||
echo `identify -format "icon: %w %h" "$1"`
|
||||
convert "$1" -depth 8 rgba:-
|
||||
}
|
||||
|
||||
(echo "# File generated by gen-x11-icon.sh" ;
|
||||
conv etc/mpv-icon-8bit-16x16.png ;
|
||||
conv etc/mpv-icon-8bit-32x32.png ;
|
||||
conv etc/mpv-icon-8bit-64x64.png) | gzip -c > video/out/x11_icon.bin
|
||||
+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 } \
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use 5.008;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Module collection to parse Matroska files.
|
||||
package Parse::Matroska;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
C<use>s L<Parse::Matroska::Reader>. See the documentation
|
||||
of the modules mentioned in L</"SEE ALSO"> for more information
|
||||
in how to use this module.
|
||||
|
||||
It's intended for this module to contain high-level interfaces
|
||||
to the other modules in the distribution.
|
||||
|
||||
=head1 SOURCE CODE
|
||||
|
||||
L<https://github.com/Kovensky/Parse-Matroska>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Parse::Matroska::Reader>, L<Parse::Matroska::Element>,
|
||||
L<Parse::Matroska::Definitions>.
|
||||
|
||||
=cut
|
||||
|
||||
use Parse::Matroska::Reader;
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,357 @@
|
||||
use 5.008;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: internal EBML grammar definitions
|
||||
package Parse::Matroska::Definitions;
|
||||
|
||||
use Parse::Matroska::Utils qw{uniq uncamelize};
|
||||
|
||||
use Exporter;
|
||||
our @ISA = qw{Exporter};
|
||||
our @EXPORT_OK = qw{elem_by_hexid %EBML_DEFINITION %MATROSKA_DEFINITION};
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Parse::Matroska::Definitions qw{elem_by_hexid};
|
||||
my $ebml_id = elem_by_hexid('1a45dfa3');
|
||||
print "EBML ID $ebml_id->{elid}'s name: $ebml_id->{name}";
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Contains the definition of the EBML grammar as expected in
|
||||
Matroska files. This module is meant mostly for internal use.
|
||||
|
||||
As this was extended from a script in mpv-player, some data
|
||||
generated is apparently useless for regular module users
|
||||
but is still relevant to the mpv-player script. Such data
|
||||
is annotated as being for mpv compatibility.
|
||||
|
||||
=head1 NOTE
|
||||
|
||||
The API of this module is not yet considered stable.
|
||||
|
||||
=head1 GLOBALS
|
||||
|
||||
These global variables are considered B<immutable>.
|
||||
|
||||
=head2 @Parse::Matroska::Definitions::global_elem_list
|
||||
|
||||
A global list of known matroska elements. Useful for
|
||||
mpv's matroska script, used for generating C headers
|
||||
that parse matroska.
|
||||
|
||||
=head2 %Parse::Matroska::Definitions::global_elem_dict
|
||||
|
||||
A global hash of known matroska elements. Used internally
|
||||
by L</elem_by_hexid($id)>.
|
||||
|
||||
=cut
|
||||
|
||||
@Parse::Matroska::Definitions::global_elem_list = ();
|
||||
%Parse::Matroska::Definitions::global_elem_dict = ();
|
||||
|
||||
=head2 %EBML_DEFINITION
|
||||
|
||||
Optionally-importable hash of known EBML IDs belonging
|
||||
to the EBML generic grammar.
|
||||
|
||||
=head2 %MATROSKA_DEFINITION
|
||||
|
||||
Optionally-importable hash of known EBML IDs belonging
|
||||
to the Matroska-specific grammar.
|
||||
|
||||
=cut
|
||||
|
||||
our %EBML_DEFINITION = define_ebml();
|
||||
our %MATROSKA_DEFINITION = define_matroska();
|
||||
|
||||
=method elem_by_hexid($id)
|
||||
|
||||
Returns an EBML Element Definition corresponding to the provided
|
||||
hexadecimal string. Returns C<undef> if the element is unknown.
|
||||
|
||||
=cut
|
||||
sub elem_by_hexid {
|
||||
my ($elid) = @_;
|
||||
return $Parse::Matroska::Definitions::global_elem_dict{$elid};
|
||||
}
|
||||
|
||||
################################################
|
||||
### Helper functions for document definition ###
|
||||
################################################
|
||||
|
||||
# used by elem when setting the 'valname' key
|
||||
use constant TYPE_MAP => {
|
||||
uint => 'uint64_t',
|
||||
str => 'char *',
|
||||
binary => 'struct bstr',
|
||||
ebml_id => 'uint32_t',
|
||||
float => 'double',
|
||||
sint => 'int64_t',
|
||||
};
|
||||
|
||||
# this will be localized to "MATROSKA" or "EBML" on the elem declarations
|
||||
our $ELEM_DEFINE_TYPE = undef;
|
||||
|
||||
=method elem($name,$elid,$valtype)
|
||||
|
||||
NOTE: never call this function yourself; it changes data structures
|
||||
that are considered immutable outside of this package.
|
||||
|
||||
Internal API function that generates the EBML Element Definitions.
|
||||
|
||||
This API function returns an array which first element is C<$elid>
|
||||
and the second is a generated hash. The generated hash is stored
|
||||
in the @global_elem_list and %global_elem_dict.
|
||||
|
||||
The generated hash contains:
|
||||
|
||||
=for :list
|
||||
= name
|
||||
The EBML Element's name, given through C<$name>.
|
||||
= elid
|
||||
The EBML Element's hex id, given through C<$elid>. Used for lookups by L</elem_by_hexid($id)>.
|
||||
= valtype
|
||||
The EBML Element's type, given through C<$valtype>, except when C<$valtype> is an arrayref.
|
||||
= multiple
|
||||
If C<$name> ends with a C<*>, this is set as true and strips the C<*> from L</name>. Used to
|
||||
mark elements that may be repeated.
|
||||
= subelements
|
||||
An arrayref of elements that may be children of this element, given through C<$valtype> if it
|
||||
is an arrayref. Sets L</valtype> to C<sub> if there are subelements.
|
||||
= subids
|
||||
An arrayref listing all the L</elid>s of subelements, C<uniq>ified.
|
||||
|
||||
The following elements are for mpv compatibility:
|
||||
|
||||
=for :list
|
||||
= definename
|
||||
Name used for generating C #defines.
|
||||
= fieldname
|
||||
Name used for generating C struct fields.
|
||||
= structname
|
||||
Name used for generating C struct names.
|
||||
= ebmltype
|
||||
A pre-#defined constant to describe the element's type.
|
||||
= valname
|
||||
Typename used when declaring a struct field referring to this element.
|
||||
|
||||
=cut
|
||||
sub elem {
|
||||
my %e = (name => shift, elid => shift, valtype => shift);
|
||||
|
||||
# strip * from name, set 'multiple' if there was one
|
||||
$e{multiple} = scalar $e{name} =~ s/\*$//;
|
||||
|
||||
# ELEM_DEFINE_TYPE is either MATROSKA or EBML
|
||||
$e{definename} = "${ELEM_DEFINE_TYPE}_ID_".uc($e{name});
|
||||
$e{fieldname} = uncamelize $e{name};
|
||||
$e{structname} = "ebml_$e{fieldname}";
|
||||
|
||||
if (ref $e{valtype} eq 'HASH') {
|
||||
$e{subelements} = $e{valtype};
|
||||
$e{subids} = uniq map { $_->{elid} } values %{$e{subelements}};
|
||||
$e{valtype} = 'sub';
|
||||
$e{ebmltype} = 'EBML_TYPE_SUBELEMENTS';
|
||||
$e{valname} = "struct $e{structname}";
|
||||
} else {
|
||||
$e{ebmltype} = "EBML_TYPE_\U$e{valtype}";
|
||||
die "Unrecognized value type $e{valtype}" unless
|
||||
defined ($e{valname} = TYPE_MAP->{$e{valtype}});
|
||||
}
|
||||
my $e = \%e;
|
||||
push @Parse::Matroska::Definitions::global_elem_list, $e;
|
||||
$Parse::Matroska::Definitions::global_elem_dict{$e{elid}} = $e;
|
||||
return ($e{elid}, $e);
|
||||
}
|
||||
|
||||
#############################################
|
||||
### EBML and Matroska document definitons ###
|
||||
#############################################
|
||||
|
||||
=method define_ebml
|
||||
|
||||
Internal function that defines the EBML generic grammar.
|
||||
|
||||
Must not be called from outside the package.
|
||||
|
||||
=cut
|
||||
sub define_ebml {
|
||||
local $ELEM_DEFINE_TYPE = 'EBML';
|
||||
return (
|
||||
elem('EBML', '1a45dfa3', {
|
||||
elem('EBMLVersion', '4286', 'uint'),
|
||||
elem('EBMLReadVersion', '42f7', 'uint'),
|
||||
elem('EBMLMaxIDLength', '42f2', 'uint'),
|
||||
elem('EBMLMaxSizeLength', '42f3', 'uint'),
|
||||
elem('DocType', '4282', 'str'),
|
||||
elem('DocTypeVersion', '4287', 'uint'),
|
||||
elem('DocTypeReadVersion', '4285', 'uint'),
|
||||
}),
|
||||
|
||||
elem('CRC32', 'bf', 'binary'),
|
||||
elem('Void', 'ec', 'binary'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
=method define_matroska
|
||||
|
||||
Internal function that defines the Matroska-specific EBML grammar.
|
||||
|
||||
Must not be called from outside the package.
|
||||
|
||||
=cut
|
||||
sub define_matroska {
|
||||
local $ELEM_DEFINE_TYPE = 'MATROSKA';
|
||||
return (
|
||||
elem('Segment', '18538067', {
|
||||
elem('SeekHead*', '114d9b74', {
|
||||
elem('Seek*', '4dbb', {
|
||||
elem('SeekID', '53ab', 'ebml_id'),
|
||||
elem('SeekPosition', '53ac', 'uint'),
|
||||
}),
|
||||
}),
|
||||
|
||||
elem('Info*', '1549a966', {
|
||||
elem('SegmentUID', '73a4', 'binary'),
|
||||
elem('PrevUID', '3cb923', 'binary'),
|
||||
elem('NextUID', '3eb923', 'binary'),
|
||||
elem('TimecodeScale', '2ad7b1', 'uint'),
|
||||
elem('DateUTC', '4461', 'sint'),
|
||||
elem('Title', '7ba9', 'str'),
|
||||
elem('MuxingApp', '4d80', 'str'),
|
||||
elem('WritingApp', '5741', 'str'),
|
||||
elem('Duration', '4489', 'float'),
|
||||
}),
|
||||
|
||||
elem('Cluster*', '1f43b675', {
|
||||
elem('Timecode', 'e7', 'uint'),
|
||||
elem('BlockGroup*', 'a0', {
|
||||
elem('Block', 'a1', 'binary'),
|
||||
elem('BlockDuration', '9b', 'uint'),
|
||||
elem('ReferenceBlock*', 'fb', 'sint'),
|
||||
elem('DiscardPadding', '75A2', 'sint'),
|
||||
}),
|
||||
elem('SimpleBlock*', 'a3', 'binary'),
|
||||
}),
|
||||
|
||||
elem('Tracks*', '1654ae6b', {
|
||||
elem('TrackEntry*', 'ae', {
|
||||
elem('TrackNumber', 'd7', 'uint'),
|
||||
elem('TrackUID', '73c5', 'uint'),
|
||||
elem('TrackType', '83', 'uint'),
|
||||
elem('FlagEnabled', 'b9', 'uint'),
|
||||
elem('FlagDefault', '88', 'uint'),
|
||||
elem('FlagForced', '55aa', 'uint'),
|
||||
elem('FlagLacing', '9c', 'uint'),
|
||||
elem('MinCache', '6de7', 'uint'),
|
||||
elem('MaxCache', '6df8', 'uint'),
|
||||
elem('DefaultDuration', '23e383', 'uint'),
|
||||
elem('TrackTimecodeScale', '23314f', 'float'),
|
||||
elem('MaxBlockAdditionID', '55ee', 'uint'),
|
||||
elem('Name', '536e', 'str'),
|
||||
elem('Language', '22b59c', 'str'),
|
||||
elem('CodecID', '86', 'str'),
|
||||
elem('CodecPrivate', '63a2', 'binary'),
|
||||
elem('CodecName', '258688', 'str'),
|
||||
elem('CodecDecodeAll', 'aa', 'uint'),
|
||||
elem('CodecDelay', '56AA', 'uint'),
|
||||
elem('SeekPreRoll', '56BB', 'uint'),
|
||||
elem('Video', 'e0', {
|
||||
elem('FlagInterlaced', '9a', 'uint'),
|
||||
elem('PixelWidth', 'b0', 'uint'),
|
||||
elem('PixelHeight', 'ba', 'uint'),
|
||||
elem('DisplayWidth', '54b0', 'uint'),
|
||||
elem('DisplayHeight', '54ba', 'uint'),
|
||||
elem('DisplayUnit', '54b2', 'uint'),
|
||||
elem('FrameRate', '2383e3', 'float'),
|
||||
elem('ColourSpace', '2eb524', 'binary'),
|
||||
elem('StereoMode', '53b8', 'uint'),
|
||||
}),
|
||||
elem('Audio', 'e1', {
|
||||
elem('SamplingFrequency', 'b5', 'float'),
|
||||
elem('OutputSamplingFrequency', '78b5', 'float'),
|
||||
elem('Channels', '9f', 'uint'),
|
||||
elem('BitDepth', '6264', 'uint'),
|
||||
}),
|
||||
elem('ContentEncodings', '6d80', {
|
||||
elem('ContentEncoding*', '6240', {
|
||||
elem('ContentEncodingOrder', '5031', 'uint'),
|
||||
elem('ContentEncodingScope', '5032', 'uint'),
|
||||
elem('ContentEncodingType', '5033', 'uint'),
|
||||
elem('ContentCompression', '5034', {
|
||||
elem('ContentCompAlgo', '4254', 'uint'),
|
||||
elem('ContentCompSettings', '4255', 'binary'),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
||||
elem('Cues', '1c53bb6b', {
|
||||
elem('CuePoint*', 'bb', {
|
||||
elem('CueTime', 'b3', 'uint'),
|
||||
elem('CueTrackPositions*', 'b7', {
|
||||
elem('CueTrack', 'f7', 'uint'),
|
||||
elem('CueClusterPosition', 'f1', 'uint'),
|
||||
elem('CueRelativePosition','f0', 'uint'),
|
||||
elem('CueDuration', 'b2', 'uint'),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
||||
elem('Attachments', '1941a469', {
|
||||
elem('AttachedFile*', '61a7', {
|
||||
elem('FileDescription', '467e', 'str'),
|
||||
elem('FileName', '466e', 'str'),
|
||||
elem('FileMimeType', '4660', 'str'),
|
||||
elem('FileData', '465c', 'binary'),
|
||||
elem('FileUID', '46ae', 'uint'),
|
||||
}),
|
||||
}),
|
||||
|
||||
elem('Chapters', '1043a770', {
|
||||
elem('EditionEntry*', '45b9', {
|
||||
elem('EditionUID', '45bc', 'uint'),
|
||||
elem('EditionFlagHidden', '45bd', 'uint'),
|
||||
elem('EditionFlagDefault', '45db', 'uint'),
|
||||
elem('EditionFlagOrdered', '45dd', 'uint'),
|
||||
elem('ChapterAtom*', 'b6', {
|
||||
elem('ChapterUID', '73c4', 'uint'),
|
||||
elem('ChapterTimeStart', '91', 'uint'),
|
||||
elem('ChapterTimeEnd', '92', 'uint'),
|
||||
elem('ChapterFlagHidden', '98', 'uint'),
|
||||
elem('ChapterFlagEnabled', '4598', 'uint'),
|
||||
elem('ChapterSegmentUID', '6e67', 'binary'),
|
||||
elem('ChapterSegmentEditionUID', '6ebc', 'uint'),
|
||||
elem('ChapterDisplay*', '80', {
|
||||
elem('ChapString', '85', 'str'),
|
||||
elem('ChapLanguage*', '437c', 'str'),
|
||||
elem('ChapCountry*', '437e', 'str'),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
elem('Tags*', '1254c367', {
|
||||
elem('Tag*', '7373', {
|
||||
elem('Targets', '63c0', {
|
||||
elem('TargetTypeValue', '68ca', 'uint'),
|
||||
elem('TargetTrackUID', '63c5', 'uint'),
|
||||
elem('TargetEditionUID', '63c9', 'uint'),
|
||||
elem('TargetChapterUID', '63c4', 'uint'),
|
||||
elem('TargetAttachmentUID', '63c6', 'uint'),
|
||||
}),
|
||||
elem('SimpleTag*', '67c8', {
|
||||
elem('TagName', '45a3', 'str'),
|
||||
elem('TagLanguage', '447a', 'str'),
|
||||
elem('TagString', '4487', 'str'),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,331 @@
|
||||
use 5.008;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: a mid-level representation of an EBML element
|
||||
package Parse::Matroska::Element;
|
||||
|
||||
use Carp;
|
||||
use List::Util qw{first};
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Parse::Matroska::Reader;
|
||||
my $reader = Parse::Matroska::Reader->new($path);
|
||||
my $elem = $reader->read_element;
|
||||
|
||||
print "ID: $elem->{elid}\n";
|
||||
print "Name: $elem->{name}\n";
|
||||
print "Length: $elem->{content_len}\n";
|
||||
print "Type: $elem->{type}\n";
|
||||
print "Child count: ", scalar(@{$elem->all_children}), "\n";
|
||||
if ($elem->{type} eq 'sub') {
|
||||
while (my $chld = $elem->next_child) {
|
||||
print "Child Name: $chld->{name}\n";
|
||||
}
|
||||
} else {
|
||||
print "Value: ", $elem->get_value, "\n";
|
||||
}
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Represents a single Matroska element as decoded by
|
||||
L<Parse::Matroska::Reader>. This is essentially a hash
|
||||
augmented with functions for delay-loading of binary
|
||||
values and children elements.
|
||||
|
||||
=head1 NOTE
|
||||
|
||||
The API of this module is not yet considered stable.
|
||||
|
||||
=attr elid
|
||||
|
||||
The EBML Element ID, suitable for passing to
|
||||
L<Parse::Matroska::Definitions/elem_by_hexid>.
|
||||
|
||||
=attr name
|
||||
|
||||
The EBML Element's name.
|
||||
|
||||
=attr type
|
||||
|
||||
The EBML Element's type. Can be C<uint>, C<sint>,
|
||||
C<float>, C<ebml_id>, C<str> or C<binary>. See L</value>
|
||||
for details.
|
||||
|
||||
Equivalent to
|
||||
C<elem_by_hexid($elem-E<gt>{value})-E<gt>{valtype}>.
|
||||
|
||||
=attr value
|
||||
|
||||
The EBML Element's value. Should be obtained through
|
||||
L</get_value>.
|
||||
|
||||
Is an unicode string if the L</type> is C<str>, that is,
|
||||
the string has already been decoded by L<Encode/decode>.
|
||||
|
||||
Is C<undef> if the L</type> is C<binary> and the contents
|
||||
were delay-loaded and not yet read. L</get_value> will
|
||||
do the delayed load if needed.
|
||||
|
||||
Is an arrayref if the L</type> is C<sub>, containing
|
||||
the children nodes that were already loaded.
|
||||
|
||||
Is a hashref if the L</type> is C<ebml_id>, containing
|
||||
the referred element's information as defined in
|
||||
L<Parse::Matroska::Definitions>. Calling
|
||||
C<elem_by_hexid($elem-E<gt>{value}-E<gt>{elid})> will
|
||||
return the same object as $elem->{value}.
|
||||
|
||||
=attr full_len
|
||||
|
||||
The entire length of this EBML Element, including
|
||||
the header's.
|
||||
|
||||
=attr size_len
|
||||
|
||||
The length of the size marker. Used when calculating
|
||||
L</full_len> from L</content_len>
|
||||
|
||||
=attr content_len
|
||||
|
||||
The length of the contents of this EBML Element,
|
||||
which excludes the header.
|
||||
|
||||
=attr reader
|
||||
|
||||
A weakened reference to the associated
|
||||
L<Parse::Matroska::Reader>.
|
||||
|
||||
=method new(%hash)
|
||||
|
||||
Creates a new Element initialized with the hash
|
||||
given as argument.
|
||||
|
||||
=cut
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
|
||||
$self->initialize(@_);
|
||||
return $self;
|
||||
}
|
||||
|
||||
=method initialize(%hash)
|
||||
|
||||
Called by L</new> on initialization.
|
||||
|
||||
=cut
|
||||
sub initialize {
|
||||
my ($self, %args) = @_;
|
||||
for (keys %args) {
|
||||
$self->{$_} = $args{$_};
|
||||
}
|
||||
$self->{depth} = 0 unless $self->{depth};
|
||||
}
|
||||
|
||||
=method skip
|
||||
|
||||
Called by the user to ignore the contents of this EBML node.
|
||||
Needed when ignoring the children of a node.
|
||||
|
||||
=cut
|
||||
sub skip {
|
||||
my ($self) = @_;
|
||||
my $reader = $self->{reader};
|
||||
return unless $reader; # we don't have to skip if there's no reader
|
||||
my $pos = $reader->getpos;
|
||||
croak "Too late to skip, reads were already done"
|
||||
if $pos ne $self->{data_pos};
|
||||
$reader->skip($self->{content_len});
|
||||
}
|
||||
|
||||
=method get_value($keep_bin)
|
||||
|
||||
Returns the value contained by this EBML element.
|
||||
|
||||
If the element has children, returns an arrayref to
|
||||
the children elements that were already encountered.
|
||||
|
||||
If the element's type is C<binary> and the value was
|
||||
delay-loaded, does the reading now.
|
||||
|
||||
If $keep_bin is true, the delay-loaded data is kept
|
||||
as the L</value>, otherwise, further calls to
|
||||
C<get_value> will reread the data from the L</reader>.
|
||||
|
||||
=cut
|
||||
sub get_value {
|
||||
my ($self, $keep_bin) = @_;
|
||||
|
||||
return undef if $self->{type} eq 'skip';
|
||||
return $self->{value} if $self->{value};
|
||||
|
||||
my $reader = $self->{reader} or
|
||||
croak "The associated Reader has been deleted";
|
||||
|
||||
# delay-loaded 'binary'
|
||||
if ($self->{type} eq 'binary') {
|
||||
croak "Cannot seek in the current Reader" unless $self->{data_pos};
|
||||
# seek to the data position...
|
||||
$reader->setpos($self->{data_pos});
|
||||
# read the data, keeping it in value if requested
|
||||
if ($keep_bin) {
|
||||
$self->{value} = $reader->readlen($self->{content_len});
|
||||
return $self->{value};
|
||||
} else {
|
||||
return $reader->readlen($self->{content_len});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=method next_child($read_bin)
|
||||
|
||||
Builtin iterator; reads and returns the next child element.
|
||||
Always returns undef if the type isn't C<sub>.
|
||||
|
||||
Returns undef at the end of the iterator and resets itself to
|
||||
point to the first element; so calling L</next_child($read_bin)>
|
||||
after the iterator returned C<undef> will return the first child.
|
||||
|
||||
The optional C<$read_bin> parameter has the children elements
|
||||
not delay-load their value if their type is C<binary>.
|
||||
|
||||
If all children elements have already been read, return
|
||||
each element in-order as would be given by
|
||||
L</all_children($recurse,$read_bin)>.
|
||||
|
||||
=cut
|
||||
sub next_child {
|
||||
my ($self, $read_bin) = @_;
|
||||
return unless $self->{type} eq 'sub';
|
||||
|
||||
if ($self->{_all_children_read}) {
|
||||
my $idx = $self->{_last_child} ||= 0;
|
||||
if ($idx == @{$self->{value}}) {
|
||||
# reset the iterator, returning undef once
|
||||
$self->{_last_child} = 0;
|
||||
return;
|
||||
}
|
||||
my $ret = $self->{value}->[$idx];
|
||||
|
||||
++$idx;
|
||||
$self->{_last_child} = $idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
my $len = defined $self->{remaining_len}
|
||||
? $self->{remaining_len}
|
||||
: $self->{content_len};
|
||||
|
||||
if ($len == 0) {
|
||||
# we've read all children; switch into $self->{value} iteration mode
|
||||
$self->{_all_children_read} = 1;
|
||||
# return undef since the iterator will reset
|
||||
return;
|
||||
}
|
||||
|
||||
$self->{pos_offset} ||= 0;
|
||||
my $pos = $self->{data_pos};
|
||||
my $reader = $self->{reader} or croak "The associated reader has been deleted";
|
||||
$reader->setpos($pos);
|
||||
$reader->{fh}->seek($self->{pos_offset}, 1) if $pos;
|
||||
|
||||
my $chld = $reader->read_element($read_bin);
|
||||
return undef unless defined $chld;
|
||||
$self->{pos_offset} += $chld->{full_len};
|
||||
|
||||
$self->{remaining_len} = $len - $chld->{full_len};
|
||||
|
||||
if ($self->{remaining_len} < 0) {
|
||||
croak "Child elements consumed $self->{remaining_len} more bytes than parent $self->{name} contained";
|
||||
}
|
||||
|
||||
$chld->{depth} = $self->{depth} + 1;
|
||||
$self->{value} ||= [];
|
||||
|
||||
push @{$self->{value}}, $chld;
|
||||
|
||||
return $chld;
|
||||
}
|
||||
|
||||
=method all_children($recurse,$read_bin)
|
||||
|
||||
Calls L</populate_children($recurse,$read_bin)> on self
|
||||
and returns an arrayref with the children nodes.
|
||||
|
||||
Both C<$recurse> and C<$read_bin> are optional and default
|
||||
to false.
|
||||
|
||||
=cut
|
||||
sub all_children {
|
||||
my ($self, $recurse, $read_bin) = @_;
|
||||
$self->populate_children($recurse, $read_bin);
|
||||
return $self->{value};
|
||||
}
|
||||
|
||||
=method children_by_name($name)
|
||||
|
||||
Searches in the already read children elements for all
|
||||
elements with the EBML name C<$name>. Returns an array
|
||||
containing all found elements. On scalar context,
|
||||
returns only the first element found.
|
||||
|
||||
Croaks if the element's C<type> isn't C<sub>.
|
||||
|
||||
=cut
|
||||
sub children_by_name {
|
||||
my ($self, $name) = @_;
|
||||
return unless defined wantarray; # don't do work if work isn't wanted
|
||||
croak "Element can't have children" unless $self->{type} eq 'sub';
|
||||
|
||||
my @found = grep { $_->{name} eq $name } @{$self->{value}};
|
||||
return @found if wantarray; # list
|
||||
return shift @found if defined wantarray; # scalar
|
||||
}
|
||||
|
||||
=method populate_children($recurse,$read_bin)
|
||||
|
||||
Populates the internal array of children elements, that is,
|
||||
requests that the associated L<Matroska::Parser::Reader> reads
|
||||
all children elements. Returns itself.
|
||||
|
||||
Returns false if the element's C<type> isn't C<sub>.
|
||||
|
||||
If C<$recurse> is provided and is true, the method will call
|
||||
itself in the children elements with the same parameters it
|
||||
received; this will build a full EBML tree.
|
||||
|
||||
If C<$read_bin> is provided and is true, disables delay-loading
|
||||
of the contents of C<binary>-type nodes, reading the contents
|
||||
to memory.
|
||||
|
||||
If both C<$recurse> and C<$read_bin> are true, entire EBML trees
|
||||
can be loaded without requiring seeks, thus behaving correctly
|
||||
on unseekable streams. If C<$read_bin> is false, the entire EBML
|
||||
tree is still loaded, but calling L</get_value> on C<binary>-type
|
||||
nodes will produce an error on unseekable streams.
|
||||
|
||||
=cut
|
||||
sub populate_children {
|
||||
my ($self, $recurse, $read_bin) = @_;
|
||||
|
||||
return unless $self->{type} eq 'sub';
|
||||
|
||||
if (@{$self->{value}} && $recurse) {
|
||||
# only recurse
|
||||
foreach (@{$self->{value}}) {
|
||||
$_->populate_children($recurse, $read_bin);
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
while (my $chld = $self->next_child($read_bin)) {
|
||||
$chld->populate_children($recurse, $read_bin) if $recurse;
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,426 @@
|
||||
use 5.008;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: a low-level reader for EBML files
|
||||
package Parse::Matroska::Reader;
|
||||
|
||||
use Parse::Matroska::Definitions qw{elem_by_hexid};
|
||||
use Parse::Matroska::Element;
|
||||
|
||||
use Carp;
|
||||
use Scalar::Util qw{openhandle weaken};
|
||||
use IO::Handle;
|
||||
use IO::File;
|
||||
use List::Util qw{first};
|
||||
use Encode;
|
||||
|
||||
use constant BIGINT_TRY => 'Pari,GMP,FastCalc';
|
||||
use Math::BigInt try => BIGINT_TRY;
|
||||
use Math::BigRat try => BIGINT_TRY;
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Parse::Matroska::Reader;
|
||||
my $reader = Parse::Matroska::Reader->new($path);
|
||||
$reader->close;
|
||||
$reader->open(\$string_with_matroska_data);
|
||||
|
||||
my $elem = $reader->read_element;
|
||||
print "Element ID: $elem->{elid}\n";
|
||||
print "Element name: $elem->{name}\n";
|
||||
if ($elem->{type} ne 'sub') {
|
||||
print "Element value: $elem->get_value\n";
|
||||
} else {
|
||||
while (my $child = $elem->next_child) {
|
||||
print "Child element: $child->{name}\n";
|
||||
}
|
||||
}
|
||||
$reader->close;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Reads EBML data, which is used in Matroska files.
|
||||
This is a low-level reader which is meant to be used as a backend
|
||||
for higher level readers. TODO: write the high level readers :)
|
||||
|
||||
=head1 NOTE
|
||||
|
||||
The API of this module is not yet considered stable.
|
||||
|
||||
=method new
|
||||
|
||||
Creates a new reader.
|
||||
Calls L</open($arg)> with its arguments if provided.
|
||||
|
||||
=cut
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
|
||||
$self->open(@_) if @_;
|
||||
return $self;
|
||||
}
|
||||
|
||||
=method open($arg)
|
||||
|
||||
Creates the internal filehandle. The argument can be:
|
||||
|
||||
=for :list
|
||||
* An open filehandle or L<IO::Handle> object.
|
||||
The filehandle is not C<dup()>ed, so calling L</close> in this
|
||||
object will close the given filehandle as well.
|
||||
* A scalar containing a path to a file.
|
||||
* On perl v5.14 or newer, a scalarref pointing to EBML data.
|
||||
For similar functionality in older perls, give an L<IO::String> object
|
||||
or the handle to an already C<open>ed scalarref.
|
||||
|
||||
=cut
|
||||
sub open {
|
||||
my ($self, $arg) = @_;
|
||||
$self->{fh} = openhandle($arg) || IO::File->new($arg, "<:raw")
|
||||
or croak "Can't open $arg: $!";
|
||||
}
|
||||
|
||||
=method close
|
||||
|
||||
Closes the internal filehandle.
|
||||
|
||||
=cut
|
||||
sub close {
|
||||
my ($self) = @_;
|
||||
$self->{fh}->close;
|
||||
delete $self->{fh};
|
||||
}
|
||||
|
||||
# equivalent to $self->readlen(1), possibly faster
|
||||
sub _getc {
|
||||
my ($self) = @_;
|
||||
my $c = $self->{fh}->getc;
|
||||
croak "Can't do read of length 1: $!" if !defined $c && $!;
|
||||
return $c;
|
||||
}
|
||||
|
||||
=method readlen($length)
|
||||
|
||||
Reads C<$length> bytes from the internal filehandle.
|
||||
|
||||
=cut
|
||||
sub readlen {
|
||||
my ($self, $len) = @_;
|
||||
my $data;
|
||||
my $readlen = $self->{fh}->read($data, $len);
|
||||
croak "Can't do read of length $len: $!"
|
||||
unless defined $readlen;
|
||||
return $data;
|
||||
}
|
||||
|
||||
# converts a byte string into an integer
|
||||
# we do so by converting the integer into a hex string (big-endian)
|
||||
# and then reading the hex-string into an integer
|
||||
sub _bin2int($) {
|
||||
my ($bin) = @_;
|
||||
# if the length is larger than 3
|
||||
# the resulting integer might be larger than INT_MAX
|
||||
if (length($bin) > 3) {
|
||||
return Math::BigInt->from_hex(unpack("H*", $bin));
|
||||
}
|
||||
return hex(unpack("H*", $bin));
|
||||
}
|
||||
|
||||
# creates a floating-point number with the given mantissa and exponent
|
||||
sub _ldexp {
|
||||
my ($mantissa, $exponent) = @_;
|
||||
my $r = new Math::BigRat($mantissa);
|
||||
return $r * Math::BigRat->new(2)**$exponent;
|
||||
}
|
||||
|
||||
# NOTE: the read_* functions are hard to read because they're ports
|
||||
# of even harder to read python functions.
|
||||
# TODO: make them readable
|
||||
|
||||
=method read_id
|
||||
|
||||
Reads an EBML ID atom in hexadecimal string format, suitable
|
||||
for passing to L<Parse::Matroska::Definitions/elem_by_hexid($id)>.
|
||||
|
||||
=cut
|
||||
sub read_id {
|
||||
my ($self) = @_;
|
||||
my $t = $self->_getc;
|
||||
return undef unless defined $t;
|
||||
my $i = 0;
|
||||
my $mask = 1<<7;
|
||||
|
||||
if (ord($t) == 0) {
|
||||
croak "Matroska Syntax error: first byte of ID was \\0"
|
||||
}
|
||||
until (ord($t) & $mask) {
|
||||
++$i;
|
||||
$mask >>= 1;
|
||||
}
|
||||
# return hex string of the bytes we just read
|
||||
return unpack "H*", ($t . $self->readlen($i));
|
||||
}
|
||||
|
||||
=method read_size
|
||||
|
||||
Reads an EBML Data Size atom, which immediately follows
|
||||
an EBML ID atom.
|
||||
|
||||
This returns an array consisting of:
|
||||
|
||||
=for :list
|
||||
0. The length of the Data Size atom.
|
||||
1. The value encoded in the Data Size atom, which is the length of all the data following it.
|
||||
|
||||
=cut
|
||||
sub read_size {
|
||||
my ($self) = @_;
|
||||
my $t = $self->_getc;
|
||||
my $i = 0;
|
||||
my $mask = 1<<7;
|
||||
|
||||
if (ord($t) == 0) {
|
||||
croak "Matroska Syntax error: first byte of data size was \\0"
|
||||
}
|
||||
until (ord($t) & $mask) {
|
||||
++$i;
|
||||
$mask >>= 1;
|
||||
}
|
||||
$t = $t & chr($mask-1); # strip length bits (keep only significant bits)
|
||||
return ($i+1, _bin2int $t . $self->readlen($i));
|
||||
}
|
||||
|
||||
=method read_str($length)
|
||||
|
||||
Reads a string of length C<$length> bytes from the internal filehandle.
|
||||
The string is already L<Encode/decode>d from C<UTF-8>, which is the
|
||||
standard Matroska string encoding.
|
||||
|
||||
=cut
|
||||
{
|
||||
my $utf8 = find_encoding("UTF-8");
|
||||
sub read_str {
|
||||
my ($self, $length) = @_;
|
||||
return $utf8->decode($self->readlen($length));
|
||||
}
|
||||
}
|
||||
|
||||
=method read_uint($length)
|
||||
|
||||
Reads an unsigned integer of length C<$length> bytes
|
||||
from the internal filehandle.
|
||||
|
||||
Returns a L<Math::BigInt> object if C<$length> is greater
|
||||
than 4.
|
||||
|
||||
=cut
|
||||
sub read_uint {
|
||||
my ($self, $length) = @_;
|
||||
return _bin2int $self->readlen($length);
|
||||
}
|
||||
|
||||
=method read_sint($length)
|
||||
|
||||
Reads a signed integer of length C<$length> bytes
|
||||
from the internal filehandle.
|
||||
|
||||
Returns a L<Math::BigInt> object if C<$length> is greater
|
||||
than 4.
|
||||
|
||||
=cut
|
||||
sub read_sint {
|
||||
my ($self, $length) = @_;
|
||||
my $i = $self->read_uint($length);
|
||||
|
||||
# Apply 2's complement to the unsigned int
|
||||
my $mask = int(2 ** ($length * 8 - 1));
|
||||
# if the most significant bit is set...
|
||||
if ($i & $mask) {
|
||||
# subtract the MSB twice
|
||||
$i -= 2 * $mask;
|
||||
}
|
||||
return $i;
|
||||
}
|
||||
|
||||
=method read_float($length)
|
||||
|
||||
Reads an IEEE floating point number of length C<$length>
|
||||
bytes from the internal filehandle.
|
||||
|
||||
Only lengths C<4> and C<8> are supported (C C<float> and C<double>).
|
||||
|
||||
=cut
|
||||
{
|
||||
my $b1 = new Math::BigInt 1;
|
||||
|
||||
sub read_float {
|
||||
my ($self, $length) = @_;
|
||||
my $i = new Math::BigInt $self->read_uint($length)->bstr;
|
||||
my $f;
|
||||
|
||||
# These evil expressions reinterpret an unsigned int as IEEE binary floats
|
||||
if ($length == 4) {
|
||||
$f = _ldexp(($i & ((1<<23) - 1)) + (1<<23), ($i>>23 & ((1<<8) - 1)) - 150);
|
||||
$f = -$f if $i & ($b1<<31);
|
||||
} elsif ($length == 8) {
|
||||
$f = _ldexp(($i & (($b1<<52) - 1)) + ($b1<<52), ($i>>52 & ((1<<12) - 1)) - 1075);
|
||||
$f = -$f if $i & ($b1<<63);
|
||||
} else {
|
||||
croak "Matroska Syntax error: unsupported IEEE float byte size $length";
|
||||
}
|
||||
|
||||
return $f;
|
||||
}
|
||||
}
|
||||
|
||||
=method read_ebml_id($length)
|
||||
|
||||
Reads an EBML ID when it's encoded as the data inside another
|
||||
EBML element, that is, when the enclosing element's C<type> is
|
||||
C<ebml_id>.
|
||||
|
||||
This returns a hashref with the EBML element description as
|
||||
defined in L<Parse::Matroska::Definitions>.
|
||||
|
||||
=cut
|
||||
sub read_ebml_id {
|
||||
my ($self, $length) = @_;
|
||||
return elem_by_hexid(unpack("H*", $self->readlen($length)));
|
||||
}
|
||||
|
||||
=method skip($length)
|
||||
|
||||
Skips C<$length> bytes in the internal filehandle.
|
||||
|
||||
=cut
|
||||
sub skip {
|
||||
my ($self, $len) = @_;
|
||||
return if $self->{fh}->can('seek') && $self->{fh}->seek($len, 1);
|
||||
$self->readlen($len);
|
||||
return;
|
||||
}
|
||||
|
||||
=method getpos
|
||||
|
||||
Wrapper for L<IO::Seekable/$io-E<gt>getpos> in the internal filehandle.
|
||||
|
||||
Returns undef if the internal filehandle can't C<getpos>.
|
||||
|
||||
=cut
|
||||
sub getpos {
|
||||
my ($self) = @_;
|
||||
return undef unless $self->{fh}->can('getpos');
|
||||
return $self->{fh}->getpos;
|
||||
}
|
||||
|
||||
=method setpos($pos)
|
||||
|
||||
Wrapper for L<IO::Seekable/$io-E<gt>setpos> in the internal filehandle.
|
||||
|
||||
Returns C<undef> if the internal filehandle can't C<setpos>.
|
||||
|
||||
Croaks if C<setpos> does not seek to the requested position,
|
||||
that is, if calling C<getpos> does not yield the same object
|
||||
as the C<$pos> argument.
|
||||
|
||||
=cut
|
||||
sub setpos {
|
||||
my ($self, $pos) = @_;
|
||||
return undef unless $pos && $self->{fh}->can('setpos');
|
||||
|
||||
my $ret = $self->{fh}->setpos($pos);
|
||||
croak "Cannot seek to correct position"
|
||||
unless $self->getpos eq $pos;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
=method read_element($read_bin)
|
||||
|
||||
Reads a full EBML element from the internal filehandle.
|
||||
|
||||
Returns a L<Parse::Matroska::Element> object initialized with
|
||||
the read data. If C<read_bin> is not present or is false, will
|
||||
delay-load the contents of C<binary> type elements, that is,
|
||||
they will only be loaded when calling C<get_value> on the
|
||||
returned L<Parse::Matroska::Element> object.
|
||||
|
||||
Does not read the children of the element if its type is
|
||||
C<sub>. Look into the L<Parse::Matroska::Element> interface
|
||||
for details in how to read children elements.
|
||||
|
||||
Pass a true C<$read_bin> if the stream being read is not
|
||||
seekable (C<getpos> is undef) and the contents of C<binary>
|
||||
elements is desired, otherwise seeking errors or internal
|
||||
filehandle corruption might occur.
|
||||
|
||||
=cut
|
||||
sub read_element {
|
||||
my ($self, $read_bin) = @_;
|
||||
return undef if $self->{fh}->eof;
|
||||
|
||||
my $elem_pos = $self->getpos;
|
||||
|
||||
my $elid = $self->read_id;
|
||||
my $elem_def = elem_by_hexid($elid);
|
||||
my ($size_len, $content_len) = $self->read_size;
|
||||
my $full_len = length($elid)/2 + $size_len + $content_len;
|
||||
|
||||
my $elem = Parse::Matroska::Element->new(
|
||||
elid => $elid,
|
||||
name => $elem_def && $elem_def->{name},
|
||||
type => $elem_def && $elem_def->{valtype},
|
||||
size_len => $size_len,
|
||||
content_len => $content_len,
|
||||
full_len => $full_len,
|
||||
reader => $self,
|
||||
elem_pos => $elem_pos,
|
||||
data_pos => $self->getpos,
|
||||
);
|
||||
weaken($elem->{reader});
|
||||
|
||||
if (defined $elem_def) {
|
||||
if ($elem->{type} eq 'sub') {
|
||||
$elem->{value} = [];
|
||||
} elsif ($elem->{type} eq 'str') {
|
||||
$elem->{value} = $self->read_str($content_len);
|
||||
} elsif ($elem->{type} eq 'ebml_id') {
|
||||
$elem->{value} = $self->read_ebml_id($content_len);
|
||||
} elsif ($elem->{type} eq 'uint') {
|
||||
$elem->{value} = $self->read_uint($content_len);
|
||||
} elsif ($elem->{type} eq 'sint') {
|
||||
$elem->{value} = $self->read_sint($content_len);
|
||||
} elsif ($elem->{type} eq 'float') {
|
||||
$elem->{value} = $self->read_float($content_len);
|
||||
} elsif ($elem->{type} eq 'skip') {
|
||||
$self->skip($content_len);
|
||||
} elsif ($elem->{type} eq 'binary') {
|
||||
if ($read_bin) {
|
||||
$elem->{value} = $self->readlen($content_len);
|
||||
} else {
|
||||
$self->skip($content_len);
|
||||
}
|
||||
} else {
|
||||
die "Matroska Definition error: type $elem->{valtype} unknown"
|
||||
}
|
||||
} else {
|
||||
$self->skip($content_len);
|
||||
}
|
||||
return $elem;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=head1 CAVEATS
|
||||
|
||||
Children elements have to be processed as soon as an element
|
||||
with children is found, or their children ignored with
|
||||
L<Parse::Matroska::Element/skip>. Not doing so doesn't cause
|
||||
errors but results in an invalid structure, with constant C<0>
|
||||
depth.
|
||||
|
||||
To work correctly in unseekable streams, either the contents
|
||||
of C<binary>-type elements has to be ignored or the C<read_bin>
|
||||
flag to C<read_element> has to be true.
|
||||
@@ -0,0 +1,37 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: internally-used helper functions
|
||||
package Parse::Matroska::Utils;
|
||||
|
||||
use Exporter;
|
||||
our @ISA = qw{Exporter};
|
||||
our @EXPORT_OK = qw{uniq uncamelize};
|
||||
|
||||
=method uniq(@array)
|
||||
|
||||
The same as L<List::MoreUtils/"uniq LIST">.
|
||||
Included to avoid depending on it since it's
|
||||
not a core module.
|
||||
|
||||
=cut
|
||||
sub uniq(@) {
|
||||
my %seen;
|
||||
return grep { !$seen{$_}++ } @_;
|
||||
}
|
||||
|
||||
=method uncamelize($string)
|
||||
|
||||
Converts a "StringLikeTHIS" into a
|
||||
"string_like_this".
|
||||
|
||||
=cut
|
||||
sub uncamelize($) {
|
||||
local $_ = shift;
|
||||
# lc followed by UC: lc_UC
|
||||
s/(?<=[a-z])([A-Z])/_\L$1/g;
|
||||
# UC followed by two lc: _UClclc
|
||||
s/([A-Z])(?=[a-z]{2})/_\L$1/g;
|
||||
# strip leading _ that the second regexp might add; lowercase all
|
||||
s/^_//; lc
|
||||
}
|
||||
Loaded 100 of 1054 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user