Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d4273cfb5 | ||
|
|
8700700de8 | ||
|
|
eafc273d2c | ||
|
|
bb48f09532 | ||
|
|
5a011c5c06 | ||
|
|
c4d6fcbb02 | ||
|
|
6250007bca | ||
|
|
803e2c5f87 | ||
|
|
40b626fd9b | ||
|
|
a3c27786c6 | ||
|
|
03ea61c895 | ||
|
|
274c6f36f5 | ||
|
|
79efe248a5 | ||
|
|
106f607ef7 | ||
|
|
c1ae1def85 | ||
|
|
a2b93e0c27 | ||
|
|
84513ba58b | ||
|
|
cf85191cb7 | ||
|
|
19c9347e47 | ||
|
|
aceeeaf9bb | ||
|
|
98e7b4e538 | ||
|
|
ca175871cd | ||
|
|
fc1017c335 | ||
|
|
67467103e8 | ||
|
|
a55e8ff31c | ||
|
|
b787a4121a | ||
|
|
adf65634d8 | ||
|
|
5beb230690 | ||
|
|
616ecd9365 | ||
|
|
c676c31815 | ||
|
|
81ceb7b6a5 | ||
|
|
33012b4141 | ||
|
|
1d51dc20ea | ||
|
|
2e113a7391 | ||
|
|
2b71bef2ba | ||
|
|
8f1ec91b45 | ||
|
|
8b7f23129d | ||
|
|
6a06e6002b | ||
|
|
de72cb2c33 | ||
|
|
4914e688cf | ||
|
|
932ecbfe9e | ||
|
|
7c9f32620f | ||
|
|
3f5b41dfa3 | ||
|
|
0dc1fe3269 | ||
|
|
654721c27b | ||
|
|
5838322975 | ||
|
|
8dff51e06c | ||
|
|
17733bd5b8 | ||
|
|
eedda59a69 | ||
|
|
8fad4b8eab | ||
|
|
bc77565838 | ||
|
|
b0ef3dd4fb | ||
|
|
1ff6d8ae2e | ||
|
|
1afc0c8b55 | ||
|
|
a1801da717 | ||
|
|
eac977b12f | ||
|
|
3adc6071b6 | ||
|
|
0c3e4b13ac | ||
|
|
22133f8efc | ||
|
|
4abd23bd95 | ||
|
|
993466fc30 | ||
|
|
ad819b5458 | ||
|
|
894dbcd1b7 | ||
|
|
90b968a67a | ||
|
|
fc4318d23e | ||
|
|
65c06511bf | ||
|
|
ee4bed25a8 | ||
|
|
6953a1ca2f | ||
|
|
c8e3ee494c | ||
|
|
363982c774 | ||
|
|
02d2c2cc97 | ||
|
|
6e143ffec3 | ||
|
|
61ff2408e3 | ||
|
|
d0b997d528 | ||
|
|
18681a7dd2 | ||
|
|
3a78eefc88 | ||
|
|
1bf474e19a | ||
|
|
bf5c4c42fa | ||
|
|
11b8cbcce7 | ||
|
|
082340b467 | ||
|
|
5f544c1081 | ||
|
|
9ea9bdf130 | ||
|
|
202f695398 | ||
|
|
f64de3ea66 | ||
|
|
61a51c57a9 | ||
|
|
b8b3163b85 | ||
|
|
d38baa65ca | ||
|
|
8b00d82b79 | ||
|
|
7cd32ec29e | ||
|
|
183af9d72e | ||
|
|
ea03ae157f | ||
|
|
a6da4faac5 | ||
|
|
773d52162e | ||
|
|
cc9e8e7e3d | ||
|
|
ee2ba599e7 | ||
|
|
26458419a7 | ||
|
|
4bd3e51fbe | ||
|
|
5da510b5fc | ||
|
|
6b18d4dba5 | ||
|
|
dd02369c32 | ||
|
|
7684483052 | ||
|
|
e6b85c9170 | ||
|
|
5d74fa7dc0 | ||
|
|
a2fa0d0b68 | ||
|
|
b86a2316df | ||
|
|
912d668ea8 | ||
|
|
c398ab7520 | ||
|
|
68839e4e77 | ||
|
|
51dbb5607e | ||
|
|
139f6b5de7 | ||
|
|
118d7a310e | ||
|
|
0a81fe1cf9 | ||
|
|
8956229ee4 | ||
|
|
992bdc9411 | ||
|
|
5d3657e092 | ||
|
|
06065dc5ef | ||
|
|
2a99e7ae12 | ||
|
|
b99dc17f8e | ||
|
|
8bb57c0387 | ||
|
|
13259f35c8 | ||
|
|
a049a3240e | ||
|
|
0af8811b15 | ||
|
|
6a8d1cdd49 | ||
|
|
b5357e8ba7 | ||
|
|
65c75112a7 | ||
|
|
e41dbc3019 | ||
|
|
7022a24542 | ||
|
|
403f489f6c | ||
|
|
e9c43258b8 | ||
|
|
c3ae2ff107 | ||
|
|
ea1b183f4c | ||
|
|
e543853a7f | ||
|
|
9340f19ba2 | ||
|
|
53798b6465 | ||
|
|
d7e587acc9 | ||
|
|
6fb12a5fda | ||
|
|
3825dc63b2 | ||
|
|
e6291697e3 | ||
|
|
6789f9b094 | ||
|
|
387f501278 | ||
|
|
39f515cb6a | ||
|
|
13dcf82a96 | ||
|
|
202f14aa29 | ||
|
|
75cab315ea | ||
|
|
ba3f6f3779 | ||
|
|
1648ff8a0f | ||
|
|
abbc8fc84a | ||
|
|
7debdde9b9 | ||
|
|
81d125efd8 | ||
|
|
ff531b71e3 | ||
|
|
3a5cbf3907 | ||
|
|
6f4d918cb7 | ||
|
|
8d29d5b5d7 | ||
|
|
39fc5e1deb | ||
|
|
e3a57272a7 | ||
|
|
486b3ce6f8 | ||
|
|
82231fd74d | ||
|
|
052584c9e2 | ||
|
|
52fea2f909 | ||
|
|
026cccaddf | ||
|
|
ea50f6fdef | ||
|
|
33c24b07e4 | ||
|
|
5f547e57e3 | ||
|
|
51f10c512e | ||
|
|
ab07caf39d | ||
|
|
2aba6972cf | ||
|
|
2f1af04745 | ||
|
|
d3111ee777 | ||
|
|
1d385b0dae | ||
|
|
7e6456f43a | ||
|
|
b6920372c2 | ||
|
|
86ab4b8a9f | ||
|
|
b81ae52f50 | ||
|
|
c245d04c37 | ||
|
|
f549cec0ac | ||
|
|
6487ba4864 | ||
|
|
1557ca6712 | ||
|
|
b344da22ae | ||
|
|
ef2bbd5a7a | ||
|
|
73a5bde518 | ||
|
|
b712095d89 | ||
|
|
3751065f97 | ||
|
|
c239b7de7e | ||
|
|
45c97aea78 | ||
|
|
b6cbf74518 | ||
|
|
0fe3e9ca5e | ||
|
|
9f30cd8292 | ||
|
|
f0ab9f05f1 | ||
|
|
dd339b200b | ||
|
|
e6dedbcc23 | ||
|
|
a314b1013f | ||
|
|
0f110ad0e2 | ||
|
|
41002c46f5 | ||
|
|
29d74ca2f5 | ||
|
|
077dd27df2 | ||
|
|
cad6fb038b | ||
|
|
733218b233 | ||
|
|
88f10ec84f | ||
|
|
2361ec35aa | ||
|
|
68d2903cb1 | ||
|
|
e2e9a7faa8 | ||
|
|
343da8d73d | ||
|
|
441febfcba | ||
|
|
cb46f73cbb | ||
|
|
1500c84226 | ||
|
|
9eef41dec1 | ||
|
|
f0fd663320 | ||
|
|
b9153ee177 | ||
|
|
f4db6b8479 | ||
|
|
5f2822d502 | ||
|
|
9664bc23d5 | ||
|
|
6f6c4a57ec | ||
|
|
caa14e3d45 | ||
|
|
3a43682355 | ||
|
|
075edc6fee | ||
|
|
2ac74977c5 | ||
|
|
01e95468f9 | ||
|
|
efbfc00741 | ||
|
|
6fe83fdc33 | ||
|
|
6ba2bbd090 | ||
|
|
9179e8ed21 | ||
|
|
c296b6204f | ||
|
|
e300bfcf3a | ||
|
|
9df797575f | ||
|
|
78b3852f7e | ||
|
|
7783f0b7d7 | ||
|
|
75d12c174f | ||
|
|
14c232bdbf | ||
|
|
47f3cc7e6b | ||
|
|
b521f15ae9 | ||
|
|
33598182a0 | ||
|
|
f66f0b34c8 | ||
|
|
af6126adbe | ||
|
|
d080851a30 | ||
|
|
41d9f2f058 | ||
|
|
89674854ce | ||
|
|
23639e5b0e | ||
|
|
e13eb3fede | ||
|
|
bf385e1140 | ||
|
|
fe872f5688 | ||
|
|
6b5e5b6843 | ||
|
|
5fca497698 | ||
|
|
995962291a | ||
|
|
b6ba87af4b | ||
|
|
fb67db8b72 | ||
|
|
bf5c97a6c3 | ||
|
|
df85c76b7f | ||
|
|
44a7cb7f0e | ||
|
|
ce65ea3345 | ||
|
|
b62634c051 | ||
|
|
75fe626aa6 | ||
|
|
fe7db61035 | ||
|
|
32f235bcef | ||
|
|
a802afb206 | ||
|
|
297f9f1bec | ||
|
|
f504661852 | ||
|
|
57eca14a45 | ||
|
|
ed8540c38e | ||
|
|
0525f5fa93 | ||
|
|
08432fcbd1 | ||
|
|
2415b69572 | ||
|
|
3ecc6d0a79 | ||
|
|
9e972ed7d2 | ||
|
|
62d4a3891a | ||
|
|
79e20ff485 | ||
|
|
74c342c6d3 | ||
|
|
f8659d0013 | ||
|
|
554c3a1bda | ||
|
|
cb604d5412 | ||
|
|
a3e8ff624c | ||
|
|
2d34171bec | ||
|
|
ffc9aa8104 | ||
|
|
b44d075164 | ||
|
|
b7d5cbd375 | ||
|
|
dc48893630 | ||
|
|
d1d5e9dda4 | ||
|
|
7fa26bfd9c | ||
|
|
4395a4f837 | ||
|
|
56616b0b01 | ||
|
|
17e3e800e1 | ||
|
|
ccf3458f8a | ||
|
|
c47ae06ed8 | ||
|
|
8716c2e88f | ||
|
|
15baf2789c | ||
|
|
03fec24e19 | ||
|
|
b8ade7c99b | ||
|
|
f845f64c2a | ||
|
|
b83bfea05d | ||
|
|
cf5c3fc31a | ||
|
|
8bfcae748c | ||
|
|
9c9cf125ad | ||
|
|
5968a307d5 | ||
|
|
d1f5b926ac | ||
|
|
2b0c620b22 | ||
|
|
98f1b5fb98 | ||
|
|
1b5b23b948 | ||
|
|
f143665864 | ||
|
|
30d147687f | ||
|
|
88a07c5f53 | ||
|
|
e24ba8fa7f | ||
|
|
ffbc85cde9 | ||
|
|
8f1a889f75 | ||
|
|
9b6c93e904 | ||
|
|
c56f5f0681 | ||
|
|
215268ea34 | ||
|
|
d2e8bc4499 | ||
|
|
c48bd0ef18 | ||
|
|
9def3682d1 | ||
|
|
5819a4e301 | ||
|
|
060599ac6a | ||
|
|
0ccceecdc6 | ||
|
|
6dc9280b58 | ||
|
|
3905c46f9a | ||
|
|
5c0c0e469b | ||
|
|
f7471b7ff4 | ||
|
|
6a0df47d94 | ||
|
|
fb8f2a812e | ||
|
|
00f8ee573b | ||
|
|
08cbac311d | ||
|
|
60a0815b4c | ||
|
|
5edd6a8caa | ||
|
|
0e72f64ff4 | ||
|
|
876ec446f3 | ||
|
|
8c39c6903b | ||
|
|
e8cdc22245 | ||
|
|
b261e1e782 | ||
|
|
9a873cc53b | ||
|
|
274e71ee8b | ||
|
|
343f5ca24b | ||
|
|
043512045e | ||
|
|
6ac0ef78c5 | ||
|
|
3f7e43c2e2 | ||
|
|
71681e04ee | ||
|
|
440e0a98ab | ||
|
|
1e684e3cca | ||
|
|
eec7660274 | ||
|
|
defcb9047e | ||
|
|
fc1c004cd8 | ||
|
|
484328fe04 | ||
|
|
ebc04333d2 | ||
|
|
5ca654301b | ||
|
|
7383b45682 | ||
|
|
fdc5388f8c | ||
|
|
76818e3dc7 | ||
|
|
d054a7181f | ||
|
|
04320d26eb | ||
|
|
c157641019 | ||
|
|
5324fb731f | ||
|
|
9213db0d49 | ||
|
|
d66f2a679b | ||
|
|
27e3f04de7 | ||
|
|
ea94b01e6b | ||
|
|
5e30e7a041 | ||
|
|
35e8b6c1e6 | ||
|
|
ca55a32922 | ||
|
|
c8cc1f401b | ||
|
|
7177ef3e1c | ||
|
|
a2fce5ba26 | ||
|
|
c3097422f2 | ||
|
|
8bb9632e27 | ||
|
|
2048ad2b8a | ||
|
|
622f9cf1aa | ||
|
|
4552ee286b | ||
|
|
062423381d | ||
|
|
2372b0a6f6 | ||
|
|
2f1eb49e9f | ||
|
|
355361e1e3 | ||
|
|
5a7b1ff4c0 | ||
|
|
591e21a2eb | ||
|
|
c6b7a4dace | ||
|
|
2a4e7dcaad | ||
|
|
d4d8b3a4fc | ||
|
|
9f0e7bb998 | ||
|
|
e66c098228 | ||
|
|
618d7f59b0 | ||
|
|
723e3ec8d9 | ||
|
|
9ab0f60d44 | ||
|
|
e5cefa346d | ||
|
|
c36aaeeb2a | ||
|
|
c4e01cc00d | ||
|
|
1d9032f011 | ||
|
|
13786dc643 | ||
|
|
69283bc0f8 | ||
|
|
633eb30cbe | ||
|
|
327cb2a06c | ||
|
|
9e6b9d8a98 | ||
|
|
726ef35aa8 | ||
|
|
4ab860cddc | ||
|
|
cc813647d5 | ||
|
|
6cd80e972e | ||
|
|
3878a59e2c | ||
|
|
2619d8eff4 | ||
|
|
d889d1fbaa | ||
|
|
e955ae9000 | ||
|
|
4236321835 | ||
|
|
4f263dce34 | ||
|
|
590caeb2bd | ||
|
|
0c2c059826 | ||
|
|
9bc39b352e | ||
|
|
ece86d1061 | ||
|
|
c92450d7fb | ||
|
|
a85eecfe40 | ||
|
|
eb14b18a33 | ||
|
|
9770ce6c44 | ||
|
|
b92d3b6d44 | ||
|
|
cd7c7d0841 | ||
|
|
b2c84a91b6 | ||
|
|
849480d0c9 | ||
|
|
a07dae57e3 | ||
|
|
72c6bf1345 | ||
|
|
2c917219cf | ||
|
|
423e53ba0b | ||
|
|
f2e25e9e1f | ||
|
|
b2657814c9 | ||
|
|
4fa6bcbb90 | ||
|
|
875aeb0f5c | ||
|
|
c72df80460 | ||
|
|
907bf83297 | ||
|
|
3659f9e416 | ||
|
|
ce05413a87 | ||
|
|
453fea87fa | ||
|
|
1393d79417 | ||
|
|
192a7344d9 | ||
|
|
e4e1dc3c79 | ||
|
|
d32bee5f01 | ||
|
|
17dbb39dec | ||
|
|
6b4f560f3c | ||
|
|
ec3c428e5f | ||
|
|
e22ae2cba0 | ||
|
|
aae47146a7 | ||
|
|
7539928c1c | ||
|
|
c55d859920 | ||
|
|
2057209057 | ||
|
|
b10dcecf7d | ||
|
|
7dde096d8a | ||
|
|
e024906408 | ||
|
|
ce8dae3f0d | ||
|
|
f519887fe7 | ||
|
|
7614f2b3a6 | ||
|
|
4d75514321 | ||
|
|
af1379c43d | ||
|
|
e65a8d7b61 | ||
|
|
5f88e6a0db | ||
|
|
3bb134969e | ||
|
|
6f6d2eb770 | ||
|
|
f3b3702025 | ||
|
|
d223959cda | ||
|
|
ae3bbc4f63 | ||
|
|
cb2cb540f4 | ||
|
|
3e0d4de988 | ||
|
|
251ecf6e4b | ||
|
|
edbb8f6286 | ||
|
|
0110b738d5 | ||
|
|
bda614bfd8 | ||
|
|
f42e4374d5 | ||
|
|
a9a55ea7f2 | ||
|
|
5e56c07417 | ||
|
|
5086b2d456 | ||
|
|
7af6e64db7 | ||
|
|
eed99d3609 | ||
|
|
a05f20ea1e | ||
|
|
ed62f56a40 | ||
|
|
37d6604d70 | ||
|
|
872b7a2654 |
No files matched your search
@@ -8,13 +8,13 @@ If you're not using git master or the latest release, update.
|
||||
|
||||
### Actual behavior
|
||||
|
||||
### Log files
|
||||
### Log file
|
||||
|
||||
Make a log file made with -v or --log-file=output.txt, paste it to
|
||||
http://sprunge.us or a similar site, and replace this text with a link to it.
|
||||
|
||||
Providing a log file is strongly encouraged. It is very helpful for reproducing
|
||||
bugs or getting important technical context. Without log it might not be
|
||||
bugs or getting important technical context. Without a log it might not be
|
||||
possible to analyze and fix certain bugs.
|
||||
|
||||
### Sample files
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
/demux/ebml_types.h
|
||||
/sub/osd_font.h
|
||||
/player/lua/*.inc
|
||||
/player/builtin_conf.inc
|
||||
/DOCS/man/mpv.1
|
||||
/DOCS/man/mpv.aux
|
||||
/DOCS/man/mpv.log
|
||||
|
||||
@@ -12,6 +12,8 @@ env:
|
||||
global:
|
||||
# Coverity token
|
||||
- secure: "H21mSRlMhk4BKS0xHZvCFGJxteCP0hRVUxTuNfM2Z9HBsyutuLEYMtViLO86VtM+Tqla3xXPzUdS4ozLwI72Ax/5ZUDXACROj73yW6QhFB5D6rLut12+FjqC7M33Qv2hl0xwgNBmR5dsm1ToP37+Wn+ecJQNvN8fkTXF+HVzOEw="
|
||||
# Travis token for mpv.io
|
||||
- secure: "nlTVLJK6kRhtXvhKCoJ3YdFGHuKaq/eHowfPw25hqRWuBOZd+HjHY5KIYjV7SxuKFDpJE4GpNcvA3Q31nsqomxpkLYgrwjg6TSazN7ZP+x85ZgV1QGFebrPfGm2n5UR5CAPAwFoeF3pZheLi4bajVzwq1fWW+x3grS188P9OZso="
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
@@ -42,6 +44,7 @@ script:
|
||||
- ./bootstrap.py
|
||||
- ./waf configure
|
||||
- ./waf build
|
||||
after_script: TOOLS/travis-rebuild-website
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
@@ -32,8 +32,26 @@ API changes
|
||||
|
||||
::
|
||||
|
||||
--- mpv 0.20.0
|
||||
--- mpv 0.19.0
|
||||
--- 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.22.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
|
||||
|
||||
@@ -104,10 +104,10 @@ Installing MSYS2
|
||||
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. Start a MinGW-w64 shell (``mingw64_shell.bat``). Note that this is different
|
||||
2. Start a MinGW-w64 shell (``mingw64.exe``). Note that this is different
|
||||
from the MSYS2 shell that is started from the final installation dialog.
|
||||
|
||||
For a 32-bit build, use ``mingw32_shell.bat``.
|
||||
For a 32-bit build, use ``mingw32.exe``.
|
||||
|
||||
Updating MSYS2
|
||||
--------------
|
||||
@@ -118,7 +118,7 @@ separately.
|
||||
```bash
|
||||
# Check for core updates. If instructed, close the shell window and reopen it
|
||||
# before continuing.
|
||||
update-core
|
||||
pacman -Syu
|
||||
|
||||
# Update everything else
|
||||
pacman -Su
|
||||
@@ -166,11 +166,8 @@ Or, compile and install both libmpv and mpv:
|
||||
./waf configure CC=gcc.exe --check-c-compiler=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
|
||||
# waf installs libmpv dll to the wrong directory, so fix it up
|
||||
mv -f /mingw64/lib/mpv-1.dll /mingw64/bin/
|
||||
```
|
||||
|
||||
Linking libmpv with MSVC programs
|
||||
|
||||
@@ -19,6 +19,91 @@ Interface changes
|
||||
|
||||
::
|
||||
|
||||
--- mpv 0.22.0 ---
|
||||
- the "audio-device-list" property now sets empty device description to the
|
||||
device name as a fallback
|
||||
- add --hidpi-window-scale option for macOS
|
||||
- add audiounit audio output for iOS
|
||||
- make --start-time work with --rebase-start-time=no
|
||||
- add --opengl-early-flush=auto mode
|
||||
- add --hwdec=vdpau-copy, deprecate vf_vdpaurb
|
||||
- add tct video output for true-color and 256-color terminals
|
||||
--- mpv 0.21.0 ---
|
||||
- unlike in older versions, setting options at runtime will now take effect
|
||||
immediately (see for example issue #3281). On the other hand, it will also
|
||||
do runtime verification and reject option changes that do not work
|
||||
(example: setting the "vf" option to a filter during playback, which fails
|
||||
to initialize - the option value will remain at its old value). In general,
|
||||
"set name value" should be mostly equivalent to "set options/name value"
|
||||
in cases where the "name" property is not deprecated and "options/name"
|
||||
exists - deviations from this are either bugs, or documented as caveats
|
||||
in the "Inconsistencies between options and properties" manpage section.
|
||||
- deprecate _all_ --vo and --ao suboptions. Generally, all suboptions are
|
||||
replaced by global options, which do exactly the same. For example,
|
||||
"--vo=opengl:scale=nearest" turns into "--scale=nearest". In some cases,
|
||||
the global option is prefixed, e.g. "--vo=opengl:pbo" turns into
|
||||
"--opengl-pbo".
|
||||
Most of the exact replacements are documented here:
|
||||
https://github.com/mpv-player/mpv/wiki/Option-replacement-list
|
||||
- remove --vo=opengl-hq. Set --profile=opengl-hq instead. Note that this
|
||||
profile does not force the VO. This means if you use the --vo option to
|
||||
set another VO, it won't work. But this also means it can be used with
|
||||
opengl-cb.
|
||||
- remove the --vo=opengl "pre-shaders", "post-shaders" and "scale-shader"
|
||||
sub-options: they were deprecated in favor of "user-shaders"
|
||||
- deprecate --vo-defaults (no replacement)
|
||||
- remove the vo-cmdline command. You can set OpenGL renderer options
|
||||
directly via properties instead.
|
||||
- deprecate the device/sink options on all AOs. Use --audio-device instead.
|
||||
- deprecate "--ao=wasapi:exclusive" and "--ao=coreaudio:exclusive",
|
||||
use --audio-exclusive instead.
|
||||
- subtle changes in how "--no-..." options are treated mean that they are
|
||||
not accessible under "options/..." anymore (instead, these are resolved
|
||||
at parsing time). This does not affect options which start with "--no-",
|
||||
but do not use the mechanism for negation options.
|
||||
(Also see client API change for API version 1.23.)
|
||||
- rename the following properties
|
||||
- "demuxer" -> "current-demuxer"
|
||||
- "fps" -> "container-fps"
|
||||
- "idle" -> "idle-active"
|
||||
- "cache" -> "cache-percent"
|
||||
the old names are deprecated and will change behavior in mpv 0.22.0.
|
||||
- remove deprecated "hwdec-active" and "hwdec-detected" properties
|
||||
- deprecate the ao and vo auto-profiles (they never made any sense)
|
||||
- deprecate "--vo=direct3d_shaders" - use "--vo=direct3d" instead.
|
||||
Change "--vo=direct3d" to always use shaders by default.
|
||||
- deprecate --playlist-pos option, renamed to --playlist-start
|
||||
- deprecate the --chapter option, as it is redundant with --start/--end,
|
||||
and conflicts with the semantics of the "chapter" property
|
||||
- rename --sub-text-* to --sub-* and --ass-* to --sub-ass-* (old options
|
||||
deprecated)
|
||||
- incompatible change to cdda:// protocol options: the part after cdda://
|
||||
now always sets the device, not the span or speed to be played. No
|
||||
separating extra "/" is needed. The hidden --cdda-device options is also
|
||||
deleted (it was redundant with the documented --cdrom-device).
|
||||
- deprecate --vo=rpi. It will be removed in mpv 0.22.0. Its functionality
|
||||
was folded into --vo=opengl, which now uses RPI hardware decoding by
|
||||
treating it as a hardware overlay (without applying GL filtering). Also
|
||||
to be changed in 0.22.0: the --fs flag will be reset to "no" by default
|
||||
(like on the other platforms).
|
||||
- deprecate --mute=auto (informally has been since 0.18.1)
|
||||
- deprecate "resume" and "suspend" IPC commands. They will be completely
|
||||
removed in 0.22.0.
|
||||
- deprecate mp.suspend(), mp.resume(), mp.resume_all() Lua scripting
|
||||
commands, as well as setting mp.use_suspend. They will be completely
|
||||
removed in 0.22.0.
|
||||
- the "seek" command's absolute seek mode will now interpret negative
|
||||
seek times as relative from the end of the file (and clamps seeks that
|
||||
still go before 0)
|
||||
- add almost all options to the property list, meaning you can change
|
||||
options without adding "options/" to the property name (a new section
|
||||
has been added to the manpage describing some conflicting behavior
|
||||
between options and properties)
|
||||
- implement changing sub-speed during playback
|
||||
- make many previously fixed options changeable at runtime (for example
|
||||
--terminal, --osc, --ytdl, can all be enable/disabled after
|
||||
mpv_initialize() - this can be extended to other still fixed options
|
||||
on user requests)
|
||||
--- mpv 0.20.0 ---
|
||||
- add --image-display-duration option - this also means that image duration
|
||||
is not influenced by --mf-fps anymore in the general case (this is an
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
Client API (libmpv) documentation and examples
|
||||
==============================================
|
||||
|
||||
libmpv lets you embed mpv as a library. It is meant to enable development of
|
||||
GUI frontends.
|
||||
|
||||
Since libmpv merely allows access to underlying mechanisms that can control
|
||||
mpv, documentation is spread over a few places:
|
||||
|
||||
- https://github.com/mpv-player/mpv/blob/master/libmpv/client.h
|
||||
- http://mpv.io/manual/master/#options
|
||||
- http://mpv.io/manual/master/#list-of-input-commands
|
||||
- http://mpv.io/manual/master/#properties
|
||||
- https://github.com/mpv-player/mpv-examples/tree/master/libmpv
|
||||
+20
-6
@@ -312,6 +312,13 @@ Available filters are:
|
||||
``mpv '--af=format=channels=5.1' '--audio-channels=5.1'`` would always force
|
||||
remixing audio to 5.1 and output it like this.
|
||||
|
||||
This filter supports the following ``af-command`` commands:
|
||||
|
||||
``set-matrix``
|
||||
Set the ``<matrix>`` argument dynamically. This can be used to change
|
||||
the mixing matrix at runtime, without reinitializing the entire filter
|
||||
chain.
|
||||
|
||||
``drc[=method:target]``
|
||||
Applies dynamic range compression. This maximizes the volume by compressing
|
||||
the audio signal's dynamic range. (Formerly called ``volnorm``.)
|
||||
@@ -394,10 +401,6 @@ 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
|
||||
@@ -406,9 +409,13 @@ Available filters are:
|
||||
``rubberband``
|
||||
High quality pitch correction with librubberband. This can be used in place
|
||||
of ``scaletempo``, and will be used to adjust audio pitch when playing
|
||||
at speed different from normal.
|
||||
at speed different from normal. It can also be used to adjust audio pitch
|
||||
without changing playback speed.
|
||||
|
||||
This filter has a number of sub-options. You can list them with
|
||||
``<pitch-scale>``
|
||||
Sets the pitch scaling factor. Frequencies are multiplied by this value.
|
||||
|
||||
This filter has a number of additional sub-options. You can list them with
|
||||
``mpv --af=rubberband=help``. This will also show the default values
|
||||
for each option. The options are not documented here, because they are
|
||||
merely passed to librubberband. Look at the librubberband documentation
|
||||
@@ -417,6 +424,13 @@ Available filters are:
|
||||
(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``.
|
||||
|
||||
``lavfi=graph``
|
||||
Filter audio using FFmpeg's libavfilter.
|
||||
|
||||
|
||||
+74
-117
@@ -4,18 +4,17 @@ AUDIO OUTPUT DRIVERS
|
||||
Audio output drivers are interfaces to different audio output facilities. The
|
||||
syntax is:
|
||||
|
||||
``--ao=<driver1[:suboption1[=value]:...],driver2,...[,]>``
|
||||
``--ao=<driver1,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. Suboptions are optional and can mostly be omitted.
|
||||
|
||||
You can also set defaults for each driver. The defaults are applied before the
|
||||
normal driver parameters.
|
||||
in the list.
|
||||
|
||||
``--ao-defaults=<driver1[:parameter1:parameter2:...],driver2,...>``
|
||||
Set defaults for each driver.
|
||||
|
||||
Deprecated. No replacement.
|
||||
|
||||
.. note::
|
||||
|
||||
See ``--ao=help`` for a list of compiled-in audio output drivers. The
|
||||
@@ -23,87 +22,19 @@ normal driver parameters.
|
||||
where PulseAudio is used. On BSD systems, ``--ao=oss`` or ``--ao=sndio``
|
||||
may work (the latter being experimental).
|
||||
|
||||
.. 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`` (Linux only)
|
||||
ALSA audio output driver
|
||||
|
||||
``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.)
|
||||
``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``.
|
||||
``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.
|
||||
``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.
|
||||
``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,
|
||||
or for static setups with a specially engineered ALSA configuration (in
|
||||
this case you should always force the same layout with ``--audio-channels``,
|
||||
or it will work only for files which use the layout implicit to your
|
||||
ALSA device).
|
||||
|
||||
.. note::
|
||||
|
||||
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:
|
||||
|
||||
``--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.
|
||||
See `ALSA audio output options`_ for options specific to this AO.
|
||||
|
||||
.. warning::
|
||||
|
||||
Handling of multichannel/surround audio changed in mpv 0.8.0 from the
|
||||
behavior in MPlayer/mplayer2 and older versions of mpv.
|
||||
|
||||
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).
|
||||
To get multichannel/surround audio, use ``--audio-channels=auto``. The
|
||||
default for this option is ``auto-safe``, which makes this audio otuput
|
||||
explicitly reject multichannel output, as there is no way to detect
|
||||
whether a certain channel layout is actually supported.
|
||||
|
||||
You can also try `using the upmix plugin <http://git.io/vfuAy>`_.
|
||||
This setup enables multichannel audio on the ``default`` device
|
||||
@@ -113,31 +44,36 @@ Available audio output drivers are:
|
||||
``oss``
|
||||
OSS audio output driver
|
||||
|
||||
``<dsp-device>``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--oss-device``
|
||||
Sets the audio output device (default: ``/dev/dsp``).
|
||||
``<mixer-device>``
|
||||
Deprecated, use ``--audio-device``.
|
||||
``--oss-mixer-device``
|
||||
Sets the audio mixer device (default: ``/dev/mixer``).
|
||||
``<mixer-channel>``
|
||||
``--oss-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.
|
||||
|
||||
``port=<name>``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--jack-port=<name>``
|
||||
Connects to the ports with the given name (default: physical ports).
|
||||
``name=<client>``
|
||||
``--jack-name=<client>``
|
||||
Client name that is passed to JACK (default: ``mpv``). Useful
|
||||
if you want to have certain connections established automatically.
|
||||
``(no-)autostart``
|
||||
``--jack-autostart=<yes|no>``
|
||||
Automatically start jackd if necessary (default: disabled). Note that
|
||||
this tends to be unreliable and will flood stdout with server messages.
|
||||
``(no-)connect``
|
||||
``--jack-connect=<yes|no>``
|
||||
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.
|
||||
``std-channel-layout=waveext|any``
|
||||
``--jack-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
|
||||
@@ -157,7 +93,9 @@ Available audio output drivers are:
|
||||
Automatically redirects to ``coreaudio_exclusive`` when playing compressed
|
||||
formats.
|
||||
|
||||
``change-physical-format=<yes|no>``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--coreaudio-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
|
||||
@@ -165,11 +103,13 @@ 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.
|
||||
|
||||
``exclusive``
|
||||
``--coreaudio-exclusive``
|
||||
Deprecated, use ``--audio-exclusive``.
|
||||
Use exclusive mode access. This merely redirects to
|
||||
``coreaudio_exclusive``, but should be preferred over using that AO
|
||||
directly.
|
||||
|
||||
|
||||
``coreaudio_exclusive`` (Mac OS X only)
|
||||
Native Mac OS X audio output driver using direct device access and
|
||||
exclusive mode (bypasses the sound server).
|
||||
@@ -183,18 +123,21 @@ Available audio output drivers are:
|
||||
``pulse``
|
||||
PulseAudio audio output driver
|
||||
|
||||
``[<host>][:<output sink>]``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--pulse-host=<host>``, ``--pulse-sink=<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).
|
||||
Deprecated, use ``--audio-device``.
|
||||
|
||||
``buffer=<1-2000|native>``
|
||||
``--pulse-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. Default: 250.
|
||||
|
||||
``latency-hacks=<yes|no>``
|
||||
``--pulse-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
|
||||
@@ -214,61 +157,67 @@ Available audio output drivers are:
|
||||
environments, such as systems where none of the other drivers
|
||||
are available.
|
||||
|
||||
``buflen=<length>``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--sdl-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>``
|
||||
``--sdl-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. Use
|
||||
``--ao=null:untimed`` for benchmarking.
|
||||
Produces no audio output but maintains video playback speed. You can use
|
||||
``--ao=null --ao-null-untimed`` for benchmarking.
|
||||
|
||||
``untimed``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--ao-null-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.
|
||||
|
||||
``buffer``
|
||||
``--ao-null-buffer``
|
||||
Simulated buffer length in seconds.
|
||||
|
||||
``outburst``
|
||||
``--ao-null-outburst``
|
||||
Simulated chunk size in samples.
|
||||
|
||||
``speed``
|
||||
``--ao-null-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.
|
||||
|
||||
``latency``
|
||||
``--ao-null-latency``
|
||||
Simulated device latency. This is additional to EOF.
|
||||
|
||||
``broken-eof``
|
||||
``--ao-null-broken-eof``
|
||||
Simulate broken audio drivers, which always add the fixed device
|
||||
latency to the reported audio playback position.
|
||||
|
||||
``broken-delay``
|
||||
``--ao-null-broken-delay``
|
||||
Simulate broken audio drivers, which don't report latency correctly.
|
||||
|
||||
``channel-layouts``
|
||||
``--ao-null-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.
|
||||
|
||||
``pcm``
|
||||
Raw PCM/WAVE file writer audio output
|
||||
|
||||
``(no-)waveheader``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--ao-pcm-waveheader=<yes|no>``
|
||||
Include or do not include the WAVE header (default: included). When
|
||||
not included, raw PCM will be generated.
|
||||
``file=<filename>``
|
||||
``--ao-pcm-file=<filename>``
|
||||
Write the sound to ``<filename>`` instead of the default
|
||||
``audiodump.wav``. If ``no-waveheader`` is specified, the default is
|
||||
``audiodump.pcm``.
|
||||
``(no-)append``
|
||||
``--ao-pcm-append=<yes|no>``
|
||||
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.
|
||||
@@ -280,14 +229,19 @@ Available audio output drivers are:
|
||||
confused with RoarAudio, which is something completely
|
||||
different.
|
||||
|
||||
``host=<name/path>``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--rsound-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>``
|
||||
``--rsound-port=<number>``
|
||||
Set the TCP port used for connecting to the server (default: 12345).
|
||||
Not used if connecting to a Unix domain socket.
|
||||
|
||||
These options are deprecated. If anyone cares enough, their functionality
|
||||
can be added back using ``--audio-device``.
|
||||
|
||||
``sndio``
|
||||
Audio output to the OpenBSD sndio sound system
|
||||
|
||||
@@ -296,16 +250,24 @@ Available audio output drivers are:
|
||||
(Note: only supports mono, stereo, 4.0, 5.1 and 7.1 channel
|
||||
layouts.)
|
||||
|
||||
``device=<device>``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--ao-sndio-device=<device>``
|
||||
sndio device to use (default: ``$AUDIODEVICE``, resp. ``snd0``).
|
||||
Deprecated, use ``--audio-device``.
|
||||
|
||||
``wasapi``
|
||||
Audio output to the Windows Audio Session API.
|
||||
|
||||
``exclusive``
|
||||
The following global options are supported by this audio output:
|
||||
|
||||
``--ao-wasapi-exclusive``
|
||||
Deprecated, use ``--audio-exclusive``.
|
||||
Requests exclusive, direct hardware access. By definition prevents
|
||||
sound playback of any other program until mpv exits.
|
||||
``device=<id>``
|
||||
``--ao-wasapi-device=<id>``
|
||||
Deprecated, use ``--audio-device``.
|
||||
|
||||
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
|
||||
@@ -313,8 +275,3 @@ Available audio output drivers are:
|
||||
|
||||
Also supports searching active devices by human-readable name. If more
|
||||
than one device matches the name, refuses loading it.
|
||||
|
||||
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,6 +8,11 @@ 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 files ``client-api-changes.rst`` and ``interface-changes.rst`` in the
|
||||
``DOCS`` sub directoryon the git repository, which document API and user
|
||||
interface changes (the latter usually documents breaking changes only, rather
|
||||
than additions).
|
||||
* 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.
|
||||
changes that happened since mplayer2 forked off MPlayer. (Not updated
|
||||
anymore.)
|
||||
+139
-191
@@ -95,7 +95,7 @@ List of Input Commands
|
||||
relative (default)
|
||||
Seek relative to current position (a negative value seeks backwards).
|
||||
absolute
|
||||
Seek to a given time.
|
||||
Seek to a given time (a negative value starts from the end of the file).
|
||||
absolute-percent
|
||||
Seek to a given percent position.
|
||||
relative-percent
|
||||
@@ -665,13 +665,6 @@ Input Commands that are Possibly Subject to Change
|
||||
(the ``ab-loop-a`` property); the second the ``B`` point, and the third
|
||||
will clear both points.
|
||||
|
||||
``vo-cmdline "<args>"``
|
||||
Reset the sub-option of the current VO. Currently works with ``opengl``
|
||||
(including ``opengl-hq``). The argument is the sub-option string usually
|
||||
passed to the VO on the command line. Not all sub-options can be set, but
|
||||
those which can will be reset even if they don't appear in the argument.
|
||||
This command might be changed or removed in the future.
|
||||
|
||||
``drop-buffers``
|
||||
Drop audio/video/demuxer buffers, and restart from fresh. Might help with
|
||||
unseekable streams that are going out of sync.
|
||||
@@ -698,6 +691,17 @@ Input Commands that are Possibly Subject to Change
|
||||
``af-command "<label>" "<cmd>" "<args>"``
|
||||
Same as ``vf-command``, but for audio filters.
|
||||
|
||||
``apply-profile "<name>"``
|
||||
Apply the contents of a named profile. This is like using ``profile=name``
|
||||
in a config file, except you can map it to a key binding to change it at
|
||||
runtime.
|
||||
|
||||
There is no such thing as "unapplying" a profile - applying a profile
|
||||
merely sets all option values listed within the profile.
|
||||
|
||||
``load-script "<path>"``
|
||||
Load a script, similar to the ``--script`` option.
|
||||
|
||||
Undocumented commands: ``tv-last-channel`` (TV/DVB only),
|
||||
``ao-reload`` (experimental/internal).
|
||||
|
||||
@@ -838,20 +842,12 @@ an option at runtime.
|
||||
Property list
|
||||
-------------
|
||||
|
||||
``osd-level`` (RW)
|
||||
See ``--osd-level``.
|
||||
.. note::
|
||||
|
||||
``osd-scale`` (RW)
|
||||
OSD font size multiplier, see ``--osd-scale``.
|
||||
|
||||
``loop`` (RW)
|
||||
See ``--loop``.
|
||||
|
||||
``loop-file`` (RW)
|
||||
See ``--loop-file`` (uses ``yes``/``no``).
|
||||
|
||||
``speed`` (RW)
|
||||
See ``--speed``.
|
||||
Most options can be set as runtime via properties as well. Just remove the
|
||||
leading ``--`` from the option name. These are not documented. Only
|
||||
properties which do not exist as option with the same name, or which have
|
||||
very different behavior from the options are documented below.
|
||||
|
||||
``audio-speed-correction``, ``video-speed-correction``
|
||||
Factor multiplied with ``speed`` at which the player attempts to play the
|
||||
@@ -911,9 +907,11 @@ Property list
|
||||
list of format names, e.g. mp4 is ``mov,mp4,m4a,3gp,3g2,mj2`` (the list
|
||||
may grow in the future for any format).
|
||||
|
||||
``demuxer``
|
||||
``current-demuxer``
|
||||
Name of the current demuxer. (This is useless.)
|
||||
|
||||
(Renamed from ``demuxer``.)
|
||||
|
||||
``stream-path``
|
||||
Filename (full path) of the stream layer filename. (This is probably
|
||||
useless. It looks like this can be different from ``path`` only when
|
||||
@@ -989,6 +987,12 @@ Property list
|
||||
Remaining length of the file in seconds. Note that the file duration is not
|
||||
always exactly known, so this is an estimate.
|
||||
|
||||
``audio-pts`` (R)
|
||||
Current audio playback position in current file in seconds. Unlike time-pos,
|
||||
this updates more often than once per frame. For audio-only files, it is
|
||||
mostly equivalent to time-pos, while for video-only files this property is
|
||||
not available.
|
||||
|
||||
``playtime-remaining``
|
||||
``time-remaining`` scaled by the current ``speed``.
|
||||
|
||||
@@ -1078,10 +1082,6 @@ Property list
|
||||
"title" MPV_FORMAT_STRING
|
||||
"default" MPV_FORMAT_FLAG
|
||||
|
||||
``ab-loop-a``, ``ab-loop-b`` (RW)
|
||||
Set/get A-B loop points. See corresponding options and ``ab-loop`` command
|
||||
for details.
|
||||
|
||||
``angle`` (RW)
|
||||
Current DVD angle.
|
||||
|
||||
@@ -1154,13 +1154,12 @@ Property list
|
||||
``af-metadata/<filter-label>``
|
||||
Equivalent to ``vf-metadata/<filter-label>``, but for audio filters.
|
||||
|
||||
``pause`` (RW)
|
||||
Pause status. This is usually ``yes`` or ``no``. See ``--pause``.
|
||||
|
||||
``idle``
|
||||
``idle-active``
|
||||
Return ``yes`` if no file is loaded, but the player is staying around
|
||||
because of the ``--idle`` option.
|
||||
|
||||
(Renamed from ``idle``.)
|
||||
|
||||
``core-idle``
|
||||
Return ``yes`` if the playback core is paused, otherwise ``no``. This can
|
||||
be different ``pause`` in special situations, such as when the player
|
||||
@@ -1236,25 +1235,12 @@ Property list
|
||||
is loaded, or when switching ordered chapter segments. This is because
|
||||
the same underlying code is used for seeking and resyncing.)
|
||||
|
||||
``hr-seek`` (RW)
|
||||
See ``--hr-seek``.
|
||||
|
||||
``mixer-active``
|
||||
Return ``yes`` if the audio mixer is active, ``no`` otherwise.
|
||||
|
||||
This option is relatively useless. Before mpv 0.18.1, it could be used to
|
||||
infer behavior of the ``volume`` property.
|
||||
|
||||
``volume`` (RW)
|
||||
Current volume (see ``--volume`` for details).
|
||||
|
||||
``volume-max`` (RW)
|
||||
Current maximum value the volume property can be set to. (Equivalent to the
|
||||
``--volume-max`` option.)
|
||||
|
||||
``mute`` (RW)
|
||||
Current mute status (``yes``/``no``).
|
||||
|
||||
``ao-volume`` (RW)
|
||||
System volume. This property is available only if mpv audio output is
|
||||
currently active, and only if the underlying implementation supports volume
|
||||
@@ -1266,9 +1252,6 @@ Property list
|
||||
Similar to ``ao-volume``, but controls the mute state. May be unimplemented
|
||||
even if ``ao-volume`` works.
|
||||
|
||||
``audio-delay`` (RW)
|
||||
See ``--audio-delay``.
|
||||
|
||||
``audio-codec``
|
||||
Audio codec selected for decoding.
|
||||
|
||||
@@ -1316,28 +1299,6 @@ Property list
|
||||
Same as ``audio-params``, but the format of the data written to the audio
|
||||
API.
|
||||
|
||||
``aid`` (RW)
|
||||
Current audio track (similar to ``--aid``).
|
||||
|
||||
``audio`` (RW)
|
||||
Alias for ``aid``.
|
||||
|
||||
``balance`` (RW)
|
||||
Audio channel balance. (The implementation of this feature is rather odd.
|
||||
It doesn't change the volumes of each channel, but instead sets up a pan
|
||||
matrix to mix the left and right channels.)
|
||||
|
||||
Deprecated.
|
||||
|
||||
``fullscreen`` (RW)
|
||||
See ``--fullscreen``.
|
||||
|
||||
``deinterlace`` (RW)
|
||||
See ``--deinterlace``.
|
||||
|
||||
``field-dominance`` (RW)
|
||||
See ``--field-dominance``
|
||||
|
||||
``colormatrix`` (R)
|
||||
Redirects to ``video-params/colormatrix``. This parameter (as well as
|
||||
similar ones) can be overridden with the ``format`` video filter.
|
||||
@@ -1345,42 +1306,9 @@ Property list
|
||||
``colormatrix-input-range`` (R)
|
||||
See ``colormatrix``.
|
||||
|
||||
``video-output-levels`` (RW)
|
||||
See ``--video-output-levels``,
|
||||
|
||||
``colormatrix-primaries`` (R)
|
||||
See ``colormatrix``.
|
||||
|
||||
``taskbar-progress`` (RW)
|
||||
See ``--taskbar-progress``.
|
||||
|
||||
``ontop`` (RW)
|
||||
See ``--ontop``.
|
||||
|
||||
``border`` (RW)
|
||||
See ``--border``.
|
||||
|
||||
``on-all-workspaces`` (RW)
|
||||
See ``--on-all-workspaces``. Unsetting may not work on all WMs.
|
||||
|
||||
``framedrop`` (RW)
|
||||
See ``--framedrop``.
|
||||
|
||||
``gamma`` (RW)
|
||||
See ``--gamma``.
|
||||
|
||||
``brightness`` (RW)
|
||||
See ``--brightness``.
|
||||
|
||||
``contrast`` (RW)
|
||||
See ``--contrast``.
|
||||
|
||||
``saturation`` (RW)
|
||||
See ``--saturation``.
|
||||
|
||||
``hue`` (RW)
|
||||
See ``--hue``.
|
||||
|
||||
``hwdec`` (RW)
|
||||
Reflects the ``--hwdec`` option.
|
||||
|
||||
@@ -1413,27 +1341,6 @@ Property list
|
||||
multiple interop drivers for the same hardware decoder, depending on
|
||||
platform and VO.
|
||||
|
||||
``hwdec-active``
|
||||
Deprecated. To be removed in mpv 0.20.0. Use ``hwdec-current`` instead.
|
||||
|
||||
Return ``yes`` or ``no``, depending on whether any type of hardware decoding
|
||||
is actually in use.
|
||||
|
||||
``hwdec-detected``
|
||||
Deprecated. To be removed in mpv 0.20.0.
|
||||
|
||||
If hardware decoding is active, this returns the hardware decoder in use.
|
||||
Otherwise, it returns either ``no``, or if applicable, the currently loaded
|
||||
hardware decoding API. This is known only once the VO has opened (and
|
||||
possibly later). With some VOs (like ``opengl``), this is never known in
|
||||
advance, but only when the decoder attempted to create the hw decoder
|
||||
successfully. Also, hw decoders with ``-copy`` suffix will return ``no``
|
||||
while no video is being decoded. All this reflects how detecting hw decoders
|
||||
are detected and used internally in mpv.
|
||||
|
||||
``panscan`` (RW)
|
||||
See ``--panscan``.
|
||||
|
||||
``video-format``
|
||||
Video format as string.
|
||||
|
||||
@@ -1534,6 +1441,9 @@ Property list
|
||||
These have the same values as ``video-out-params/dw`` and
|
||||
``video-out-params/dh``.
|
||||
|
||||
``video-dec-params``
|
||||
Exactly like ``video-params``, but no overrides applied.
|
||||
|
||||
``video-out-params``
|
||||
Same as ``video-params``, but after video filters have been applied. If
|
||||
there are no video filters in use, this will contain the same values as
|
||||
@@ -1556,10 +1466,12 @@ Property list
|
||||
``video-frame-info/tff``
|
||||
``video-frame-info/repeat``
|
||||
|
||||
``fps``
|
||||
``container-fps``
|
||||
Container FPS. This can easily contain bogus values. For videos that use
|
||||
modern container formats or video codecs, this will often be incorrect.
|
||||
|
||||
(Renamed from ``fps``.)
|
||||
|
||||
``estimated-vf-fps``
|
||||
Estimated/measured FPS of the video filter chain output. (If no filters
|
||||
are used, this corresponds to decoder output.) This uses the average of
|
||||
@@ -1580,7 +1492,10 @@ Property list
|
||||
|
||||
``display-names``
|
||||
Names of the displays that the mpv window covers. On X11, these
|
||||
are the xrandr names (LVDS1, HDMI1, DP1, VGA1, etc.).
|
||||
are the xrandr names (LVDS1, HDMI1, DP1, VGA1, etc.). On Windows, these
|
||||
are the GDI names (\\.\DISPLAY1, \\.\DISPLAY2, etc.) and the first display
|
||||
in the list will be the one that Windows considers associated with the
|
||||
window (as determined by the MonitorFromWindow API.)
|
||||
|
||||
``display-fps`` (RW)
|
||||
The refresh rate of the current display. Currently, this is the lowest FPS
|
||||
@@ -1600,6 +1515,9 @@ Property list
|
||||
``video-aspect`` (RW)
|
||||
Video aspect, see ``--video-aspect``.
|
||||
|
||||
If video is active, this reports the effective aspect value, instead of
|
||||
the value of the ``--video-aspect`` option.
|
||||
|
||||
``osd-width``, ``osd-height``
|
||||
Last known OSD width (can be 0). This is needed if you want to use the
|
||||
``overlay-add`` command. It gives you the actual OSD size, which can be
|
||||
@@ -1608,71 +1526,24 @@ Property list
|
||||
``osd-par``
|
||||
Last known OSD display pixel aspect (can be 0).
|
||||
|
||||
``vid`` (RW)
|
||||
Current video track (similar to ``--vid``).
|
||||
|
||||
``video`` (RW)
|
||||
Alias for ``vid``.
|
||||
|
||||
``video-align-x``, ``video-align-y`` (RW)
|
||||
See ``--video-align-x`` and ``--video-align-y``.
|
||||
|
||||
``video-pan-x``, ``video-pan-y`` (RW)
|
||||
See ``--video-pan-x`` and ``--video-pan-y``.
|
||||
|
||||
``video-zoom`` (RW)
|
||||
See ``--video-zoom``.
|
||||
|
||||
``video-unscaled`` (W)
|
||||
See ``--video-unscaled``.
|
||||
|
||||
``program`` (W)
|
||||
Switch TS program (write-only).
|
||||
|
||||
``dvb-channel`` (W)
|
||||
Pair of integers: card,channel of current DVB stream.
|
||||
Can be switched to switch to another channel on the same card.
|
||||
Can be switched to switch to another channel on the same card.
|
||||
|
||||
``dvb-channel-name`` (RW)
|
||||
Name of current DVB program.
|
||||
On write, a channel-switch to the named channel on the same
|
||||
card is performed. Can also be used for channel switching.
|
||||
card is performed. Can also be used for channel switching.
|
||||
|
||||
``sid`` (RW)
|
||||
Current subtitle track (similar to ``--sid``).
|
||||
``sub-text``
|
||||
Return the current subtitle text. Formatting is stripped. If a subtitle
|
||||
is selected, but no text is currently visible, or the subtitle is not
|
||||
text-based (i.e. DVD/BD subtitles), an empty string is returned.
|
||||
|
||||
``secondary-sid`` (RW)
|
||||
Secondary subtitle track (see ``--secondary-sid``).
|
||||
|
||||
``sub`` (RW)
|
||||
Alias for ``sid``.
|
||||
|
||||
``sub-delay`` (RW)
|
||||
See ``--sub-delay``.
|
||||
|
||||
``sub-pos`` (RW)
|
||||
See ``--sub-pos``.
|
||||
|
||||
``sub-visibility`` (RW)
|
||||
See ``--sub-visibility``.
|
||||
|
||||
``sub-forced-only`` (RW)
|
||||
See ``--sub-forced-only``.
|
||||
|
||||
``sub-scale`` (RW)
|
||||
Subtitle font size multiplier.
|
||||
|
||||
``ass-force-margins`` (RW)
|
||||
See ``--ass-force-margins``.
|
||||
|
||||
``sub-use-margins`` (RW)
|
||||
See ``--sub-use-margins``.
|
||||
|
||||
``ass-vsfilter-aspect-compat`` (RW)
|
||||
See ``--ass-vsfilter-aspect-compat``.
|
||||
|
||||
``ass-style-override`` (RW)
|
||||
See ``--ass-style-override``.
|
||||
This property is experimental and might be removed in the future.
|
||||
|
||||
``stream-capture`` (RW)
|
||||
A filename, see ``--stream-capture``. Setting this will start capture using
|
||||
@@ -1884,11 +1755,8 @@ Property list
|
||||
"title" MPV_FORMAT_STRING
|
||||
"time" MPV_FORMAT_DOUBLE
|
||||
|
||||
``af`` (RW)
|
||||
See ``--af`` and the ``af`` command.
|
||||
|
||||
``vf`` (RW)
|
||||
See ``--vf`` and the ``vf`` command.
|
||||
``af``, ``vf`` (RW)
|
||||
See ``--vf``/``--af`` and the ``vf``/``af`` command.
|
||||
|
||||
When querying the property with the client API using ``MPV_FORMAT_NODE``,
|
||||
or with Lua ``mp.get_property_native``, this will return a mpv_node with
|
||||
@@ -1906,12 +1774,6 @@ Property list
|
||||
|
||||
It's also possible to write the property using this format.
|
||||
|
||||
``video-rotate`` (RW)
|
||||
See ``--video-rotate`` option.
|
||||
|
||||
``video-stereo-mode`` (RW)
|
||||
See ``--video-stereo-mode`` option.
|
||||
|
||||
``seekable``
|
||||
Return whether it's generally possible to seek in the current file.
|
||||
|
||||
@@ -2050,8 +1912,9 @@ Property list
|
||||
|
||||
The ``name`` is what is to be passed to the ``--audio-device`` option (and
|
||||
often a rather cryptic audio API-specific ID), while ``description`` is
|
||||
human readable free form text. The description is an empty string if none
|
||||
was received.
|
||||
human readable free form text. The description is set to the device name
|
||||
(minus mpv-specific ``<driver>/`` prefix) if no description is available
|
||||
or the description would have been an empty string.
|
||||
|
||||
The special entry with the name set to ``auto`` selects the default audio
|
||||
output driver and the default device.
|
||||
@@ -2151,6 +2014,10 @@ Property list
|
||||
require reloading the file for changes to take effect. If there is an
|
||||
equivalent property, prefer setting the property instead.
|
||||
|
||||
There shouldn't be any reason to access ``options/<name>`` instead of
|
||||
``<name>``, except in situations in which the properties have different
|
||||
behavior or conflicting semantics.
|
||||
|
||||
``file-local-options/<name>``
|
||||
Similar to ``options/<name>``, but when setting an option through this
|
||||
property, the option is reset to its old value once the current file has
|
||||
@@ -2204,6 +2071,87 @@ Property list
|
||||
``property-list``
|
||||
Return the list of top-level properties.
|
||||
|
||||
``profile-list``
|
||||
Return the list of profiles and their contents. This is highly
|
||||
implementation-specific, and may change any time. Currently, it returns
|
||||
an array of options for each profile. Each option has a name and a value,
|
||||
with the value currently always being a string. Note that the options array
|
||||
is not a map, as order matters and duplicate entries are possible. Recursive
|
||||
profiles are not expanded, and show up as special ``profile`` options.
|
||||
|
||||
Inconsistencies between options and properties
|
||||
----------------------------------------------
|
||||
|
||||
You can access (almost) all options as properties, though there are some
|
||||
caveats with some properties (due to historical reasons):
|
||||
|
||||
``vid``, ``aid``, ``sid``
|
||||
While playback is active, you can set existing tracks only. (The option
|
||||
allows setting any track ID, and which tracks to enable is chosen at
|
||||
loading time.)
|
||||
|
||||
Option changes at runtime are affected by this as well.
|
||||
|
||||
``deinterlace``
|
||||
While video is active, this behaves differently from the option. It will
|
||||
never return the ``auto`` value (but the state as observed by the video
|
||||
chain). If you set ``auto``, the property will set this as the option value,
|
||||
and will return the actual video chain state as observed instead of auto.
|
||||
|
||||
``video-aspect``
|
||||
While video is active, always returns the effective aspect ratio. Setting
|
||||
a special value (like ``no``, values ``<= 0``) will make the property
|
||||
set this as option, and return whatever actual aspect was derived from the
|
||||
option setting.
|
||||
|
||||
``brightness`` (and other color options)
|
||||
If ``--vo=xv`` is used, these properties may return the adapter's current
|
||||
values instead of the option values.
|
||||
|
||||
``display-fps``
|
||||
If a VO is created, this will return either the actual display FPS, or
|
||||
an invalid value, instead of the option value.
|
||||
|
||||
``vf``, ``af``
|
||||
If you set the properties during playback, and the filter chain fails to
|
||||
reinitialize, the new value will be rejected. Setting the option or
|
||||
setting the property outside of playback will always succeed/fail in the
|
||||
same way. Also, there are no ``vf-add`` etc. properties, but you can use
|
||||
the ``vf``/``af`` group of commands to achieve the same.
|
||||
|
||||
Option changes at runtime are affected by this as well.
|
||||
|
||||
``edition``
|
||||
While a file is loaded, the property will always return the effective
|
||||
edition, and setting the ``auto`` value will show somewhat strange behavior
|
||||
(the property eventually switching to whatever is the default edition).
|
||||
|
||||
``playlist``
|
||||
The property is read-only and returns the current internal playlist. The
|
||||
option is for loading playlist during command line parsing. For client API
|
||||
uses, you should use the ``loadlist`` command instead.
|
||||
|
||||
``window-scale``
|
||||
Might verify the set value when setting while a window is created.
|
||||
|
||||
``audio-file``, ``sub-file``, ``external-file``
|
||||
These options/properties are actually lists of filenames. To make the
|
||||
command-line interface easier, each ``--audio-file=...`` option appends
|
||||
the full string to the internal list. However, when used as properties,
|
||||
every time you set the property as a string the internal list will be
|
||||
replaced with a single entry containing the string you set. ``,`` or other
|
||||
separators are never used. You have to use ``MPV_FORMAT_NODE_ARRAY`` (or
|
||||
corresponding API, e.g. ``mp.set_property_native()`` with a table in Lua)
|
||||
to set multiple entries.
|
||||
|
||||
Strictly speaking, option access via API (e.g. ``mpv_set_option_string()``)
|
||||
has the same problem, and it's only a difference between CLI/API.
|
||||
|
||||
``demuxer``, ``idle``, ``length``, ``audio-samplerate``, ``audio-channels``, ``audio-format``, ``fps``, ``cache``, ``playlist-pos``, ``chapter``
|
||||
These behave completely different as property, but are deprecated (newer
|
||||
aliases which don't conflict have been added). After the deprecation period
|
||||
they will be changed to the proper option behavior.
|
||||
|
||||
Property Expansion
|
||||
------------------
|
||||
|
||||
|
||||
@@ -242,12 +242,16 @@ extra commands can also be used as part of the protocol:
|
||||
command.
|
||||
|
||||
``suspend``
|
||||
Deprecated, will be removed completely in 0.21.0.
|
||||
|
||||
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``
|
||||
Deprecated, will be removed completely in 0.21.0.
|
||||
|
||||
Undo one ``suspend`` call. ``suspend`` increments an internal counter, and
|
||||
``resume`` decrements it. When 0 is reached, the player is actually resumed.
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
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 ``libmpv/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/libmpv/client.h
|
||||
- http://mpv.io/manual/master/#options
|
||||
- http://mpv.io/manual/master/#list-of-input-commands
|
||||
- http://mpv.io/manual/master/#properties
|
||||
- https://github.com/mpv-player/mpv-examples/tree/master/libmpv
|
||||
+23
-1
@@ -412,6 +412,8 @@ These also live in the ``mp`` module, but are documented separately as they
|
||||
are useful only in special situations.
|
||||
|
||||
``mp.suspend()``
|
||||
This function has been deprecated in mpv 0.21.0 (no replacement).
|
||||
|
||||
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
|
||||
@@ -420,11 +422,15 @@ are useful only in special situations.
|
||||
Before mpv 0.17.0, this was automatically called by the event handler.
|
||||
|
||||
``mp.resume()``
|
||||
This function has been deprecated in mpv 0.21.0 (no replacement).
|
||||
|
||||
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 function has been deprecated in mpv 0.21.0 (no replacement).
|
||||
|
||||
This resets the internal suspend counter and resumes the player. (It's
|
||||
like calling ``mp.resume()`` until the player is actually resumed.)
|
||||
|
||||
@@ -450,6 +456,13 @@ 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.enable_messages(level)``
|
||||
Set the minimum log level of which mpv message output to receive. These
|
||||
messages are normally printed to the terminal. By calling this function,
|
||||
@@ -633,7 +646,16 @@ strictly part of the guaranteed API.
|
||||
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.subprocess_detached(t)``
|
||||
Runs an external process and detaches it from mpv's control.
|
||||
|
||||
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.
|
||||
|
||||
The function returns ``nil``.
|
||||
|
||||
``utils.parse_json(str [, trail])``
|
||||
Parses the given string argument as JSON, and returns it as a Lua table. On
|
||||
|
||||
+42
-39
@@ -143,11 +143,11 @@ Ctrl + and Ctrl -
|
||||
u
|
||||
Switch between applying no style overrides to SSA/ASS subtitles, and
|
||||
overriding them almost completely with the normal subtitle style. See
|
||||
``--ass-style-override`` for more info.
|
||||
``--sub-ass-style-override`` for more info.
|
||||
|
||||
V
|
||||
Toggle subtitle VSFilter aspect compatibility mode. See
|
||||
``--ass-vsfilter-aspect-compat`` for more info.
|
||||
``--sub-ass-vsfilter-aspect-compat`` for more info.
|
||||
|
||||
r and t
|
||||
Move subtitles up/down.
|
||||
@@ -268,15 +268,16 @@ command line.
|
||||
The suboption parser can quote strings with ``"`` and ``[...]``.
|
||||
Additionally, there is a special form of quoting with ``%n%`` described below.
|
||||
|
||||
For example, the ``opengl`` VO can take multiple options:
|
||||
For example, assume the hypothetical ``foo`` filter can take multiple options:
|
||||
|
||||
``mpv test.mkv --vo=opengl:scale=lanczos:icc-profile=file.icc,xv``
|
||||
``mpv test.mkv --vf=foo:option1=value1:option2:option3=value3,bar``
|
||||
|
||||
This passes ``scale=lanczos`` and ``icc-profile=file.icc`` to ``opengl``,
|
||||
and also specifies ``xv`` as fallback VO. If the icc-profile path contains
|
||||
spaces or characters like ``,`` or ``:``, you need to quote them:
|
||||
This passes ``option1`` and ``option3`` to the ``foo`` filter, with ``option2``
|
||||
as flag (implicitly ``option2=yes``), and adds a ``bar`` filter after that. If
|
||||
an option contains spaces or characters like ``,`` or ``:``, you need to quote
|
||||
them:
|
||||
|
||||
``mpv '--vo=opengl:icc-profile="file with spaces.icc",xv'``
|
||||
``mpv '--vf=foo:option1="option value with spaces",bar'``
|
||||
|
||||
Shells may actually strip some quotes from the string passed to the commandline,
|
||||
so the example quotes the string twice, ensuring that mpv receives the ``"``
|
||||
@@ -297,11 +298,11 @@ It is started with ``%`` and has the following format::
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
``mpv --ao=pcm:file=%10%C:test.wav test.avi``
|
||||
``mpv '--vf=foo:option1=%11%quoted text' test.avi``
|
||||
|
||||
Or in a script:
|
||||
|
||||
``mpv --ao=pcm:file=%`expr length "$NAME"`%"$NAME" test.avi``
|
||||
``mpv --vf=foo:option1=%`expr length "$NAME"`%"$NAME" test.avi``
|
||||
|
||||
Suboptions passed to the client API are also subject to escaping. Using
|
||||
``mpv_set_option_string()`` is exactly like passing ``--name=data`` to the
|
||||
@@ -309,8 +310,7 @@ command line (but without shell processing of the string). Some options
|
||||
support passing values in a more structured way instead of flat strings, and
|
||||
can avoid the suboption parsing mess. For example, ``--vf`` supports
|
||||
``MPV_FORMAT_NODE``, which lets you pass suboptions as a nested data structure
|
||||
of maps and arrays. (``--vo`` supports this in the same way, although this
|
||||
fact is undocumented.)
|
||||
of maps and arrays.
|
||||
|
||||
Paths
|
||||
-----
|
||||
@@ -528,7 +528,8 @@ profile name ``default`` to continue with normal options.
|
||||
|
||||
[slow]
|
||||
profile-desc="some profile name"
|
||||
vo=opengl:scale=ewa_lanczos:scale-radius=16
|
||||
# reference a builtin profile
|
||||
profile=opengl-hq
|
||||
|
||||
[fast]
|
||||
vo=vdpau
|
||||
@@ -549,10 +550,6 @@ Some profiles are loaded automatically. The following example demonstrates this:
|
||||
|
||||
::
|
||||
|
||||
[vo.vdpau]
|
||||
# Use hardware decoding
|
||||
hwdec=vdpau
|
||||
|
||||
[protocol.dvd]
|
||||
profile-desc="profile for dvd:// streams"
|
||||
alang=en
|
||||
@@ -561,11 +558,7 @@ Some profiles are loaded automatically. The following example demonstrates this:
|
||||
profile-desc="profile for .flv files"
|
||||
vf=flip
|
||||
|
||||
[ao.alsa]
|
||||
device=spdif
|
||||
|
||||
The profile name follows the schema ``type.name``, where type can be ``vo``
|
||||
to match the value the ``--vo`` option is set to, ``ao`` for ``--ao``,
|
||||
The profile name follows the schema ``type.name``, where type can be
|
||||
``protocol`` for the input/output protocol in use (see ``--list-protocols``),
|
||||
and ``extension`` for the extension of the path of the currently played file
|
||||
(*not* the file format).
|
||||
@@ -653,6 +646,15 @@ PROTOCOLS
|
||||
either aliases to documented protocols, or are just redirections to
|
||||
protocols implemented and documented in FFmpeg.
|
||||
|
||||
``ytdl://...``
|
||||
By default, the youtube-dl hook script (enabled by default for mpv CLI)
|
||||
only looks at http URLs. Prefixing an URL with ``ytdl://`` forces it to
|
||||
be always processed by the script. This can also be used to invoke special
|
||||
youtube-dl functionality like playing a video by ID or invoking search.
|
||||
|
||||
Keep in mind that you can't pass youtube-dl command line options by this,
|
||||
and you have to use ``--ytdl-raw-options`` instead.
|
||||
|
||||
``-``
|
||||
Play data from stdin.
|
||||
|
||||
@@ -688,7 +690,7 @@ PROTOCOLS
|
||||
``mf://[filemask|@listfile]`` ``--mf-...``
|
||||
Play a series of images as video.
|
||||
|
||||
``cdda://track[-endtrack][:speed][/device]`` ``--cdrom-device=PATH`` ``--cdda-...``
|
||||
``cdda://[device]`` ``--cdrom-device=PATH`` ``--cdda-...``
|
||||
Play CD.
|
||||
|
||||
``lavf://...``
|
||||
@@ -743,24 +745,32 @@ Currently this happens only in the following cases:
|
||||
or file associations provided by desktop environments)
|
||||
- if started from explorer.exe on Windows (technically, if it was started on
|
||||
Windows, and all of the stdout/stderr/stdin handles are unset)
|
||||
- manually adding ``--profile=pseudo-gui`` to the command line
|
||||
- started out of the bundle on OSX
|
||||
- if you manually use ``--player-operation-mode=pseudo-gui`` on the command line
|
||||
|
||||
This mode implicitly adds ``--profile=pseudo-gui`` to the command line, with
|
||||
the ``pseudo-gui`` profile being predefined with the following contents:
|
||||
This mode applies options from the builtin profile ``builtin-pseudo-gui``, but
|
||||
only if these haven't been set in the user's config file or on the command line.
|
||||
Also, for compatibility with the old pseudo-gui behavior, the options in the
|
||||
``pseudo-gui`` profile are applied unconditionally. In addition, the profile
|
||||
makes sure to enable the pseudo-GUI mode, so that ``--profile=pseudo-gui``
|
||||
works like in older mpv releases. The profiles are currently defined as follows:
|
||||
|
||||
::
|
||||
|
||||
[pseudo-gui]
|
||||
[builtin-pseudo-gui]
|
||||
terminal=no
|
||||
force-window=yes
|
||||
idle=once
|
||||
screenshot-directory=~~desktop/
|
||||
[pseudo-gui]
|
||||
player-operation-mode=pseudo-gui
|
||||
|
||||
This follows the mpv config file format. To customize pseudo-GUI mode, you can
|
||||
put your own ``pseudo-gui`` profile into your ``mpv.conf``. This profile will
|
||||
enhance the default profile, rather than overwrite it.
|
||||
.. warning::
|
||||
|
||||
The profile always overrides other settings in ``mpv.conf``.
|
||||
Currently, you can extend the ``pseudo-gui`` profile in the config file the
|
||||
normal way. This is deprecated. In future mpv releases, the behavior might
|
||||
change, and not apply your additional settings, and/or use a different
|
||||
profile name.
|
||||
|
||||
|
||||
.. include:: options.rst
|
||||
@@ -785,14 +795,7 @@ The profile always overrides other settings in ``mpv.conf``.
|
||||
|
||||
.. include:: changes.rst
|
||||
|
||||
|
||||
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 ``libmpv/client.h`` in the mpv
|
||||
source code repository. This provides a C API. Bindings for other languages
|
||||
might be available (see wiki).
|
||||
.. include:: libmpv.rst
|
||||
|
||||
ENVIRONMENT VARIABLES
|
||||
=====================
|
||||
|
||||
+1215
-178
File diff suppressed because it is too large.
Load diff
+142
-126
@@ -20,22 +20,25 @@ The Interface
|
||||
|
||||
::
|
||||
|
||||
+------------------+-----------+--------------------+
|
||||
| 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 |
|
||||
+----------------+--------------+-------------------+
|
||||
+---------+----------+----------------------------+-------------+
|
||||
| pl prev | pl next | title | cache |
|
||||
+------+--+---+------+---------+-----------+------+-------+-----+
|
||||
| play | skip | skip | time | seekbar | time | audio | sub |
|
||||
| | back | frwd | elapsed | | left | | |
|
||||
+------+------+------+---------+-----------+------+-------+-----+
|
||||
|
||||
|
||||
playlist prev
|
||||
pl prev
|
||||
============= ================================================
|
||||
left-click play previous file in playlist
|
||||
right-click show playlist
|
||||
shift+L-click show playlist
|
||||
============= ================================================
|
||||
|
||||
pl next
|
||||
============= ================================================
|
||||
left-click play next file in playlist
|
||||
right-click show playlist
|
||||
shift+L-click show playlist
|
||||
============= ================================================
|
||||
|
||||
@@ -47,55 +50,33 @@ title
|
||||
right-click show filename
|
||||
============= ================================================
|
||||
|
||||
playlist next
|
||||
============= ================================================
|
||||
left-click play next file in playlist
|
||||
shift+L-click show playlist
|
||||
============= ================================================
|
||||
|
||||
audio and sub
|
||||
| Displays selected track and amount of available tracks
|
||||
|
||||
============= ================================================
|
||||
left-click cycle audio/sub tracks forward
|
||||
right-click cycle audio/sub tracks backwards
|
||||
shift+L-click show available audio/sub tracks
|
||||
============= ================================================
|
||||
|
||||
skip back
|
||||
============= ================================================
|
||||
left-click go to beginning of chapter / previous chapter
|
||||
shift+L-click show chapters
|
||||
============= ================================================
|
||||
|
||||
seek back
|
||||
============= ================================================
|
||||
left-click skip back 5 seconds
|
||||
right-click skip back 30 seconds
|
||||
shift-L-click skip back 1 frame
|
||||
============= ================================================
|
||||
cache
|
||||
| Shows current cache fill status
|
||||
|
||||
play
|
||||
============= ================================================
|
||||
left-click toggle play/pause
|
||||
============= ================================================
|
||||
|
||||
seek frwd
|
||||
skip back
|
||||
============= ================================================
|
||||
left-click skip forward 10 seconds
|
||||
right-click skip forward 60 seconds
|
||||
shift-L-click skip forward 1 frame
|
||||
left-click go to beginning of chapter / previous chapter
|
||||
right-click show chapters
|
||||
shift+L-click show chapters
|
||||
============= ================================================
|
||||
|
||||
skip frwd
|
||||
============= ================================================
|
||||
left-click go to next chapter
|
||||
right-click show chapters
|
||||
shift+L-click show chapters
|
||||
============= ================================================
|
||||
|
||||
fullscreen
|
||||
time elapsed
|
||||
| Shows current playback position timestamp
|
||||
|
||||
============= ================================================
|
||||
left-click toggle fullscreen
|
||||
left-click toggle displaying timecodes with milliseconds
|
||||
============= ================================================
|
||||
|
||||
seekbar
|
||||
@@ -105,23 +86,22 @@ seekbar
|
||||
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
|
||||
time left
|
||||
| Shows remaining playback time timestamp
|
||||
|
||||
============= ================================================
|
||||
left-click toggle between total and remaining time
|
||||
============= ================================================
|
||||
|
||||
audio and sub
|
||||
| Displays selected track and amount of available tracks
|
||||
|
||||
============= ================================================
|
||||
left-click cycle audio/sub tracks forward
|
||||
right-click cycle audio/sub tracks backwards
|
||||
shift+L-click show available audio/sub tracks
|
||||
============= ================================================
|
||||
|
||||
Key Bindings
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@@ -167,85 +147,117 @@ Example::
|
||||
Configurable Options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``showwindowed``
|
||||
| Default: yes
|
||||
| Enable the OSC when windowed
|
||||
|
||||
``showfullscreen``
|
||||
| Default: yes
|
||||
| Enable the OSC when fullscreen
|
||||
|
||||
``scalewindowed``
|
||||
| Default: 1.0
|
||||
| Scale factor of the OSC when windowed
|
||||
|
||||
``scalefullscreen``
|
||||
| Default: 1.0
|
||||
| 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: 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, -1 (top) to 1 (bottom)
|
||||
|
||||
``halign``
|
||||
| Default: 0.0
|
||||
| Horizontal alignment, -1 (left) to 1 (right)
|
||||
|
||||
``boxalpha``
|
||||
| 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
|
||||
|
||||
``fadeduration``
|
||||
| Default: 200
|
||||
| Duration of fade out 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.
|
||||
|
||||
``minmousemove``
|
||||
| Default: 3
|
||||
| Minimum amount of pixels the mouse has to move between ticks to make
|
||||
the OSC show up
|
||||
|
||||
``layout``
|
||||
| Default: box
|
||||
| The layout for the OSC. Currently available are: box, slimbox,
|
||||
bottombar and topbar.
|
||||
Default: bottombar
|
||||
|
||||
The layout for the OSC. Currently available are: box, slimbox,
|
||||
bottombar and topbar. Default pre-0.21.0 was 'box'.
|
||||
|
||||
``seekbarstyle``
|
||||
| Default: slider
|
||||
| Sets the style of the seekbar, slider (diamond marker) or bar (fill)
|
||||
Default: bar
|
||||
|
||||
Sets the style of the seekbar, slider (diamond marker), knob (circle
|
||||
marker with guide), or bar (fill).
|
||||
Default pre-0.21.0 was 'slider'.
|
||||
|
||||
``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
|
||||
|
||||
``showfullscreen``
|
||||
Default: yes
|
||||
|
||||
Enable the OSC when fullscreen
|
||||
|
||||
``scalewindowed``
|
||||
Default: 1.0
|
||||
|
||||
Scale factor of the OSC when windowed.
|
||||
|
||||
``scalefullscreen``
|
||||
Default: 1.0
|
||||
|
||||
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: 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, -1 (top) to 1 (bottom)
|
||||
|
||||
``halign``
|
||||
Default: 0.0
|
||||
|
||||
Horizontal alignment, -1 (left) to 1 (right)
|
||||
|
||||
``barmargin``
|
||||
Default: 0
|
||||
|
||||
Margin from bottom (bottombar) or top (topbar), in pixels
|
||||
|
||||
``boxalpha``
|
||||
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
|
||||
|
||||
``fadeduration``
|
||||
Default: 200
|
||||
|
||||
Duration of fade out in ms, 0 = no fade
|
||||
|
||||
``tooltipborder``
|
||||
Default: 1
|
||||
|
||||
Size of the tooltip outline when using bottombar or topbar layouts
|
||||
|
||||
``timetotal``
|
||||
| Default: no
|
||||
| Show total time instead of time remaining
|
||||
Default: no
|
||||
|
||||
Show total time instead of time remaining
|
||||
|
||||
``timems``
|
||||
| Default: no
|
||||
| Display timecodes with milliseconds
|
||||
Default: no
|
||||
|
||||
Display timecodes with milliseconds
|
||||
|
||||
``visibility``
|
||||
| Default: auto (auto hide/show on mouse move)
|
||||
| Also supports ``never`` and ``always``
|
||||
Default: auto (auto hide/show on mouse move)
|
||||
|
||||
Also supports ``never`` and ``always``
|
||||
|
||||
Script Commands
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -269,3 +281,7 @@ to set auto mode (the default) with ``b``::
|
||||
a script-message osc-visibility never
|
||||
b script-message osc-visibility auto
|
||||
|
||||
``osc-playlist``, ``osc-chapterlist``, ``osc-tracklist``
|
||||
Shows a limited view of the respective type of list using the OSC. First
|
||||
argument is duration in seconds.
|
||||
|
||||
@@ -807,6 +807,8 @@ Available filters are:
|
||||
Apply high quality VDPAU scaling (needs capable hardware).
|
||||
|
||||
``vdpaurb``
|
||||
This filter is deprecated. Use ``--hwdec=vdpau-copy`` instead.
|
||||
|
||||
VDPAU video read back. Works with ``--vo=vdpau`` and ``--vo=opengl`` only.
|
||||
This filter will read back frames decoded by VDPAU so that other filters,
|
||||
which are not normally compatible with VDPAU, can be used like normal.
|
||||
|
||||
+141
-965
File diff suppressed because it is too large.
Load diff
@@ -7,8 +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.
|
||||
|
||||
.. note::
|
||||
These lists are incomplete.
|
||||
.. admonition:: Warning
|
||||
This document is not updated anymore, and is incomplete and outdated.
|
||||
|
||||
General Changes from MPlayer to mpv
|
||||
-----------------------------------
|
||||
@@ -262,13 +262,14 @@ 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-text-*``, ``--osd-*``
|
||||
``-subfont-*`` ``--sub-*``, ``--osd-*``
|
||||
``-subfont-text-scale`` ``--sub-scale``
|
||||
``-subfont`` ``--sub-text-font``
|
||||
``-subfont`` ``--sub-font``
|
||||
``-subfps`` ``--sub-fps``
|
||||
``-subpos`` ``--sub-pos``
|
||||
``-sws`` ``--sws-scaler``
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
* [Overview](#overview)
|
||||
* [Downloads](#downloads)
|
||||
* [Changelog](#changelog)
|
||||
* [Compilation](#compilation)
|
||||
* [FFmpeg vs. Libav](#ffmpeg-vs-libav)
|
||||
* [Release cycle](#release-cycle)
|
||||
@@ -14,6 +15,7 @@
|
||||
* [Contributing](#contributing)
|
||||
* [Relation to MPlayer and mplayer2](#relation-to-mplayer-and-mplayer2)
|
||||
* [Wiki](https://github.com/mpv-player/mpv/wiki)
|
||||
* [FAQ](https://github.com/mpv-player/mpv/wiki/FAQ)
|
||||
* [Man pages](http://mpv.io/manual/master/)
|
||||
* [Contact](#contact)
|
||||
* [License](#license)
|
||||
@@ -47,6 +49,20 @@ Releases can be found on the [release list][releases].
|
||||
For semi-official builds and third-party packages please see
|
||||
[mpv.io](http://mpv.io/installation/).
|
||||
|
||||
## Changelog
|
||||
|
||||
|
||||
There is no completely 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 inidcated in
|
||||
[restore-old-bindings.conf][restore-old-bindings].
|
||||
|
||||
## Compilation
|
||||
|
||||
|
||||
@@ -197,7 +213,7 @@ 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].
|
||||
and now unmaintained list of changes is located [here][mplayer-changes].
|
||||
|
||||
## Contact
|
||||
|
||||
@@ -220,6 +236,9 @@ only if discretion is required.
|
||||
[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
|
||||
[mplayer-changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/mplayer-changes.rst
|
||||
[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
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+257
-1
@@ -1,3 +1,258 @@
|
||||
Release 0.22.0
|
||||
==============
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- audio/out: add AudioUnit output driver for iOS
|
||||
- demux_mkv: parse Matroska colorimetry metadata
|
||||
- filter_kernels: add ability to taper kernels/windows, add tukey window
|
||||
- osc: add seekbarstyle=knob (#2365)
|
||||
- video/out: add tct as modern caca alternative for true-color and 256-color terminals
|
||||
- video: add --hwdec=vdpau-copy mode
|
||||
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- vf_vdpaurb: deprecated in favor of --hwdec=vdpau-copy
|
||||
|
||||
|
||||
Options and Commands
|
||||
--------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- --opengl-early-flush=auto
|
||||
- --scale-taper, --scale-wtaper
|
||||
- --scale-wblur
|
||||
- macOS: --hidpi-window-scale (#3716)
|
||||
- osc: add script message handlers for chapter/track/playlists
|
||||
|
||||
|
||||
Fixes and Minor Enhancements
|
||||
----------------------------
|
||||
|
||||
- Apply --autofit-larger after --autofit-smaller (#3753)
|
||||
- Set subtitle track title to indicate hearing/visual impaired tracks
|
||||
- ao_alsa: disable chmap API use for mono/stereo (#2905, #3045)
|
||||
- build: add required failure message for libavfilter check (#3692)
|
||||
- build: fix compilation with mingw-w64/Clang (#3800)
|
||||
- build: make VideoToolbox available on iOS
|
||||
- command: fix reset-on-next-file=all and tv-freq option (#3708)
|
||||
- command: if window-scale can't be set properly, set it as option (#3724)
|
||||
- demux_mkv: don't recursively resolve timeline for opened reference files
|
||||
- demux_mkv: fix ordered chapter sources with ordered editions
|
||||
- opengl: compile against iOS OpenGLES implementation
|
||||
- options: handle legacy no-* sub-options
|
||||
- osc: add alpha animation to tooltip (fix lingering tooltip)
|
||||
- osc: change default deadzonesize to 0.5
|
||||
- osc: don't wrap the title
|
||||
- osc: fix crash after reaching a certain position in limited lists (#3691)
|
||||
- osc: fix crash with no chapters
|
||||
- osc: fix crashes when dragging seekbar across file changes (#3210)
|
||||
- osc: fix displaying only half of the entries when at the end of a list
|
||||
- osc: fix missing chapter ticks with seekbarstyle=bar
|
||||
- osc: slimbox: fix clipping with seekbarstyle=bar (#3737)
|
||||
- osc: top/bottombar: also scale when min-width is reached to match box/slimbox behavior
|
||||
- osc: top/bottombar: dynamically size timecodes according to timems
|
||||
- osc: top/bottombar: rescale layout to same size with scale=1
|
||||
- osc: top/bottombar: scale title if too large like box
|
||||
- player: consistently initialize screensaver state with --force-window
|
||||
- player: enable no-video subtitle display on coverart too
|
||||
- player: make --start-time work with --rebase-start-time=no
|
||||
- player: make sure non-video subtitle rendering is reset if video resumes (#3770)
|
||||
- player: removing last playlist entry while looping should not stop (#3808)
|
||||
- player: show subtitles on VO if --force-window is used (#3684)
|
||||
- player: speed up audio/video re-sync when there is a huge delay
|
||||
- vdpau: fix hwdec uninit (#3788)
|
||||
- vo_opengl: blend against background color for --alpha=blend
|
||||
- vo_opengl: context_rpi: fix stdatomic usage (#3699)
|
||||
- vo_opengl: fix --blend-subtitles handling (#3773)
|
||||
- vo_opengl: fix redrawing with hardware decoding (#3773)
|
||||
- vo_opengl: partially re-enable glFlush() calls (#3670)
|
||||
- ytdl_hook: sort chapters by time
|
||||
|
||||
|
||||
This listing is not complete. Check DOCS/client-api-changes.rst for a history
|
||||
of changes to the client API, and DOCS/interface-changes.rst for a history
|
||||
of changes to other user-visible interfaces.
|
||||
|
||||
A complete changelog can be seen by running `git log v0.21.0..v0.22.0`
|
||||
in the git repository or by visiting either
|
||||
https://github.com/mpv-player/mpv/compare/v0.21.0...v0.22.0 or
|
||||
https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.21.0..v0.22.0
|
||||
|
||||
|
||||
Release 0.21.0
|
||||
==============
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- config: allow profile forward-references in default profile
|
||||
- demux_lavf: "support" mov edit lists and log errors if used (FFmpeg only)
|
||||
- hwdec: Add support for CUDA and cuvid/NvDecode (mostly useful on Linux where VDPAU still lacks HEVC Main 10 support; keep using DXVA2 on Windows)
|
||||
- osc: add right-click behavior to playlist and chapter buttons
|
||||
- osc: add user-alterable margin for top/bottombar
|
||||
- rpi: add --hwdec=rpi-copy (#3604)
|
||||
- sd_lavc: enable teletext
|
||||
- vaapi: support drm devices when running in vaapi-copy mode
|
||||
- vd_lavc: Add hwdec wrapper for crystalhd
|
||||
- vo_opengl: add hw overlay support and use it for RPI
|
||||
- vo_opengl: basic mali fbdev support
|
||||
- vo_opengl: rpi: merge vo_rpi features
|
||||
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- vo_opengl: remove pre/post/scale-shaders in favor of user-shaders
|
||||
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- config: deprecate ao and vo auto-profiles
|
||||
- vo_rpi: deprecate this VO
|
||||
|
||||
|
||||
Options and Commands
|
||||
--------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- add --teletext-page option
|
||||
- af_pan: add af-command support to change the matrix
|
||||
- af_rubberband: add af-command and option to change the pitch
|
||||
- command: add a load-script command
|
||||
- command: add a video-dec-params property
|
||||
- command: add an apply-profile command
|
||||
- command: add audio-pts property to get the audio pts
|
||||
- command: add options to property list
|
||||
- command: add sub-text property for current subtitle text
|
||||
- command: export profile list as a property (#977)
|
||||
- options: add --hwdec=yes as alias for --hwdec=auto
|
||||
- player: add --player-operation-mode=pseudo-gui
|
||||
- player: add --video-osd=no option to disable video OSD (#3387)
|
||||
- player: add --watch-later-directory option
|
||||
- stream_bluray: select title by playlist (bd://mpls/[playlist])
|
||||
- vo_opengl: disable glFlush() by default, and add an option to enable it for testing
|
||||
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- af_rubberband: default to channels=together
|
||||
- command: allow absolute seeks relative to end of stream
|
||||
- command: make bitrate properties observable
|
||||
- command: make most options observable
|
||||
- msg: make --log-file and --dump-stats accept config path expansion (#3591)
|
||||
- options: deprecate --playlist-pos to --playlist-start
|
||||
- options: make input options generally runtime-settable
|
||||
- options: rename subtitle options (--sub-text- → --sub; --ass- → --sub-ass-)
|
||||
- osc: change default layout to bottombar, seekbarstyle to bar, use larger scalewindowed and scalefullscreen
|
||||
- player: make --terminal, --log-file, --dump-stats, --osc, --ytdl, --audio-*, --priority, --stop-screensaver (#3615) settable at runtime
|
||||
- vo_drm: change CLI options
|
||||
- Change connector selection to accept human readable names (such as eDP-1, HDMI-A-2) rather than arbitrary numbers.
|
||||
- Change GPU selection to accept GPU number rather than device paths.
|
||||
- Merge connector and GPU selection into one --drm-connector.
|
||||
- Add support for --drm-connector=help.
|
||||
- Add support for --drm-* in EGL backend.
|
||||
- vo_opengl: rename 3dlut-size to icc-3dlut-size
|
||||
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- command: remove hwdec-active and hwdec-detected properties
|
||||
- command: remove vo-cmdline
|
||||
- options: drop unreferenced --bluray-angle option
|
||||
- vo, ao: disable positional parameter suboptions
|
||||
- vo_xv: replace no-colorkey with ck-method=none
|
||||
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- all VOs and AOs: deprecate sub-options, add them as global options
|
||||
- command: deprecate "cache" property, replace with "cache-percent"
|
||||
- deprecate --vo-defaults
|
||||
- deprecate --vo=opengl-hq alias in favor of opengl-hq profile
|
||||
- options: deprecate --mute=auto
|
||||
- vo_direct3d: deprecate direct3d_shaders alias, use shaders by default
|
||||
- vo_opengl: deprecate 'drm-egl' backend and introduce 'drm' instead
|
||||
|
||||
|
||||
Fixes and Minor Enhancements
|
||||
----------------------------
|
||||
|
||||
- TOOLS/zsh.pl: die if we can't parse main options
|
||||
- TOOLS/zsh.pl: don't filter files by extension (#2273)
|
||||
- ao_alsa: try to fallback to "hdmi" before "iec958" for spdif
|
||||
- ao_rsound: fix compilation (#3501)
|
||||
- aspect: use nominal width instead of actual width for video-unscaled
|
||||
- audio/out: prevent underruns with spdif under certain conditions
|
||||
- audio: fix late audio start (#3610)
|
||||
- audio: fix missed wakeup when changing audio output device
|
||||
- audio: fix segfault when yanking USB DAC
|
||||
- charset_conv: Use CP949 instead of EUC-KR
|
||||
- command: don't log "ignore" command with -v verbosity
|
||||
- command: try selecting the next track if track switching fails (#3446)
|
||||
- demux_mkv: don't crash if --ordered-chapters-files fails
|
||||
- displayconfig: treat a refresh rate of 1 as invalid
|
||||
- ipc: log when listening to IPC socket (#3598)
|
||||
- macOS: fix fullscreen regression on 10.11 and newer (#3364)
|
||||
- mp_image: fix clearing to black with p010 format
|
||||
- osc: Fix scaling issues when toggling fullscreen (#3429)
|
||||
- osc: align text vertically in top/bottombar (#2093)
|
||||
- osc: change seekbar background's alpha scaling
|
||||
- osc: fix display of chapters and playlist scaling
|
||||
- osc: move tooltip to inside seekbar for top/bottombar
|
||||
- osc: show playlist/chapter list on prev/next instead of osd
|
||||
- path: default ~~ paths to home directory (#3591)
|
||||
- player: do not let pseudo-gui override user config settings
|
||||
- player: enable reading from stdin after loading input.conf
|
||||
- player: fix instant subtitle refresh on track switches
|
||||
- player: make --force-window work with opengl-cb
|
||||
- stream_file: don't use poll() on directories (would hang on macOS; #3530, #3649)
|
||||
- stream_lavf: fix determining seekability (#1701)
|
||||
- sub: actually apply text alignment options to non-ASS subtitles
|
||||
- terminal-win: support modifier keys in console input (#3625)
|
||||
- vo_drm: fix segfault when using invalid card
|
||||
- vo_opengl: apply 90° rotation to chroma texture size (#3568)
|
||||
- vo_opengl: fix incorrect video rendering after vdpau preemption recovery
|
||||
- vo_opengl: partially fix dumb-mode cropping with rotation
|
||||
- vo_opengl: rpi: use overlay for yuv420p too
|
||||
- w32_common: initialize playback status as soon as possible (#3482)
|
||||
- wayland: reject resize events with either dimension being 0 (#3679)
|
||||
- x11: fix external fullscreen update (#3570)
|
||||
- ytdl_hook: Add title to playlist items if available
|
||||
- ytdl_hook: Set aspect ratio for anamorphic video
|
||||
- ytdl_hook: Support playlist entries without subtitles
|
||||
- ytdl_hook: add chapters by parsing video's description
|
||||
- ytdl_hook: don't add subtitles with unknown duration
|
||||
- ytdl_hook: temporarily force disable dash segments formats
|
||||
|
||||
|
||||
This listing is not complete. Check DOCS/client-api-changes.rst for a history
|
||||
of changes to the client API, and DOCS/interface-changes.rst for a history
|
||||
of changes to other user-visible interfaces.
|
||||
|
||||
A complete changelog can be seen by running `git log v0.20.0..v0.21.0`
|
||||
in the git repository or by visiting either
|
||||
https://github.com/mpv-player/mpv/compare/v0.20.0...v0.21.0 or
|
||||
https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.20.0..v0.21.0
|
||||
|
||||
|
||||
Release 0.20.0
|
||||
==============
|
||||
|
||||
@@ -10,6 +265,7 @@ Added
|
||||
- aspect: add --video-unscaled=downscale-big
|
||||
- player: add --image-display-duration option to control duration of image display (#3425)
|
||||
- vo_opengl: angle: new flag (dcomposition) to control DirectComposition
|
||||
- command: add sub-speed property
|
||||
|
||||
|
||||
Fixes and Minor Enhancements
|
||||
@@ -38,7 +294,7 @@ of changes to other user-visible interfaces.
|
||||
A complete changelog can be seen by running `git log v0.19.0..v0.20.0`
|
||||
in the git repository or by visiting either
|
||||
https://github.com/mpv-player/mpv/compare/v0.19.0...v0.20.0 or
|
||||
http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.20.0..v0.20.0
|
||||
http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.19.0..v0.20.0
|
||||
|
||||
|
||||
Release 0.19.0
|
||||
|
||||
@@ -29,7 +29,7 @@ pacman -S --noconfirm --needed \
|
||||
$MINGW_PACKAGE_PREFIX-libjpeg-turbo \
|
||||
$MINGW_PACKAGE_PREFIX-lua51 \
|
||||
$MINGW_PACKAGE_PREFIX-rubberband \
|
||||
$MINGW_PACKAGE_PREFIX-uchardet-git
|
||||
$MINGW_PACKAGE_PREFIX-uchardet
|
||||
|
||||
# Delete unused packages to reduce space used in the Appveyor cache
|
||||
pacman -Sc --noconfirm
|
||||
@@ -269,6 +269,33 @@ sub define_matroska {
|
||||
elem('FrameRate', '2383e3', 'float'),
|
||||
elem('ColourSpace', '2eb524', 'binary'),
|
||||
elem('StereoMode', '53b8', 'uint'),
|
||||
elem('Colour', '55B0', {
|
||||
elem('MatrixCoefficients', '55B1', 'uint'),
|
||||
elem('BitsPerChannel', '55B2', 'uint'),
|
||||
elem('ChromaSubsamplingHorz', '55B3', 'uint'),
|
||||
elem('ChromaSubsamplingVert', '55B4', 'uint'),
|
||||
elem('CbSubsamplingHorz', '55B5', 'uint'),
|
||||
elem('CbSubsamplingVert', '55B6', 'uint'),
|
||||
elem('ChromaSitingHorz', '55B7', 'uint'),
|
||||
elem('ChromaSitingVert', '55B8', 'uint'),
|
||||
elem('Range', '55B9', 'uint'),
|
||||
elem('TransferCharacteristics', '55BA', 'uint'),
|
||||
elem('Primaries', '55BB', 'uint'),
|
||||
elem('MaxCLL', '55BC', 'uint'),
|
||||
elem('MaxFALL', '55BD', 'uint'),
|
||||
elem('MasteringMetadata', '55D0', {
|
||||
elem('PrimaryRChromaticityX', '55D1', 'float'),
|
||||
elem('PrimaryRChromaticityY', '55D2', 'float'),
|
||||
elem('PrimaryGChromaticityX', '55D3', 'float'),
|
||||
elem('PrimaryGChromaticityY', '55D4', 'float'),
|
||||
elem('PrimaryBChromaticityX', '55D5', 'float'),
|
||||
elem('PrimaryBChromaticityY', '55D6', 'float'),
|
||||
elem('WhitePointChromaticityX', '55D7', 'float'),
|
||||
elem('WhitePointChromaticityY', '55D8', 'float'),
|
||||
elem('LuminanceMax', '55D9', 'float'),
|
||||
elem('LuminanceMin', '55DA', 'float'),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
elem('Audio', 'e1', {
|
||||
elem('SamplingFrequency', 'b5', 'float'),
|
||||
|
||||
@@ -6,9 +6,17 @@
|
||||
|
||||
local utils = require("mp.utils")
|
||||
|
||||
for i,name in ipairs(mp.get_property_native("property-list")) do
|
||||
function observe(name)
|
||||
mp.observe_property(name, "native", function(name, val)
|
||||
print("property '" .. name .. "' changed to '" ..
|
||||
utils.to_string(val) .. "'")
|
||||
end)
|
||||
end
|
||||
|
||||
for i,name in ipairs(mp.get_property_native("property-list")) do
|
||||
observe(name)
|
||||
end
|
||||
|
||||
for i,name in ipairs(mp.get_property_native("options")) do
|
||||
observe("options/" .. name)
|
||||
end
|
||||
@@ -1 +1 @@
|
||||
profile=pseudo-gui
|
||||
player-operation-mode=pseudo-gui
|
||||
+6
-5
@@ -151,15 +151,16 @@ for cur in ax:
|
||||
cur.addLegend(offset = (-1, 1))
|
||||
for e in G.sevents:
|
||||
cur = ax[1 if e.type == "value" else 0]
|
||||
if not cur in G.curveno:
|
||||
G.curveno[cur] = 0
|
||||
args = {'name': e.name,'antialias':True}
|
||||
color = mkColor(colors[G.curveno[cur] % len(colors)])
|
||||
if e.type == "event-signal":
|
||||
args['symbol'] = e.marker
|
||||
args['pen'] = None
|
||||
args['symbolBrush'] = pg.mkBrush(color, width=0)
|
||||
else:
|
||||
if not cur in G.curveno:
|
||||
G.curveno[cur] = 0
|
||||
args['pen'] = pg.mkPen(mkColor(colors[G.curveno[cur] % len(colors)]), width=0)
|
||||
G.curveno[cur] += 1
|
||||
args['pen'] = pg.mkPen(color, width=0)
|
||||
G.curveno[cur] += 1
|
||||
n = cur.plot([x for x,y in e.vals], [y for x,y in e.vals], **args)
|
||||
|
||||
QtGui.QApplication.instance().exec_()
|
||||
+1
-1
@@ -107,7 +107,7 @@ class Libav < TravisDepsBuilder
|
||||
end
|
||||
|
||||
def configure
|
||||
[super, "--cc=#{ENV['CC']}"].join(" ")
|
||||
[super, "--cc=#{ENV['CC']} --disable-asm"].join(" ")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "x$LIBAV" != "xffmpeg-git" ] || [ "x$TRAVIS_OS_NAME" != "xlinux" ]; then
|
||||
# trigger build only on one of the matrix nodes
|
||||
exit;
|
||||
fi
|
||||
|
||||
if [ "x$TRAVIS_BRANCH" != "xmaster" ]; then
|
||||
# only rebuild website with pushes to master
|
||||
exit;
|
||||
fi
|
||||
|
||||
TRAVIS_COMMIT_RANGE=${TRAVIS_COMMIT_RANGE:-HEAD^..}
|
||||
if git diff --quiet --name-only --exit-code "$TRAVIS_COMMIT_RANGE" DOCS/; then
|
||||
exit;
|
||||
fi
|
||||
|
||||
body='{
|
||||
"request": {
|
||||
"branch":"master"
|
||||
}}'
|
||||
|
||||
curl -s -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Travis-API-Version: 3" \
|
||||
-H "Authorization: token $WEBSITE_TRAVIS_TOKEN" \
|
||||
-d "$body" \
|
||||
https://api.travis-ci.org/repo/mpv-player%2Fmpv.io/requests
|
||||
+4
-2
@@ -4,11 +4,14 @@
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use warnings FATAL => 'uninitialized';
|
||||
|
||||
my $mpv = $ARGV[0] || 'mpv';
|
||||
|
||||
my @opts = parse_main_opts('--list-options', '^ (\-\-[^\s\*]*)\*?\s*(.*)');
|
||||
|
||||
die "Couldn't find any options" unless (@opts);
|
||||
|
||||
my @ao = parse_opts('--ao=help', '^ ([^\s\:]*)\s*: (.*)');
|
||||
my @vo = parse_opts('--vo=help', '^ ([^\s\:]*)\s*: (.*)');
|
||||
|
||||
@@ -141,8 +144,7 @@ $runtime_completions
|
||||
local expl
|
||||
_tags files urls
|
||||
while _tags; do
|
||||
_requested files expl 'media file' _files -g \\
|
||||
"*.(#i)(asf|asx|avi|f4v|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|opus|qt|rm|ts|vob|wav|webm|wma|wmv|wv)(-.)" && rc=0
|
||||
_requested files expl 'media file' _files && rc=0
|
||||
if _requested urls; then
|
||||
while _next_label urls expl URL; do
|
||||
_urls "\$expl[@]" && rc=0
|
||||
|
||||
@@ -103,7 +103,10 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
lavc_context->refcounted_frames = 1;
|
||||
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
lavc_context->codec_id = lavc_codec->id;
|
||||
lavc_context->time_base = ctx->codec_timebase;
|
||||
|
||||
#if LIBAVCODEC_VERSION_MICRO >= 100
|
||||
lavc_context->pkt_timebase = ctx->codec_timebase;
|
||||
#endif
|
||||
|
||||
if (opts->downmix && mpopts->audio_output_channels.num_chmaps == 1) {
|
||||
lavc_context->request_channel_layout =
|
||||
@@ -242,7 +245,8 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
|
||||
if (!got_frame)
|
||||
return 0;
|
||||
|
||||
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, &priv->codec_timebase);
|
||||
double out_pts = mp_pts_from_av(MP_AVFRAME_DEC_PTS(priv->avframe),
|
||||
&priv->codec_timebase);
|
||||
|
||||
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
|
||||
if (!mpframe)
|
||||
|
||||
@@ -180,10 +180,14 @@ static void fix_audio_pts(struct dec_audio *da)
|
||||
|
||||
if (da->current_frame->pts != MP_NOPTS_VALUE) {
|
||||
double newpts = da->current_frame->pts;
|
||||
|
||||
if (da->pts != MP_NOPTS_VALUE)
|
||||
MP_STATS(da, "value %f audio-pts-err", da->pts - newpts);
|
||||
|
||||
// Keep the interpolated timestamp if it doesn't deviate more
|
||||
// than 1 ms from the real one. (MKV rounded timestamps.)
|
||||
if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - newpts) > 0.001)
|
||||
da->pts = da->current_frame->pts;
|
||||
da->pts = newpts;
|
||||
}
|
||||
|
||||
if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
|
||||
@@ -253,12 +257,15 @@ void audio_work(struct dec_audio *da)
|
||||
struct demux_packet *new_segment = da->new_segment;
|
||||
da->new_segment = NULL;
|
||||
|
||||
// Could avoid decoder reinit; would still need flush.
|
||||
da->codec = new_segment->codec;
|
||||
if (da->ad_driver)
|
||||
da->ad_driver->uninit(da);
|
||||
da->ad_driver = NULL;
|
||||
audio_init_best_codec(da);
|
||||
if (da->codec == new_segment->codec) {
|
||||
audio_reset_decoding(da);
|
||||
} else {
|
||||
da->codec = new_segment->codec;
|
||||
if (da->ad_driver)
|
||||
da->ad_driver->uninit(da);
|
||||
da->ad_driver = NULL;
|
||||
audio_init_best_codec(da);
|
||||
}
|
||||
|
||||
da->start = new_segment->start;
|
||||
da->end = new_segment->end;
|
||||
|
||||
+4
-4
@@ -166,10 +166,10 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
|
||||
.replaygain_data = s->replaygain_data,
|
||||
.out_pool = mp_audio_pool_create(af),
|
||||
};
|
||||
struct m_config *config = m_config_from_obj_desc(af, s->log, &desc);
|
||||
if (m_config_apply_defaults(config, name, s->opts->af_defs) < 0)
|
||||
goto error;
|
||||
if (m_config_set_obj_params(config, args) < 0)
|
||||
struct m_config *config =
|
||||
m_config_from_obj_desc_and_args(af, s->log, NULL, &desc,
|
||||
name, s->opts->af_defs, args);
|
||||
if (!config)
|
||||
goto error;
|
||||
af->priv = config->optstruct;
|
||||
|
||||
|
||||
+138
-124
@@ -28,12 +28,11 @@
|
||||
#include "af.h"
|
||||
|
||||
// Data for specific instances of this filter
|
||||
typedef struct af_pan_s
|
||||
{
|
||||
int nch; // Number of output channels; zero means same as input
|
||||
float level[AF_NCH][AF_NCH]; // Gain level for each channel
|
||||
char *matrixstr;
|
||||
}af_pan_t;
|
||||
typedef struct af_pan_s {
|
||||
int nch; // Number of output channels; zero means same as input
|
||||
float level[AF_NCH][AF_NCH]; // Gain level for each channel
|
||||
char *matrixstr;
|
||||
} af_pan_t;
|
||||
|
||||
static void set_channels(struct mp_audio *mpa, int num)
|
||||
{
|
||||
@@ -44,129 +43,17 @@ static void set_channels(struct mp_audio *mpa, int num)
|
||||
mp_audio_set_channels(mpa, &map);
|
||||
}
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
static void parse_matrix(struct af_instance *af, const char *cp)
|
||||
{
|
||||
af_pan_t* s = af->priv;
|
||||
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
set_channels(af->data, s->nch ? s->nch: ((struct mp_audio*)arg)->nch);
|
||||
|
||||
if((af->data->format != ((struct mp_audio*)arg)->format) ||
|
||||
(af->data->bps != ((struct mp_audio*)arg)->bps)){
|
||||
mp_audio_set_format((struct mp_audio*)arg, af->data->format);
|
||||
return AF_FALSE;
|
||||
}
|
||||
return AF_OK;
|
||||
case AF_CONTROL_SET_PAN_LEVEL:{
|
||||
int i;
|
||||
int ch = ((af_control_ext_t*)arg)->ch;
|
||||
float* level = ((af_control_ext_t*)arg)->arg;
|
||||
if (ch >= AF_NCH)
|
||||
return AF_FALSE;
|
||||
for(i=0;i<AF_NCH;i++)
|
||||
s->level[ch][i] = level[i];
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_SET_PAN_NOUT:
|
||||
// Reinit must be called after this function has been called
|
||||
|
||||
// Sanity check
|
||||
if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
|
||||
MP_ERR(af, "The number of output channels must be"
|
||||
" between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
|
||||
return AF_ERROR;
|
||||
}
|
||||
s->nch=((int*)arg)[0];
|
||||
return AF_OK;
|
||||
case AF_CONTROL_SET_PAN_BALANCE:{
|
||||
float val = *(float*)arg;
|
||||
if (s->nch)
|
||||
return AF_ERROR;
|
||||
if (af->data->nch >= 2) {
|
||||
s->level[0][0] = MPMIN(1.f, 1.f - val);
|
||||
s->level[0][1] = MPMAX(0.f, val);
|
||||
s->level[1][0] = MPMAX(0.f, -val);
|
||||
s->level[1][1] = MPMIN(1.f, 1.f + val);
|
||||
}
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_GET_PAN_BALANCE:
|
||||
if (s->nch)
|
||||
return AF_ERROR;
|
||||
*(float*)arg = s->level[0][1] - s->level[1][0];
|
||||
return AF_OK;
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *c)
|
||||
{
|
||||
if (!c)
|
||||
return 0;
|
||||
struct mp_audio *l = mp_audio_pool_get(af->out_pool, &af->fmt_out, c->samples);
|
||||
if (!l) {
|
||||
talloc_free(c);
|
||||
return -1;
|
||||
}
|
||||
mp_audio_copy_attributes(l, c);
|
||||
|
||||
af_pan_t* s = af->priv; // Setup for this instance
|
||||
float* in = c->planes[0]; // Input audio data
|
||||
float* out = NULL; // Output audio data
|
||||
float* end = in+c->samples*c->nch; // End of loop
|
||||
int nchi = c->nch; // Number of input channels
|
||||
int ncho = l->nch; // Number of output channels
|
||||
register int j,k;
|
||||
|
||||
out = l->planes[0];
|
||||
// Execute panning
|
||||
// FIXME: Too slow
|
||||
while(in < end){
|
||||
for(j=0;j<ncho;j++){
|
||||
register float x = 0.0;
|
||||
register float* tin = in;
|
||||
for(k=0;k<nchi;k++)
|
||||
x += tin[k] * s->level[j][k];
|
||||
out[j] = x;
|
||||
}
|
||||
out+= ncho;
|
||||
in+= nchi;
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
af_add_output_frame(af, l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->filter_frame = filter_frame;
|
||||
af_pan_t *s = af->priv;
|
||||
int n = 0;
|
||||
int j,k;
|
||||
|
||||
int nch = s->nch;
|
||||
if(nch && AF_OK != control(af,AF_CONTROL_SET_PAN_NOUT, &nch))
|
||||
return AF_ERROR;
|
||||
|
||||
// Read pan values
|
||||
char *cp = s->matrixstr;
|
||||
j = 0; k = 0;
|
||||
while(cp && k < AF_NCH){
|
||||
int j = 0, k = 0, n;
|
||||
while (*cp && k < AF_NCH) {
|
||||
sscanf(cp, "%f%n" , &s->level[j][k], &n);
|
||||
MP_VERBOSE(af, "Pan level from channel %i to"
|
||||
" channel %i = %f\n",k,j,s->level[j][k]);
|
||||
cp =&cp[n];
|
||||
" channel %i = %f\n", k, j, s->level[j][k]);
|
||||
cp = &cp[n];
|
||||
j++;
|
||||
if(j>=nch){
|
||||
if (j >= s->nch) {
|
||||
j = 0;
|
||||
k++;
|
||||
}
|
||||
@@ -174,6 +61,133 @@ static int af_open(struct af_instance* af){
|
||||
break;
|
||||
cp++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance *af, int cmd, void *arg)
|
||||
{
|
||||
af_pan_t* s = af->priv;
|
||||
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:
|
||||
// Sanity check
|
||||
if (!arg)
|
||||
return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
set_channels(af->data, s->nch ? s->nch : ((struct mp_audio*)arg)->nch);
|
||||
|
||||
if ((af->data->format != ((struct mp_audio*)arg)->format) ||
|
||||
(af->data->bps != ((struct mp_audio*)arg)->bps)) {
|
||||
mp_audio_set_format((struct mp_audio*)arg, af->data->format);
|
||||
return AF_FALSE;
|
||||
}
|
||||
return AF_OK;
|
||||
case AF_CONTROL_SET_PAN_LEVEL: {
|
||||
int i;
|
||||
int ch = ((af_control_ext_t*)arg)->ch;
|
||||
float *level = ((af_control_ext_t*)arg)->arg;
|
||||
if (ch >= AF_NCH)
|
||||
return AF_FALSE;
|
||||
for (i = 0; i < AF_NCH; i++)
|
||||
s->level[ch][i] = level[i];
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_SET_PAN_NOUT:
|
||||
// Reinit must be called after this function has been called
|
||||
// Sanity check
|
||||
if (((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH) {
|
||||
MP_ERR(af, "The number of output channels must be"
|
||||
" between 1 and %i. Current value is %i\n",
|
||||
AF_NCH, ((int*)arg)[0]);
|
||||
return AF_ERROR;
|
||||
}
|
||||
s->nch = ((int*)arg)[0];
|
||||
return AF_OK;
|
||||
case AF_CONTROL_SET_PAN_BALANCE: {
|
||||
float val = *(float*)arg;
|
||||
if (s->nch)
|
||||
return AF_ERROR;
|
||||
if (af->data->nch >= 2) {
|
||||
s->level[0][0] = MPMIN(1.f, 1.f - val);
|
||||
s->level[0][1] = MPMAX(0.f, val);
|
||||
s->level[1][0] = MPMAX(0.f, -val);
|
||||
s->level[1][1] = MPMIN(1.f, 1.f + val);
|
||||
}
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_GET_PAN_BALANCE:
|
||||
if (s->nch)
|
||||
return AF_ERROR;
|
||||
*(float*)arg = s->level[0][1] - s->level[1][0];
|
||||
return AF_OK;
|
||||
case AF_CONTROL_COMMAND: {
|
||||
char **args = arg;
|
||||
if (!strcmp(args[0], "set-matrix")) {
|
||||
parse_matrix(af, args[1]);
|
||||
return CONTROL_OK;
|
||||
} else {
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *c)
|
||||
{
|
||||
if (!c)
|
||||
return 0;
|
||||
struct mp_audio *l = mp_audio_pool_get(af->out_pool, &af->fmt_out, c->samples);
|
||||
if (!l) {
|
||||
talloc_free(c);
|
||||
return -1;
|
||||
}
|
||||
mp_audio_copy_attributes(l, c);
|
||||
|
||||
af_pan_t* s = af->priv; // Setup for this instance
|
||||
float *in = c->planes[0]; // Input audio data
|
||||
float *out = NULL; // Output audio data
|
||||
float *end = in+c->samples * c->nch; // End of loop
|
||||
int nchi = c->nch; // Number of input channels
|
||||
int ncho = l->nch; // Number of output channels
|
||||
register int j, k;
|
||||
|
||||
out = l->planes[0];
|
||||
// Execute panning
|
||||
// FIXME: Too slow
|
||||
while (in < end) {
|
||||
for (j = 0; j < ncho; j++) {
|
||||
register float x = 0.0;
|
||||
register float *tin = in;
|
||||
for (k = 0; k < nchi; k++)
|
||||
x += tin[k] * s->level[j][k];
|
||||
out[j] = x;
|
||||
}
|
||||
out += ncho;
|
||||
in += nchi;
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
af_add_output_frame(af, l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance *af)
|
||||
{
|
||||
af->control = control;
|
||||
af->filter_frame = filter_frame;
|
||||
af_pan_t *s = af->priv;
|
||||
int nch = s->nch;
|
||||
if (nch && AF_OK != control(af, AF_CONTROL_SET_PAN_NOUT, &nch))
|
||||
return AF_ERROR;
|
||||
|
||||
// Read pan values
|
||||
if (s->matrixstr)
|
||||
parse_matrix(af, s->matrixstr);
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
struct priv {
|
||||
RubberBandState rubber;
|
||||
double speed;
|
||||
double pitch;
|
||||
struct mp_audio *pending;
|
||||
bool needs_reset;
|
||||
// Estimate how much librubberband has buffered internally.
|
||||
@@ -44,6 +45,14 @@ static void update_speed(struct af_instance *af, double new_speed)
|
||||
rubberband_set_time_ratio(p->rubber, 1.0 / p->speed);
|
||||
}
|
||||
|
||||
static void update_pitch(struct af_instance *af, double new_pitch)
|
||||
{
|
||||
struct priv *p = af->priv;
|
||||
|
||||
p->pitch = new_pitch;
|
||||
rubberband_set_pitch_scale(p->rubber, p->pitch);
|
||||
}
|
||||
|
||||
static int control(struct af_instance *af, int cmd, void *arg)
|
||||
{
|
||||
struct priv *p = af->priv;
|
||||
@@ -72,6 +81,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
}
|
||||
|
||||
update_speed(af, p->speed);
|
||||
update_pitch(af, p->pitch);
|
||||
control(af, AF_CONTROL_RESET, NULL);
|
||||
|
||||
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
|
||||
@@ -87,6 +97,19 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
p->pending = NULL;
|
||||
p->rubber_delay = 0;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_COMMAND: {
|
||||
char **args = arg;
|
||||
if (!strcmp(args[0], "set-pitch")) {
|
||||
char *endptr;
|
||||
double pitch = strtod(args[1], &endptr);
|
||||
if (*endptr || pitch < 0.01 || pitch > 100.0)
|
||||
return CONTROL_ERROR;
|
||||
update_pitch(af, pitch);
|
||||
return CONTROL_OK;
|
||||
} else {
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
@@ -187,9 +210,11 @@ const struct af_info af_info_rubberband = {
|
||||
.priv_size = sizeof(struct priv),
|
||||
.priv_defaults = &(const struct priv) {
|
||||
.speed = 1.0,
|
||||
.pitch = 1.0,
|
||||
.opt_pitch = RubberBandOptionPitchHighConsistency,
|
||||
.opt_transients = RubberBandOptionTransientsMixed,
|
||||
.opt_formant = RubberBandOptionFormantPreserved,
|
||||
.opt_channels = RubberBandOptionChannelsTogether,
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_CHOICE("transients", opt_transients, 0,
|
||||
@@ -220,6 +245,7 @@ const struct af_info af_info_rubberband = {
|
||||
OPT_CHOICE("channels", opt_channels, 0,
|
||||
({"apart", RubberBandOptionChannelsApart},
|
||||
{"together", RubberBandOptionChannelsTogether})),
|
||||
OPT_DOUBLE("pitch-scale", pitch, M_OPT_RANGE, .min = 0.01, .max = 100),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
+53
-26
@@ -28,7 +28,6 @@
|
||||
#include "audio/format.h"
|
||||
#include "audio/audio.h"
|
||||
|
||||
#include "input/input.h"
|
||||
#include "options/options.h"
|
||||
#include "options/m_config.h"
|
||||
#include "common/msg.h"
|
||||
@@ -36,6 +35,7 @@
|
||||
#include "common/global.h"
|
||||
|
||||
extern const struct ao_driver audio_out_oss;
|
||||
extern const struct ao_driver audio_out_audiounit;
|
||||
extern const struct ao_driver audio_out_coreaudio;
|
||||
extern const struct ao_driver audio_out_coreaudio_exclusive;
|
||||
extern const struct ao_driver audio_out_rsound;
|
||||
@@ -53,6 +53,9 @@ extern const struct ao_driver audio_out_sdl;
|
||||
|
||||
static const struct ao_driver * const audio_out_drivers[] = {
|
||||
// native:
|
||||
#if HAVE_AUDIOUNIT
|
||||
&audio_out_audiounit,
|
||||
#endif
|
||||
#if HAVE_COREAUDIO
|
||||
&audio_out_coreaudio,
|
||||
#endif
|
||||
@@ -109,6 +112,8 @@ static bool get_desc(struct m_obj_desc *dst, int index)
|
||||
.priv_size = ao->priv_size,
|
||||
.priv_defaults = ao->priv_defaults,
|
||||
.options = ao->options,
|
||||
.global_opts = ao->global_opts,
|
||||
.legacy_prefix = ao->legacy_prefix,
|
||||
.hidden = ao->encode,
|
||||
.p = ao,
|
||||
};
|
||||
@@ -121,11 +126,15 @@ const struct m_obj_list ao_obj_list = {
|
||||
.description = "audio outputs",
|
||||
.allow_unknown_entries = true,
|
||||
.allow_trailer = true,
|
||||
.disallow_positional_parameters = true,
|
||||
};
|
||||
|
||||
static struct ao *ao_alloc(bool probing, struct mpv_global *global,
|
||||
struct input_ctx *input_ctx, char *name, char **args)
|
||||
void (*wakeup_cb)(void *ctx), void *wakeup_ctx,
|
||||
char *name, char **args)
|
||||
{
|
||||
assert(wakeup_cb);
|
||||
|
||||
struct MPOpts *opts = global->opts;
|
||||
struct mp_log *log = mp_log_new(NULL, global->log, "ao");
|
||||
struct m_obj_desc desc;
|
||||
@@ -139,15 +148,17 @@ static struct ao *ao_alloc(bool probing, struct mpv_global *global,
|
||||
*ao = (struct ao) {
|
||||
.driver = desc.p,
|
||||
.probing = probing,
|
||||
.input_ctx = input_ctx,
|
||||
.global = global,
|
||||
.wakeup_cb = wakeup_cb,
|
||||
.wakeup_ctx = wakeup_ctx,
|
||||
.log = mp_log_new(ao, log, name),
|
||||
.def_buffer = opts->audio_buffer,
|
||||
.client_name = talloc_strdup(ao, opts->audio_client_name),
|
||||
};
|
||||
struct m_config *config = m_config_from_obj_desc(ao, ao->log, &desc);
|
||||
if (m_config_apply_defaults(config, name, opts->ao_defs) < 0)
|
||||
goto error;
|
||||
if (m_config_set_obj_params(config, args) < 0)
|
||||
struct m_config *config =
|
||||
m_config_from_obj_desc_and_args(ao, ao->log, global, &desc,
|
||||
name, opts->ao_defs, args);
|
||||
if (!config)
|
||||
goto error;
|
||||
ao->priv = config->optstruct;
|
||||
return ao;
|
||||
@@ -157,12 +168,12 @@ error:
|
||||
}
|
||||
|
||||
static struct ao *ao_init(bool probing, struct mpv_global *global,
|
||||
struct input_ctx *input_ctx,
|
||||
void (*wakeup_cb)(void *ctx), void *wakeup_ctx,
|
||||
struct encode_lavc_context *encode_lavc_ctx, int flags,
|
||||
int samplerate, int format, struct mp_chmap channels,
|
||||
char *dev, char *name, char **args)
|
||||
{
|
||||
struct ao *ao = ao_alloc(probing, global, input_ctx, name, args);
|
||||
struct ao *ao = ao_alloc(probing, global, wakeup_cb, wakeup_ctx, name, args);
|
||||
if (!ao)
|
||||
return NULL;
|
||||
ao->samplerate = samplerate;
|
||||
@@ -193,8 +204,9 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
|
||||
snprintf(redirect, sizeof(redirect), "%s", ao->redirect);
|
||||
snprintf(rdevice, sizeof(rdevice), "%s", ao->device ? ao->device : "");
|
||||
talloc_free(ao);
|
||||
return ao_init(probing, global, input_ctx, encode_lavc_ctx, flags,
|
||||
samplerate, format, channels, rdevice, redirect, NULL);
|
||||
return ao_init(probing, global, wakeup_cb, wakeup_ctx,
|
||||
encode_lavc_ctx, flags, samplerate, format, channels,
|
||||
rdevice, redirect, NULL);
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
@@ -244,7 +256,7 @@ static void split_ao_device(void *tmp, char *opt, char **out_ao, char **out_dev)
|
||||
|
||||
struct ao *ao_init_best(struct mpv_global *global,
|
||||
int init_flags,
|
||||
struct input_ctx *input_ctx,
|
||||
void (*wakeup_cb)(void *ctx), void *wakeup_ctx,
|
||||
struct encode_lavc_context *encode_lavc_ctx,
|
||||
int samplerate, int format, struct mp_chmap channels)
|
||||
{
|
||||
@@ -300,8 +312,8 @@ struct ao *ao_init_best(struct mpv_global *global,
|
||||
dev = pref_dev;
|
||||
mp_verbose(log, "Using preferred device '%s'\n", dev);
|
||||
}
|
||||
ao = ao_init(probing, global, input_ctx, encode_lavc_ctx, init_flags,
|
||||
samplerate, format, channels, dev,
|
||||
ao = ao_init(probing, global, wakeup_cb, wakeup_ctx, encode_lavc_ctx,
|
||||
init_flags, samplerate, format, channels, dev,
|
||||
entry->name, entry->attribs);
|
||||
if (ao)
|
||||
break;
|
||||
@@ -405,8 +417,7 @@ int ao_query_and_reset_events(struct ao *ao, int events)
|
||||
static void ao_add_events(struct ao *ao, int events)
|
||||
{
|
||||
atomic_fetch_or(&ao->events_, events);
|
||||
if (ao->input_ctx)
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
ao->wakeup_cb(ao->wakeup_ctx);
|
||||
}
|
||||
|
||||
// Request that the player core destroys and recreates the AO. Fully thread-safe.
|
||||
@@ -490,7 +501,8 @@ bool ao_untimed(struct ao *ao)
|
||||
|
||||
struct ao_hotplug {
|
||||
struct mpv_global *global;
|
||||
struct input_ctx *input_ctx;
|
||||
void (*wakeup_cb)(void *ctx);
|
||||
void *wakeup_ctx;
|
||||
// A single AO instance is used to listen to hotplug events. It wouldn't
|
||||
// make much sense to allow multiple AO drivers; all sane platforms have
|
||||
// a single such audio API.
|
||||
@@ -502,12 +514,14 @@ struct ao_hotplug {
|
||||
};
|
||||
|
||||
struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
|
||||
struct input_ctx *input_ctx)
|
||||
void (*wakeup_cb)(void *ctx),
|
||||
void *wakeup_ctx)
|
||||
{
|
||||
struct ao_hotplug *hp = talloc_ptrtype(NULL, hp);
|
||||
*hp = (struct ao_hotplug){
|
||||
.global = global,
|
||||
.input_ctx = input_ctx,
|
||||
.wakeup_cb = wakeup_cb,
|
||||
.wakeup_ctx = wakeup_ctx,
|
||||
.needs_update = true,
|
||||
};
|
||||
return hp;
|
||||
@@ -515,14 +529,10 @@ struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
|
||||
|
||||
static void get_devices(struct ao *ao, struct ao_device_list *list)
|
||||
{
|
||||
int num = list->num_devices;
|
||||
if (ao->driver->list_devs) {
|
||||
ao->driver->list_devs(ao, list);
|
||||
} else {
|
||||
char name[80] = "Default";
|
||||
if (num > 1)
|
||||
mp_snprintf_cat(name, sizeof(name), " (%s)", ao->driver->name);
|
||||
ao_device_list_add(list, ao, &(struct ao_device_desc){"", name});
|
||||
ao_device_list_add(list, ao, &(struct ao_device_desc){"", ""});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +570,7 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp)
|
||||
if (d == &audio_out_null)
|
||||
break; // don't add unsafe/special entries
|
||||
|
||||
struct ao *ao = ao_alloc(true, hp->global, hp->input_ctx,
|
||||
struct ao *ao = ao_alloc(true, hp->global, hp->wakeup_cb, hp->wakeup_ctx,
|
||||
(char *)d->name, NULL);
|
||||
if (!ao)
|
||||
continue;
|
||||
@@ -585,6 +595,19 @@ void ao_device_list_add(struct ao_device_list *list, struct ao *ao,
|
||||
{
|
||||
struct ao_device_desc c = *e;
|
||||
const char *dname = ao->driver->name;
|
||||
char buf[80];
|
||||
if (!c.desc || !c.desc[0]) {
|
||||
if (c.name && c.name[0]) {
|
||||
c.desc = c.name;
|
||||
} else if (list->num_devices) {
|
||||
// Assume this is the default device.
|
||||
snprintf(buf, sizeof(buf), "Default (%s)", dname);
|
||||
c.desc = buf;
|
||||
} else {
|
||||
// First default device (and maybe the only one).
|
||||
c.desc = "Default";
|
||||
}
|
||||
}
|
||||
c.name = c.name[0] ? talloc_asprintf(list, "%s/%s", dname, c.name)
|
||||
: talloc_strdup(list, dname);
|
||||
c.desc = talloc_strdup(list, c.desc);
|
||||
@@ -601,9 +624,13 @@ void ao_hotplug_destroy(struct ao_hotplug *hp)
|
||||
talloc_free(hp);
|
||||
}
|
||||
|
||||
static void dummy_wakeup(void *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
void ao_print_devices(struct mpv_global *global, struct mp_log *log)
|
||||
{
|
||||
struct ao_hotplug *hp = ao_hotplug_create(global, NULL);
|
||||
struct ao_hotplug *hp = ao_hotplug_create(global, dummy_wakeup, NULL);
|
||||
struct ao_device_list *list = ao_hotplug_get_device_list(hp);
|
||||
mp_info(log, "List of detected audio devices:\n");
|
||||
for (int n = 0; n < list->num_devices; n++) {
|
||||
|
||||
+5
-2
@@ -58,6 +58,8 @@ enum {
|
||||
AO_INIT_SAFE_MULTICHANNEL_ONLY = 1 << 1,
|
||||
// Stream silence as long as no audio is playing.
|
||||
AO_INIT_STREAM_SILENCE = 1 << 2,
|
||||
// Force exclusive mode, i.e. lock out the system mixer.
|
||||
AO_INIT_EXCLUSIVE = 1 << 3,
|
||||
};
|
||||
|
||||
typedef struct ao_control_vol {
|
||||
@@ -83,7 +85,7 @@ struct mp_audio;
|
||||
|
||||
struct ao *ao_init_best(struct mpv_global *global,
|
||||
int init_flags,
|
||||
struct input_ctx *input_ctx,
|
||||
void (*wakeup_cb)(void *ctx), void *wakeup_ctx,
|
||||
struct encode_lavc_context *encode_lavc_ctx,
|
||||
int samplerate, int format, struct mp_chmap channels);
|
||||
void ao_uninit(struct ao *ao);
|
||||
@@ -106,7 +108,8 @@ void ao_hotplug_event(struct ao *ao);
|
||||
|
||||
struct ao_hotplug;
|
||||
struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
|
||||
struct input_ctx *input_ctx);
|
||||
void (*wakeup_cb)(void *ctx),
|
||||
void *wakeup_ctx);
|
||||
void ao_hotplug_destroy(struct ao_hotplug *hp);
|
||||
bool ao_hotplug_check_update(struct ao_hotplug *hp);
|
||||
const char *ao_hotplug_get_detected_device(struct ao_hotplug *hp);
|
||||
|
||||
+110
-57
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "options/options.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/m_option.h"
|
||||
#include "common/msg.h"
|
||||
#include "osdep/endian.h"
|
||||
@@ -48,6 +49,37 @@
|
||||
#include "internal.h"
|
||||
#include "audio/format.h"
|
||||
|
||||
struct ao_alsa_opts {
|
||||
char *device;
|
||||
char *mixer_device;
|
||||
char *mixer_name;
|
||||
int mixer_index;
|
||||
int resample;
|
||||
int ni;
|
||||
int ignore_chmap;
|
||||
};
|
||||
|
||||
#define OPT_BASE_STRUCT struct ao_alsa_opts
|
||||
static const struct m_sub_options ao_alsa_conf = {
|
||||
.opts = (const struct m_option[]) {
|
||||
OPT_STRING("alsa-device", device, 0, DEVICE_OPT_DEPRECATION),
|
||||
OPT_FLAG("alsa-resample", resample, 0),
|
||||
OPT_STRING("alsa-mixer-device", mixer_device, 0),
|
||||
OPT_STRING("alsa-mixer-name", mixer_name, 0),
|
||||
OPT_INTRANGE("alsa-mixer-index", mixer_index, 0, 0, 99),
|
||||
OPT_FLAG("alsa-non-interleaved", ni, 0),
|
||||
OPT_FLAG("alsa-ignore-chmap", ignore_chmap, 0),
|
||||
{0}
|
||||
},
|
||||
.defaults = &(const struct ao_alsa_opts) {
|
||||
.mixer_device = "default",
|
||||
.mixer_name = "Master",
|
||||
.mixer_index = 0,
|
||||
.ni = 0,
|
||||
},
|
||||
.size = sizeof(struct ao_alsa_opts),
|
||||
};
|
||||
|
||||
struct priv {
|
||||
snd_pcm_t *alsa;
|
||||
bool device_lost;
|
||||
@@ -61,13 +93,7 @@ struct priv {
|
||||
|
||||
snd_output_t *output;
|
||||
|
||||
char *cfg_device;
|
||||
char *cfg_mixer_device;
|
||||
char *cfg_mixer_name;
|
||||
int cfg_mixer_index;
|
||||
int cfg_resample;
|
||||
int cfg_ni;
|
||||
int cfg_ignore_chmap;
|
||||
struct ao_alsa_opts *opts;
|
||||
};
|
||||
|
||||
#define BUFFER_TIME 250000 // 250ms
|
||||
@@ -125,13 +151,13 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
|
||||
snd_mixer_selem_id_alloca(&sid);
|
||||
|
||||
snd_mixer_selem_id_set_index(sid, p->cfg_mixer_index);
|
||||
snd_mixer_selem_id_set_name(sid, p->cfg_mixer_name);
|
||||
snd_mixer_selem_id_set_index(sid, p->opts->mixer_index);
|
||||
snd_mixer_selem_id_set_name(sid, p->opts->mixer_name);
|
||||
|
||||
err = snd_mixer_open(&handle, 0);
|
||||
CHECK_ALSA_ERROR("Mixer open error");
|
||||
|
||||
err = snd_mixer_attach(handle, p->cfg_mixer_device);
|
||||
err = snd_mixer_attach(handle, p->opts->mixer_device);
|
||||
CHECK_ALSA_ERROR("Mixer attach error");
|
||||
|
||||
err = snd_mixer_selem_register(handle, NULL, NULL);
|
||||
@@ -350,12 +376,18 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
|
||||
struct mp_chmap entry;
|
||||
if (mp_chmap_from_alsa(&entry, &maps[i]->map)) {
|
||||
struct mp_chmap reorder = entry;
|
||||
if (maps[i]->type == SND_CHMAP_TYPE_VAR)
|
||||
mp_chmap_reorder_norm(&reorder);
|
||||
MP_DBG(ao, "Got supported channel map: %s (type %s) -> %s -> %s\n",
|
||||
aname, snd_pcm_chmap_type_name(maps[i]->type),
|
||||
mp_chmap_to_str(&entry), mp_chmap_to_str(&reorder));
|
||||
mp_chmap_sel_add_map(&chmap_sel, &reorder);
|
||||
mp_chmap_reorder_norm(&reorder);
|
||||
|
||||
MP_DBG(ao, "got ALSA chmap: %s (%s) -> %s", aname,
|
||||
snd_pcm_chmap_type_name(maps[i]->type),
|
||||
mp_chmap_to_str(&entry));
|
||||
if (!mp_chmap_equals(&entry, &reorder))
|
||||
MP_DBG(ao, " -> %s", mp_chmap_to_str(&reorder));
|
||||
MP_DBG(ao, "\n");
|
||||
|
||||
struct mp_chmap final =
|
||||
maps[i]->type == SND_CHMAP_TYPE_VAR ? reorder : entry;
|
||||
mp_chmap_sel_add_map(&chmap_sel, &final);
|
||||
} else {
|
||||
MP_VERBOSE(ao, "skipping unknown ALSA channel map: %s\n", aname);
|
||||
}
|
||||
@@ -447,7 +479,7 @@ static int set_chmap(struct ao *ao, struct mp_chmap *dev_chmap, int num_channels
|
||||
|
||||
MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap));
|
||||
|
||||
if (p->cfg_ignore_chmap) {
|
||||
if (p->opts->ignore_chmap) {
|
||||
MP_VERBOSE(ao, "user set ignore-chmap; ignoring the channel map.\n");
|
||||
} else if (af_fmt_is_spdif(ao->format)) {
|
||||
MP_VERBOSE(ao, "using spdif passthrough; ignoring the channel map.\n");
|
||||
@@ -456,10 +488,14 @@ static int set_chmap(struct ao *ao, struct mp_chmap *dev_chmap, int num_channels
|
||||
} else if (chmap.num != num_channels) {
|
||||
MP_WARN(ao, "ALSA channel map conflicts with channel count!\n");
|
||||
} else {
|
||||
MP_VERBOSE(ao, "using the ALSA channel map.\n");
|
||||
if (mp_chmap_equals(&chmap, &ao->channels))
|
||||
if (mp_chmap_equals(&chmap, &ao->channels)) {
|
||||
MP_VERBOSE(ao, "which is what we requested.\n");
|
||||
ao->channels = chmap;
|
||||
} else if (!mp_chmap_is_valid(dev_chmap)) {
|
||||
MP_VERBOSE(ao, "ignoring the ALSA channel map.\n");
|
||||
} else {
|
||||
MP_VERBOSE(ao, "using the ALSA channel map.\n");
|
||||
ao->channels = chmap;
|
||||
}
|
||||
}
|
||||
|
||||
free(alsa_chmap);
|
||||
@@ -564,11 +600,16 @@ static int try_open_device(struct ao *ao, const char *device, int mode)
|
||||
bstr dev;
|
||||
bstr_split_tok(bstr0(device), ":", &dev, &(bstr){0});
|
||||
if (bstr_equals0(dev, "default")) {
|
||||
ac3_device = append_params(tmp, "iec958", params);
|
||||
MP_VERBOSE(ao, "got error %d; opening iec fallback device '%s'\n",
|
||||
err, ac3_device);
|
||||
err = snd_pcm_open
|
||||
(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, mode);
|
||||
const char *const fallbacks[] = {"hdmi", "iec958", NULL};
|
||||
for (int n = 0; fallbacks[n]; n++) {
|
||||
char *ndev = append_params(tmp, fallbacks[n], params);
|
||||
MP_VERBOSE(ao, "got error %d; opening iec fallback "
|
||||
"device '%s'\n", err, ndev);
|
||||
err = snd_pcm_open
|
||||
(&p->alsa, ndev, SND_PCM_STREAM_PLAYBACK, mode);
|
||||
if (err >= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
talloc_free(tmp);
|
||||
@@ -615,8 +656,8 @@ static int init_device(struct ao *ao, int mode)
|
||||
const char *device = "default";
|
||||
if (ao->device)
|
||||
device = ao->device;
|
||||
if (p->cfg_device && p->cfg_device[0])
|
||||
device = p->cfg_device;
|
||||
if (p->opts->device && p->opts->device[0])
|
||||
device = p->opts->device;
|
||||
|
||||
err = try_open_device(ao, device, mode);
|
||||
CHECK_ALSA_ERROR("Playback open error");
|
||||
@@ -641,7 +682,7 @@ static int init_device(struct ao *ao, int mode)
|
||||
|
||||
// Some ALSA drivers have broken delay reporting, so disable the ALSA
|
||||
// resampling plugin by default.
|
||||
if (!p->cfg_resample) {
|
||||
if (!p->opts->resample) {
|
||||
err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0);
|
||||
CHECK_ALSA_ERROR("Unable to disable resampling");
|
||||
}
|
||||
@@ -683,18 +724,27 @@ static int init_device(struct ao *ao, int mode)
|
||||
CHECK_ALSA_ERROR("Unable to set format");
|
||||
dump_hw_params(ao, MSGL_DEBUG, "HW params after format:\n", alsa_hwparams);
|
||||
|
||||
struct mp_chmap dev_chmap = ao->channels;
|
||||
if (af_fmt_is_spdif(ao->format) || p->cfg_ignore_chmap) {
|
||||
dev_chmap.num = 0; // disable chmap API
|
||||
} else if (dev_chmap.num == 1 && dev_chmap.speaker[0] == MP_SPEAKER_ID_FC) {
|
||||
// As yet another ALSA API inconsistency, mono is not reported correctly.
|
||||
dev_chmap.num = 0;
|
||||
} else if (query_chmaps(ao, &dev_chmap)) {
|
||||
ao->channels = dev_chmap;
|
||||
} else {
|
||||
// Assume only stereo and mono are supported.
|
||||
mp_chmap_from_channels(&ao->channels, MPMIN(2, dev_chmap.num));
|
||||
dev_chmap.num = 0;
|
||||
// Stereo, or mono if input is 1 channel.
|
||||
struct mp_chmap reduced;
|
||||
mp_chmap_from_channels(&reduced, MPMIN(2, ao->channels.num));
|
||||
|
||||
struct mp_chmap dev_chmap = {0};
|
||||
if (!af_fmt_is_spdif(ao->format) && !p->opts->ignore_chmap &&
|
||||
!mp_chmap_equals(&ao->channels, &reduced))
|
||||
{
|
||||
struct mp_chmap res = ao->channels;
|
||||
if (query_chmaps(ao, &res))
|
||||
dev_chmap = res;
|
||||
|
||||
// Whatever it is, we dumb it down to mono or stereo. Some drivers may
|
||||
// return things like bl-br, but the user (probably) still wants stereo.
|
||||
// This also handles the failure case (dev_chmap.num==0).
|
||||
if (dev_chmap.num <= 2) {
|
||||
dev_chmap.num = 0;
|
||||
ao->channels = reduced;
|
||||
} else if (dev_chmap.num) {
|
||||
ao->channels = dev_chmap;
|
||||
}
|
||||
}
|
||||
|
||||
int num_channels = ao->channels.num;
|
||||
@@ -744,8 +794,13 @@ static int init_device(struct ao *ao, int mode)
|
||||
if (num_channels != ao->channels.num) {
|
||||
int req = ao->channels.num;
|
||||
mp_chmap_from_channels(&ao->channels, MPMIN(2, num_channels));
|
||||
mp_chmap_fill_na(&ao->channels, num_channels);
|
||||
MP_ERR(ao, "Asked for %d channels, got %d - fallback to %s.\n", req,
|
||||
num_channels, mp_chmap_to_str(&ao->channels));
|
||||
if (num_channels != ao->channels.num) {
|
||||
MP_FATAL(ao, "mismatching channel counts.\n");
|
||||
goto alsa_error;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &p->buffersize);
|
||||
@@ -806,7 +861,9 @@ alsa_error:
|
||||
static int init(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
if (!p->cfg_ni)
|
||||
p->opts = mp_get_config_group(ao, ao->global, &ao_alsa_conf);
|
||||
|
||||
if (!p->opts->ni)
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
MP_VERBOSE(ao, "using ALSA version: %s\n", snd_asoundlib_version());
|
||||
@@ -1088,7 +1145,8 @@ static bool is_useless_device(char *name)
|
||||
{
|
||||
char *crap[] = {"front", "rear", "center_lfe", "side", "surround21",
|
||||
"surround40", "surround41", "surround50", "surround51", "surround71",
|
||||
"sysdefault", "pulse", "null", "dsnoop", "dmix", "hw", "iec958"};
|
||||
"sysdefault", "pulse", "null", "dsnoop", "dmix", "hw", "iec958",
|
||||
"default"};
|
||||
for (int i = 0; i < MP_ARRAY_SIZE(crap); i++) {
|
||||
int l = strlen(crap[i]);
|
||||
if (name && strncmp(name, crap[i], l) == 0 &&
|
||||
@@ -1104,6 +1162,8 @@ static void list_devs(struct ao *ao, struct ao_device_list *list)
|
||||
if (snd_device_name_hint(-1, "pcm", &hints) < 0)
|
||||
return;
|
||||
|
||||
ao_device_list_add(list, ao, &(struct ao_device_desc){"", ""});
|
||||
|
||||
for (int n = 0; hints[n]; n++) {
|
||||
char *name = snd_device_name_get_hint(hints[n], "NAME");
|
||||
char *desc = snd_device_name_get_hint(hints[n], "DESC");
|
||||
@@ -1125,8 +1185,6 @@ static void list_devs(struct ao *ao, struct ao_device_list *list)
|
||||
snd_device_name_free_hint(hints);
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
|
||||
const struct ao_driver audio_out_alsa = {
|
||||
.description = "ALSA audio output",
|
||||
.name = "alsa",
|
||||
@@ -1144,20 +1202,15 @@ const struct ao_driver audio_out_alsa = {
|
||||
.wakeup = ao_wakeup_poll,
|
||||
.list_devs = list_devs,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.priv_defaults = &(const struct priv) {
|
||||
.cfg_mixer_device = "default",
|
||||
.cfg_mixer_name = "Master",
|
||||
.cfg_mixer_index = 0,
|
||||
.cfg_ni = 0,
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("device", cfg_device, 0),
|
||||
OPT_FLAG("resample", cfg_resample, 0),
|
||||
OPT_STRING("mixer-device", cfg_mixer_device, 0),
|
||||
OPT_STRING("mixer-name", cfg_mixer_name, 0),
|
||||
OPT_INTRANGE("mixer-index", cfg_mixer_index, 0, 0, 99),
|
||||
OPT_FLAG("non-interleaved", cfg_ni, 0),
|
||||
OPT_FLAG("ignore-chmap", cfg_ignore_chmap, 0),
|
||||
OPT_SUBOPT_LEGACY("device", "alsa-device"),
|
||||
OPT_SUBOPT_LEGACY("resample", "alsa-resample"),
|
||||
OPT_SUBOPT_LEGACY("mixer-device", "alsa-mixer-device"),
|
||||
OPT_SUBOPT_LEGACY("mixer-name", "alsa-mixer-name"),
|
||||
OPT_SUBOPT_LEGACY("mixer-index", "alsa-mixer-index"),
|
||||
OPT_SUBOPT_LEGACY("non-interleaved", "alsa-non-interleaved"),
|
||||
OPT_SUBOPT_LEGACY("ignore-chmap", "alsa-ignore-chmap"),
|
||||
{0}
|
||||
},
|
||||
.global_opts = &ao_alsa_conf,
|
||||
};
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ao.h"
|
||||
#include "internal.h"
|
||||
#include "audio/format.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "options/m_option.h"
|
||||
#include "misc/ring.h"
|
||||
#include "common/msg.h"
|
||||
#include "ao_coreaudio_utils.h"
|
||||
#include "ao_coreaudio_chmap.h"
|
||||
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/CoreAudioTypes.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <mach/mach_time.h>
|
||||
|
||||
struct priv {
|
||||
AudioUnit audio_unit;
|
||||
double device_latency;
|
||||
};
|
||||
|
||||
static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
|
||||
const AudioTimeStamp *ts, UInt32 bus,
|
||||
UInt32 frames, AudioBufferList *buffer_list)
|
||||
{
|
||||
struct ao *ao = ctx;
|
||||
struct priv *p = ao->priv;
|
||||
void *planes[MP_NUM_CHANNELS] = {0};
|
||||
|
||||
for (int n = 0; n < ao->num_planes; n++)
|
||||
planes[n] = buffer_list->mBuffers[n].mData;
|
||||
|
||||
int64_t end = mp_time_us();
|
||||
end += ca_frames_to_us(ao, frames);
|
||||
end += p->device_latency * 1e6;
|
||||
ao_read_data(ao, planes, frames, end);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static bool init_audiounit(struct ao *ao)
|
||||
{
|
||||
AudioStreamBasicDescription asbd;
|
||||
OSStatus err;
|
||||
uint32_t size;
|
||||
struct priv *p = ao->priv;
|
||||
AVAudioSession *instance = AVAudioSession.sharedInstance;
|
||||
AVAudioSessionPortDescription *port = nil;
|
||||
NSInteger maxChannels = instance.maximumOutputNumberOfChannels;
|
||||
NSInteger prefChannels = MIN(maxChannels, ao->channels.num);
|
||||
|
||||
[instance setCategory:AVAudioSessionCategoryPlayback error:nil];
|
||||
[instance setMode:AVAudioSessionModeMoviePlayback error:nil];
|
||||
[instance setActive:YES error:nil];
|
||||
[instance setPreferredOutputNumberOfChannels:prefChannels error:nil];
|
||||
|
||||
AudioComponentDescription desc = (AudioComponentDescription) {
|
||||
.componentType = kAudioUnitType_Output,
|
||||
.componentSubType = kAudioUnitSubType_RemoteIO,
|
||||
.componentManufacturer = kAudioUnitManufacturer_Apple,
|
||||
.componentFlags = 0,
|
||||
.componentFlagsMask = 0,
|
||||
};
|
||||
|
||||
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
|
||||
if (comp == NULL) {
|
||||
MP_ERR(ao, "unable to find audio component\n");
|
||||
goto coreaudio_error;
|
||||
}
|
||||
|
||||
err = AudioComponentInstanceNew(comp, &(p->audio_unit));
|
||||
CHECK_CA_ERROR("unable to open audio component");
|
||||
|
||||
err = AudioUnitInitialize(p->audio_unit);
|
||||
CHECK_CA_ERROR_L(coreaudio_error_component,
|
||||
"unable to initialize audio unit");
|
||||
|
||||
if (af_fmt_is_spdif(ao->format) || instance.outputNumberOfChannels <= 2) {
|
||||
ao->channels = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
|
||||
} else {
|
||||
port = instance.currentRoute.outputs.firstObject;
|
||||
if (port.channels.count == 2 &&
|
||||
port.channels[0].channelLabel == kAudioChannelLabel_Unknown) {
|
||||
// Special case when using an HDMI adapter. The iOS device will
|
||||
// perform SPDIF conversion for us, so send all available channels
|
||||
// using the AC3 mapping.
|
||||
ao->channels = (struct mp_chmap)MP_CHMAP6(FL, FC, FR, SL, SR, LFE);
|
||||
} else {
|
||||
ao->channels.num = (uint8_t)port.channels.count;
|
||||
for (AVAudioSessionChannelDescription *ch in port.channels) {
|
||||
ao->channels.speaker[ch.channelNumber - 1] =
|
||||
ca_label_to_mp_speaker_id(ch.channelLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ca_fill_asbd(ao, &asbd);
|
||||
size = sizeof(AudioStreamBasicDescription);
|
||||
err = AudioUnitSetProperty(p->audio_unit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &asbd, size);
|
||||
|
||||
CHECK_CA_ERROR_L(coreaudio_error_audiounit,
|
||||
"unable to set the input format on the audio unit");
|
||||
|
||||
AURenderCallbackStruct render_cb = (AURenderCallbackStruct) {
|
||||
.inputProc = render_cb_lpcm,
|
||||
.inputProcRefCon = ao,
|
||||
};
|
||||
|
||||
err = AudioUnitSetProperty(p->audio_unit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &render_cb,
|
||||
sizeof(AURenderCallbackStruct));
|
||||
|
||||
CHECK_CA_ERROR_L(coreaudio_error_audiounit,
|
||||
"unable to set render callback on audio unit");
|
||||
|
||||
return true;
|
||||
|
||||
coreaudio_error_audiounit:
|
||||
AudioUnitUninitialize(p->audio_unit);
|
||||
coreaudio_error_component:
|
||||
AudioComponentInstanceDispose(p->audio_unit);
|
||||
coreaudio_error:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void stop(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
OSStatus err = AudioOutputUnitStop(p->audio_unit);
|
||||
CHECK_CA_WARN("can't stop audio unit");
|
||||
}
|
||||
|
||||
static void start(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
AVAudioSession *instance = AVAudioSession.sharedInstance;
|
||||
|
||||
p->device_latency = [instance outputLatency];
|
||||
|
||||
OSStatus err = AudioOutputUnitStart(p->audio_unit);
|
||||
CHECK_CA_WARN("can't start audio unit");
|
||||
}
|
||||
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
AudioOutputUnitStop(p->audio_unit);
|
||||
AudioUnitUninitialize(p->audio_unit);
|
||||
AudioComponentInstanceDispose(p->audio_unit);
|
||||
|
||||
[AVAudioSession.sharedInstance
|
||||
setActive:NO
|
||||
withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
|
||||
error:nil];
|
||||
}
|
||||
|
||||
static int init(struct ao *ao)
|
||||
{
|
||||
if (!init_audiounit(ao))
|
||||
goto coreaudio_error;
|
||||
|
||||
return CONTROL_OK;
|
||||
|
||||
coreaudio_error:
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
|
||||
const struct ao_driver audio_out_audiounit = {
|
||||
.description = "AudioUnit (iOS)",
|
||||
.name = "audiounit",
|
||||
.uninit = uninit,
|
||||
.init = init,
|
||||
.pause = stop,
|
||||
.resume = start,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.options = (const struct m_option[]){
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -143,6 +143,8 @@ static int init(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
p->exclusive |= ao->init_flags & AO_INIT_EXCLUSIVE;
|
||||
|
||||
if (!af_fmt_is_pcm(ao->format) || p->exclusive) {
|
||||
MP_VERBOSE(ao, "redirecting to coreaudio_exclusive\n");
|
||||
ao->redirect = "coreaudio_exclusive";
|
||||
@@ -427,7 +429,9 @@ const struct ao_driver audio_out_coreaudio = {
|
||||
.priv_size = sizeof(struct priv),
|
||||
.options = (const struct m_option[]){
|
||||
OPT_FLAG("change-physical-format", change_physical_format, 0),
|
||||
OPT_FLAG("exclusive", exclusive, 0),
|
||||
OPT_FLAG("exclusive", exclusive, 0,
|
||||
.deprecation_message = "use --audio-exclusive"),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "coreaudio",
|
||||
};
|
||||
@@ -56,7 +56,7 @@ static const int speaker_map[][2] = {
|
||||
{ 0, -1 },
|
||||
};
|
||||
|
||||
static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
|
||||
int ca_label_to_mp_speaker_id(AudioChannelLabel label)
|
||||
{
|
||||
for (int i = 0; speaker_map[i][1] >= 0; i++)
|
||||
if (speaker_map[i][0] == label)
|
||||
@@ -64,6 +64,7 @@ static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if HAVE_COREAUDIO
|
||||
static void ca_log_layout(struct ao *ao, int l, AudioChannelLayout *layout)
|
||||
{
|
||||
if (!mp_msg_test(ao->log, l))
|
||||
@@ -327,3 +328,4 @@ void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
|
||||
MP_WARN(ao, "mismatching channels - falling back to %s\n",
|
||||
mp_chmap_to_str(out_map));
|
||||
}
|
||||
#endif
|
||||
@@ -22,8 +22,12 @@
|
||||
|
||||
struct mp_chmap;
|
||||
|
||||
int ca_label_to_mp_speaker_id(AudioChannelLabel label);
|
||||
|
||||
#if HAVE_COREAUDIO
|
||||
bool ca_init_chmap(struct ao *ao, AudioDeviceID device);
|
||||
void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
|
||||
struct mp_chmap *out_map);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "internal.h"
|
||||
#include "audio/format.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "osdep/atomic.h"
|
||||
#include "options/m_option.h"
|
||||
#include "common/msg.h"
|
||||
#include "audio/out/ao_coreaudio_chmap.h"
|
||||
|
||||
@@ -22,15 +22,17 @@
|
||||
* on CoreAudio but not the AUHAL (such as using AudioQueue services).
|
||||
*/
|
||||
|
||||
#include <CoreAudio/HostTime.h>
|
||||
|
||||
#include "audio/out/ao_coreaudio_utils.h"
|
||||
#include "audio/out/ao_coreaudio_properties.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/endian.h"
|
||||
#include "osdep/semaphore.h"
|
||||
#include "audio/format.h"
|
||||
|
||||
#if HAVE_COREAUDIO
|
||||
#include "audio/out/ao_coreaudio_properties.h"
|
||||
#include <CoreAudio/HostTime.h>
|
||||
#endif
|
||||
|
||||
CFStringRef cfstr_from_cstr(char *str)
|
||||
{
|
||||
return CFStringCreateWithCString(NULL, str, CA_CFSTR_ENCODING);
|
||||
@@ -46,6 +48,7 @@ char *cfstr_get_cstr(CFStringRef cfstr)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#if HAVE_COREAUDIO
|
||||
static bool ca_is_output_device(struct ao *ao, AudioDeviceID dev)
|
||||
{
|
||||
size_t n_buffers;
|
||||
@@ -142,6 +145,7 @@ OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device)
|
||||
coreaudio_error:
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
|
||||
{
|
||||
@@ -306,6 +310,7 @@ int64_t ca_frames_to_us(struct ao *ao, uint32_t frames)
|
||||
return frames / (float) ao->samplerate * 1e6;
|
||||
}
|
||||
|
||||
#if HAVE_COREAUDIO
|
||||
int64_t ca_get_latency(const AudioTimeStamp *ts)
|
||||
{
|
||||
uint64_t out = AudioConvertHostTimeToNanos(ts->mHostTime);
|
||||
@@ -522,4 +527,4 @@ coreaudio_error:
|
||||
sem_destroy(&wakeup);
|
||||
return format_set;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -49,7 +49,9 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message);
|
||||
} while (0)
|
||||
|
||||
void ca_get_device_list(struct ao *ao, struct ao_device_list *list);
|
||||
#if HAVE_COREAUDIO
|
||||
OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device);
|
||||
#endif
|
||||
|
||||
bool ca_formatid_is_compressed(uint32_t formatid);
|
||||
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd);
|
||||
@@ -63,8 +65,11 @@ bool ca_asbd_is_better(AudioStreamBasicDescription *req,
|
||||
AudioStreamBasicDescription *new);
|
||||
|
||||
int64_t ca_frames_to_us(struct ao *ao, uint32_t frames);
|
||||
#if HAVE_COREAUDIO
|
||||
int64_t ca_get_latency(const AudioTimeStamp *ts);
|
||||
#endif
|
||||
|
||||
#if HAVE_COREAUDIO
|
||||
bool ca_stream_supports_compressed(struct ao *ao, AudioStreamID stream);
|
||||
OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid);
|
||||
OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid);
|
||||
@@ -73,5 +78,6 @@ OSStatus ca_enable_mixing(struct ao *ao, AudioDeviceID device, bool changed);
|
||||
int64_t ca_get_device_latency_us(struct ao *ao, AudioDeviceID device);
|
||||
bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
|
||||
AudioStreamBasicDescription change_format);
|
||||
#endif
|
||||
|
||||
#endif /* MPV_COREAUDIO_UTILS_H */
|
||||
+42
-22
@@ -34,24 +34,48 @@
|
||||
#include "internal.h"
|
||||
#include "audio/format.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/m_option.h"
|
||||
|
||||
#include <jack/jack.h>
|
||||
|
||||
struct priv {
|
||||
jack_client_t *client;
|
||||
float jack_latency;
|
||||
char *cfg_port;
|
||||
char *cfg_client_name;
|
||||
struct jack_opts {
|
||||
char *port;
|
||||
char *client_name;
|
||||
int connect;
|
||||
int autostart;
|
||||
int stdlayout;
|
||||
};
|
||||
|
||||
#define OPT_BASE_STRUCT struct jack_opts
|
||||
static const struct m_sub_options ao_jack_conf = {
|
||||
.opts = (const struct m_option[]){
|
||||
OPT_STRING("jack-port", port, 0),
|
||||
OPT_STRING("jack-name", client_name, 0),
|
||||
OPT_FLAG("jack-autostart", autostart, 0),
|
||||
OPT_FLAG("jack-connect", connect, 0),
|
||||
OPT_CHOICE("jack-std-channel-layout", stdlayout, 0,
|
||||
({"waveext", 0}, {"any", 1})),
|
||||
{0}
|
||||
},
|
||||
.defaults = &(const struct jack_opts) {
|
||||
.client_name = "mpv",
|
||||
.connect = 1,
|
||||
},
|
||||
.size = sizeof(struct jack_opts),
|
||||
};
|
||||
|
||||
struct priv {
|
||||
jack_client_t *client;
|
||||
float jack_latency;
|
||||
int last_chunk;
|
||||
|
||||
int num_ports;
|
||||
jack_port_t *ports[MP_NUM_CHANNELS];
|
||||
|
||||
int activated;
|
||||
|
||||
struct jack_opts *opts;
|
||||
};
|
||||
|
||||
static int process(jack_nframes_t nframes, void *arg)
|
||||
@@ -77,7 +101,7 @@ connect_to_outports(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
char *port_name = (p->cfg_port && p->cfg_port[0]) ? p->cfg_port : NULL;
|
||||
char *port_name = (p->opts->port && p->opts->port[0]) ? p->opts->port : NULL;
|
||||
const char **matching_ports = NULL;
|
||||
int port_flags = JackPortIsInput;
|
||||
int i;
|
||||
@@ -144,7 +168,7 @@ static void resume(struct ao *ao)
|
||||
if (jack_activate(p->client))
|
||||
MP_FATAL(ao, "activate failed\n");
|
||||
|
||||
if (p->connect)
|
||||
if (p->opts->connect)
|
||||
connect_to_outports(ao);
|
||||
}
|
||||
}
|
||||
@@ -155,9 +179,11 @@ static int init(struct ao *ao)
|
||||
struct mp_chmap_sel sel = {0};
|
||||
jack_options_t open_options;
|
||||
|
||||
p->opts = mp_get_config_group(ao, ao->global, &ao_jack_conf);
|
||||
|
||||
ao->format = AF_FORMAT_FLOATP;
|
||||
|
||||
switch (p->stdlayout) {
|
||||
switch (p->opts->stdlayout) {
|
||||
case 0:
|
||||
mp_chmap_sel_add_waveext(&sel);
|
||||
break;
|
||||
@@ -170,10 +196,10 @@ static int init(struct ao *ao)
|
||||
goto err_chmap;
|
||||
|
||||
open_options = JackNullOption;
|
||||
if (!p->autostart)
|
||||
if (!p->opts->autostart)
|
||||
open_options |= JackNoStartServer;
|
||||
|
||||
p->client = jack_client_open(p->cfg_client_name, open_options, NULL);
|
||||
p->client = jack_client_open(p->opts->client_name, open_options, NULL);
|
||||
if (!p->client) {
|
||||
MP_FATAL(ao, "cannot open server\n");
|
||||
goto err_client_open;
|
||||
@@ -213,8 +239,6 @@ static void uninit(struct ao *ao)
|
||||
jack_client_close(p->client);
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
|
||||
const struct ao_driver audio_out_jack = {
|
||||
.description = "JACK audio output",
|
||||
.name = "jack",
|
||||
@@ -222,17 +246,13 @@ const struct ao_driver audio_out_jack = {
|
||||
.uninit = uninit,
|
||||
.resume = resume,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.priv_defaults = &(const struct priv) {
|
||||
.cfg_client_name = "mpv",
|
||||
.connect = 1,
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("port", cfg_port, 0),
|
||||
OPT_STRING("name", cfg_client_name, 0),
|
||||
OPT_FLAG("autostart", autostart, 0),
|
||||
OPT_FLAG("connect", connect, 0),
|
||||
OPT_CHOICE("std-channel-layout", stdlayout, 0,
|
||||
({"waveext", 0}, {"any", 1})),
|
||||
OPT_SUBOPT_LEGACY("port", "jack-port"),
|
||||
OPT_SUBOPT_LEGACY("name", "jack-name"),
|
||||
OPT_SUBOPT_LEGACY("autostart", "jack-autostart"),
|
||||
OPT_SUBOPT_LEGACY("connect", "jack-connect"),
|
||||
OPT_SUBOPT_LEGACY("std-channel-layout", "jack-std-channel-layout"),
|
||||
{0}
|
||||
},
|
||||
.global_opts = &ao_jack_conf,
|
||||
};
|
||||
@@ -241,4 +241,5 @@ const struct ao_driver audio_out_null = {
|
||||
OPT_CHANNELS("channel-layouts", channel_layouts, 0),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "ao-null",
|
||||
};
|
||||
+18
-2
@@ -24,6 +24,19 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifndef AL_FORMAT_MONO_FLOAT32
|
||||
#define AL_FORMAT_MONO_FLOAT32 0x10010
|
||||
#endif
|
||||
#ifndef AL_FORMAT_STEREO_FLOAT32
|
||||
#define AL_FORMAT_STEREO_FLOAT32 0x10011
|
||||
#endif
|
||||
#ifndef AL_FORMAT_MONO_DOUBLE_EXT
|
||||
#define AL_FORMAT_MONO_DOUBLE_EXT 0x10012
|
||||
#endif
|
||||
#include <OpenAL/MacOSX_OALExtensions.h>
|
||||
#else
|
||||
#ifdef OPENAL_AL_H
|
||||
#include <OpenAL/alc.h>
|
||||
#include <OpenAL/al.h>
|
||||
@@ -33,6 +46,7 @@
|
||||
#include <AL/al.h>
|
||||
#include <AL/alext.h>
|
||||
#endif
|
||||
#endif // __APPLE__
|
||||
|
||||
#include "common/msg.h"
|
||||
|
||||
@@ -96,7 +110,7 @@ static int validate_device_opt(struct mp_log *log, const m_option_t *opt,
|
||||
mp_info(log, " '%s'\n", list);
|
||||
list = list + strlen(list) + 1;
|
||||
}
|
||||
return M_OPT_EXIT - 1;
|
||||
return M_OPT_EXIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -356,7 +370,9 @@ const struct ao_driver audio_out_openal = {
|
||||
.list_devs = list_devs,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING_VALIDATE("device", cfg_device, 0, validate_device_opt),
|
||||
OPT_STRING_VALIDATE("device", cfg_device, 0, validate_device_opt,
|
||||
DEVICE_OPT_DEPRECATION),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "ao-openal",
|
||||
};
|
||||
@@ -246,4 +246,5 @@ const struct ao_driver audio_out_opensles = {
|
||||
OPT_INTRANGE("sample-rate", cfg_sample_rate, 0, 1000, 100000),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "opensles",
|
||||
};
|
||||
+2
-1
@@ -648,9 +648,10 @@ const struct ao_driver audio_out_oss = {
|
||||
.oss_mixer_device = PATH_DEV_MIXER,
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("device", dsp, 0),
|
||||
OPT_STRING("device", dsp, 0, DEVICE_OPT_DEPRECATION),
|
||||
OPT_STRING("mixer-device", oss_mixer_device, 0),
|
||||
OPT_STRING("mixer-channel", cfg_oss_mixer_channel, 0),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "oss",
|
||||
};
|
||||
+1
-2
@@ -152,8 +152,6 @@ static int init(struct ao *ao)
|
||||
priv->outputfilename,
|
||||
priv->waveheader ? "WAVE" : "RAW PCM", ao->samplerate,
|
||||
ao->channels.num, af_fmt_to_str(ao->format));
|
||||
MP_INFO(ao, "Info: Faster dumping is achieved with --no-video\n");
|
||||
MP_INFO(ao, "Info: To write WAVE files use --ao=pcm:waveheader (default).\n");
|
||||
|
||||
priv->fp = fopen(priv->outputfilename, priv->append ? "ab" : "wb");
|
||||
if (!priv->fp) {
|
||||
@@ -226,4 +224,5 @@ const struct ao_driver audio_out_pcm = {
|
||||
OPT_FLAG("append", append, 0),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "ao-pcm",
|
||||
};
|
||||
@@ -836,9 +836,10 @@ const struct ao_driver audio_out_pulse = {
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("host", cfg_host, 0),
|
||||
OPT_STRING("sink", cfg_sink, 0),
|
||||
OPT_STRING("sink", cfg_sink, 0, DEVICE_OPT_DEPRECATION),
|
||||
OPT_CHOICE_OR_INT("buffer", cfg_buffer, 0, 1, 2000, ({"native", 0})),
|
||||
OPT_FLAG("latency-hacks", cfg_latency_hacks, 0),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "pulse",
|
||||
};
|
||||
@@ -156,9 +156,12 @@ const struct ao_driver audio_out_rsound = {
|
||||
.resume = audio_resume,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("host", host, 0),
|
||||
OPT_STRING("port", port, 0),
|
||||
OPT_STRING("host", host, 0,
|
||||
.deprecation_message = "request --audio-device support on issue tracker"),
|
||||
OPT_STRING("port", port, 0,
|
||||
.deprecation_message = "request --audio-device support on issue tracker"),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "rsound",
|
||||
};
|
||||
|
||||
@@ -212,4 +212,5 @@ const struct ao_driver audio_out_sdl = {
|
||||
OPT_FLOAT("buflen", buflen, 0),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "sdl",
|
||||
};
|
||||
@@ -320,7 +320,9 @@ const struct ao_driver audio_out_sndio = {
|
||||
.reset = reset,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("device", dev, 0, OPTDEF_STR(SIO_DEVANY)),
|
||||
OPT_STRING("device", dev, 0, OPTDEF_STR(SIO_DEVANY),
|
||||
DEVICE_OPT_DEPRECATION),
|
||||
{0}
|
||||
},
|
||||
.legacy_prefix = "ao-sndio",
|
||||
};
|
||||
@@ -275,6 +275,8 @@ static int init(struct ao *ao)
|
||||
struct wasapi_state *state = ao->priv;
|
||||
state->log = ao->log;
|
||||
|
||||
state->opt_exclusive |= ao->init_flags & AO_INIT_EXCLUSIVE;
|
||||
|
||||
state->deviceID = wasapi_find_deviceID(ao);
|
||||
if (!state->deviceID) {
|
||||
uninit(ao);
|
||||
@@ -495,8 +497,10 @@ const struct ao_driver audio_out_wasapi = {
|
||||
.hotplug_uninit = hotplug_uninit,
|
||||
.priv_size = sizeof(wasapi_state),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_FLAG("exclusive", opt_exclusive, 0),
|
||||
OPT_STRING("device", opt_device, 0),
|
||||
OPT_FLAG("exclusive", opt_exclusive, 0,
|
||||
.deprecation_message = "use --audio-exclusive"),
|
||||
OPT_STRING("device", opt_device, 0, DEVICE_OPT_DEPRECATION),
|
||||
{NULL},
|
||||
},
|
||||
.legacy_prefix = "ao-wasapi",
|
||||
};
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <endpointvolume.h>
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "osdep/atomic.h"
|
||||
#include "osdep/windows_utils.h"
|
||||
#include "internal.h"
|
||||
#include "ao.h"
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <initguid.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "ao_wasapi.h"
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <math.h>
|
||||
#include <wchar.h>
|
||||
#include <windows.h>
|
||||
#include <initguid.h>
|
||||
#include <errors.h>
|
||||
#include <ksguid.h>
|
||||
#include <ksmedia.h>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "osdep/atomics.h"
|
||||
#include "osdep/atomic.h"
|
||||
#include "audio/out/ao.h"
|
||||
|
||||
/* global data used by ao.c and ao drivers */
|
||||
@@ -40,8 +40,10 @@ struct ao {
|
||||
const struct ao_driver *api; // entrypoints to the wrapper (push.c/pull.c)
|
||||
const struct ao_driver *driver;
|
||||
void *priv;
|
||||
struct mpv_global *global;
|
||||
struct encode_lavc_context *encode_lavc_ctx;
|
||||
struct input_ctx *input_ctx;
|
||||
void (*wakeup_cb)(void *ctx);
|
||||
void *wakeup_ctx;
|
||||
struct mp_log *log; // Using e.g. "[ao/coreaudio]" as prefix
|
||||
int init_flags; // AO_INIT_* flags
|
||||
bool stream_silence; // if audio inactive, just play silence
|
||||
@@ -180,6 +182,8 @@ struct ao_driver {
|
||||
int priv_size;
|
||||
const void *priv_defaults;
|
||||
const struct m_option *options;
|
||||
const struct m_sub_options *global_opts;
|
||||
const char *legacy_prefix;
|
||||
};
|
||||
|
||||
// These functions can be called by AOs.
|
||||
@@ -203,4 +207,7 @@ bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
|
||||
void ao_device_list_add(struct ao_device_list *list, struct ao *ao,
|
||||
struct ao_device_desc *e);
|
||||
|
||||
#define DEVICE_OPT_DEPRECATION \
|
||||
.deprecation_message = "use --audio-device instead"
|
||||
|
||||
#endif
|
||||
+2
-2
@@ -30,7 +30,7 @@
|
||||
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/threads.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "osdep/atomic.h"
|
||||
#include "misc/ring.h"
|
||||
|
||||
/*
|
||||
@@ -151,7 +151,7 @@ int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us)
|
||||
end:
|
||||
|
||||
if (need_wakeup)
|
||||
mp_input_wakeup_nolock(ao->input_ctx);
|
||||
ao->wakeup_cb(ao->wakeup_ctx);
|
||||
|
||||
// pad with silence (underflow/paused/eof)
|
||||
for (int n = 0; n < ao->num_planes; n++)
|
||||
|
||||
+5
-3
@@ -35,7 +35,7 @@
|
||||
|
||||
#include "osdep/threads.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "osdep/atomic.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "audio/audio_buffer.h"
|
||||
@@ -182,6 +182,7 @@ static int unlocked_get_space(struct ao *ao)
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
int space = mp_audio_buffer_get_write_available(p->buffer);
|
||||
if (ao->driver->get_space) {
|
||||
int align = af_format_sample_alignment(ao->format);
|
||||
// The following code attempts to keep the total buffered audio to
|
||||
// ao->buffer in order to improve latency.
|
||||
int device_space = ao->driver->get_space(ao);
|
||||
@@ -191,6 +192,7 @@ static int unlocked_get_space(struct ao *ao)
|
||||
// byte based and doesn't do proper chunked processing.
|
||||
int min_buffer = ao->buffer + 64;
|
||||
int missing = min_buffer - device_buffered - soft_buffered;
|
||||
missing = (missing + align - 1) / align * align;
|
||||
// But always keep the device's buffer filled as much as we can.
|
||||
int device_missing = device_space - soft_buffered;
|
||||
missing = MPMAX(missing, device_missing);
|
||||
@@ -305,7 +307,7 @@ static void ao_play_data(struct ao *ao)
|
||||
bool more = needed >= (r == space ? ao->device_buffer / 4 : 1) && !stuck &&
|
||||
!(flags & AOPLAY_FINAL_CHUNK);
|
||||
if (more)
|
||||
mp_input_wakeup(ao->input_ctx); // request more data
|
||||
ao->wakeup_cb(ao->wakeup_ctx); // request more data
|
||||
MP_TRACE(ao, "in=%d flags=%d space=%d r=%d wa/pl=%d/%d needed=%d more=%d\n",
|
||||
max, flags, space, r, p->wait_on_ao, p->still_playing, needed, more);
|
||||
}
|
||||
@@ -347,7 +349,7 @@ static void *playthread(void *arg)
|
||||
}
|
||||
|
||||
if (was_playing && !p->still_playing)
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
ao->wakeup_cb(ao->wakeup_ctx);
|
||||
pthread_cond_signal(&p->wakeup); // for draining
|
||||
|
||||
if (p->still_playing && timeout > 0) {
|
||||
|
||||
+6
-1
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/log.h>
|
||||
@@ -104,7 +105,10 @@ AVRational mp_get_codec_timebase(struct mp_codec_params *c)
|
||||
tb.den *= (r.num + r.den - 1) / r.den;
|
||||
}
|
||||
|
||||
av_reduce(&tb.num, &tb.den, tb.num, tb.den, 1000000);
|
||||
av_reduce(&tb.num, &tb.den, tb.num, tb.den, INT_MAX);
|
||||
|
||||
if (tb.num < 1 || tb.den < 1)
|
||||
tb = AV_TIME_BASE_Q;
|
||||
|
||||
return tb;
|
||||
}
|
||||
@@ -160,6 +164,7 @@ void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb)
|
||||
dst->side_data_elems = mpkt->avpacket->side_data_elems;
|
||||
if (dst->data == mpkt->avpacket->data)
|
||||
dst->buf = mpkt->avpacket->buf;
|
||||
dst->flags |= mpkt->avpacket->flags;
|
||||
}
|
||||
if (mpkt && tb && tb->num > 0 && tb->den > 0)
|
||||
dst->duration = mpkt->duration / av_q2d(*tb);
|
||||
|
||||
@@ -46,4 +46,13 @@ void mp_set_avdict(struct AVDictionary **dict, char **kv);
|
||||
void mp_avdict_print_unset(struct mp_log *log, int msgl, struct AVDictionary *d);
|
||||
int mp_set_avopts(struct mp_log *log, void *avobj, char **kv);
|
||||
|
||||
#if (LIBAVCODEC_VERSION_MICRO >= 100 && \
|
||||
LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 61, 100)) || \
|
||||
(LIBAVCODEC_VERSION_MICRO < 100 && \
|
||||
LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 24, 0))
|
||||
#define MP_AVFRAME_DEC_PTS(frame) ((frame)->pts)
|
||||
#else
|
||||
#define MP_AVFRAME_DEC_PTS(frame) ((frame)->pkt_pts)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+3
-2
@@ -18,6 +18,7 @@
|
||||
#ifndef MPLAYER_MPCOMMON_H
|
||||
#define MPLAYER_MPCOMMON_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
@@ -64,8 +65,8 @@ enum {
|
||||
DATA_EOF = -2,
|
||||
};
|
||||
|
||||
extern const char *const mpv_version;
|
||||
extern const char *const mpv_builddate;
|
||||
extern const char mpv_version[];
|
||||
extern const char mpv_builddate[];
|
||||
|
||||
char *mp_format_time(double time, bool fractions);
|
||||
char *mp_format_time_fmt(const char *fmt, double time);
|
||||
|
||||
+19
-19
@@ -36,25 +36,25 @@
|
||||
#define OPT_BASE_STRUCT struct encode_opts
|
||||
const struct m_sub_options encode_config = {
|
||||
.opts = (const m_option_t[]) {
|
||||
OPT_STRING("o", file, CONF_GLOBAL | CONF_NOCFG | CONF_PRE_PARSE),
|
||||
OPT_STRING("of", format, CONF_GLOBAL),
|
||||
OPT_STRINGLIST("ofopts*", fopts, CONF_GLOBAL),
|
||||
OPT_FLOATRANGE("ofps", fps, CONF_GLOBAL, 0.0, 1000000.0),
|
||||
OPT_FLOATRANGE("omaxfps", maxfps, CONF_GLOBAL, 0.0, 1000000.0),
|
||||
OPT_STRING("ovc", vcodec, CONF_GLOBAL),
|
||||
OPT_STRINGLIST("ovcopts*", vopts, CONF_GLOBAL),
|
||||
OPT_STRING("oac", acodec, CONF_GLOBAL),
|
||||
OPT_STRINGLIST("oacopts*", aopts, CONF_GLOBAL),
|
||||
OPT_FLAG("oharddup", harddup, CONF_GLOBAL),
|
||||
OPT_FLOATRANGE("ovoffset", voffset, CONF_GLOBAL, -1000000.0, 1000000.0),
|
||||
OPT_FLOATRANGE("oaoffset", aoffset, CONF_GLOBAL, -1000000.0, 1000000.0),
|
||||
OPT_FLAG("ocopyts", copyts, CONF_GLOBAL),
|
||||
OPT_FLAG("orawts", rawts, CONF_GLOBAL),
|
||||
OPT_FLAG("oautofps", autofps, CONF_GLOBAL),
|
||||
OPT_FLAG("oneverdrop", neverdrop, CONF_GLOBAL),
|
||||
OPT_FLAG("ovfirst", video_first, CONF_GLOBAL),
|
||||
OPT_FLAG("oafirst", audio_first, CONF_GLOBAL),
|
||||
OPT_FLAG("ometadata", metadata, CONF_GLOBAL),
|
||||
OPT_STRING("o", file, M_OPT_FIXED | CONF_NOCFG | CONF_PRE_PARSE),
|
||||
OPT_STRING("of", format, M_OPT_FIXED),
|
||||
OPT_STRINGLIST("ofopts*", fopts, M_OPT_FIXED),
|
||||
OPT_FLOATRANGE("ofps", fps, M_OPT_FIXED, 0.0, 1000000.0),
|
||||
OPT_FLOATRANGE("omaxfps", maxfps, M_OPT_FIXED, 0.0, 1000000.0),
|
||||
OPT_STRING("ovc", vcodec, M_OPT_FIXED),
|
||||
OPT_STRINGLIST("ovcopts*", vopts, M_OPT_FIXED),
|
||||
OPT_STRING("oac", acodec, M_OPT_FIXED),
|
||||
OPT_STRINGLIST("oacopts*", aopts, M_OPT_FIXED),
|
||||
OPT_FLAG("oharddup", harddup, M_OPT_FIXED),
|
||||
OPT_FLOATRANGE("ovoffset", voffset, M_OPT_FIXED, -1000000.0, 1000000.0),
|
||||
OPT_FLOATRANGE("oaoffset", aoffset, M_OPT_FIXED, -1000000.0, 1000000.0),
|
||||
OPT_FLAG("ocopyts", copyts, M_OPT_FIXED),
|
||||
OPT_FLAG("orawts", rawts, M_OPT_FIXED),
|
||||
OPT_FLAG("oautofps", autofps, M_OPT_FIXED),
|
||||
OPT_FLAG("oneverdrop", neverdrop, M_OPT_FIXED),
|
||||
OPT_FLAG("ovfirst", video_first, M_OPT_FIXED),
|
||||
OPT_FLAG("oafirst", audio_first, M_OPT_FIXED),
|
||||
OPT_FLAG("ometadata", metadata, M_OPT_FIXED),
|
||||
{0}
|
||||
},
|
||||
.size = sizeof(struct encode_opts),
|
||||
|
||||
+5
-1
@@ -5,9 +5,13 @@
|
||||
// The only purpose of this is to make mpv library-safe.
|
||||
// Think hard before adding new members.
|
||||
struct mpv_global {
|
||||
struct MPOpts *opts;
|
||||
struct mp_log *log;
|
||||
struct m_config_shadow *config;
|
||||
struct mp_client_api *client_api;
|
||||
|
||||
// Using this is deprecated and should be avoided (missing synchronization).
|
||||
// Use m_config_cache to access mpv_global.config instead.
|
||||
struct MPOpts *opts;
|
||||
};
|
||||
|
||||
#endif
|
||||
+50
-22
@@ -27,12 +27,13 @@
|
||||
#include "mpv_talloc.h"
|
||||
|
||||
#include "misc/bstr.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "osdep/atomic.h"
|
||||
#include "common/common.h"
|
||||
#include "common/global.h"
|
||||
#include "misc/ring.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "options/options.h"
|
||||
#include "options/path.h"
|
||||
#include "osdep/terminal.h"
|
||||
#include "osdep/io.h"
|
||||
#include "osdep/timer.h"
|
||||
@@ -59,6 +60,8 @@ struct mp_log_root {
|
||||
int num_buffers;
|
||||
FILE *log_file;
|
||||
FILE *stats_file;
|
||||
char *log_path;
|
||||
char *stats_path;
|
||||
// --- must be accessed atomically
|
||||
/* This is incremented every time the msglevels must be reloaded.
|
||||
* (This is perhaps better than maintaining a globally accessible and
|
||||
@@ -286,6 +289,7 @@ static void write_log_file(struct mp_log *log, int lev, char *text)
|
||||
(mp_time_us() - MP_START_TIME) / 1e6,
|
||||
mp_log_levels[lev][0],
|
||||
log->verbose_prefix, text);
|
||||
fflush(root->log_file);
|
||||
}
|
||||
|
||||
static void write_msg_to_buffers(struct mp_log *log, int lev, char *text)
|
||||
@@ -457,6 +461,43 @@ void mp_msg_init(struct mpv_global *global)
|
||||
mp_msg_update_msglevels(global);
|
||||
}
|
||||
|
||||
// If opt is different from *current_path, reopen *file and update *current_path.
|
||||
// If there's an error, _append_ it to err_buf.
|
||||
// *current_path and *file are, rather trickily, only accessible under the
|
||||
// mp_msg_lock.
|
||||
static void reopen_file(char *opt, char **current_path, FILE **file,
|
||||
const char *type, struct mpv_global *global)
|
||||
{
|
||||
void *tmp = talloc_new(NULL);
|
||||
bool fail = false;
|
||||
|
||||
char *new_path = mp_get_user_path(tmp, global, opt);
|
||||
if (!new_path)
|
||||
new_path = "";
|
||||
|
||||
pthread_mutex_lock(&mp_msg_lock); // for *current_path/*file
|
||||
|
||||
char *old_path = *current_path ? *current_path : "";
|
||||
if (strcmp(old_path, new_path) != 0) {
|
||||
if (*file)
|
||||
fclose(*file);
|
||||
*file = NULL;
|
||||
talloc_free(*current_path);
|
||||
*current_path = talloc_strdup(NULL, new_path);
|
||||
if (new_path[0]) {
|
||||
*file = fopen(new_path, "wb");
|
||||
fail = !*file;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mp_msg_lock);
|
||||
|
||||
if (fail)
|
||||
mp_err(global->log, "Failed to open %s file '%s'\n", type, new_path);
|
||||
|
||||
talloc_free(tmp);
|
||||
}
|
||||
|
||||
void mp_msg_update_msglevels(struct mpv_global *global)
|
||||
{
|
||||
struct mp_log_root *root = global->log->root;
|
||||
@@ -480,11 +521,14 @@ void mp_msg_update_msglevels(struct mpv_global *global)
|
||||
m_option_type_msglevels.copy(NULL, &root->msg_levels,
|
||||
&global->opts->msg_levels);
|
||||
|
||||
if (!root->log_file && opts->log_file && opts->log_file[0])
|
||||
root->log_file = fopen(opts->log_file, "wb");
|
||||
|
||||
atomic_fetch_add(&root->reload_counter, 1);
|
||||
pthread_mutex_unlock(&mp_msg_lock);
|
||||
|
||||
reopen_file(opts->log_file, &root->log_path, &root->log_file,
|
||||
"log", global);
|
||||
|
||||
reopen_file(opts->dump_stats, &root->stats_path, &root->stats_file,
|
||||
"stats", global);
|
||||
}
|
||||
|
||||
void mp_msg_force_stderr(struct mpv_global *global, bool force_stderr)
|
||||
@@ -499,8 +543,10 @@ void mp_msg_uninit(struct mpv_global *global)
|
||||
struct mp_log_root *root = global->log->root;
|
||||
if (root->stats_file)
|
||||
fclose(root->stats_file);
|
||||
talloc_free(root->stats_path);
|
||||
if (root->log_file)
|
||||
fclose(root->log_file);
|
||||
talloc_free(root->log_path);
|
||||
m_option_type_msglevels.free(&root->msg_levels);
|
||||
talloc_free(root);
|
||||
global->log = NULL;
|
||||
@@ -582,24 +628,6 @@ struct mp_log_buffer_entry *mp_msg_log_buffer_read(struct mp_log_buffer *buffer)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int mp_msg_open_stats_file(struct mpv_global *global, const char *path)
|
||||
{
|
||||
struct mp_log_root *root = global->log->root;
|
||||
int r;
|
||||
|
||||
pthread_mutex_lock(&mp_msg_lock);
|
||||
|
||||
if (root->stats_file)
|
||||
fclose(root->stats_file);
|
||||
root->stats_file = fopen(path, "wb");
|
||||
r = root->stats_file ? 0 : -1;
|
||||
|
||||
pthread_mutex_unlock(&mp_msg_lock);
|
||||
|
||||
mp_msg_update_msglevels(global);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Thread-safety: fully thread-safe, but keep in mind that the lifetime of
|
||||
// log must be guaranteed during the call.
|
||||
// Never call this from signal handlers.
|
||||
|
||||
@@ -29,7 +29,6 @@ struct mp_log_buffer *mp_msg_log_buffer_new(struct mpv_global *global,
|
||||
void mp_msg_log_buffer_destroy(struct mp_log_buffer *buffer);
|
||||
struct mp_log_buffer_entry *mp_msg_log_buffer_read(struct mp_log_buffer *buffer);
|
||||
|
||||
int mp_msg_open_stats_file(struct mpv_global *global, const char *path);
|
||||
int mp_msg_find_level(const char *s);
|
||||
|
||||
extern const char *const mp_log_levels[MSGL_MAX + 1];
|
||||
|
||||
+2
-2
@@ -22,5 +22,5 @@
|
||||
#define BUILDDATE "UNKNOWN"
|
||||
#endif
|
||||
|
||||
const char *const mpv_version = "mpv " VERSION;
|
||||
const char *const mpv_builddate = BUILDDATE;
|
||||
const char mpv_version[] = "mpv " VERSION;
|
||||
const char mpv_builddate[] = BUILDDATE;
|
||||
+68
-29
@@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <math.h>
|
||||
@@ -28,7 +29,8 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "options/options.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/m_option.h"
|
||||
#include "mpv_talloc.h"
|
||||
#include "common/msg.h"
|
||||
#include "common/global.h"
|
||||
@@ -81,6 +83,34 @@ const demuxer_desc_t *const demuxer_list[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
struct demux_opts {
|
||||
int max_packs;
|
||||
int max_bytes;
|
||||
double min_secs;
|
||||
int force_seekable;
|
||||
double min_secs_cache;
|
||||
};
|
||||
|
||||
#define OPT_BASE_STRUCT struct demux_opts
|
||||
|
||||
const struct m_sub_options demux_conf = {
|
||||
.opts = (const struct m_option[]){
|
||||
OPT_DOUBLE("demuxer-readahead-secs", min_secs, M_OPT_MIN, .min = 0),
|
||||
OPT_INTRANGE("demuxer-max-packets", max_packs, 0, 0, INT_MAX),
|
||||
OPT_INTRANGE("demuxer-max-bytes", max_bytes, 0, 0, INT_MAX),
|
||||
OPT_FLAG("force-seekable", force_seekable, 0),
|
||||
OPT_DOUBLE("cache-secs", min_secs_cache, M_OPT_MIN, .min = 0),
|
||||
{0}
|
||||
},
|
||||
.size = sizeof(struct demux_opts),
|
||||
.defaults = &(const struct demux_opts){
|
||||
.max_packs = 16000,
|
||||
.max_bytes = 400 * 1024 * 1024,
|
||||
.min_secs = 1.0,
|
||||
.min_secs_cache = 10.0,
|
||||
},
|
||||
};
|
||||
|
||||
struct demux_internal {
|
||||
struct mp_log *log;
|
||||
|
||||
@@ -473,7 +503,7 @@ static double get_refresh_seek_pts(struct demux_internal *in)
|
||||
// Streams which didn't have any packets yet will return all packets,
|
||||
// other streams return packets only starting from the last position.
|
||||
if (ds->last_pos != -1 || ds->last_dts != MP_NOPTS_VALUE)
|
||||
ds->refreshing = true;
|
||||
ds->refreshing |= ds->selected;
|
||||
}
|
||||
|
||||
// Seek back to player's current position, with a small offset added.
|
||||
@@ -568,7 +598,7 @@ static bool read_packet(struct demux_internal *in)
|
||||
for (int n = 0; n < in->num_streams; n++) {
|
||||
struct demux_stream *ds = in->streams[n]->ds;
|
||||
active |= ds->active;
|
||||
read_more |= ds->active && !ds->head;
|
||||
read_more |= (ds->active && !ds->head) || ds->refreshing;
|
||||
packs += ds->packs;
|
||||
bytes += ds->bytes;
|
||||
if (ds->active && ds->last_ts != MP_NOPTS_VALUE && in->min_secs > 0 &&
|
||||
@@ -602,11 +632,13 @@ static bool read_packet(struct demux_internal *in)
|
||||
return false;
|
||||
}
|
||||
|
||||
double seek_pts = get_refresh_seek_pts(in);
|
||||
bool refresh_seek = seek_pts != MP_NOPTS_VALUE;
|
||||
read_more |= refresh_seek;
|
||||
|
||||
if (!read_more)
|
||||
return false;
|
||||
|
||||
double seek_pts = get_refresh_seek_pts(in);
|
||||
|
||||
// Actually read a packet. Drop the lock while doing so, because waiting
|
||||
// for disk or network I/O can take time.
|
||||
in->idle = false;
|
||||
@@ -615,7 +647,7 @@ static bool read_packet(struct demux_internal *in)
|
||||
|
||||
struct demuxer *demux = in->d_thread;
|
||||
|
||||
if (seek_pts != MP_NOPTS_VALUE) {
|
||||
if (refresh_seek) {
|
||||
MP_VERBOSE(in, "refresh seek to %f\n", seek_pts);
|
||||
demux->desc->seek(demux, seek_pts, SEEK_BACKWARD | SEEK_HR);
|
||||
}
|
||||
@@ -649,8 +681,7 @@ static void ds_get_packets(struct demux_stream *ds)
|
||||
struct demux_internal *in = ds->in;
|
||||
MP_DBG(in, "reading packet for %s\n", t);
|
||||
in->eof = false; // force retry
|
||||
ds->eof = false;
|
||||
while (ds->selected && !ds->head && !ds->eof) {
|
||||
while (ds->selected && !ds->head) {
|
||||
ds->active = true;
|
||||
// Note: the following code marks EOF if it can't continue
|
||||
if (in->threading) {
|
||||
@@ -660,6 +691,8 @@ static void ds_get_packets(struct demux_stream *ds)
|
||||
} else {
|
||||
read_packet(in);
|
||||
}
|
||||
if (ds->eof)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1185,7 +1218,6 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
.stream = stream,
|
||||
.seekable = stream->seekable,
|
||||
.filepos = -1,
|
||||
.opts = global->opts,
|
||||
.global = global,
|
||||
.log = mp_log_new(demuxer, log, desc->name),
|
||||
.glog = log,
|
||||
@@ -1198,22 +1230,24 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
!demuxer->stream->uncached_stream->seekable)
|
||||
demuxer->seekable = false;
|
||||
|
||||
struct demux_opts *opts = mp_get_config_group(demuxer, global, &demux_conf);
|
||||
|
||||
struct demux_internal *in = demuxer->in = talloc_ptrtype(demuxer, in);
|
||||
*in = (struct demux_internal){
|
||||
.log = demuxer->log,
|
||||
.d_thread = talloc(demuxer, struct demuxer),
|
||||
.d_buffer = talloc(demuxer, struct demuxer),
|
||||
.d_user = demuxer,
|
||||
.min_secs = demuxer->opts->demuxer_min_secs,
|
||||
.max_packs = demuxer->opts->demuxer_max_packs,
|
||||
.max_bytes = demuxer->opts->demuxer_max_bytes,
|
||||
.min_secs = opts->min_secs,
|
||||
.max_packs = opts->max_packs,
|
||||
.max_bytes = opts->max_bytes,
|
||||
.initial_state = true,
|
||||
};
|
||||
pthread_mutex_init(&in->lock, NULL);
|
||||
pthread_cond_init(&in->wakeup, NULL);
|
||||
|
||||
if (stream->uncached_stream)
|
||||
in->min_secs = MPMAX(in->min_secs, demuxer->opts->demuxer_min_secs_cache);
|
||||
in->min_secs = MPMAX(in->min_secs, opts->min_secs_cache);
|
||||
|
||||
*in->d_thread = *demuxer;
|
||||
*in->d_buffer = *demuxer;
|
||||
@@ -1244,7 +1278,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
mp_verbose(log, "Detected file format: %s\n", desc->desc);
|
||||
if (!in->d_thread->seekable)
|
||||
mp_verbose(log, "Stream is not seekable.\n");
|
||||
if (!in->d_thread->seekable && demuxer->opts->force_seekable) {
|
||||
if (!in->d_thread->seekable && opts->force_seekable) {
|
||||
mp_warn(log, "Not seekable, but enabling seeking on user request.\n");
|
||||
in->d_thread->seekable = true;
|
||||
in->d_thread->partially_seekable = true;
|
||||
@@ -1254,16 +1288,18 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
demux_changed(in->d_thread, DEMUX_EVENT_ALL);
|
||||
demux_update(demuxer);
|
||||
stream_control(demuxer->stream, STREAM_CTRL_SET_READAHEAD, &(int){false});
|
||||
struct timeline *tl = timeline_load(global, log, demuxer);
|
||||
if (tl) {
|
||||
struct demuxer_params params2 = {0};
|
||||
params2.timeline = tl;
|
||||
struct demuxer *sub = open_given_type(global, log,
|
||||
&demuxer_desc_timeline, stream,
|
||||
¶ms2, DEMUX_CHECK_FORCE);
|
||||
if (sub)
|
||||
return sub;
|
||||
timeline_destroy(tl);
|
||||
if (!(params && params->disable_timeline)) {
|
||||
struct timeline *tl = timeline_load(global, log, demuxer);
|
||||
if (tl) {
|
||||
struct demuxer_params params2 = {0};
|
||||
params2.timeline = tl;
|
||||
struct demuxer *sub =
|
||||
open_given_type(global, log, &demuxer_desc_timeline, stream,
|
||||
¶ms2, DEMUX_CHECK_FORCE);
|
||||
if (sub)
|
||||
return sub;
|
||||
timeline_destroy(tl);
|
||||
}
|
||||
}
|
||||
return demuxer;
|
||||
}
|
||||
@@ -1336,7 +1372,6 @@ struct demuxer *demux_open_url(const char *url,
|
||||
struct mp_cancel *cancel,
|
||||
struct mpv_global *global)
|
||||
{
|
||||
struct MPOpts *opts = global->opts;
|
||||
struct demuxer_params dummy = {0};
|
||||
if (!params)
|
||||
params = &dummy;
|
||||
@@ -1344,10 +1379,14 @@ struct demuxer *demux_open_url(const char *url,
|
||||
cancel, global);
|
||||
if (!s)
|
||||
return NULL;
|
||||
if (params->allow_capture)
|
||||
stream_set_capture_file(s, opts->stream_capture);
|
||||
if (params->allow_capture) {
|
||||
char *f;
|
||||
mp_read_option_raw(global, "stream-capture", &m_option_type_string, &f);
|
||||
stream_set_capture_file(s, f);
|
||||
talloc_free(f);
|
||||
}
|
||||
if (!params->disable_cache)
|
||||
stream_enable_cache(&s, &opts->stream_cache);
|
||||
stream_enable_cache_defaults(&s);
|
||||
struct demuxer *d = demux_open(s, params, global);
|
||||
if (d) {
|
||||
demux_maybe_replace_stream(d);
|
||||
@@ -1624,7 +1663,7 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
|
||||
int num_packets = 0;
|
||||
for (int n = 0; n < in->num_streams; n++) {
|
||||
struct demux_stream *ds = in->streams[n]->ds;
|
||||
if (ds->active) {
|
||||
if (ds->active && !(!ds->head && ds->eof)) {
|
||||
r->underrun |= !ds->head && !ds->eof;
|
||||
r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
|
||||
r->ts_range[1] = MP_PTS_MIN(r->ts_range[1], ds->last_ts);
|
||||
|
||||
+1
-1
@@ -161,6 +161,7 @@ struct demuxer_params {
|
||||
int matroska_wanted_segment;
|
||||
bool *matroska_was_valid;
|
||||
struct timeline *timeline;
|
||||
bool disable_timeline;
|
||||
// -- demux_open_url() only
|
||||
int stream_flags;
|
||||
bool allow_capture;
|
||||
@@ -207,7 +208,6 @@ typedef struct demuxer {
|
||||
struct mp_tags *metadata;
|
||||
|
||||
void *priv; // demuxer-specific internal data
|
||||
struct MPOpts *opts;
|
||||
struct mpv_global *global;
|
||||
struct mp_log *log, *glog;
|
||||
struct demuxer_params *params;
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "demux.h"
|
||||
#include "timeline.h"
|
||||
#include "common/msg.h"
|
||||
#include "common/global.h"
|
||||
#include "options/path.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "common/common.h"
|
||||
|
||||
+51
-21
@@ -35,7 +35,6 @@
|
||||
#include <libavutil/display.h>
|
||||
#include <libavutil/opt.h>
|
||||
|
||||
#include "options/options.h"
|
||||
#include "common/msg.h"
|
||||
#include "common/tags.h"
|
||||
#include "common/av_common.h"
|
||||
@@ -45,6 +44,7 @@
|
||||
#include "stream/stream.h"
|
||||
#include "demux.h"
|
||||
#include "stheader.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/m_option.h"
|
||||
#include "options/path.h"
|
||||
|
||||
@@ -69,22 +69,32 @@ struct demux_lavf_opts {
|
||||
char **avopts;
|
||||
int hacks;
|
||||
int genptsmode;
|
||||
char *sub_cp;
|
||||
int rtsp_transport;
|
||||
};
|
||||
|
||||
const struct m_sub_options demux_lavf_conf = {
|
||||
.opts = (const m_option_t[]) {
|
||||
OPT_INTRANGE("probesize", probesize, 0, 32, INT_MAX),
|
||||
OPT_STRING("format", format, 0),
|
||||
OPT_FLOATRANGE("analyzeduration", analyzeduration, 0, 0, 3600),
|
||||
OPT_INTRANGE("buffersize", buffersize, 0, 1, 10 * 1024 * 1024,
|
||||
OPTDEF_INT(BIO_BUFFER_SIZE)),
|
||||
OPT_FLAG("allow-mimetype", allow_mimetype, 0),
|
||||
OPT_INTRANGE("probescore", probescore, 0, 1, AVPROBE_SCORE_MAX),
|
||||
OPT_STRING("cryptokey", cryptokey, 0),
|
||||
OPT_FLAG("hacks", hacks, 0),
|
||||
OPT_CHOICE("genpts-mode", genptsmode, 0,
|
||||
OPT_INTRANGE("demuxer-lavf-probesize", probesize, 0, 32, INT_MAX),
|
||||
OPT_STRING("demuxer-lavf-format", format, 0),
|
||||
OPT_FLOATRANGE("demuxer-lavf-analyzeduration", analyzeduration, 0,
|
||||
0, 3600),
|
||||
OPT_INTRANGE("demuxer-lavf-buffersize", buffersize, 0, 1,
|
||||
10 * 1024 * 1024, OPTDEF_INT(BIO_BUFFER_SIZE)),
|
||||
OPT_FLAG("demuxer-lavf-allow-mimetype", allow_mimetype, 0),
|
||||
OPT_INTRANGE("demuxer-lavf-probescore", probescore, 0,
|
||||
1, AVPROBE_SCORE_MAX),
|
||||
OPT_STRING("demuxer-lavf-cryptokey", cryptokey, 0),
|
||||
OPT_FLAG("demuxer-lavf-hacks", hacks, 0),
|
||||
OPT_CHOICE("demuxer-lavf-genpts-mode", genptsmode, 0,
|
||||
({"lavf", 1}, {"no", 0})),
|
||||
OPT_KEYVALUELIST("o", avopts, 0),
|
||||
OPT_KEYVALUELIST("demuxer-lavf-o", avopts, 0),
|
||||
OPT_STRING("sub-codepage", sub_cp, 0),
|
||||
OPT_CHOICE("rtsp-transport", rtsp_transport, 0,
|
||||
({"lavf", 0},
|
||||
{"udp", 1},
|
||||
{"tcp", 2},
|
||||
{"http", 3})),
|
||||
{0}
|
||||
},
|
||||
.size = sizeof(struct demux_lavf_opts),
|
||||
@@ -95,6 +105,8 @@ const struct m_sub_options demux_lavf_conf = {
|
||||
// user is supposed to retry with larger probe sizes until a higher
|
||||
// value is reached.
|
||||
.probescore = AVPROBE_SCORE_MAX/4 + 1,
|
||||
.sub_cp = "auto",
|
||||
.rtsp_transport = 2,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -174,6 +186,9 @@ typedef struct lavf_priv {
|
||||
int cur_program;
|
||||
char *mime_type;
|
||||
double seek_delay;
|
||||
|
||||
struct demux_lavf_opts *opts;
|
||||
double mf_fps;
|
||||
} lavf_priv_t;
|
||||
|
||||
// At least mp4 has name="mov,mp4,m4a,3gp,3g2,mj2", so we split the name
|
||||
@@ -271,7 +286,7 @@ static void list_formats(struct demuxer *demuxer)
|
||||
static void convert_charset(struct demuxer *demuxer)
|
||||
{
|
||||
lavf_priv_t *priv = demuxer->priv;
|
||||
char *cp = demuxer->opts->sub_cp;
|
||||
char *cp = priv->opts->sub_cp;
|
||||
if (!cp || mp_charset_is_utf8(cp))
|
||||
return;
|
||||
bstr data = stream_read_complete(priv->stream, NULL, 128 * 1024 * 1024);
|
||||
@@ -313,9 +328,8 @@ static const char *const prefixes[] =
|
||||
|
||||
static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
|
||||
{
|
||||
struct MPOpts *opts = demuxer->opts;
|
||||
struct demux_lavf_opts *lavfdopts = opts->demux_lavf;
|
||||
lavf_priv_t *priv = demuxer->priv;
|
||||
struct demux_lavf_opts *lavfdopts = priv->opts;
|
||||
struct stream *s = priv->stream;
|
||||
|
||||
priv->filename = remove_prefix(s->url, prefixes);
|
||||
@@ -606,7 +620,7 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
|
||||
if (st->avg_frame_rate.num)
|
||||
sh->codec->fps = av_q2d(st->avg_frame_rate);
|
||||
if (priv->format_hack.image_format)
|
||||
sh->codec->fps = demuxer->opts->mf_fps;
|
||||
sh->codec->fps = priv->mf_fps;
|
||||
sh->codec->par_w = st->sample_aspect_ratio.num;
|
||||
sh->codec->par_h = st->sample_aspect_ratio.den;
|
||||
|
||||
@@ -685,6 +699,10 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
|
||||
AVDictionaryEntry *title = av_dict_get(st->metadata, "title", NULL, 0);
|
||||
if (title && title->value)
|
||||
sh->title = talloc_strdup(sh, title->value);
|
||||
if (!sh->title && st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED)
|
||||
sh->title = talloc_asprintf(sh, "visual impaired");
|
||||
if (!sh->title && st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
|
||||
sh->title = talloc_asprintf(sh, "hearing impaired");
|
||||
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
|
||||
if (lang && lang->value)
|
||||
sh->lang = talloc_strdup(sh, lang->value);
|
||||
@@ -736,8 +754,6 @@ static int interrupt_cb(void *ctx)
|
||||
|
||||
static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
{
|
||||
struct MPOpts *opts = demuxer->opts;
|
||||
struct demux_lavf_opts *lavfdopts = opts->demux_lavf;
|
||||
AVFormatContext *avfc;
|
||||
AVDictionaryEntry *t = NULL;
|
||||
float analyze_duration = 0;
|
||||
@@ -745,6 +761,15 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
demuxer->priv = priv;
|
||||
priv->stream = demuxer->stream;
|
||||
|
||||
priv->opts = mp_get_config_group(priv, demuxer->global, &demux_lavf_conf);
|
||||
struct demux_lavf_opts *lavfdopts = priv->opts;
|
||||
|
||||
int index_mode;
|
||||
mp_read_option_raw(demuxer->global, "index", &m_option_type_choice,
|
||||
&index_mode);
|
||||
mp_read_option_raw(demuxer->global, "mf-fps", &m_option_type_double,
|
||||
&priv->mf_fps);
|
||||
|
||||
if (lavf_check_file(demuxer, check) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -756,7 +781,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
parse_cryptokey(avfc, lavfdopts->cryptokey);
|
||||
if (lavfdopts->genptsmode)
|
||||
avfc->flags |= AVFMT_FLAG_GENPTS;
|
||||
if (opts->index_mode != 1)
|
||||
if (index_mode != 1)
|
||||
avfc->flags |= AVFMT_FLAG_IGNIDX;
|
||||
|
||||
#if LIBAVFORMAT_VERSION_MICRO >= 100
|
||||
@@ -789,7 +814,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
priv->stream->type == STREAMTYPE_AVDEVICE ||
|
||||
priv->format_hack.no_stream)
|
||||
{
|
||||
mp_setup_av_network_options(&dopts, demuxer->global, demuxer->log, opts);
|
||||
mp_setup_av_network_options(&dopts, demuxer->global, demuxer->log);
|
||||
// This might be incorrect.
|
||||
demuxer->seekable = true;
|
||||
} else {
|
||||
@@ -812,7 +837,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
|
||||
if (matches_avinputformat_name(priv, "rtsp")) {
|
||||
const char *transport = NULL;
|
||||
switch (opts->network_rtsp_transport) {
|
||||
switch (lavfdopts->rtsp_transport) {
|
||||
case 1: transport = "udp"; break;
|
||||
case 2: transport = "tcp"; break;
|
||||
case 3: transport = "http"; break;
|
||||
@@ -917,6 +942,11 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
|
||||
#endif
|
||||
dp->pos = pkt->pos;
|
||||
dp->keyframe = pkt->flags & AV_PKT_FLAG_KEY;
|
||||
#if LIBAVFORMAT_VERSION_MICRO >= 100 && \
|
||||
LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 50, 100)
|
||||
if (pkt->flags & AV_PKT_FLAG_DISCARD)
|
||||
MP_ERR(demux, "Edit lists are not correctly supported (FFmpeg issue).\n");
|
||||
#endif
|
||||
av_packet_unref(pkt);
|
||||
|
||||
if (priv->format_hack.clear_filepos)
|
||||
|
||||
+10
-4
@@ -27,6 +27,7 @@
|
||||
#include "mpv_talloc.h"
|
||||
#include "common/msg.h"
|
||||
#include "options/options.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/path.h"
|
||||
#include "misc/ctype.h"
|
||||
|
||||
@@ -304,10 +305,15 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check)
|
||||
if (!mf || mf->nr_of_files < 1)
|
||||
goto error;
|
||||
|
||||
char *force_type = demuxer->opts->mf_type;
|
||||
double mf_fps;
|
||||
char *mf_type;
|
||||
mp_read_option_raw(demuxer->global, "mf-fps", &m_option_type_double, &mf_fps);
|
||||
mp_read_option_raw(demuxer->global, "mf-type", &m_option_type_string, &mf_type);
|
||||
|
||||
const char *codec = mp_map_mimetype_to_video_codec(demuxer->stream->mime_type);
|
||||
if (!codec || (force_type && force_type[0]))
|
||||
codec = probe_format(mf, force_type, check);
|
||||
if (!codec || (mf_type && mf_type[0]))
|
||||
codec = probe_format(mf, mf_type, check);
|
||||
talloc_free(mf_type);
|
||||
if (!codec)
|
||||
goto error;
|
||||
|
||||
@@ -320,7 +326,7 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check)
|
||||
c->codec = codec;
|
||||
c->disp_w = 0;
|
||||
c->disp_h = 0;
|
||||
c->fps = demuxer->opts->mf_fps;
|
||||
c->fps = mf_fps;
|
||||
c->reliable_fps = true;
|
||||
|
||||
demux_add_sh_stream(demuxer, sh);
|
||||
|
||||
+63
-15
@@ -43,7 +43,7 @@
|
||||
|
||||
#include "mpv_talloc.h"
|
||||
#include "common/av_common.h"
|
||||
#include "options/options.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/m_option.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "stream/stream.h"
|
||||
@@ -107,6 +107,7 @@ typedef struct mkv_track {
|
||||
double v_frate;
|
||||
uint32_t colorspace;
|
||||
int stereo_mode;
|
||||
struct mp_colorspace color;
|
||||
|
||||
uint32_t a_channels, a_bps;
|
||||
float a_sfreq;
|
||||
@@ -162,6 +163,8 @@ struct block_info {
|
||||
};
|
||||
|
||||
typedef struct mkv_demuxer {
|
||||
struct demux_mkv_opts *opts;
|
||||
|
||||
int64_t segment_start, segment_end;
|
||||
|
||||
double duration;
|
||||
@@ -179,6 +182,9 @@ typedef struct mkv_demuxer {
|
||||
mkv_index_t *indexes;
|
||||
size_t num_indexes;
|
||||
bool index_complete;
|
||||
int index_mode;
|
||||
|
||||
int edition_id;
|
||||
|
||||
struct header_elem {
|
||||
int32_t id;
|
||||
@@ -531,6 +537,42 @@ static void parse_trackaudio(struct demuxer *demuxer, struct mkv_track *track,
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_trackcolour(struct demuxer *demuxer, struct mkv_track *track,
|
||||
struct ebml_colour *colour)
|
||||
{
|
||||
// Note: As per matroska spec, the order is consistent with ISO/IEC
|
||||
// 23001-8:2013/DCOR1, which is the same order used by libavutil/pixfmt.h,
|
||||
// so we can just re-use our avcol_ conversion functions.
|
||||
if (colour->n_matrix_coefficients) {
|
||||
track->color.space = avcol_spc_to_mp_csp(colour->matrix_coefficients);
|
||||
MP_VERBOSE(demuxer, "| + Matrix: %s\n",
|
||||
m_opt_choice_str(mp_csp_names, track->color.space));
|
||||
}
|
||||
if (colour->n_primaries) {
|
||||
track->color.primaries = avcol_pri_to_mp_csp_prim(colour->primaries);
|
||||
MP_VERBOSE(demuxer, "| + Primaries: %s\n",
|
||||
m_opt_choice_str(mp_csp_prim_names, track->color.primaries));
|
||||
}
|
||||
if (colour->n_transfer_characteristics) {
|
||||
track->color.gamma = avcol_trc_to_mp_csp_trc(colour->transfer_characteristics);
|
||||
MP_VERBOSE(demuxer, "| + Gamma: %s\n",
|
||||
m_opt_choice_str(mp_csp_trc_names, track->color.gamma));
|
||||
}
|
||||
if (colour->n_range) {
|
||||
track->color.levels = avcol_range_to_mp_csp_levels(colour->range);
|
||||
MP_VERBOSE(demuxer, "| + Levels: %s\n",
|
||||
m_opt_choice_str(mp_csp_levels_names, track->color.levels));
|
||||
}
|
||||
if (colour->n_mastering_metadata) {
|
||||
struct ebml_mastering_metadata *mastering = &colour->mastering_metadata;
|
||||
|
||||
if (mastering->n_luminance_max) {
|
||||
track->color.sig_peak = mastering->luminance_max;
|
||||
MP_VERBOSE(demuxer, "| + HDR peak: %f\n", track->color.sig_peak);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
|
||||
struct ebml_video *video)
|
||||
{
|
||||
@@ -570,6 +612,8 @@ static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
|
||||
MP_WARN(demuxer, "Unknown StereoMode: %d\n", (int)video->stereo_mode);
|
||||
}
|
||||
}
|
||||
if (video->n_colour)
|
||||
parse_trackcolour(demuxer, track, &video->colour);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -747,11 +791,10 @@ static void add_block_position(demuxer_t *demuxer, struct mkv_track *track,
|
||||
|
||||
static int demux_mkv_read_cues(demuxer_t *demuxer)
|
||||
{
|
||||
struct MPOpts *opts = demuxer->opts;
|
||||
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
|
||||
stream_t *s = demuxer->stream;
|
||||
|
||||
if (opts->index_mode != 1 || mkv_d->index_complete) {
|
||||
if (mkv_d->index_mode != 1 || mkv_d->index_complete) {
|
||||
ebml_read_skip(demuxer->log, -1, s);
|
||||
return 0;
|
||||
}
|
||||
@@ -810,9 +853,9 @@ done:
|
||||
|
||||
static int demux_mkv_read_chapters(struct demuxer *demuxer)
|
||||
{
|
||||
struct MPOpts *opts = demuxer->opts;
|
||||
mkv_demuxer_t *mkv_d = demuxer->priv;
|
||||
stream_t *s = demuxer->stream;
|
||||
int wanted_edition = opts->edition_id;
|
||||
int wanted_edition = mkv_d->edition_id;
|
||||
uint64_t wanted_edition_uid = demuxer->matroska_data.uid.edition;
|
||||
|
||||
/* A specific edition UID was requested; ignore the user option which is
|
||||
@@ -858,6 +901,7 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer)
|
||||
if (wanted_edition_uid) {
|
||||
MP_ERR(demuxer, "Unable to find expected edition uid: %"PRIu64"\n",
|
||||
wanted_edition_uid);
|
||||
talloc_free(parse_ctx.talloc_ctx);
|
||||
return -1;
|
||||
} else {
|
||||
selected_edition = 0;
|
||||
@@ -1191,10 +1235,9 @@ static int read_deferred_element(struct demuxer *demuxer,
|
||||
|
||||
static void read_deferred_cues(demuxer_t *demuxer)
|
||||
{
|
||||
struct MPOpts *opts = demuxer->opts;
|
||||
mkv_demuxer_t *mkv_d = demuxer->priv;
|
||||
|
||||
if (mkv_d->index_complete || opts->index_mode != 1)
|
||||
if (mkv_d->index_complete || mkv_d->index_mode != 1)
|
||||
return;
|
||||
|
||||
for (int n = 0; n < mkv_d->num_headers; n++) {
|
||||
@@ -1393,6 +1436,7 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
sh_v->par_h = p.p_h;
|
||||
|
||||
sh_v->stereo_mode = track->stereo_mode;
|
||||
sh_v->color = track->color;
|
||||
|
||||
done:
|
||||
demux_add_sh_stream(demuxer, sh);
|
||||
@@ -1855,7 +1899,6 @@ static int read_mkv_segment_header(demuxer_t *demuxer, int64_t *segment_end)
|
||||
static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
|
||||
{
|
||||
stream_t *s = demuxer->stream;
|
||||
struct MPOpts *opts = demuxer->opts;
|
||||
mkv_demuxer_t *mkv_d;
|
||||
int64_t start_pos;
|
||||
int64_t end_pos;
|
||||
@@ -1882,6 +1925,12 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
|
||||
mkv_d->a_skip_preroll = 1;
|
||||
mkv_d->skip_to_timecode = INT64_MIN;
|
||||
|
||||
mp_read_option_raw(demuxer->global, "index", &m_option_type_choice,
|
||||
&mkv_d->index_mode);
|
||||
mp_read_option_raw(demuxer->global, "edition", &m_option_type_choice,
|
||||
&mkv_d->edition_id);
|
||||
mkv_d->opts = mp_get_config_group(mkv_d, demuxer->global, &demux_mkv_conf);
|
||||
|
||||
if (demuxer->params && demuxer->params->matroska_was_valid)
|
||||
*demuxer->params->matroska_was_valid = true;
|
||||
|
||||
@@ -1941,7 +1990,7 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
|
||||
process_tags(demuxer);
|
||||
|
||||
probe_first_timestamp(demuxer);
|
||||
if (opts->demux_mkv->probe_duration)
|
||||
if (mkv_d->opts->probe_duration)
|
||||
probe_last_timestamp(demuxer, start_pos);
|
||||
|
||||
return 0;
|
||||
@@ -2730,7 +2779,6 @@ static int create_index_until(struct demuxer *demuxer, int64_t timecode)
|
||||
static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
|
||||
int64_t target_timecode, int flags)
|
||||
{
|
||||
struct MPOpts *opts = demuxer->opts;
|
||||
struct mkv_demuxer *mkv_d = demuxer->priv;
|
||||
struct mkv_index *index = NULL;
|
||||
|
||||
@@ -2758,9 +2806,9 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
|
||||
if (flags & FLAG_SUBPREROLL) {
|
||||
// Find the cluster with the highest filepos, that has a timestamp
|
||||
// still lower than min_tc.
|
||||
double secs = opts->demux_mkv->subtitle_preroll_secs;
|
||||
double secs = mkv_d->opts->subtitle_preroll_secs;
|
||||
if (mkv_d->index_has_durations)
|
||||
secs = MPMAX(secs, opts->demux_mkv->subtitle_preroll_secs_index);
|
||||
secs = MPMAX(secs, mkv_d->opts->subtitle_preroll_secs_index);
|
||||
int64_t pre = MPMIN(INT64_MAX, secs * 1e9 / mkv_d->tc_scale);
|
||||
int64_t min_tc = pre < index->timecode ? index->timecode - pre : 0;
|
||||
uint64_t prev_target = 0;
|
||||
@@ -2823,7 +2871,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, double seek_pts, int flags)
|
||||
int cueflags = (flags & SEEK_BACKWARD) ? FLAG_BACKWARD : 0;
|
||||
|
||||
mkv_d->subtitle_preroll = NUM_SUB_PREROLL_PACKETS;
|
||||
int preroll_opt = demuxer->opts->demux_mkv->subtitle_preroll;
|
||||
int preroll_opt = mkv_d->opts->subtitle_preroll;
|
||||
if (((flags & SEEK_HR) || preroll_opt == 1 ||
|
||||
(preroll_opt == 2 && mkv_d->index_has_durations))
|
||||
&& st_active[STREAM_SUB] && st_active[STREAM_VIDEO])
|
||||
@@ -2917,7 +2965,7 @@ static void probe_last_timestamp(struct demuxer *demuxer, int64_t start_pos)
|
||||
|
||||
// In full mode, we start reading data from the current file position,
|
||||
// which works because this function is called after headers are parsed.
|
||||
if (demuxer->opts->demux_mkv->probe_duration != 2) {
|
||||
if (mkv_d->opts->probe_duration != 2) {
|
||||
read_deferred_cues(demuxer);
|
||||
if (mkv_d->index_complete) {
|
||||
// Find last cluster that still has video packets
|
||||
@@ -2974,7 +3022,7 @@ static void probe_first_timestamp(struct demuxer *demuxer)
|
||||
{
|
||||
mkv_demuxer_t *mkv_d = demuxer->priv;
|
||||
|
||||
if (!demuxer->opts->demux_mkv->probe_start_time)
|
||||
if (!mkv_d->opts->probe_start_time)
|
||||
return;
|
||||
|
||||
struct block_info block;
|
||||
|
||||
+32
-25
@@ -32,10 +32,10 @@
|
||||
#include "mpv_talloc.h"
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "common/global.h"
|
||||
#include "demux/demux.h"
|
||||
#include "demux/timeline.h"
|
||||
#include "demux/matroska.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/options.h"
|
||||
#include "options/path.h"
|
||||
#include "misc/bstr.h"
|
||||
@@ -46,6 +46,7 @@
|
||||
struct tl_ctx {
|
||||
struct mp_log *log;
|
||||
struct mpv_global *global;
|
||||
struct MPOpts *opts;
|
||||
struct timeline *tl;
|
||||
|
||||
struct demuxer *demuxer;
|
||||
@@ -169,6 +170,7 @@ static bool check_file_seg(struct tl_ctx *ctx, char *filename, int segment)
|
||||
.matroska_wanted_uids = ctx->uids,
|
||||
.matroska_wanted_segment = segment,
|
||||
.matroska_was_valid = &was_valid,
|
||||
.disable_timeline = true,
|
||||
.disable_cache = true,
|
||||
};
|
||||
struct mp_cancel *cancel = ctx->tl->cancel;
|
||||
@@ -186,31 +188,34 @@ static bool check_file_seg(struct tl_ctx *ctx, char *filename, int segment)
|
||||
if (ctx->sources[i])
|
||||
continue;
|
||||
/* Accept the source if the segment uid matches and the edition
|
||||
* either matches or isn't specified. */
|
||||
* either matches or isn't specified. */
|
||||
if (!memcmp(uid->segment, m->uid.segment, 16) &&
|
||||
(!uid->edition || uid->edition == m->uid.edition))
|
||||
{
|
||||
MP_INFO(ctx, "Match for source %d: %s\n", i, d->filename);
|
||||
|
||||
for (int j = 0; j < m->num_ordered_chapters; j++) {
|
||||
struct matroska_chapter *c = m->ordered_chapters + j;
|
||||
if (!uid->edition) {
|
||||
m->uid.edition = 0;
|
||||
} else {
|
||||
for (int j = 0; j < m->num_ordered_chapters; j++) {
|
||||
struct matroska_chapter *c = m->ordered_chapters + j;
|
||||
|
||||
if (!c->has_segment_uid)
|
||||
continue;
|
||||
if (!c->has_segment_uid)
|
||||
continue;
|
||||
|
||||
if (has_source_request(ctx, &c->uid))
|
||||
continue;
|
||||
if (has_source_request(ctx, &c->uid))
|
||||
continue;
|
||||
|
||||
/* Set the requested segment. */
|
||||
MP_TARRAY_GROW(NULL, ctx->uids, ctx->num_sources);
|
||||
ctx->uids[ctx->num_sources] = c->uid;
|
||||
/* Set the requested segment. */
|
||||
MP_TARRAY_GROW(NULL, ctx->uids, ctx->num_sources);
|
||||
ctx->uids[ctx->num_sources] = c->uid;
|
||||
|
||||
/* Add a new source slot. */
|
||||
MP_TARRAY_APPEND(NULL, ctx->sources, ctx->num_sources, NULL);
|
||||
/* Add a new source slot. */
|
||||
MP_TARRAY_APPEND(NULL, ctx->sources, ctx->num_sources, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (stream_wants_cache(d->stream, &ctx->global->opts->stream_cache))
|
||||
{
|
||||
if (stream_wants_cache(d->stream, ctx->opts->stream_cache)) {
|
||||
free_demuxer_and_stream(d);
|
||||
params.disable_cache = false;
|
||||
params.matroska_wanted_uids = ctx->uids; // potentially reallocated, same data
|
||||
@@ -247,7 +252,7 @@ static bool missing(struct tl_ctx *ctx)
|
||||
|
||||
static void find_ordered_chapter_sources(struct tl_ctx *ctx)
|
||||
{
|
||||
struct MPOpts *opts = ctx->global->opts;
|
||||
struct MPOpts *opts = ctx->opts;
|
||||
void *tmp = talloc_new(NULL);
|
||||
int num_filenames = 0;
|
||||
char **filenames = NULL;
|
||||
@@ -260,7 +265,7 @@ static void find_ordered_chapter_sources(struct tl_ctx *ctx)
|
||||
struct playlist *pl =
|
||||
playlist_parse_file(opts->ordered_chapters_files, ctx->global);
|
||||
talloc_steal(tmp, pl);
|
||||
for (struct playlist_entry *e = pl->first; e; e = e->next)
|
||||
for (struct playlist_entry *e = pl ? pl->first : NULL; e; e = e->next)
|
||||
MP_TARRAY_APPEND(tmp, filenames, num_filenames, e->filename);
|
||||
} else if (ctx->demuxer->stream->uncached_type != STREAMTYPE_FILE) {
|
||||
MP_WARN(ctx, "Playback source is not a "
|
||||
@@ -317,7 +322,7 @@ static int64_t add_timeline_part(struct tl_ctx *ctx,
|
||||
* early; we don't want to try seeking over a one frame gap. */
|
||||
int64_t join_diff = start - ctx->last_end_time;
|
||||
if (ctx->num_parts == 0
|
||||
|| FFABS(join_diff) > ctx->global->opts->chapter_merge_threshold * 1e6
|
||||
|| FFABS(join_diff) > ctx->opts->chapter_merge_threshold * 1e6
|
||||
|| source != ctx->timeline[ctx->num_parts - 1].source)
|
||||
{
|
||||
struct timeline_part new = {
|
||||
@@ -378,7 +383,7 @@ static void build_timeline_loop(struct tl_ctx *ctx,
|
||||
|
||||
/* If we're the source or it's a non-ordered edition reference,
|
||||
* just add a timeline part from the source. */
|
||||
if (current_source == j || !linked_m->num_ordered_chapters) {
|
||||
if (current_source == j || !linked_m->uid.edition) {
|
||||
uint64_t source_full_length =
|
||||
demuxer_get_time_length(linked_source) * 1e9;
|
||||
uint64_t source_length = source_full_length - c->start;
|
||||
@@ -505,20 +510,22 @@ void build_ordered_chapter_timeline(struct timeline *tl)
|
||||
if (!demuxer->matroska_data.ordered_chapters)
|
||||
return;
|
||||
|
||||
if (!demuxer->global->opts->ordered_chapters) {
|
||||
MP_INFO(demuxer, "File uses ordered chapters, but "
|
||||
"you have disabled support for them. Ignoring.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct tl_ctx *ctx = talloc_ptrtype(tl, ctx);
|
||||
*ctx = (struct tl_ctx){
|
||||
.log = tl->log,
|
||||
.global = tl->global,
|
||||
.tl = tl,
|
||||
.demuxer = demuxer,
|
||||
.opts = mp_get_config_group(ctx, tl->global, NULL),
|
||||
};
|
||||
|
||||
if (!ctx->opts->ordered_chapters) {
|
||||
MP_INFO(demuxer, "File uses ordered chapters, but "
|
||||
"you have disabled support for them. Ignoring.\n");
|
||||
talloc_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
MP_INFO(ctx, "File uses ordered chapters, will build edit timeline.\n");
|
||||
|
||||
struct matroska_data *m = &demuxer->matroska_data;
|
||||
|
||||
+5
-3
@@ -22,8 +22,8 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "options/m_config.h"
|
||||
#include "options/m_option.h"
|
||||
#include "options/options.h"
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "demux.h"
|
||||
@@ -129,7 +129,8 @@ struct priv {
|
||||
|
||||
static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
|
||||
{
|
||||
struct demux_rawaudio_opts *opts = demuxer->opts->demux_rawaudio;
|
||||
struct demux_rawaudio_opts *opts =
|
||||
mp_get_config_group(demuxer, demuxer->global, &demux_rawaudio_conf);
|
||||
|
||||
if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
|
||||
return -1;
|
||||
@@ -169,7 +170,8 @@ static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
|
||||
|
||||
static int demux_rawvideo_open(demuxer_t *demuxer, enum demux_check check)
|
||||
{
|
||||
struct demux_rawvideo_opts *opts = demuxer->opts->demux_rawvideo;
|
||||
struct demux_rawvideo_opts *opts =
|
||||
mp_get_config_group(demuxer, demuxer->global, &demux_rawvideo_conf);
|
||||
|
||||
if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
|
||||
return -1;
|
||||
|
||||
@@ -93,7 +93,6 @@ static void switch_segment(struct demuxer *demuxer, struct segment *new,
|
||||
double start_pts, int flags)
|
||||
{
|
||||
struct priv *p = demuxer->priv;
|
||||
bool new_segment = p->current != new;
|
||||
|
||||
if (!(flags & (SEEK_FORWARD | SEEK_BACKWARD)))
|
||||
flags |= SEEK_BACKWARD | SEEK_HR;
|
||||
@@ -107,8 +106,7 @@ static void switch_segment(struct demuxer *demuxer, struct segment *new,
|
||||
|
||||
for (int n = 0; n < p->num_streams; n++) {
|
||||
struct virtual_stream *vs = &p->streams[n];
|
||||
if (new_segment)
|
||||
vs->new_segment = true;
|
||||
vs->new_segment = true;
|
||||
vs->eos_packets = 0;
|
||||
}
|
||||
|
||||
|
||||
+16
-8
@@ -25,15 +25,23 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
|
||||
if (check > DEMUX_CHECK_REQUEST || demuxer->stream->type != STREAMTYPE_TV)
|
||||
return -1;
|
||||
|
||||
tv_param_t *params = m_sub_options_copy(demuxer, &tv_params_conf,
|
||||
demuxer->opts->tv_params);
|
||||
struct tv_stream_params *sparams = demuxer->stream->priv;
|
||||
if (sparams->channel && sparams->channel[0]) {
|
||||
talloc_free(params->channel);
|
||||
params->channel = talloc_strdup(NULL, sparams->channel);
|
||||
tv_param_t *params = mp_get_config_group(demuxer, demuxer->global,
|
||||
&tv_params_conf);
|
||||
bstr urlparams = bstr0(demuxer->stream->path);
|
||||
bstr channel, input;
|
||||
bstr_split_tok(urlparams, "/", &channel, &input);
|
||||
if (channel.len) {
|
||||
talloc_free(params->channels);
|
||||
params->channel = bstrto0(NULL, channel);
|
||||
}
|
||||
if (input.len) {
|
||||
bstr r;
|
||||
params->input = bstrtoll(input, &r, 0);
|
||||
if (r.len) {
|
||||
MP_ERR(demuxer->stream, "invalid input: '%.*s'\n", BSTR_P(input));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (sparams->input >= 0)
|
||||
params->input = sparams->input;
|
||||
|
||||
assert(demuxer->priv==NULL);
|
||||
if(!(tvh=tv_begin(params, demuxer->log))) return -1;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "common/common.h"
|
||||
#include "audio/chmap.h"
|
||||
#include "video/csputils.h"
|
||||
|
||||
struct MPOpts;
|
||||
struct demuxer;
|
||||
@@ -93,6 +94,7 @@ struct mp_codec_params {
|
||||
int disp_w, disp_h; // display size
|
||||
int rotate; // intended display rotation, in degrees, [0, 359]
|
||||
int stereo_mode; // mp_stereo3d_mode (0 if none/unknown)
|
||||
struct mp_colorspace color; // colorspace info where available
|
||||
|
||||
// STREAM_VIDEO + STREAM_AUDIO
|
||||
int bits_per_coded_sample;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# This file is baked into the mpv binary at compile time, and automatically
|
||||
# loaded at early initialization time. Some of the profiles are automatically
|
||||
# applied at later stages during loading.
|
||||
|
||||
[pseudo-gui]
|
||||
player-operation-mode=pseudo-gui
|
||||
|
||||
[builtin-pseudo-gui]
|
||||
terminal=no
|
||||
force-window=yes
|
||||
idle=once
|
||||
screenshot-directory=~~desktop/
|
||||
|
||||
[libmpv]
|
||||
config=no
|
||||
idle=yes
|
||||
terminal=no
|
||||
input-terminal=no
|
||||
osc=no
|
||||
ytdl=no
|
||||
input-default-bindings=no
|
||||
input-vo-keyboard=no
|
||||
stop-playback-on-init-failure=yes
|
||||
# OSX/Cocoa global input hooks
|
||||
input-appleremote=no
|
||||
input-media-keys=no
|
||||
input-app-events=no
|
||||
|
||||
[encoding]
|
||||
vo=lavc
|
||||
ao=lavc
|
||||
keep-open=no
|
||||
force-window=no
|
||||
gapless-audio=yes
|
||||
resume-playback=no
|
||||
load-scripts=no
|
||||
osc=no
|
||||
framedrop=no
|
||||
|
||||
[opengl-hq]
|
||||
scale=spline36
|
||||
cscale=spline36
|
||||
dscale=mitchell
|
||||
dither-depth=auto
|
||||
correct-downscaling=yes
|
||||
sigmoid-upscaling=yes
|
||||
deband=yes
|
||||
+2
-2
@@ -109,10 +109,10 @@
|
||||
#t add sub-pos +1 # down
|
||||
#v cycle sub-visibility
|
||||
# stretch SSA/ASS subtitles with anamorphic videos to match historical
|
||||
#V cycle ass-vsfilter-aspect-compat
|
||||
#V cycle sub-ass-vsfilter-aspect-compat
|
||||
# switch between applying no style overrides to SSA/ASS subtitles, and
|
||||
# overriding them almost completely with the normal subtitle style
|
||||
#u cycle-values ass-style-override "force" "no"
|
||||
#u cycle-values sub-ass-style-override "force" "no"
|
||||
#j cycle sub # cycle through subtitles
|
||||
#J cycle sub down # ...backwards
|
||||
#SHARP cycle audio # switch audio streams
|
||||
|
||||
+5
-3
@@ -53,9 +53,11 @@
|
||||
#ontop=yes
|
||||
|
||||
# Specify default video driver (see --vo=help for a list).
|
||||
# This one selects high quality video scaling etc. - can cause problems with
|
||||
# some drivers and GPUs.
|
||||
#vo=opengl-hq
|
||||
#vo=opengl
|
||||
|
||||
# Specify high quality video rendering preset (for OpenGL VO only)
|
||||
# Can cause performance problems with some drivers and GPUs.
|
||||
#profile=opengl-hq
|
||||
|
||||
# Force video to lock on the display's refresh rate, and change video and audio
|
||||
# speed to some degree to ensure synchronous playback - can cause problems
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ Comment[zh-CN]=播放电影和歌曲
|
||||
Comment[zh-TW]=播放電影和歌曲
|
||||
Icon=mpv
|
||||
TryExec=mpv
|
||||
Exec=mpv --profile=pseudo-gui -- %U
|
||||
Exec=mpv --player-operation-mode=pseudo-gui -- %U
|
||||
Terminal=false
|
||||
Categories=AudioVideo;Audio;Video;Player;TV;
|
||||
MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/ogg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;audio/flac;audio/mp4;
|
||||
|
||||
+4
-2
@@ -186,8 +186,6 @@ const struct mp_cmd_def mp_cmds[] = {
|
||||
{ MP_CMD_VF, "vf", { ARG_STRING, ARG_STRING } },
|
||||
{ MP_CMD_VF_COMMAND, "vf-command", { ARG_STRING, ARG_STRING, ARG_STRING } },
|
||||
|
||||
{ MP_CMD_VO_CMDLINE, "vo-cmdline", { ARG_STRING } },
|
||||
|
||||
{ MP_CMD_SCRIPT_BINDING, "script-binding", { ARG_STRING },
|
||||
.allow_auto_repeat = true, .on_updown = true},
|
||||
|
||||
@@ -226,6 +224,10 @@ const struct mp_cmd_def mp_cmds[] = {
|
||||
{"reselect", 1})),
|
||||
}},
|
||||
|
||||
{ MP_CMD_APPLY_PROFILE, "apply-profile", {ARG_STRING } },
|
||||
|
||||
{ MP_CMD_LOAD_SCRIPT, "load-script", {ARG_STRING} },
|
||||
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
+4
-3
@@ -99,9 +99,6 @@ enum mp_command_type {
|
||||
MP_CMD_VF,
|
||||
MP_CMD_VF_COMMAND,
|
||||
|
||||
/// Video output commands
|
||||
MP_CMD_VO_CMDLINE,
|
||||
|
||||
/// Internal for Lua scripts
|
||||
MP_CMD_SCRIPT_BINDING,
|
||||
MP_CMD_SCRIPT_MESSAGE,
|
||||
@@ -117,6 +114,10 @@ enum mp_command_type {
|
||||
|
||||
MP_CMD_RESCAN_EXTERNAL_FILES,
|
||||
|
||||
MP_CMD_APPLY_PROFILE,
|
||||
|
||||
MP_CMD_LOAD_SCRIPT,
|
||||
|
||||
// Internal
|
||||
MP_CMD_COMMAND_LIST, // list of sub-commands in args[0].v.p
|
||||
};
|
||||
|
||||
+110
-83
@@ -22,6 +22,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -34,7 +35,6 @@
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "osdep/io.h"
|
||||
#include "osdep/semaphore.h"
|
||||
#include "misc/rendezvous.h"
|
||||
|
||||
#include "input.h"
|
||||
@@ -73,6 +73,7 @@ struct cmd_bind {
|
||||
};
|
||||
|
||||
struct cmd_bind_section {
|
||||
char *owner;
|
||||
struct cmd_bind *binds;
|
||||
int num_binds;
|
||||
char *section;
|
||||
@@ -96,15 +97,13 @@ struct cmd_queue {
|
||||
|
||||
struct input_ctx {
|
||||
pthread_mutex_t mutex;
|
||||
sem_t wakeup;
|
||||
struct mp_log *log;
|
||||
struct mpv_global *global;
|
||||
struct m_config_cache *opts_cache;
|
||||
struct input_opts *opts;
|
||||
|
||||
bool using_alt_gr;
|
||||
bool using_ar;
|
||||
bool using_cocoa_media_keys;
|
||||
bool win_drag;
|
||||
|
||||
// Autorepeat stuff
|
||||
short ar_state;
|
||||
@@ -146,6 +145,9 @@ struct input_ctx {
|
||||
struct cmd_queue cmd_queue;
|
||||
|
||||
struct mp_cancel *cancel;
|
||||
|
||||
void (*wakeup_cb)(void *ctx);
|
||||
void *wakeup_ctx;
|
||||
};
|
||||
|
||||
static int parse_config(struct input_ctx *ictx, bool builtin, bstr data,
|
||||
@@ -170,28 +172,30 @@ struct input_opts {
|
||||
int enable_mouse_movements;
|
||||
int vo_key_input;
|
||||
int test;
|
||||
int allow_win_drag;
|
||||
};
|
||||
|
||||
const struct m_sub_options input_config = {
|
||||
.opts = (const m_option_t[]) {
|
||||
OPT_STRING("conf", config_file, CONF_GLOBAL | M_OPT_FILE),
|
||||
OPT_INT("ar-delay", ar_delay, CONF_GLOBAL),
|
||||
OPT_INT("ar-rate", ar_rate, CONF_GLOBAL),
|
||||
OPT_PRINT("keylist", mp_print_key_list),
|
||||
OPT_PRINT("cmdlist", mp_print_cmd_list),
|
||||
OPT_FLAG("default-bindings", default_bindings, CONF_GLOBAL),
|
||||
OPT_FLAG("test", test, CONF_GLOBAL),
|
||||
OPT_INTRANGE("doubleclick-time", doubleclick_time, 0, 0, 1000),
|
||||
OPT_FLAG("right-alt-gr", use_alt_gr, CONF_GLOBAL),
|
||||
OPT_INTRANGE("key-fifo-size", key_fifo_size, CONF_GLOBAL, 2, 65000),
|
||||
OPT_FLAG("cursor", enable_mouse_movements, CONF_GLOBAL),
|
||||
OPT_FLAG("vo-keyboard", vo_key_input, CONF_GLOBAL),
|
||||
OPT_FLAG("x11-keyboard", vo_key_input, CONF_GLOBAL), // old alias
|
||||
OPT_STRING("input-conf", config_file, M_OPT_FIXED | M_OPT_FILE),
|
||||
OPT_INT("input-ar-delay", ar_delay, 0),
|
||||
OPT_INT("input-ar-rate", ar_rate, 0),
|
||||
OPT_PRINT("input-keylist", mp_print_key_list),
|
||||
OPT_PRINT("input-cmdlist", mp_print_cmd_list),
|
||||
OPT_FLAG("input-default-bindings", default_bindings, 0),
|
||||
OPT_FLAG("input-test", test, 0),
|
||||
OPT_INTRANGE("input-doubleclick-time", doubleclick_time, 0, 0, 1000),
|
||||
OPT_FLAG("input-right-alt-gr", use_alt_gr, 0),
|
||||
OPT_INTRANGE("input-key-fifo-size", key_fifo_size, 0, 2, 65000),
|
||||
OPT_FLAG("input-cursor", enable_mouse_movements, 0),
|
||||
OPT_FLAG("input-vo-keyboard", vo_key_input, 0),
|
||||
#if HAVE_COCOA
|
||||
OPT_FLAG("appleremote", use_appleremote, CONF_GLOBAL),
|
||||
OPT_FLAG("media-keys", use_media_keys, CONF_GLOBAL),
|
||||
OPT_FLAG("app-events", use_app_events, CONF_GLOBAL),
|
||||
OPT_FLAG("input-appleremote", use_appleremote, 0),
|
||||
OPT_FLAG("input-media-keys", use_media_keys, 0),
|
||||
OPT_FLAG("input-app-events", use_app_events, M_OPT_FIXED),
|
||||
#endif
|
||||
OPT_FLAG("window-dragging", allow_win_drag, 0),
|
||||
OPT_REPLACED("input-x11-keyboard", "input-vo-keyboard"),
|
||||
{0}
|
||||
},
|
||||
.size = sizeof(struct input_opts),
|
||||
@@ -209,7 +213,9 @@ const struct m_sub_options input_config = {
|
||||
#endif
|
||||
.default_bindings = 1,
|
||||
.vo_key_input = 1,
|
||||
.allow_win_drag = 1,
|
||||
},
|
||||
.change_flags = UPDATE_INPUT,
|
||||
};
|
||||
|
||||
static const char builtin_input_conf[] =
|
||||
@@ -845,33 +851,18 @@ static mp_cmd_t *check_autorepeat(struct input_ctx *ictx)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mp_input_wait(struct input_ctx *ictx, double seconds)
|
||||
double mp_input_get_delay(struct input_ctx *ictx)
|
||||
{
|
||||
input_lock(ictx);
|
||||
double seconds = INFINITY;
|
||||
adjust_max_wait_time(ictx, &seconds);
|
||||
input_unlock(ictx);
|
||||
while (sem_trywait(&ictx->wakeup) == 0)
|
||||
seconds = -1;
|
||||
if (seconds > 0) {
|
||||
MP_STATS(ictx, "start sleep");
|
||||
struct timespec ts =
|
||||
mp_time_us_to_timespec(mp_add_timeout(mp_time_us(), seconds));
|
||||
sem_timedwait(&ictx->wakeup, &ts);
|
||||
MP_STATS(ictx, "end sleep");
|
||||
}
|
||||
}
|
||||
|
||||
void mp_input_wakeup_nolock(struct input_ctx *ictx)
|
||||
{
|
||||
// Some audio APIs discourage use of locking in their audio callback,
|
||||
// and these audio callbacks happen to call mp_input_wakeup_nolock()
|
||||
// when new data is needed. This is why we use semaphores here.
|
||||
sem_post(&ictx->wakeup);
|
||||
return seconds;
|
||||
}
|
||||
|
||||
void mp_input_wakeup(struct input_ctx *ictx)
|
||||
{
|
||||
mp_input_wakeup_nolock(ictx);
|
||||
ictx->wakeup_cb(ictx->wakeup_ctx);
|
||||
}
|
||||
|
||||
mp_cmd_t *mp_input_read_cmd(struct input_ctx *ictx)
|
||||
@@ -996,7 +987,8 @@ bool mp_input_test_mouse_active(struct input_ctx *ictx, int x, int y)
|
||||
bool mp_input_test_dragging(struct input_ctx *ictx, int x, int y)
|
||||
{
|
||||
input_lock(ictx);
|
||||
bool r = !ictx->win_drag || test_mouse(ictx, x, y, MP_INPUT_ALLOW_VO_DRAGGING);
|
||||
bool r = !ictx->opts->allow_win_drag ||
|
||||
test_mouse(ictx, x, y, MP_INPUT_ALLOW_VO_DRAGGING);
|
||||
input_unlock(ictx);
|
||||
return r;
|
||||
}
|
||||
@@ -1021,13 +1013,19 @@ static void remove_binds(struct cmd_bind_section *bs, bool builtin)
|
||||
}
|
||||
|
||||
void mp_input_define_section(struct input_ctx *ictx, char *name, char *location,
|
||||
char *contents, bool builtin)
|
||||
char *contents, bool builtin, char *owner)
|
||||
{
|
||||
if (!name || !name[0])
|
||||
return; // parse_config() changes semantics with restrict_section==empty
|
||||
input_lock(ictx);
|
||||
// Delete:
|
||||
struct cmd_bind_section *bs = get_bind_section(ictx, bstr0(name));
|
||||
if ((!bs->owner || (owner && strcmp(bs->owner, owner) != 0)) &&
|
||||
strcmp(bs->section, "default") != 0)
|
||||
{
|
||||
talloc_free(bs->owner);
|
||||
bs->owner = talloc_strdup(bs, owner);
|
||||
}
|
||||
remove_binds(bs, builtin);
|
||||
if (contents && contents[0]) {
|
||||
// Redefine:
|
||||
@@ -1039,6 +1037,21 @@ void mp_input_define_section(struct input_ctx *ictx, char *name, char *location,
|
||||
input_unlock(ictx);
|
||||
}
|
||||
|
||||
void mp_input_remove_sections_by_owner(struct input_ctx *ictx, char *owner)
|
||||
{
|
||||
input_lock(ictx);
|
||||
struct cmd_bind_section *bs = ictx->cmd_bind_sections;
|
||||
while (bs) {
|
||||
if (bs->owner && owner && strcmp(bs->owner, owner) == 0) {
|
||||
mp_input_disable_section(ictx, bs->section);
|
||||
remove_binds(bs, false);
|
||||
remove_binds(bs, true);
|
||||
}
|
||||
bs = bs->next;
|
||||
}
|
||||
input_unlock(ictx);
|
||||
}
|
||||
|
||||
static bool bind_matches_key(struct cmd_bind *bind, int num_keys, const int *keys)
|
||||
{
|
||||
if (bind->num_keys != num_keys)
|
||||
@@ -1195,22 +1208,23 @@ done:
|
||||
return r;
|
||||
}
|
||||
|
||||
struct input_ctx *mp_input_init(struct mpv_global *global)
|
||||
struct input_ctx *mp_input_init(struct mpv_global *global,
|
||||
void (*wakeup_cb)(void *ctx),
|
||||
void *wakeup_ctx)
|
||||
{
|
||||
|
||||
struct input_ctx *ictx = talloc_ptrtype(NULL, ictx);
|
||||
*ictx = (struct input_ctx){
|
||||
.global = global,
|
||||
.opts = talloc_zero(ictx, struct input_opts), // replaced later
|
||||
.ar_state = -1,
|
||||
.log = mp_log_new(ictx, global->log, "input"),
|
||||
.mouse_section = "default",
|
||||
.opts_cache = m_config_cache_alloc(ictx, global, &input_config),
|
||||
.wakeup_cb = wakeup_cb,
|
||||
.wakeup_ctx = wakeup_ctx,
|
||||
};
|
||||
|
||||
if (sem_init(&ictx->wakeup, 0, 0)) {
|
||||
MP_FATAL(ictx, "mpv doesn't work on systems without POSIX semaphores.\n");
|
||||
abort();
|
||||
}
|
||||
ictx->opts = ictx->opts_cache->opts;
|
||||
|
||||
mpthread_mutex_init_recursive(&ictx->mutex);
|
||||
|
||||
@@ -1221,13 +1235,45 @@ struct input_ctx *mp_input_init(struct mpv_global *global)
|
||||
return ictx;
|
||||
}
|
||||
|
||||
void mp_input_load(struct input_ctx *ictx)
|
||||
static void reload_opts(struct input_ctx *ictx, bool shutdown)
|
||||
{
|
||||
struct input_opts *input_conf =
|
||||
m_sub_options_copy(ictx, &input_config, ictx->global->opts->input_opts);
|
||||
m_config_cache_update(ictx->opts_cache);
|
||||
|
||||
talloc_free(ictx->opts);
|
||||
ictx->opts = input_conf;
|
||||
#if HAVE_COCOA
|
||||
struct input_opts *opts = ictx->opts;
|
||||
|
||||
if (ictx->using_ar != (opts->use_appleremote && !shutdown)) {
|
||||
ictx->using_ar = !ictx->using_ar;
|
||||
if (ictx->using_ar) {
|
||||
cocoa_init_apple_remote();
|
||||
} else {
|
||||
cocoa_uninit_apple_remote();
|
||||
}
|
||||
}
|
||||
|
||||
if (ictx->using_cocoa_media_keys != (opts->use_media_keys && !shutdown)) {
|
||||
ictx->using_cocoa_media_keys = !ictx->using_cocoa_media_keys;
|
||||
if (ictx->using_cocoa_media_keys) {
|
||||
cocoa_init_media_keys();
|
||||
} else {
|
||||
cocoa_uninit_media_keys();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void mp_input_update_opts(struct input_ctx *ictx)
|
||||
{
|
||||
input_lock(ictx);
|
||||
reload_opts(ictx, false);
|
||||
input_unlock(ictx);
|
||||
}
|
||||
|
||||
void mp_input_load_config(struct input_ctx *ictx)
|
||||
{
|
||||
input_lock(ictx);
|
||||
|
||||
reload_opts(ictx, false);
|
||||
|
||||
// "Uncomment" the default key bindings in etc/input.conf and add them.
|
||||
// All lines that do not start with '# ' are parsed.
|
||||
@@ -1240,8 +1286,8 @@ void mp_input_load(struct input_ctx *ictx)
|
||||
}
|
||||
|
||||
bool config_ok = false;
|
||||
if (input_conf->config_file)
|
||||
config_ok = parse_config_file(ictx, input_conf->config_file, true);
|
||||
if (ictx->opts->config_file && ictx->opts->config_file[0])
|
||||
config_ok = parse_config_file(ictx, ictx->opts->config_file, true);
|
||||
if (!config_ok) {
|
||||
// Try global conf dir
|
||||
void *tmp = talloc_new(NULL);
|
||||
@@ -1251,32 +1297,17 @@ void mp_input_load(struct input_ctx *ictx)
|
||||
talloc_free(tmp);
|
||||
}
|
||||
|
||||
if (input_conf->use_alt_gr) {
|
||||
ictx->using_alt_gr = true;
|
||||
}
|
||||
|
||||
#if HAVE_COCOA
|
||||
if (input_conf->use_app_events) {
|
||||
if (ictx->opts->use_app_events)
|
||||
cocoa_start_event_monitor();
|
||||
}
|
||||
|
||||
if (input_conf->use_appleremote) {
|
||||
cocoa_init_apple_remote();
|
||||
ictx->using_ar = true;
|
||||
}
|
||||
|
||||
if (input_conf->use_media_keys) {
|
||||
cocoa_init_media_keys();
|
||||
ictx->using_cocoa_media_keys = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
ictx->win_drag = ictx->global->opts->allow_win_drag;
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
if (ictx->global->opts->input_file && *ictx->global->opts->input_file)
|
||||
mp_input_pipe_add(ictx, ictx->global->opts->input_file);
|
||||
#endif
|
||||
|
||||
input_unlock(ictx);
|
||||
}
|
||||
|
||||
static void clear_queue(struct cmd_queue *queue)
|
||||
@@ -1293,21 +1324,14 @@ void mp_input_uninit(struct input_ctx *ictx)
|
||||
if (!ictx)
|
||||
return;
|
||||
|
||||
#if HAVE_COCOA
|
||||
if (ictx->using_ar) {
|
||||
cocoa_uninit_apple_remote();
|
||||
}
|
||||
|
||||
if (ictx->using_cocoa_media_keys) {
|
||||
cocoa_uninit_media_keys();
|
||||
}
|
||||
#endif
|
||||
input_lock(ictx);
|
||||
reload_opts(ictx, true);
|
||||
input_unlock(ictx);
|
||||
|
||||
close_input_sources(ictx);
|
||||
clear_queue(&ictx->cmd_queue);
|
||||
talloc_free(ictx->current_down_cmd);
|
||||
pthread_mutex_destroy(&ictx->mutex);
|
||||
sem_destroy(&ictx->wakeup);
|
||||
talloc_free(ictx);
|
||||
}
|
||||
|
||||
@@ -1320,7 +1344,10 @@ void mp_input_set_cancel(struct input_ctx *ictx, struct mp_cancel *cancel)
|
||||
|
||||
bool mp_input_use_alt_gr(struct input_ctx *ictx)
|
||||
{
|
||||
return ictx->using_alt_gr;
|
||||
input_lock(ictx);
|
||||
bool r = ictx->opts->use_alt_gr;
|
||||
input_unlock(ictx);
|
||||
return r;
|
||||
}
|
||||
|
||||
struct mp_cmd *mp_input_parse_cmd(struct input_ctx *ictx, bstr str,
|
||||
|
||||
+14
-9
@@ -196,9 +196,13 @@ void mp_input_disable_all_sections(struct input_ctx *ictx);
|
||||
// builtin: create as builtin section; this means if the user defines bindings
|
||||
// using "{name}", they won't be ignored or overwritten - instead,
|
||||
// they are preferred to the bindings defined with this call
|
||||
// owner: string ID of the client which defined this, or NULL
|
||||
// If the section already exists, its bindings are removed and replaced.
|
||||
void mp_input_define_section(struct input_ctx *ictx, char *name, char *location,
|
||||
char *contents, bool builtin);
|
||||
char *contents, bool builtin, char *owner);
|
||||
|
||||
// Remove all sections that have been defined by the given owner.
|
||||
void mp_input_remove_sections_by_owner(struct input_ctx *ictx, char *owner);
|
||||
|
||||
// Define where on the screen the named input section should receive.
|
||||
// Setting a rectangle of size 0 unsets the mouse area.
|
||||
@@ -219,22 +223,23 @@ bool mp_input_test_dragging(struct input_ctx *ictx, int x, int y);
|
||||
|
||||
// Initialize the input system
|
||||
struct mpv_global;
|
||||
struct input_ctx *mp_input_init(struct mpv_global *global);
|
||||
struct input_ctx *mp_input_init(struct mpv_global *global,
|
||||
void (*wakeup_cb)(void *ctx),
|
||||
void *wakeup_ctx);
|
||||
|
||||
// Load config, options, and devices.
|
||||
void mp_input_load(struct input_ctx *ictx);
|
||||
void mp_input_load_config(struct input_ctx *ictx);
|
||||
|
||||
void mp_input_update_opts(struct input_ctx *ictx);
|
||||
|
||||
void mp_input_uninit(struct input_ctx *ictx);
|
||||
|
||||
// Sleep for the given amount of seconds, until mp_input_wakeup() is called,
|
||||
// or new input arrives. seconds<=0 returns immediately.
|
||||
void mp_input_wait(struct input_ctx *ictx, double seconds);
|
||||
// Return number of seconds until the next autorepeat event will be generated.
|
||||
// Returns INFINITY if no autorepeated key is active.
|
||||
double mp_input_get_delay(struct input_ctx *ictx);
|
||||
|
||||
// Wake up sleeping input loop from another thread.
|
||||
void mp_input_wakeup(struct input_ctx *ictx);
|
||||
|
||||
void mp_input_wakeup_nolock(struct input_ctx *ictx);
|
||||
|
||||
// Used to asynchronously abort playback. Needed because the core still can
|
||||
// block on network in some situations.
|
||||
struct mp_cancel;
|
||||
|
||||
+9
-5
@@ -20,6 +20,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -97,6 +98,11 @@ static void *client_thread(void *p)
|
||||
{
|
||||
pthread_detach(pthread_self());
|
||||
|
||||
// We don't use MSG_NOSIGNAL because the moldy fruit OS doesn't support it.
|
||||
struct sigaction sa = { .sa_handler = SIG_IGN, .sa_flags = SA_RESTART };
|
||||
sigfillset(&sa.sa_mask);
|
||||
sigaction(SIGPIPE, &sa, NULL);
|
||||
|
||||
int rc;
|
||||
|
||||
struct client_arg *arg = p;
|
||||
@@ -118,15 +124,11 @@ static void *client_thread(void *p)
|
||||
};
|
||||
|
||||
fcntl(arg->client_fd, F_SETFL, fcntl(arg->client_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||
mpv_suspend(arg->client);
|
||||
|
||||
while (1) {
|
||||
rc = poll(fds, 2, 0);
|
||||
if (rc == 0) {
|
||||
mpv_resume(arg->client);
|
||||
if (rc == 0)
|
||||
rc = poll(fds, 2, -1);
|
||||
mpv_suspend(arg->client);
|
||||
}
|
||||
if (rc < 0) {
|
||||
MP_ERR(arg, "Poll error\n");
|
||||
continue;
|
||||
@@ -337,6 +339,8 @@ static void *ipc_thread(void *p)
|
||||
goto done;
|
||||
}
|
||||
|
||||
MP_VERBOSE(arg, "Listening to IPC socket.\n");
|
||||
|
||||
int client_num = 0;
|
||||
|
||||
struct pollfd fds[2] = {
|
||||
|
||||
+3
-5
@@ -222,7 +222,6 @@ static void *client_thread(void *p)
|
||||
MP_VERBOSE(arg, "Client connected\n");
|
||||
|
||||
mpv_set_wakeup_callback(arg->client, wakeup_cb, wakeup_event);
|
||||
mpv_suspend(arg->client);
|
||||
|
||||
// Do the first read operation on the pipe
|
||||
if ((ioerr = async_read(arg->client_h, buf, 4096, &ol))) {
|
||||
@@ -233,11 +232,8 @@ static void *client_thread(void *p)
|
||||
while (1) {
|
||||
HANDLE handles[] = { wakeup_event, ol.hEvent };
|
||||
int n = WaitForMultipleObjects(2, handles, FALSE, 0);
|
||||
if (n == WAIT_TIMEOUT) {
|
||||
mpv_resume(arg->client);
|
||||
if (n == WAIT_TIMEOUT)
|
||||
n = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
|
||||
mpv_suspend(arg->client);
|
||||
}
|
||||
|
||||
switch (n) {
|
||||
case WAIT_OBJECT_0: // wakeup_event
|
||||
@@ -381,6 +377,8 @@ static void *ipc_thread(void *p)
|
||||
goto done;
|
||||
}
|
||||
|
||||
MP_VERBOSE(arg, "Listening to IPC pipe.\n");
|
||||
|
||||
while (1) {
|
||||
DWORD err = ConnectNamedPipe(server, &ol) ? 0 : GetLastError();
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
#include <io.h>
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "osdep/atomic.h"
|
||||
#include "osdep/io.h"
|
||||
#include "input.h"
|
||||
|
||||
|
||||
+58
-20
@@ -135,10 +135,6 @@ extern "C" {
|
||||
* (used through libass), ALSA, FFmpeg, and possibly more.
|
||||
* - The FPU precision must be set at least to double precision.
|
||||
* - On Windows, mpv will call timeBeginPeriod(1).
|
||||
* - On UNIX, every mpv_initialize() call will block SIGPIPE. This is done
|
||||
* because FFmpeg makes unsafe use of OpenSSL and GnuTLS, which can raise
|
||||
* this signal under certain circumstances. Once these libraries (or FFmpeg)
|
||||
* are fixed, libmpv will not block the signal anymore.
|
||||
* - On memory exhaustion, mpv will kill the process.
|
||||
*
|
||||
* Encoding of filenames
|
||||
@@ -215,7 +211,7 @@ extern "C" {
|
||||
* relational operators (<, >, <=, >=).
|
||||
*/
|
||||
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
|
||||
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 22)
|
||||
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 23)
|
||||
|
||||
/**
|
||||
* Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with.
|
||||
@@ -370,10 +366,11 @@ const char *mpv_client_name(mpv_handle *ctx);
|
||||
* and needs to be initialized to be actually used with most other API
|
||||
* functions.
|
||||
*
|
||||
* Most API functions will return MPV_ERROR_UNINITIALIZED in the uninitialized
|
||||
* state. You can call mpv_set_option() (or mpv_set_option_string() and other
|
||||
* variants) to set initial options. After this, call mpv_initialize() to start
|
||||
* the player, and then use e.g. mpv_command() to start playback of a file.
|
||||
* Some API functions will return MPV_ERROR_UNINITIALIZED in the uninitialized
|
||||
* state. You can call mpv_set_property() (or mpv_set_property_string() and
|
||||
* other variants, and before mpv 0.21.0 mpv_set_option() etc.) to set initial
|
||||
* options. After this, call mpv_initialize() to start the player, and then use
|
||||
* e.g. mpv_command() to start playback of a file.
|
||||
*
|
||||
* The point of separating handle creation and actual initialization is that
|
||||
* you can configure things which can't be changed during runtime.
|
||||
@@ -401,7 +398,7 @@ const char *mpv_client_name(mpv_handle *ctx);
|
||||
* options.
|
||||
*
|
||||
* The mpv command line parser is not available through this API, but you can
|
||||
* set individual options with mpv_set_option(). Files for playback must be
|
||||
* set individual options with mpv_set_property(). Files for playback must be
|
||||
* loaded with mpv_command() or others.
|
||||
*
|
||||
* Note that you should avoid doing concurrent accesses on the uninitialized
|
||||
@@ -422,6 +419,17 @@ mpv_handle *mpv_create(void);
|
||||
* This function needs to be called to make full use of the client API if the
|
||||
* client API handle was created with mpv_create().
|
||||
*
|
||||
* Only the following options require to be set _before_ mpv_initialize():
|
||||
* - options which are only read at initialization time:
|
||||
* - config
|
||||
* - config-dir
|
||||
* - input-conf
|
||||
* - load-scripts
|
||||
* - script
|
||||
* - player-operation-mode
|
||||
* - input-app-events (OSX)
|
||||
* - all encoding mode options
|
||||
*
|
||||
* @return error code
|
||||
*/
|
||||
int mpv_initialize(mpv_handle *ctx);
|
||||
@@ -492,10 +500,6 @@ mpv_handle *mpv_create_client(mpv_handle *ctx, const char *name);
|
||||
* possible that some options were successfully set even if any of these errors
|
||||
* happen.
|
||||
*
|
||||
* The same restrictions as with mpv_set_option() apply: some options can't
|
||||
* be set outside of idle or uninitialized state, and many options don't
|
||||
* take effect immediately.
|
||||
*
|
||||
* @param filename absolute path to the config file on the local filesystem
|
||||
* @return error code
|
||||
*/
|
||||
@@ -518,6 +522,11 @@ int mpv_load_config_file(mpv_handle *ctx, const char *filename);
|
||||
* mpv_suspend() is not allowed.
|
||||
*
|
||||
* Calling this on an uninitialized player (see mpv_create()) will deadlock.
|
||||
*
|
||||
* @deprecated This function, as well as mpv_resume(), are deprecated, and
|
||||
* will stop doing anything soon. Their semantics were never
|
||||
* well-defined, and their usefulness is extremely limited. The
|
||||
* calls will remain stubs in order to keep ABI compatibility.
|
||||
*/
|
||||
void mpv_suspend(mpv_handle *ctx);
|
||||
|
||||
@@ -778,16 +787,36 @@ void mpv_free_node_contents(mpv_node *node);
|
||||
* works in uninitialized state (see mpv_create()), and in some cases in at
|
||||
* runtime.
|
||||
*
|
||||
* Changing options at runtime does not always work. For some options, attempts
|
||||
* to change them simply fails. Many other options may require reloading the
|
||||
* file for changes to take effect. In general, you should prefer calling
|
||||
* mpv_set_property() to change settings during playback, because the property
|
||||
* mechanism guarantees that changes take effect immediately.
|
||||
*
|
||||
* Using a format other than MPV_FORMAT_NODE is equivalent to constructing a
|
||||
* mpv_node with the given format and data, and passing the mpv_node to this
|
||||
* function.
|
||||
*
|
||||
* Note: this is semi-deprecated. For most purposes, this is not needed anymore.
|
||||
* Starting with mpv version 0.21.0 (version 1.23) most options can be set
|
||||
* with mpv_set_property() (and related functions), and even before
|
||||
* mpv_initialize(). In some obscure corner cases, using this function
|
||||
* to set options might still be required (see below, and also section
|
||||
* "Inconsistencies between options and properties" on the manpage). Once
|
||||
* these are resolved, the option setting functions might be fully
|
||||
* deprecated.
|
||||
*
|
||||
* The following options still need to be set either _before_
|
||||
* mpv_initialize() with mpv_set_property() (or related functions), or
|
||||
* with mpv_set_option() (or related functions) at any time:
|
||||
* - options shadowed by deprecated properties:
|
||||
* - demuxer (property deprecated in 0.21.0)
|
||||
* - idle (property deprecated in 0.21.0)
|
||||
* - fps (property deprecated in 0.21.0)
|
||||
* - cache (property deprecated in 0.21.0)
|
||||
* - length (property deprecated in 0.10.0)
|
||||
* - audio-samplerate (property deprecated in 0.10.0)
|
||||
* - audio-channels (property deprecated in 0.10.0)
|
||||
* - audio-format (property deprecated in 0.10.0)
|
||||
* - deprecated options shadowed by properties:
|
||||
* - chapter (option deprecated in 0.21.0)
|
||||
* - playlist-pos (option deprecated in 0.21.0)
|
||||
* The deprecated properties will be removed in mpv 0.22.0.
|
||||
*
|
||||
* @param name Option name. This is the same as on the mpv command line, but
|
||||
* without the leading "--".
|
||||
* @param format see enum mpv_format.
|
||||
@@ -893,6 +922,15 @@ int mpv_command_node_async(mpv_handle *ctx, uint64_t reply_userdata,
|
||||
* mpv_node with the given format and data, and passing the mpv_node to this
|
||||
* function. (Before API version 1.21, this was different.)
|
||||
*
|
||||
* Note: starting with mpv 0.21.0 (client API version 1.23), this can be used to
|
||||
* set options in general. It even can be used before mpv_initialize()
|
||||
* has been called. If called before mpv_initialize(), setting properties
|
||||
* not backed by options will result in MPV_ERROR_PROPERTY_UNAVAILABLE.
|
||||
* In some cases, properties and options still conflict. In these cases,
|
||||
* mpv_set_property() accesses the options before mpv_initialize(), and
|
||||
* the properties after mpv_initialize(). These conflicts will be removed
|
||||
* in mpv 0.22.0. See mpv_set_option() for further remarks.
|
||||
*
|
||||
* @param name The property name. See input.rst for a list of properties.
|
||||
* @param format see enum mpv_format.
|
||||
* @param[in] data Option value.
|
||||
|
||||
@@ -190,6 +190,29 @@ extern "C" {
|
||||
* In previous libmpv releases, this used "GL_MP_D3D_interfaces" and
|
||||
* "glMPGetD3DInterface". This is deprecated; use glMPGetNativeDisplay instead
|
||||
* (the semantics are 100% compatible).
|
||||
*
|
||||
* Windowing system interop on RPI
|
||||
* -------------------------------
|
||||
*
|
||||
* The RPI uses no proper interop, but hardware overlays instead. To place the
|
||||
* overlay correctly, you can communicate the window parameters as follows to
|
||||
* libmpv. gl->MPGetNativeDisplay("MPV_RPI_WINDOW") return an array of type int
|
||||
* with the following 4 elements:
|
||||
* 0: display number (default 0)
|
||||
* 1: layer number of the GL layer - video will be placed in the layer
|
||||
* directly below (default: 0)
|
||||
* 2: absolute x position of the GL context (default: 0)
|
||||
* 3: absolute y position of the GL context (default: 0)
|
||||
* The (x,y) position must be the absolute screen pixel position of the
|
||||
* top/left pixel of the dispmanx layer used for the GL context. If you render
|
||||
* to a FBO, the position must be that of the final position of the FBO
|
||||
* contents on screen. You can't transform or scale the video other than what
|
||||
* mpv will render to the video overlay. The defaults are suitable for
|
||||
* rendering the video at fullscreen.
|
||||
* The parameters are checked on every draw by calling MPGetNativeDisplay and
|
||||
* checking the values in the returned array for changes. The returned array
|
||||
* must remain valid until the libmpv render function returns; then it can be
|
||||
* deallocated by the API user.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <QList>
|
||||
#include <QHash>
|
||||
#include <QSharedPointer>
|
||||
#include <QMetaType>
|
||||
|
||||
#include <mpv/client.h>
|
||||
|
||||
@@ -227,6 +228,8 @@ struct node_autofree {
|
||||
* Return the given property as mpv_node converted to QVariant, or QVariant()
|
||||
* on error.
|
||||
*
|
||||
* @deprecated use get_property() instead
|
||||
*
|
||||
* @param name the property name
|
||||
*/
|
||||
static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name)
|
||||
@@ -240,6 +243,8 @@ static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name
|
||||
|
||||
/**
|
||||
* Set the given property as mpv_node converted from the QVariant argument.
|
||||
|
||||
* @deprecated use set_property() instead
|
||||
*/
|
||||
static inline int set_property_variant(mpv_handle *ctx, const QString &name,
|
||||
const QVariant &v)
|
||||
@@ -250,6 +255,8 @@ static inline int set_property_variant(mpv_handle *ctx, const QString &name,
|
||||
|
||||
/**
|
||||
* Set the given option as mpv_node converted from the QVariant argument.
|
||||
*
|
||||
* @deprecated use set_property() instead
|
||||
*/
|
||||
static inline int set_option_variant(mpv_handle *ctx, const QString &name,
|
||||
const QVariant &v)
|
||||
@@ -261,6 +268,8 @@ static inline int set_option_variant(mpv_handle *ctx, const QString &name,
|
||||
/**
|
||||
* mpv_command_node() equivalent. Returns QVariant() on error (and
|
||||
* unfortunately, the same on success).
|
||||
*
|
||||
* @deprecated use command() instead
|
||||
*/
|
||||
static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
|
||||
{
|
||||
@@ -272,7 +281,94 @@ static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
|
||||
return node_to_variant(&res);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to return error codes wrapped in QVariant for functions which
|
||||
* return QVariant.
|
||||
*
|
||||
* You can use get_error() or is_error() to extract the error status from a
|
||||
* QVariant value.
|
||||
*/
|
||||
struct ErrorReturn
|
||||
{
|
||||
/**
|
||||
* enum mpv_error value (or a value outside of it if ABI was extended)
|
||||
*/
|
||||
int error;
|
||||
|
||||
ErrorReturn() : error(0) {}
|
||||
explicit ErrorReturn(int err) : error(err) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the mpv error code packed into a QVariant, or 0 (success) if it's not
|
||||
* an error value.
|
||||
*
|
||||
* @return error code (<0) or success (>=0)
|
||||
*/
|
||||
static inline int get_error(const QVariant &v)
|
||||
{
|
||||
if (!v.canConvert<ErrorReturn>())
|
||||
return 0;
|
||||
return v.value<ErrorReturn>().error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the QVariant carries a mpv error code.
|
||||
*/
|
||||
static inline bool is_error(const QVariant &v)
|
||||
{
|
||||
return get_error(v) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given property as mpv_node converted to QVariant, or QVariant()
|
||||
* on error.
|
||||
*
|
||||
* @param name the property name
|
||||
* @return the property value, or an ErrorReturn with the error code
|
||||
*/
|
||||
static inline QVariant get_property(mpv_handle *ctx, const QString &name)
|
||||
{
|
||||
mpv_node node;
|
||||
int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node);
|
||||
if (err < 0)
|
||||
return QVariant::fromValue(ErrorReturn(err));
|
||||
node_autofree f(&node);
|
||||
return node_to_variant(&node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given property as mpv_node converted from the QVariant argument.
|
||||
*
|
||||
* @return mpv error code (<0 on error, >= 0 on success)
|
||||
*/
|
||||
static inline int set_property(mpv_handle *ctx, const QString &name,
|
||||
const QVariant &v)
|
||||
{
|
||||
node_builder node(v);
|
||||
return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
|
||||
}
|
||||
|
||||
/**
|
||||
* mpv_command_node() equivalent.
|
||||
*
|
||||
* @param args command arguments, with args[0] being the command name as string
|
||||
* @return the property value, or an ErrorReturn with the error code
|
||||
*/
|
||||
static inline QVariant command(mpv_handle *ctx, const QVariant &args)
|
||||
{
|
||||
node_builder node(args);
|
||||
mpv_node res;
|
||||
int err = mpv_command_node(ctx, node.node(), &res);
|
||||
if (err < 0)
|
||||
return QVariant::fromValue(ErrorReturn(err));
|
||||
node_autofree f(&res);
|
||||
return node_to_variant(&res);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(mpv::qt::ErrorReturn)
|
||||
|
||||
#endif
|
||||
@@ -291,6 +291,11 @@ bstr mp_iconv_to_utf8(struct mp_log *log, bstr buf, const char *cp, int flags)
|
||||
if (strcasecmp(cp, "UTF-8-BROKEN") == 0)
|
||||
return bstr_sanitize_utf8_latin1(NULL, buf);
|
||||
|
||||
// Force CP949 over EUC-KR since iconv distinguishes them and
|
||||
// EUC-KR causes error on CP949 encoded data
|
||||
if (strcasecmp(cp, "EUC-KR") == 0)
|
||||
cp = "CP949";
|
||||
|
||||
iconv_t icdsc;
|
||||
if ((icdsc = iconv_open("UTF-8", cp)) == (iconv_t) (-1)) {
|
||||
if (flags & MP_ICONV_VERBOSE)
|
||||
|
||||
+132
-73
@@ -28,19 +28,31 @@ struct mp_dispatch_queue {
|
||||
struct mp_dispatch_item *head, *tail;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t cond;
|
||||
int suspend_requested;
|
||||
bool suspended;
|
||||
void (*wakeup_fn)(void *wakeup_ctx);
|
||||
void *wakeup_ctx;
|
||||
// This lock grant access to the target thread's state during suspend mode.
|
||||
// During suspend mode, the target thread is blocked in the function
|
||||
// mp_dispatch_queue_process(), however this function may be processing
|
||||
// dispatch queue items. This lock serializes the dispatch queue processing
|
||||
// and external mp_dispatch_lock() calls.
|
||||
// Invariant: can be held only while suspended==true, and suspend_requested
|
||||
// must be >0 (unless mp_dispatch_queue_process() locks it). In particular,
|
||||
// suspend mode must not be left while the lock is held.
|
||||
pthread_mutex_t exclusive_lock;
|
||||
// Make mp_dispatch_queue_process() exit if it's idle.
|
||||
bool interrupted;
|
||||
// The target thread is blocked by mp_dispatch_queue_process(). Note that
|
||||
// mp_dispatch_lock() can set this from true to false to keep the thread
|
||||
// blocked (this stops if from processing other dispatch items, and from
|
||||
// other threads to return from mp_dispatch_lock(), making it an exclusive
|
||||
// lock).
|
||||
bool idling;
|
||||
// A mp_dispatch_lock() call is requesting an exclusive lock.
|
||||
bool lock_request;
|
||||
// Used to block out threads calling mp_dispatch_queue_process() while
|
||||
// they're externall locked via mp_dispatch_lock().
|
||||
// We could use a simple counter (increment it instead of adding a frame,
|
||||
// also increment it when locking), but with this we can perform some
|
||||
// minimal debug checks.
|
||||
struct lock_frame *frame;
|
||||
};
|
||||
|
||||
struct lock_frame {
|
||||
struct lock_frame *prev;
|
||||
pthread_t thread;
|
||||
pthread_t locked_thread;
|
||||
bool locked;
|
||||
};
|
||||
|
||||
struct mp_dispatch_item {
|
||||
@@ -55,11 +67,11 @@ static void queue_dtor(void *p)
|
||||
{
|
||||
struct mp_dispatch_queue *queue = p;
|
||||
assert(!queue->head);
|
||||
assert(!queue->suspend_requested);
|
||||
assert(!queue->suspended);
|
||||
assert(!queue->idling);
|
||||
assert(!queue->lock_request);
|
||||
assert(!queue->frame);
|
||||
pthread_cond_destroy(&queue->cond);
|
||||
pthread_mutex_destroy(&queue->lock);
|
||||
pthread_mutex_destroy(&queue->exclusive_lock);
|
||||
}
|
||||
|
||||
// A dispatch queue lets other threads run callbacks in a target thread.
|
||||
@@ -76,7 +88,6 @@ struct mp_dispatch_queue *mp_dispatch_create(void *ta_parent)
|
||||
struct mp_dispatch_queue *queue = talloc_ptrtype(ta_parent, queue);
|
||||
*queue = (struct mp_dispatch_queue){0};
|
||||
talloc_set_destructor(queue, queue_dtor);
|
||||
pthread_mutex_init(&queue->exclusive_lock, NULL);
|
||||
pthread_mutex_init(&queue->lock, NULL);
|
||||
pthread_cond_init(&queue->cond, NULL);
|
||||
return queue;
|
||||
@@ -111,6 +122,10 @@ static void mp_dispatch_append(struct mp_dispatch_queue *queue,
|
||||
// Wake up the main thread; note that other threads might wait on this
|
||||
// condition for reasons, so broadcast the condition.
|
||||
pthread_cond_broadcast(&queue->cond);
|
||||
// No wakeup callback -> assume mp_dispatch_queue_process() needs to be
|
||||
// interrupted instead.
|
||||
if (!queue->wakeup_fn)
|
||||
queue->interrupted = true;
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
if (queue->wakeup_fn)
|
||||
queue->wakeup_fn(queue->wakeup_ctx);
|
||||
@@ -170,24 +185,47 @@ void mp_dispatch_run(struct mp_dispatch_queue *queue,
|
||||
}
|
||||
|
||||
// Process any outstanding dispatch items in the queue. This also handles
|
||||
// suspending or locking the target thread.
|
||||
// suspending or locking the this thread from another thread via
|
||||
// mp_dispatch_lock().
|
||||
// The timeout specifies the minimum wait time. The actual time spent in this
|
||||
// function can be much higher if the suspending/locking functions are used, or
|
||||
// if executing the dispatch items takes time. On the other hand, this function
|
||||
// can return much earlier than the timeout due to sporadic wakeups.
|
||||
// It is also guaranteed that if at least one queue item was processed, the
|
||||
// function will return as soon as possible, ignoring the timeout. This
|
||||
// simplifies users, such as re-checking conditions before waiting. (It will
|
||||
// still process the remaining queue items, and wait for unsuspend.)
|
||||
// Note that this will strictly return only after:
|
||||
// - timeout has passed,
|
||||
// - all queue items were processed,
|
||||
// - the possibly acquired lock has been released
|
||||
// It's possible to cancel the timeout by calling mp_dispatch_interrupt().
|
||||
void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout)
|
||||
{
|
||||
int64_t wait = timeout > 0 ? mp_add_timeout(mp_time_us(), timeout) : 0;
|
||||
struct lock_frame frame = {
|
||||
.thread = pthread_self(),
|
||||
};
|
||||
|
||||
pthread_mutex_lock(&queue->lock);
|
||||
queue->suspended = true;
|
||||
// Wake up thread which called mp_dispatch_suspend().
|
||||
pthread_cond_broadcast(&queue->cond);
|
||||
while (queue->head || queue->suspend_requested || wait > 0) {
|
||||
if (queue->head) {
|
||||
frame.prev = queue->frame;
|
||||
queue->frame = &frame;
|
||||
// Logically, the queue is idling if the target thread is blocked in
|
||||
// mp_dispatch_queue_process() doing nothing, so it's not possible to call
|
||||
// it again. (Reentrant calls via callbacks temporarily reset the field.)
|
||||
assert(!queue->idling);
|
||||
queue->idling = true;
|
||||
// Wake up thread which called mp_dispatch_lock().
|
||||
if (queue->lock_request)
|
||||
pthread_cond_broadcast(&queue->cond);
|
||||
while (1) {
|
||||
if (queue->lock_request || queue->frame != &frame || frame.locked) {
|
||||
// Block due to something having called mp_dispatch_lock(). This
|
||||
// is either a lock "acquire" (lock_request=true), or a lock in
|
||||
// progress, with the possibility the thread which called
|
||||
// mp_dispatch_lock() is now calling mp_dispatch_queue_process()
|
||||
// (the latter means we must ignore any queue state changes,
|
||||
// until it has been unlocked again).
|
||||
pthread_cond_wait(&queue->cond, &queue->lock);
|
||||
if (queue->frame == &frame && !frame.locked)
|
||||
assert(queue->idling);
|
||||
} else if (queue->head) {
|
||||
struct mp_dispatch_item *item = queue->head;
|
||||
queue->head = item->next;
|
||||
if (!queue->head)
|
||||
@@ -195,66 +233,50 @@ void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout)
|
||||
item->next = NULL;
|
||||
// Unlock, because we want to allow other threads to queue items
|
||||
// while the dispatch item is processed.
|
||||
// At the same time, exclusive_lock must be held to protect the
|
||||
// thread's user state.
|
||||
// At the same time, we must prevent other threads from returning
|
||||
// from mp_dispatch_lock(), which is done by idling=false.
|
||||
queue->idling = false;
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
pthread_mutex_lock(&queue->exclusive_lock);
|
||||
|
||||
item->fn(item->fn_data);
|
||||
pthread_mutex_unlock(&queue->exclusive_lock);
|
||||
|
||||
pthread_mutex_lock(&queue->lock);
|
||||
assert(!queue->idling);
|
||||
queue->idling = true;
|
||||
// Wakeup mp_dispatch_run(), also mp_dispatch_lock().
|
||||
pthread_cond_broadcast(&queue->cond);
|
||||
if (item->asynchronous) {
|
||||
talloc_free(item);
|
||||
} else {
|
||||
item->completed = true;
|
||||
// Wakeup mp_dispatch_run()
|
||||
pthread_cond_broadcast(&queue->cond);
|
||||
}
|
||||
} else if (wait > 0 && !queue->interrupted) {
|
||||
struct timespec ts = mp_time_us_to_timespec(wait);
|
||||
if (pthread_cond_timedwait(&queue->cond, &queue->lock, &ts))
|
||||
wait = 0;
|
||||
} else {
|
||||
if (wait > 0) {
|
||||
struct timespec ts = mp_time_us_to_timespec(wait);
|
||||
pthread_cond_timedwait(&queue->cond, &queue->lock, &ts);
|
||||
} else {
|
||||
pthread_cond_wait(&queue->cond, &queue->lock);
|
||||
}
|
||||
}
|
||||
wait = 0;
|
||||
}
|
||||
queue->suspended = false;
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
}
|
||||
|
||||
// Set the target thread into suspend mode: in this mode, the thread will enter
|
||||
// mp_dispatch_queue_process(), process any outstanding dispatch items, and
|
||||
// wait for new items when done (instead of exiting the process function).
|
||||
// Multiple threads can enter suspend mode at the same time. Suspend mode is
|
||||
// not a synchronization mechanism; it merely makes sure the target thread does
|
||||
// not leave mp_dispatch_queue_process(), even if it's done. mp_dispatch_lock()
|
||||
// can be used for exclusive access.
|
||||
void mp_dispatch_suspend(struct mp_dispatch_queue *queue)
|
||||
{
|
||||
pthread_mutex_lock(&queue->lock);
|
||||
queue->suspend_requested++;
|
||||
while (!queue->suspended) {
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
if (queue->wakeup_fn)
|
||||
queue->wakeup_fn(queue->wakeup_ctx);
|
||||
pthread_mutex_lock(&queue->lock);
|
||||
if (queue->suspended)
|
||||
break;
|
||||
pthread_cond_wait(&queue->cond, &queue->lock);
|
||||
}
|
||||
}
|
||||
queue->idling = false;
|
||||
assert(!frame.locked);
|
||||
assert(queue->frame == &frame);
|
||||
queue->frame = frame.prev;
|
||||
queue->interrupted = false;
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
}
|
||||
|
||||
// Undo mp_dispatch_suspend().
|
||||
void mp_dispatch_resume(struct mp_dispatch_queue *queue)
|
||||
// If the queue is inside of mp_dispatch_queue_process(), make it return as
|
||||
// soon as all work items have been run, without waiting for the timeout. This
|
||||
// does not make it return early if it's blocked by a mp_dispatch_lock().
|
||||
// If mp_dispatch_queue_process() is called in a reentrant way (including the
|
||||
// case where another thread calls mp_dispatch_lock() and then
|
||||
// mp_dispatch_queue_process()), this affects only the "topmost" invocation.
|
||||
void mp_dispatch_interrupt(struct mp_dispatch_queue *queue)
|
||||
{
|
||||
pthread_mutex_lock(&queue->lock);
|
||||
assert(queue->suspended);
|
||||
assert(queue->suspend_requested > 0);
|
||||
queue->suspend_requested--;
|
||||
if (queue->suspend_requested == 0)
|
||||
pthread_cond_broadcast(&queue->cond);
|
||||
queue->interrupted = true;
|
||||
pthread_cond_broadcast(&queue->cond);
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
}
|
||||
|
||||
@@ -265,13 +287,50 @@ void mp_dispatch_resume(struct mp_dispatch_queue *queue)
|
||||
// and the mutex behavior applies to this function only.
|
||||
void mp_dispatch_lock(struct mp_dispatch_queue *queue)
|
||||
{
|
||||
mp_dispatch_suspend(queue);
|
||||
pthread_mutex_lock(&queue->exclusive_lock);
|
||||
pthread_mutex_lock(&queue->lock);
|
||||
// First grab the queue lock. Something else could be holding the lock.
|
||||
while (queue->lock_request)
|
||||
pthread_cond_wait(&queue->cond, &queue->lock);
|
||||
queue->lock_request = true;
|
||||
// And now wait until the target thread gets "trapped" within the
|
||||
// mp_dispatch_queue_process() call, which will mean we get exclusive
|
||||
// access to the target's thread state.
|
||||
while (!queue->idling) {
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
if (queue->wakeup_fn)
|
||||
queue->wakeup_fn(queue->wakeup_ctx);
|
||||
pthread_mutex_lock(&queue->lock);
|
||||
if (queue->idling)
|
||||
break;
|
||||
pthread_cond_wait(&queue->cond, &queue->lock);
|
||||
}
|
||||
assert(queue->lock_request);
|
||||
assert(queue->frame); // must be set if idling
|
||||
assert(!queue->frame->locked); // no recursive locking on the same level
|
||||
// "Lock".
|
||||
queue->frame->locked = true;
|
||||
queue->frame->locked_thread = pthread_self();
|
||||
// Reset state for recursive mp_dispatch_queue_process() calls.
|
||||
queue->lock_request = false;
|
||||
queue->idling = false;
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
}
|
||||
|
||||
// Undo mp_dispatch_lock().
|
||||
void mp_dispatch_unlock(struct mp_dispatch_queue *queue)
|
||||
{
|
||||
pthread_mutex_unlock(&queue->exclusive_lock);
|
||||
mp_dispatch_resume(queue);
|
||||
pthread_mutex_lock(&queue->lock);
|
||||
// Must be called atfer a mp_dispatch_lock().
|
||||
assert(queue->frame);
|
||||
assert(queue->frame->locked);
|
||||
assert(pthread_equal(queue->frame->locked_thread, pthread_self()));
|
||||
// "Unlock".
|
||||
queue->frame->locked = false;
|
||||
// This must have been set to false during locking (except temporarily
|
||||
// during recursive mp_dispatch_queue_process() calls).
|
||||
assert(!queue->idling);
|
||||
queue->idling = true;
|
||||
// Wakeup mp_dispatch_queue_process(), and maybe other mp_dispatch_lock()s.
|
||||
pthread_cond_broadcast(&queue->cond);
|
||||
pthread_mutex_unlock(&queue->lock);
|
||||
}
|
||||
+1
-2
@@ -15,8 +15,7 @@ void mp_dispatch_enqueue_autofree(struct mp_dispatch_queue *queue,
|
||||
void mp_dispatch_run(struct mp_dispatch_queue *queue,
|
||||
mp_dispatch_fn fn, void *fn_data);
|
||||
void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout);
|
||||
void mp_dispatch_suspend(struct mp_dispatch_queue *queue);
|
||||
void mp_dispatch_resume(struct mp_dispatch_queue *queue);
|
||||
void mp_dispatch_interrupt(struct mp_dispatch_queue *queue);
|
||||
void mp_dispatch_lock(struct mp_dispatch_queue *queue);
|
||||
void mp_dispatch_unlock(struct mp_dispatch_queue *queue);
|
||||
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
#include "common/common.h"
|
||||
|
||||
#include "node.h"
|
||||
|
||||
// Init a node with the given format. If parent is not NULL, it is set as
|
||||
// parent allocation according to m_option_type_node rules (which means
|
||||
// the mpv_node_list allocs are used for chaining the TA allocations).
|
||||
// format == MPV_FORMAT_NONE will simply initialize it with all-0.
|
||||
void node_init(struct mpv_node *dst, int format, struct mpv_node *parent)
|
||||
{
|
||||
// Other formats need to be initialized manually.
|
||||
assert(format == MPV_FORMAT_NODE_MAP || format == MPV_FORMAT_NODE_ARRAY ||
|
||||
format == MPV_FORMAT_FLAG || format == MPV_FORMAT_INT64 ||
|
||||
format == MPV_FORMAT_DOUBLE || format == MPV_FORMAT_NONE);
|
||||
|
||||
void *ta_parent = NULL;
|
||||
if (parent) {
|
||||
assert(parent->format == MPV_FORMAT_NODE_MAP ||
|
||||
parent->format == MPV_FORMAT_NODE_ARRAY);
|
||||
ta_parent = parent->u.list;
|
||||
}
|
||||
|
||||
*dst = (struct mpv_node){ .format = format };
|
||||
if (format == MPV_FORMAT_NODE_MAP || format == MPV_FORMAT_NODE_ARRAY)
|
||||
dst->u.list = talloc_zero(ta_parent, struct mpv_node_list);
|
||||
}
|
||||
|
||||
// Add an entry to a MPV_FORMAT_NODE_ARRAY.
|
||||
// m_option_type_node memory management rules apply.
|
||||
struct mpv_node *node_array_add(struct mpv_node *dst, int format)
|
||||
{
|
||||
struct mpv_node_list *list = dst->u.list;
|
||||
assert(dst->format == MPV_FORMAT_NODE_ARRAY && dst->u.list);
|
||||
MP_TARRAY_GROW(list, list->values, list->num);
|
||||
node_init(&list->values[list->num], format, dst);
|
||||
return &list->values[list->num++];
|
||||
}
|
||||
|
||||
// Add an entry to a MPV_FORMAT_NODE_MAP. Keep in mind that this does
|
||||
// not check for already existing entries under the same key.
|
||||
// m_option_type_node memory management rules apply.
|
||||
struct mpv_node *node_map_add(struct mpv_node *dst, const char *key, int format)
|
||||
{
|
||||
assert(key);
|
||||
|
||||
struct mpv_node_list *list = dst->u.list;
|
||||
assert(dst->format == MPV_FORMAT_NODE_MAP && dst->u.list);
|
||||
MP_TARRAY_GROW(list, list->values, list->num);
|
||||
MP_TARRAY_GROW(list, list->keys, list->num);
|
||||
list->keys[list->num] = talloc_strdup(list, key);
|
||||
node_init(&list->values[list->num], format, dst);
|
||||
return &list->values[list->num++];
|
||||
}
|
||||
|
||||
// Add a string entry to a MPV_FORMAT_NODE_MAP. Keep in mind that this does
|
||||
// not check for already existing entries under the same key.
|
||||
// m_option_type_node memory management rules apply.
|
||||
void node_map_add_string(struct mpv_node *dst, const char *key, const char *val)
|
||||
{
|
||||
assert(val);
|
||||
|
||||
struct mpv_node *entry = node_map_add(dst, key, MPV_FORMAT_NONE);
|
||||
entry->format = MPV_FORMAT_STRING;
|
||||
entry->u.string = talloc_strdup(dst->u.list, val);
|
||||
}
|
||||
Loaded 100 of 252 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user