Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6beed16016 | ||
|
|
abfa51e890 | ||
|
|
a7794d6fc1 | ||
|
|
4d6470fde0 | ||
|
|
b9f073d5cb | ||
|
|
d5b6419eec | ||
|
|
679de2b295 | ||
|
|
8f9cb82f4f | ||
|
|
b4fdb25611 | ||
|
|
a9cac8ade0 | ||
|
|
4422c1698f | ||
|
|
6eab622e7b | ||
|
|
6a5f3c3928 | ||
|
|
721ec88068 | ||
|
|
f3c699a904 | ||
|
|
42fc0d96fc | ||
|
|
bbbedddddc | ||
|
|
e34c02981f | ||
|
|
37ad3e79dd | ||
|
|
328d0c7fc3 | ||
|
|
26409165ed | ||
|
|
a7e35fadb1 | ||
|
|
b9a6e18720 | ||
|
|
33689ba945 | ||
|
|
a96dceaae2 | ||
|
|
002c387781 | ||
|
|
9ed399f27c | ||
|
|
0d9275b931 | ||
|
|
b2f44f131f | ||
|
|
5f81da0b88 | ||
|
|
01c452cfba | ||
|
|
ba799895e1 | ||
|
|
438ae9f742 | ||
|
|
015a7638fb | ||
|
|
1f4e087e6f | ||
|
|
0ed627d5ab | ||
|
|
1127bb41c2 | ||
|
|
f58646c85c | ||
|
|
d224c89034 | ||
|
|
63b92944d0 | ||
|
|
d939de5a71 | ||
|
|
0f834d0d5a | ||
|
|
375437f204 | ||
|
|
ceea08d7fe | ||
|
|
b76df84687 | ||
|
|
8db3a5b3e4 | ||
|
|
eccea21361 | ||
|
|
52ed30e9b8 | ||
|
|
76d2ba6283 | ||
|
|
048cd1e73d | ||
|
|
d7cfe38f06 | ||
|
|
283a300aa9 | ||
|
|
099f2138bb | ||
|
|
b7ef055917 | ||
|
|
4002258b3f | ||
|
|
3b471462eb | ||
|
|
9b41d17dc0 | ||
|
|
5523c04af5 | ||
|
|
625fb59d08 | ||
|
|
71a88d75ff | ||
|
|
938008fff1 | ||
|
|
d723970279 | ||
|
|
0e37672270 | ||
|
|
2d35c84ebf | ||
|
|
bdbbf4ae48 | ||
|
|
774e9ef261 | ||
|
|
b38766133b | ||
|
|
604daa861f | ||
|
|
322c79a6e1 | ||
|
|
6bcb8d200f | ||
|
|
d9c42ff9ff | ||
|
|
e0ad4ec8aa | ||
|
|
8a46b5b711 | ||
|
|
052b6d406d | ||
|
|
f56a7af9f6 | ||
|
|
90ba8040d9 | ||
|
|
26e779c9aa | ||
|
|
fe19db9cc0 | ||
|
|
196e2d426c | ||
|
|
99e9294fc9 | ||
|
|
2ca19ba1b5 | ||
|
|
d0216842d3 | ||
|
|
527550dfe6 | ||
|
|
275694a002 | ||
|
|
8c3a92d6cb | ||
|
|
488b5e3ac3 | ||
|
|
afb97c517d | ||
|
|
b99f78412b | ||
|
|
a4ba496539 | ||
|
|
73fc12c4e2 | ||
|
|
63402eb1dd | ||
|
|
8bcd9ea671 | ||
|
|
e37fae4f9e | ||
|
|
047c761f90 | ||
|
|
99389eb174 | ||
|
|
af930e2a2e | ||
|
|
5f4e4ede89 | ||
|
|
4a6fdb7361 | ||
|
|
f6158c324e | ||
|
|
f534c83da6 | ||
|
|
39f6824b30 | ||
|
|
477eefa63f | ||
|
|
87713640ae | ||
|
|
63bd171c0d | ||
|
|
73086a6ba3 | ||
|
|
0126999cde | ||
|
|
2a6b10c9ca | ||
|
|
3fa249b4e1 | ||
|
|
0a55f43181 | ||
|
|
5aa2d496e6 | ||
|
|
76ba91415b | ||
|
|
20a1418af6 | ||
|
|
277dc33268 | ||
|
|
cb2461044b | ||
|
|
1eb7db70d5 | ||
|
|
a8c764acb4 | ||
|
|
62a66e2ff8 | ||
|
|
e8f8d3462e | ||
|
|
7669fe39f6 | ||
|
|
6a1902a2bf | ||
|
|
192af725e6 | ||
|
|
d27bb1ce80 | ||
|
|
e795a1781f | ||
|
|
6e4abfb017 | ||
|
|
62195c174a | ||
|
|
79efe85cf5 | ||
|
|
5f1d6b6e5a | ||
|
|
d64ff3aa51 | ||
|
|
3287825bd2 | ||
|
|
9e65b5db0b | ||
|
|
d9d8c9ce13 | ||
|
|
35e9d75083 | ||
|
|
4a84cd37f3 |
No files matched your search
+1
-1
@@ -47,7 +47,7 @@ notifications:
|
||||
email: false
|
||||
irc:
|
||||
channels:
|
||||
- "irc.freenode.org#mpv-player-dev"
|
||||
- "irc.freenode.org#mpv-devel"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ API changes
|
||||
|
||||
::
|
||||
|
||||
--- mpv 0.9.0 is released ---
|
||||
1.17 - add MPV_FORMAT_BYTE_ARRAY
|
||||
--- mpv 0.9.0 will be released ---
|
||||
1.16 - add mpv_opengl_cb_report_flip()
|
||||
- introduce mpv_opengl_cb_draw() and deprecate mpv_opengl_cb_render()
|
||||
1.15 - mpv_initialize() will now load config files. This requires setting
|
||||
|
||||
@@ -3,6 +3,7 @@ QT += qml quick
|
||||
HEADERS += main.h
|
||||
SOURCES += main.cpp
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG += mpv
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ QT += qml quick
|
||||
HEADERS += main.h
|
||||
SOURCES += main.cpp
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG += mpv
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
TARGET = qtexample
|
||||
TEMPLATE = app
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG += mpv
|
||||
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ How to contribute
|
||||
General
|
||||
-------
|
||||
|
||||
The main contact for mpv development is IRC, specifically #mpv-player
|
||||
and #mpv-player-dev on Freenode.
|
||||
The main contact for mpv development is IRC, specifically #mpv
|
||||
and #mpv-devel on Freenode.
|
||||
|
||||
Sending patches
|
||||
---------------
|
||||
|
||||
+13
-10
@@ -1167,17 +1167,18 @@ Property list
|
||||
``field-dominance`` (RW)
|
||||
See ``--field-dominance``
|
||||
|
||||
``colormatrix`` (RW)
|
||||
See ``--colormatrix``.
|
||||
``colormatrix`` (R)
|
||||
Redirects to ``video-params/colormatrix``. This parameter (as well as
|
||||
similar ones) can be overridden with the ``format`` video filter.
|
||||
|
||||
``colormatrix-input-range`` (RW)
|
||||
See ``--colormatrix-input-range``.
|
||||
``colormatrix-input-range`` (R)
|
||||
See ``colormatrix``.
|
||||
|
||||
``colormatrix-output-range`` (RW)
|
||||
See ``--colormatrix-output-range``.
|
||||
``colormatrix-output-range`` (R)
|
||||
See ``colormatrix``.
|
||||
|
||||
``colormatrix-primaries`` (RW)
|
||||
See ``--colormatrix-primaries``.
|
||||
``colormatrix-primaries`` (R)
|
||||
See ``colormatrix``.
|
||||
|
||||
``ontop`` (RW)
|
||||
See ``--ontop``.
|
||||
@@ -1778,7 +1779,6 @@ Property list
|
||||
For many complex types, this isn't very accurate.
|
||||
|
||||
``option-info/<name>/set-from-commandline``
|
||||
|
||||
Return ``yes`` if the option was set from the mpv command line,
|
||||
``no`` otherwise. What this is set to if the option is e.g. changed
|
||||
at runtime is left undefined (meaning it could change in the future).
|
||||
@@ -1805,7 +1805,10 @@ Property Expansion
|
||||
------------------
|
||||
|
||||
All string arguments to input commands as well as certain options (like
|
||||
``--term-playing-msg``) are subject to property expansion.
|
||||
``--term-playing-msg``) are subject to property expansion. Note that property
|
||||
expansion does not work in places where e.g. numeric parameters are expected.
|
||||
(For example, the ``add`` command does not do property expansion. The ``set``
|
||||
command is an exception and not a general rule.)
|
||||
|
||||
.. admonition:: Example for input.conf
|
||||
|
||||
|
||||
@@ -73,6 +73,10 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
Run the given command. This is similar to the commands used in input.conf.
|
||||
See `List of Input Commands`_.
|
||||
|
||||
By default, this will show something on the OSD (depending on the command),
|
||||
as if it was used in ``input.conf``. See `Input Command Prefixes`_ how
|
||||
to influence OSD usage per command.
|
||||
|
||||
Returns ``true`` on success, or ``nil, error`` on error.
|
||||
|
||||
``mp.commandv(arg1, arg2, ...)``
|
||||
@@ -94,6 +98,9 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
the ``expand-properties`` prefix, or the ``mp.get_property`` family of
|
||||
functions.
|
||||
|
||||
Unlike ``mp.command``, this will not use OSD by default either (except
|
||||
for some OSd-specific commands).
|
||||
|
||||
``mp.command_native(table [,def])``
|
||||
Similar to ``mp.commandv``, but pass the argument list as table. This has
|
||||
the advantage that in at least some cases, arguments can be passed as
|
||||
|
||||
+4
-1
@@ -378,7 +378,8 @@ Location and Syntax
|
||||
You can put all of the options in configuration files which will be read every
|
||||
time mpv is run. The system-wide configuration file 'mpv.conf' is in your
|
||||
configuration directory (e.g. ``/etc/mpv`` or ``/usr/local/etc/mpv``), the
|
||||
user-specific one is ``~/.config/mpv/mpv.conf``.
|
||||
user-specific one is ``~/.config/mpv/mpv.conf``. For details and platform
|
||||
specifics see the `FILES`_ section.
|
||||
User-specific options override system-wide options and options given on the
|
||||
command line override either. The syntax of the configuration files is
|
||||
``option=<value>``; everything after a *#* is considered a comment. Options
|
||||
@@ -796,6 +797,8 @@ input command can take an exit code: in this case, that exit code is returned.
|
||||
FILES
|
||||
=====
|
||||
|
||||
For Windows-specifics, see `FILES ON WINDOWS`_ section.
|
||||
|
||||
``/usr/local/etc/mpv/mpv.conf``
|
||||
mpv system-wide settings (depends on ``--prefix`` passed to configure - mpv
|
||||
in default configuration will use ``/usr/local/etc/mpv/`` as config
|
||||
|
||||
@@ -459,12 +459,12 @@ Program Behavior
|
||||
``--ytdl-raw-options=<key>=<value>[,<key>=<value>[,...]]``
|
||||
Pass arbitraty options to youtube-dl. Parameter and argument should be
|
||||
passed as a key-value pair. Options without argument must include ``=``.
|
||||
|
||||
|
||||
There is no sanity checking so it's possible to break things (i.e.
|
||||
passing invalid parameters to youtube-dl).
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
|
||||
``--ytdl-raw-options=username=user,password=pass``
|
||||
``--ytdl-raw-options=force-ipv6=``
|
||||
|
||||
@@ -498,6 +498,10 @@ Video
|
||||
Do not play video. With some demuxers this may not work. In those cases
|
||||
you can try ``--vo=null`` instead.
|
||||
|
||||
mpv will try to download the audio only if media is streamed with
|
||||
youtube-dl, because it saves bandwidth. This is done by setting the ytdl_format
|
||||
to "bestaudio/best" in the ytdl_hook.lua script.
|
||||
|
||||
``--untimed``
|
||||
Do not sleep when outputting video frames. Useful for benchmarks when used
|
||||
with ``--no-audio.``
|
||||
@@ -2140,7 +2144,7 @@ Demuxer
|
||||
Play a raw YUV sample::
|
||||
|
||||
mpv sample-720x576.yuv --demuxer=rawvideo \
|
||||
--demuxer-rawvideo=w=720:h=576
|
||||
--demuxer-rawvideo-w=720 --demuxer-rawvideo-h=576
|
||||
|
||||
``--demuxer-rawvideo-format=<value>``
|
||||
Color space (fourcc) in hex or string for ``--demuxer=rawvideo``
|
||||
|
||||
+11
-10
@@ -169,34 +169,35 @@ Configurable Options
|
||||
|
||||
``showwindowed``
|
||||
| Default: yes
|
||||
| Show OSC when windowed?
|
||||
| Enable the OSC when windowed
|
||||
|
||||
``showfullscreen``
|
||||
| Default: yes
|
||||
| Show OSC when fullscreen?
|
||||
| Enable the OSC when fullscreen
|
||||
|
||||
``scalewindowed``
|
||||
| Default: 1
|
||||
| Scaling of the controller when windowed
|
||||
| Default: 1.0
|
||||
| Scale factor of the OSC when windowed
|
||||
|
||||
``scalefullscreen``
|
||||
| Default: 1
|
||||
| Scaling of the controller when fullscreen
|
||||
| Default: 1.0
|
||||
| Scale factor of the OSC when fullscreen
|
||||
|
||||
``scaleforcedwindow``
|
||||
| Default: 2
|
||||
| Scaling of the controller when rendered on a forced (dummy) window
|
||||
| Default: 2.0
|
||||
| Scale factor of the OSC when rendered on a forced (dummy) window
|
||||
|
||||
``vidscale``
|
||||
| Default: yes
|
||||
| Scale the controller with the video?
|
||||
| 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
|
||||
| Default: 0.0
|
||||
| Horizontal alignment, -1 (left) to 1 (right)
|
||||
|
||||
``boxalpha``
|
||||
|
||||
+1
-1
@@ -198,7 +198,7 @@ Available filters are:
|
||||
``<aspect>``
|
||||
Force an aspect ratio.
|
||||
|
||||
``format[=fmt[:outfmt]]``
|
||||
``format=fmt=<value>:colormatrix=<value>:...``
|
||||
Restricts the color space for the next filter without doing any conversion.
|
||||
Use together with the scale filter for a real conversion.
|
||||
|
||||
|
||||
+2
-2
@@ -586,7 +586,7 @@ Available video output drivers are:
|
||||
always used, and typically only when using extended scalers.)
|
||||
``fmt`` can be one of: rgb, rgba, rgb8, rgb10, rgb10_a2, rgb16, rgb16f,
|
||||
rgb32f, rgba12, rgba16, rgba16f, rgba32f.
|
||||
Default: rgba.
|
||||
Default: rgba16.
|
||||
|
||||
``gamma=<0.1..2.0>``
|
||||
Set a gamma value (default: 1.0). If gamma is adjusted in other ways
|
||||
@@ -744,7 +744,7 @@ Available video output drivers are:
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
--vo=opengl:scale=spline36:cscale=spline36:dscale=mitchell:dither-depth=auto:fbo-format=rgba16:fancy-downscaling:sigmoid-upscaling
|
||||
--vo=opengl:scale=spline36:cscale=spline36:dscale=mitchell:dither-depth=auto:fancy-downscaling:sigmoid-upscaling
|
||||
|
||||
Note that some cheaper LCDs do dithering that gravely interferes with
|
||||
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
|
||||
|
||||
@@ -49,6 +49,7 @@ Input
|
||||
* Classic LIRC support was removed. Install remotes as input devices instead.
|
||||
This way they will send X11 key events to the mpv window, which can be bound
|
||||
using the normal ``input.conf``.
|
||||
Also see: http://github.com/mpv-player/mpv/wiki/IR-remotes
|
||||
* Joystick support was removed. It was considered useless and was the cause
|
||||
of some problems (e.g. a laptop's accelerator being recognized as joystick).
|
||||
|
||||
|
||||
@@ -175,8 +175,8 @@ Most activity happens on the IRC channel and the github issue tracker. The
|
||||
mailing lists are mostly unused.
|
||||
|
||||
- **Github issue tracker**: [issue tracker][issue-tracker] (report bugs here)
|
||||
- **User IRC Channel**: `#mpv-player` on `irc.freenode.net`
|
||||
- **Developer IRC Channel**: `#mpv-player-dev` on `irc.freenode.net`
|
||||
- **User IRC Channel**: `#mpv` on `irc.freenode.net`
|
||||
- **Developer IRC Channel**: `#mpv-devel` on `irc.freenode.net`
|
||||
- **Users Mailing List**: `mpv-users@googlegroups.com` ([Archive / Subscribe][mpv-users]).
|
||||
- **Devel Mailing List**: `mpv-devel@googlegroups.com` ([Archive / Subscribe][mpv-devel])
|
||||
|
||||
|
||||
+694
@@ -0,0 +1,694 @@
|
||||
Release 0.9.2
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- The Lua check now also checks for `lua52.pc`, as used by Arch Linux testing.
|
||||
- (X11) `vo_opengl`'s `icc-profile-auto` now queries the current ICC profile
|
||||
relative to the center of the window.
|
||||
- `ao_coreaudio`, `ao_alsa` now support adding dummy padding channels for
|
||||
better compatibility with hardware decoders that only support specific
|
||||
channel counts (e.g. 5.1 now should work on a decoder that only accepts 7.1).
|
||||
- Channel fallback (in case the audio device doesn't natively support a given
|
||||
channel layout) has been improved.
|
||||
- `vf_vapoursynth` now rejects unaligned video instead of outputting corrupted
|
||||
video.
|
||||
- mpv now tries to autoload `.sup` subtitles as well.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- `vo_opengl`'s default for `fbo-format` is now `rgba16`, to avoid rounding
|
||||
errors when using non-default `cscale` (issue #1918).
|
||||
- Improved framedrop behavior when playing video that has higher framerate
|
||||
than the display (issue #1897).
|
||||
- Trying to play a directory will no longer spam `Connection lost!` to the
|
||||
console log.
|
||||
- (Linux) Several `vo_rpi` bugfixes.
|
||||
- (Linux) Several `vo_drm` bugfixes. Pan&Scan is now supported.
|
||||
- (X11) Fix fullscreen behavior on certain window managers (issues #1937,
|
||||
#1920).
|
||||
- (OSX) The OSD no longer always shows up on startup.
|
||||
- (OSX) Several `ao_coreaudio` and `ao_coreaudio_exclusive` bugfixes.
|
||||
- (OSX) Fixed potential crash on exit when using Cocoa.
|
||||
- (ClientAPI) `vo_opengl_cb` now actually applies options changed at runtime.
|
||||
- (OSX, ClientAPI) Cocoa now works when both the `cplayer` (`mpv`) and `libmpv`
|
||||
are built at the same time; however, `libmpv` now always creates an
|
||||
application singleton. Cocoa has to be disabled completely to prevent
|
||||
`libmpv` from creating the singleton.
|
||||
|
||||
This listing is not complete. A complete changelog can be seen by running
|
||||
`git log v0.9.1..` in the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/v0.9.1...release/0.9.
|
||||
|
||||
Release 0.9.1
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- mpv's IRC channel moved from #mpv-player to #mpv on chat.freenode.net.
|
||||
- Documentation updates.
|
||||
- The default value for the `--ytdl-format` option is now `best`, in order to
|
||||
work around `youtube-dl`'s 2015.04.26 release enabling DASH by default, as
|
||||
FFmpeg / Libav do not yet properly support DASH.
|
||||
- When seeking, the current timestamp will show the predicted seek timestamp
|
||||
instead, until the final timestamp is resolved. Improves UI responsiveness
|
||||
on slow streams and/or large seeks.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Corrected the release marker on DOCS/client-api-changes. The release manager
|
||||
forgot to fix it before release...
|
||||
- Fix `vo_vdpau` rendering garbage lines on H.264 video with non-mod16 size
|
||||
(issue #1863).
|
||||
- Fix a crash on exit if the "sub_reload" command had run successfully.
|
||||
- Fixed seeking with the mouse when `osc-seekbarstyle=bar` is set (issue
|
||||
#1876).
|
||||
- (IPC) Fixed encoding of UTF-8 data in JSON (issue #1874).
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
This listing is not complete. A complete changelog can be seen by running
|
||||
`git log v0.9.0..v0.9.1` in the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/v0.9.0...v0.9.1.
|
||||
|
||||
Release 0.9.0
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
Changes that may break users' config files have been annotated with a `(!)`.
|
||||
|
||||
- Note: mpv is not compatible with Lua 5.3. Lua 5.1 or 5.2 is required.
|
||||
- The minimum required libass version is now 0.12.1 or newer.
|
||||
- The minimum required FFmpeg version is now 2.4.0 (equiv. Libav 11) or newer.
|
||||
- The internal libmpg123 support was removed. This was already not used by
|
||||
default in the previous release.
|
||||
- `(!)` The LIRC support was removed. Configure LIRC remotes as input devices
|
||||
instead.
|
||||
- `(!)` The Linux Joystick support was removed.
|
||||
- `(!)` `vf_screenshot` was removed, as they are now handled at a VO level and
|
||||
is compatible with all VOs.
|
||||
- `(!)` `--ass-use-margins` has been renamed to `--sub-use-margins` and applies
|
||||
only to plain-text (non-ASS) subtitles (enabled by default). The new
|
||||
`--ass-force-margins` option applies only to ASS subtitles (disabled by
|
||||
default). To get the old behaviour back, enable both at the same time.
|
||||
- `(!)` The `--sub-scale-with-window` option now only applies to plain text
|
||||
(non-ASS) subtitles (enabled by default). The new `--ass-scale-with-window`
|
||||
option does the same but only with ASS subtitles (disabled by default).
|
||||
- `(!)` The range for the `param1` for the `gaussian` `vo_opengl` scaler has
|
||||
been redefined. Instead of being an arbitrary 1-100 range, have a default
|
||||
value of 1.0, and anything higher is blurrier.
|
||||
- `(!)` The `seek`, `playlist_next, `playlist_prev`, `loadfile` and `loadlist`
|
||||
parameters no longer accept numerical parameters where symbolic parameter
|
||||
names exist.
|
||||
- `(!)` `vo_opengl` changes:
|
||||
- `(!)` The `smoothmotion` suboption has been renamed to `interpolation`.
|
||||
The old name is still supported for now.
|
||||
- `(!)` The `bilinear_slow` scaler has been renamed to `triangle`.
|
||||
- `(!)` `scale-down` has been renamed to `dscale` and now has its own set of
|
||||
config options (e.g. `dscale-radius`).
|
||||
- `(!)` Scaler radius no longer defaults to `3` but to a preferred value
|
||||
that may be different for each filter.
|
||||
- The `scale-radius` option may now go down as low as `0.5`, which is the
|
||||
value used by the `nearest` filter.
|
||||
- `spline36` is the new `cscale` default for `opengl-hq`. This might break
|
||||
setups that use `fbo-format=rgb8`. To work around it, leave `fbo-format`
|
||||
as its default, or set to something higher than 8, or set `cscale=bilinear`,
|
||||
the previous default.
|
||||
- `(!)` `vf_format` no longer converts video to YUYV if there is no parameter.
|
||||
Video is now passed unchanged unless a format is specifically requested.
|
||||
- `(!)` The `--colormatrix`, `--colormatrix-input-range`,
|
||||
`--colormatrix-output-range` and `--colormatrix-primaries` options have been
|
||||
converted into `vf_format` suboptions. See commit 27715b7 and the manual for
|
||||
details.
|
||||
- `vf_mirror`'s implementation was replaced with calling into `libavfilter`'s
|
||||
`vf_hflip` filter, thus depending on `libavfilter` to function.
|
||||
- The `device` subption to `ao_wasapi` has been deprecated in favor of
|
||||
`--audio-device`.
|
||||
- `--video-rotate` now allows 360 as an argument instead of stopping at 359.
|
||||
- Several improvements to `af_scaletempo`.
|
||||
- Options that have multiple options and also include a "yes" option now
|
||||
default to that if specified with no arguments.
|
||||
- The default value of `--cache-default` is now 150000 (153.6 MB, ~146 MiB).
|
||||
- JPEG screenshots now use the same subsampling as the source video. The images
|
||||
are still RGB regardless of source format though.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- mpv no longer saves position on files that can't be resumed (issue #1701).
|
||||
- (X11) Fix the player thinking the mouse has left the window in some WMs /
|
||||
embeddings (issue #1672).
|
||||
- mpv no longer freezes on wayland when the compositor stops asking it to draw
|
||||
itself (e.g. when minimized) (issue #249).
|
||||
- `.ac3` files are no longer rejected by `--audio-file-auto` (issue #1759).
|
||||
- `ao_wasapi` now automatically enables `exclusive` when passthrough is
|
||||
attempted (issue #1742).
|
||||
- Attempt to fix flickering on Intel VAAPI drivers with `--hwdec=vaapi` and
|
||||
`--vo=opengl` (issue #1765).
|
||||
- `youtube-dl` will no longer download video streams when video playback
|
||||
is disabled with `--no-video`.
|
||||
- (Windows) mpv now prevents system sleep when playing a video-only file.
|
||||
Previously, only files with an opened audio track would prevent sleep.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- `vo_opengl` features:
|
||||
- Added `ewa_ginseng`, `ewa_hanning`, `robidoux`, `robidouxsharp`,
|
||||
`oversample` and `haasnsoft` scalers.
|
||||
- There are now `ewa_lanczossoft` and `ewa_lanczossharp` aliases to
|
||||
`ewa_lanczos` that are tuned to be blurrier and sharper, respectively.
|
||||
- Added `gamma-auto` option that uses ambient light sensors to automatically
|
||||
adjust the video gamma. See commit c028d78 for details.
|
||||
- Added `blend-subtitles` option to draw subtitles directly into the video
|
||||
instead of rendered afterwards. Potentially necessary for correct rendering
|
||||
with files that use ASS subtitles for typesetting in combination with an
|
||||
`icc-profile`. There is a default option for drawing after upscaling,
|
||||
and a `video` option for drawing before upscaling. See details and warning
|
||||
on the manual.
|
||||
- There is now a `tscale` option, used to choose the temporal scaler used
|
||||
in the `interpolation` mode (previously `smoothmotion`).
|
||||
- There is a new `scale-blur` parameter to adjust the amount of blur that
|
||||
most of the filters produce. Deviating from the default may introduce
|
||||
artifacts in EWA filters.
|
||||
- (Windows) There is now a `dwmflush` option that might help improve
|
||||
rendering of high-fps video. Disabled by default. See manual for options.
|
||||
- New Linux-only `vo_drm` video output driver. Uses the direct rendering /
|
||||
kernel modesetting drivers to draw directly to the framebuffer, but with
|
||||
no hardware acceleration. See manual for details.
|
||||
- New `pseudo-gui` builtin profile, automatically used when launched from
|
||||
`mpv.desktop` by opening `mpv.exe` on windows (`mpv.com` still works as
|
||||
usual), or by opening the `mpv.app` bundle. The `pseudo-gui` tries to make
|
||||
the player window behave closer to what a desktop player would do, by not
|
||||
immediately closing and allowing the user to drag&drop files for playback.
|
||||
See manual for details.
|
||||
- mpv can now play directories by automatically playing their contents instead.
|
||||
Works everywhere but on Windows, due to issues with Windows' C runtime.
|
||||
- Add support to pitch correct stretched audio with librubberband.
|
||||
- Add support for the Raspberry Pi 2's hardware decoder when FFmpeg (or Libav)
|
||||
is built with `--enable-mmal`. See commit 8fff125 for details.
|
||||
- The `--cache` option now accepts a "yes" option, that always enables a
|
||||
`--cache-default`-sized cache on all cases a cache can be used, unless
|
||||
`--cache-default` disables caching.
|
||||
- `ao_pulse`, `ao_coreaudio` and `ao_wasapi` now support device hotplugging.
|
||||
- New `--osd-align-x` and `--osd-align-y` options can be used to align the OSD
|
||||
independently from subtitles.
|
||||
- New `--osd-bold` and `--sub-text-bold` options can be enabled to bold all
|
||||
OSD or plain-text subtitle text, respectively.
|
||||
- Added a default keybind to the `u` key that enables/disables ASS style
|
||||
overriding. When enabled, is equivalent to `--ass-style-override=force`.
|
||||
- There is now a `MOUSE_ENTER` keybind, that is called when the mouse cursor
|
||||
enters the VO from outside.
|
||||
- The new `--ytdl-params` option can be set to arguments that are always given
|
||||
to `youtube-dl` invocations. There is no sanity checking, so invalid options
|
||||
can prevent `mpv` from working with http URLs.
|
||||
- There's a new `--demuxer-mkv-fix-timestamps` option, enabled by default, that
|
||||
tries to guess more accurate video timestamps by using FPS information, if
|
||||
available. See manual for details.
|
||||
- The new `--window-scale` option can be used to scale the video window by the
|
||||
specified multiplier, before other options such as `--autofit` are applied.
|
||||
- `vo_direct3d_shaders` now supports NV12 colorspace without using stretchrect,
|
||||
but it seems no drivers actually support that.
|
||||
- (Client API) Added a `rescan_external_files` command, as requested on issue
|
||||
#1586.
|
||||
- (Client API) If enabled, initializing the Client API will now also load the
|
||||
user's `mpv.conf`.
|
||||
- (Client API) There's a new `mpv_opengl_cb_report_flip()` call for API users
|
||||
to call to inform mpv of when exactly a frame was displayed.
|
||||
- (Client API) The `mpv_opengl_cb_render()` function was deprecated in favor
|
||||
of the new, simpler `mpv_opengl_cb_draw()` function.
|
||||
- (Client API) There is a new `screenshot_raw` command. See `input.rst` for
|
||||
details.
|
||||
- The `--input-file` argument may now also be a file descriptor in the form
|
||||
"fd://N", where N is the FD number.
|
||||
|
||||
This listing is not complete. A complete changelog can be seen by running
|
||||
`git log v0.8.0..v0.9.0` in the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/v0.8.0...v0.9.0 (only 250 commits).
|
||||
|
||||
Release 0.8.3
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Documentation fixes and updates.
|
||||
- (Client API) Clarify `mpv_opengl_cb_render`'s viewport parameter behavior.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- (X11) Fix crash on `vo_xv:no-colorkey` with the Overlay adapter. (bug #1629)
|
||||
- (X11) `--stop-screensaver` is now implemented by calling into
|
||||
`xdg-screensaver`, fixing some compatibility issues that prevented it from
|
||||
actually stopping screensavers.
|
||||
- Make the video equalizer work correctly on some VAAPI drivers. (bug #1647)
|
||||
- Prevent OSD from disappearing when clicking on mozplugger. (bug #1672)
|
||||
- The new DVB-S2 support code, using S2API, now builds on FreeBSD.
|
||||
- Fix decoding of seekable matroska from unseekable network stream. (bug #1656)
|
||||
- (OSX) Fix crashing when closing a VO's window (usually at exit). (bug #1657)
|
||||
- (OSX) Unhide the mouse cursor when over the Dock or Launchpad. (bug #513)
|
||||
- (OSX) Fix mouse cursor autohiding when the player is fullscreen.
|
||||
- `vf_vavpp` (VAAPI postprocessing) now deinterlaces video correctly.
|
||||
- `smb://` streams now use the stream cache (used to be inefficient).
|
||||
- (Windows) Do not hide the mouse cursor when it is hovering the window menu.
|
||||
- Fix 8-channel output on `ao_jack`. (bug #1688)
|
||||
- Fix `--mf-fps` parameter on JPEG files. (bug #1689)
|
||||
- Fix anamorphic scaling being ignored if it was very minor.
|
||||
|
||||
This listing is not complete. A complete changelog can be seen by running
|
||||
`git log v0.8.2..v0.8.3` in the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/v0.8.2...v0.8.3.
|
||||
|
||||
Release 0.8.2
|
||||
=============
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Fix OSD placement accidentally messed up in 0.8.1 (commit 22863d6).
|
||||
|
||||
Release 0.8.1
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- DOCS/client-api-changes has been correctly updated for the v0.8.0 release
|
||||
series.
|
||||
- Quality and major perfomance improvements to smoothmotion.
|
||||
- Silence the "[ytdl_hook] WARNING: video doesn't have subtitles" warning.
|
||||
- Other documentation updates.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Attempt to fix OpenGL shader compilation on Intel windows drivers. (bug
|
||||
#1536)
|
||||
- Attempt to improve the OpenGL shader's compatibility with GLES2.
|
||||
- (Windows) Fix noise when seeking while using wasapi:exclusive. (bug #1529)
|
||||
- Fix the waf-based build system when used with newer versions of waf with
|
||||
python3.
|
||||
- Documentation fixes. (includes bug #1608)
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- mpv now tries to load '.vtt' subtitles.
|
||||
|
||||
This listing is not complete. A complete changelog can be seen by running
|
||||
`git log v0.8.0..v0.8.1` in the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/v0.8.0...v0.8.1.
|
||||
|
||||
Release 0.8.0
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
Changes that may break users' config files have been annotated with a `(!)`.
|
||||
|
||||
- `(!)` vo_opengl_old has been removed. OpenGL rendering now requires hardware
|
||||
that can do at least OpenGL 2.1, the oldest version supported by vo_opengl.
|
||||
- `(!)` vf_pp has been removed. A version of it can still be accessed through
|
||||
lavfilter (e.g. --vf=lavfi=[pp...]), but it receives no QP information.
|
||||
Also, vf_dlopen no longer passes QP information to its loaded plugin either
|
||||
(it's always 0).
|
||||
- `(!)` vf_softpulldown, vf_swapuv, vf_phase, vf_divtc and vf_ilpack have all
|
||||
been removed. Ports or alternative versions of these filters are available
|
||||
through libavfilter (vf_lavfi).
|
||||
- vf_pullup and vf_noise now are simply wrappers to their libavfilter
|
||||
equivalents. Both are only currently available on FFmpeg (not Libav).
|
||||
- `(!)` ao_portaudio has been removed. There is good support for the native
|
||||
audio drivers of all major desktop platforms, as well as ao_sdl and
|
||||
ao_openal as fallbacks.
|
||||
- `(!)` vo_opengl's `lscale` suboption has been renamed to `scale`; `lradius`
|
||||
is now `scale-radius`, `lparam1` and `lparam2` are now respectively
|
||||
`scale-param1` and `scale-param2`; `lscale-down` is now `scale-down`.
|
||||
The `cscale` suboption remains as is.
|
||||
- `(!)` Several of the `vo_opengl` radius-preset aliases supported by `scale`
|
||||
have been removed; use `scale-radius` to set if needed. For example, use
|
||||
`--vo=opengl:scale=lanczos:scale-radius=2` instead of `scale=lanczos2`.
|
||||
The default radius is recommended for most filters.
|
||||
- `(!)` vo_opengl no longer supports the `stereo` suboption. The anaglyph
|
||||
effect can be reproduced with the stereo3d filter. The quadbuffer support,
|
||||
which requires expensive specialized hardware to begin with, is removed.
|
||||
- `(!)` The `approx-gamma` suboption to vo_opengl has been removed. The
|
||||
BT.1886 specification says that it's actually how it's supposed to be done
|
||||
so it is now the default when `srgb` or `icc-profile` are enabled. This does
|
||||
not include BT.1886's gamma drop.
|
||||
- `(!)` The `no-scale-sep` and `indirect` suboptions to vo_opengl have been
|
||||
removed. These are now autodetected and enabled whenever they would have
|
||||
benefit.
|
||||
- `(!)` The `--lua` and `--lua-opts` parameters / options are now called
|
||||
`--script` and `--script-opts`, respectively. The `lua` subdir of the mpv
|
||||
config dir is also now expected to be called `scripts` instead.
|
||||
- `(!)` The `--fixed-vo` option has been removed. It has been the default for
|
||||
a long time anyway, and disabling fixed-vo is not useful.
|
||||
- vo_opengl_hq has been updated to take into account new features.
|
||||
`fancy-downscaling` and `sigmoid-upscaling` are enabled,
|
||||
"mitchell" is now the default for `scale-down`.
|
||||
- The `sub-visibility` OSD message is now clearer about whether subtitles
|
||||
are hidden or just not available / selected.
|
||||
- The device IDs given to `--audio-device` for use with ao_coreaudio now use
|
||||
UIDs, so they don't change when devices are added/removed or after reboots.
|
||||
- `--msg-level` now also accepts ',' as separator.
|
||||
- (Client API) The client API now refuses to initialize if the LC_NUMERIC
|
||||
locale is not "C".
|
||||
- (Client API) The native type of the `msg-level` parameter is now a key-value
|
||||
list. Setting or reading it as a string still works.
|
||||
- (Slave API) The `get_property` command is now deprecated.
|
||||
- Documentation updates.
|
||||
- PDF documentation is now disabled by default due to rst2pdf being fickle,
|
||||
causing weird build errors.
|
||||
- Print desync messages with negative A/V sync as well. While rare, it could
|
||||
happen with some uses of `--autosync`.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- (Windows) Avoid resizing the video window when player is minimized, might
|
||||
address bug #1547.
|
||||
- (NetBSD) Fix build with v4l2.
|
||||
- (Linux) Attempt to address conflicts with the pulseaudio mixer. (bug #1578)
|
||||
- Multiple cdda:// fixes. (bugs #1555, #1560)
|
||||
- VP9 timestamps no longer cause "missing PTS" warnings with new enough FFmpeg
|
||||
builds.
|
||||
- Fix a crash when using H.264 hardware decoding on new enough libavcodecs.
|
||||
(bug #1587)
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- vo_opengl now supports frame blending to eliminate stuttering when the video
|
||||
framerate does not nicely match the display framerate through the
|
||||
`smoothmotion` suboption. This is not meant to artificially increase the
|
||||
video's FPS, so there is no "soap opera effect" or difficulties with some
|
||||
video types (e.g. anime).
|
||||
- vo_opengl now supports sigmoidal upscaling (e.g. for fullscreen), which
|
||||
reduces ringing induced by upscaling, enabled through the
|
||||
`sigmoid-upscaling` suboption.
|
||||
- vo_opengl now supports ewa_lanczos (Jinc) scaling, which provides higher
|
||||
quality with less aliasing. It supports an experimental `scale-antiringing`
|
||||
parameter, which tries to further reduce video ringing.
|
||||
- vo_opengl now has a `linear-scaling` suboption, that makes the scalers work
|
||||
in linear light. Implied by the `srgb`, `icc-profile` or the new
|
||||
`sigmoid-upscaling` suboption.
|
||||
- vo_opengl has improved downscaling for higher downscale ratios when
|
||||
`fancy-downscaling` is enabled.
|
||||
- Add `--keep-open=always` to make `--keep-open` apply to all files instead of
|
||||
only the last.
|
||||
- mpv now filters the tags that are printed on the console to try to hide
|
||||
useless metadata. The filter works as a whitelist, and can be configured
|
||||
with the `--display-tags` option.
|
||||
- Add a `--sub-scale-by-window` option that, when set to 'no', prevents
|
||||
subtitles from changing their pixel size when the window changes sizes.
|
||||
- vo_opengl now technically has GLES 2 and 3 support (but not GLES 1). GLES 3
|
||||
support is only tested on Mesa's software emulation. GLES 2 has been tested
|
||||
on nvidia drivers. Both GLES modes are feature deficient and inefficient due
|
||||
to GLES limitations, so they should not be preferred over the standard
|
||||
OpenGL mode.
|
||||
- (X11) vo_opengl now supports the `icc-profile-auto` option on X11 hosts.
|
||||
- The 'A' key now by default cycles through a list of preset aspect ratios.
|
||||
Meant to be used to work around broken files.
|
||||
- There is now a "force" mode for `--loop`. Works like "inf", but tries to
|
||||
open files/streams even if there was an error on the previous attempt.
|
||||
- There is now a `--log-file` option to write mpv's log messages to a specifed
|
||||
file.
|
||||
- There is now a `--audio-file-auto` option to automatically load an external
|
||||
audio file with certain constraints. Will only load external audio if the
|
||||
playing file has a video stream. Set to "exact" by default. (bug #967)
|
||||
- There is now a `--network-timeout` option to specify a timeout in seconds
|
||||
for network access. If 0 (default), uses the libavformat default. If a
|
||||
protocol that doesn't support timeouts is used, this option is ignored.
|
||||
- (X11) The XF86 special keys are now all mappable in `input.conf`. All keys
|
||||
documented in `XF86keysym.h` are available, but only as their numeric codes.
|
||||
- (Linux) The DVB implementation now supports DVB-S2 through S2API.
|
||||
- (Linux) The VDR format for `channels.conf` is now supported and preferred.
|
||||
See http://linuxtv.org/vdrwiki/index.php/Syntax_of_channels.conf for the
|
||||
syntax. Tuning to DVB-S2 channels requires the VDR-format `channels.conf`.
|
||||
- (Linux) There is now a `--dvbin-full-transponder` option for handling
|
||||
special broadcast cases where PIDs switch, or just to work around incomplete
|
||||
PID data.
|
||||
- (Client API) There is now an opengl_cb VO, which receives the OpenGL context
|
||||
from the libmpv client. This allows a client to render libmpv video directly
|
||||
to a provided OpenGL context without having to use --wid embedding.
|
||||
- (Client API) The aforementioned --wid parameter can now be set at any time,
|
||||
but has no effect if a file is already loaded.
|
||||
- (Client API) The list of DVD titles can now be queried.
|
||||
- (Client API) There is now a `filtered-metadata` property, containing only
|
||||
the tags allowed by `--display-tags`.
|
||||
- (Client API) There is now support for XEmbed `--wid` targets, such as a
|
||||
`GtkSocket`'s `gtk_socket_get_id()`.
|
||||
- (Client API) There is now a `file-format` property with a symbolic name
|
||||
for the file format. In some cases this might be a comma separated list
|
||||
of various different extensions due to libavformat idiosyncrasies.
|
||||
- (Client API) There is now a `mouse` command for generating mouse events
|
||||
over the video window.
|
||||
- (Client API) There is now a `partially-seekable` property that indicates
|
||||
whether a file is only considered `seekable` because of the stream cache,
|
||||
but would not be seekable otherwise (e.g. realtime stream, or HTTP with
|
||||
no resume support). Small relative seeks may be fine, but larger seeks
|
||||
will likely fail.
|
||||
- (Client API) There is now an MPV_EVENT_QUEUE_OVERFLOW event that is posted
|
||||
whenever events have to be dropped because the queue is full. No further
|
||||
events are posted until the MPV_EVENT_QUEUE_OVERFLOW is consumed to avoid
|
||||
duplicated posting.
|
||||
- (Client API) There is now a helper `mpv_wait_async_requests()` function
|
||||
that blocks until all known async requests have completed. (bug #1542)
|
||||
- (Client API) There is now a `detected-hwdec` property that returns the
|
||||
detected hardware decoder when one is successfully opened. This probably
|
||||
only returns a valid value after playback has started.
|
||||
- (Client API) There are now `audio_add`, `audio_remove` and `audio_reload`
|
||||
counterparts to the equivalent `sub_` commands for handling external audio
|
||||
files.
|
||||
|
||||
This listing is not complete. A complete changelog can be seen by running
|
||||
`git log v0.7.0..v0.8.0` in the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/v0.7.0...v0.8.0 (only 250 commits).
|
||||
|
||||
Release 0.7.3
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Several documentation updates and typo fixes.
|
||||
- Don't show "0%" position when the stream is infinite / has unknown length.
|
||||
- Fixes the config file loading order so that lower priority `mpv.conf` files
|
||||
don't override higher priority `config` files.
|
||||
- (OSX) Remove coreaudio_exclusive from the "auto" codec list.
|
||||
- Failing to create a GL3 context is now a warning instead of error, to reduce
|
||||
user confusion.
|
||||
- The subtitle decoder now gets reset when cycling subtitles. This makes the
|
||||
currently shown subtitle event disappear even if cycling back to the current
|
||||
subtitle track.
|
||||
- `--shuffle` and `--merge-files` now affects the contents of playlist files,
|
||||
instead of just the list of files given as arguments to mpv.
|
||||
- `./waf install` avoids installing a few additional data/config files if only
|
||||
libmpv was built.
|
||||
- Improved channel mapping when the file's channel map doesn't match the AO's
|
||||
available channel maps.
|
||||
- (OSX) VDA now gracefully refuses to run on non-OpenGL3-capable systems instead
|
||||
of trying and failing to build its shaders.
|
||||
- Add workaround for vf_vapoursynth filters that expect an FPS.
|
||||
- The default value for `--screenshot-template` now shows up in
|
||||
`--list-options`.
|
||||
- `ao_pulse`'s `latency-hacks` suboption is now off by default, as it causes
|
||||
issues with newer pulseaudio releases. If A/V Sync issues happen, either
|
||||
add the `latency-hacks=on` pulse suboption in mpv.conf, or update the
|
||||
pulseaudio daemon (bug #1430).
|
||||
- (Linux) `ao_alsa` now deals better with audio device disconnection.
|
||||
- Client API: timestamp properties that have no value return "no" instead of
|
||||
magic number.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- (Windows, OSX) Many `ao_wasapi` and `ao_coreaudio` fixes.
|
||||
- (OSX) Make the window title update correctly on OS X Yosemite.
|
||||
- Fixes for handling mono audio on various AOs.
|
||||
- (Linux) Fixes resuming from suspend on ao_alsa.
|
||||
- Fixes for playlist file parsing.
|
||||
- Overly long options in the --list-options output now break the column layout
|
||||
instead of getting truncated; fixes shell completion scripts.
|
||||
- Fix rendering resolution of certain DVB subtitles (bug #1425).
|
||||
- Fix EDL or --merge-files breaking timestamps with .avi files.
|
||||
- Workaround for libavcodec bug with the VP9 codec parser (bug #1448),
|
||||
fixes possible crash.
|
||||
- Improve robustness of the matroska parser with broken files (bugs #1457,
|
||||
#1461).
|
||||
- Improve 10bit video compatibility with older GPUs (specially Intel) (bug
|
||||
#1383).
|
||||
- Fixes flashing the VO window when playing a list of files that includes
|
||||
non-media files (bug #1459).
|
||||
- Workaround for window embedding in OpenBox (bug #1235)
|
||||
- Fixes for several crashes and lockups (bugs #838, #1389, #1408, #1463, #1473,
|
||||
#1474).
|
||||
- Fixes for the zsh completion script (bugs #997)
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- mpv now prints the contents of its config.h when running in verbose mode, to
|
||||
aid with debugging.
|
||||
- The `include=` option now accepts `~` to refer to the HOME dir (bug #1406).
|
||||
- `af_volume` now prints ReplayGain values in verbose mode.
|
||||
- m3u playlists that don't have the `#EXTM3U` header are now accepted if they
|
||||
"look like" ASCII or UTF-8 text and have the .m3u extension.
|
||||
- Chapter marks in the seek bar now update when switching files.
|
||||
- Supports embedded cover art in MKV files (bug #1374).
|
||||
- (Windows) Video window can now be resized even with --no-border.
|
||||
- (Windows) Client API: the "run" command now works on Windows too.
|
||||
- (Linux) vo_wayland now supports key modifiers (Meta, Alt, Control, Shift)
|
||||
- Client API: "display-names" property is now observable.
|
||||
|
||||
This listing is not complete. A complete changelog can be seen by running
|
||||
`git log v0.7.2..v0.7.3` in the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/v0.7.2...v0.7.3.
|
||||
|
||||
Release 0.7.2
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Give precedence to the DVD menu navigation keyboard bindings so that user
|
||||
defined LEFT/RIGHT/... bindings don't break DVD menu navigation.
|
||||
- Try to fallback to the "default" device if the selected device is busy in the
|
||||
alsa AO.
|
||||
- Don't create Dock icon for audio only files on OS X.
|
||||
- Save screenshots to desktop when using the app bundle on OS X.
|
||||
- Restore ab-loop settings with playback resume.
|
||||
- Bump required youtube-dl version to 2014.11.26 and enable the ytdl_hook Lua
|
||||
script by default (now playing videos from YouTube and the like will work
|
||||
out of the box without any configuration change needed).
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Don't signal an error if --stream-dump is used.
|
||||
- Fix removing key bindings from Lua scripts.
|
||||
- Reject channel descriptions with too many channels in the coreaudio AO.
|
||||
- Don't async redraw when waiting for VO redraw on OS X (this fixes the very
|
||||
annoying glitch where the black bars disappear for a single frame when going
|
||||
fullscreen).
|
||||
- Fix mono playback with the also AO.
|
||||
- Don't crash if framebuffers are not available in the opengl VO.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- Try to handle multi-arc videos in the ytdl_hook Lua script.
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 47ec404..c7d6b21`` in
|
||||
the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/47ec404...c7d6b21.
|
||||
|
||||
Release 0.7.1
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Don't show the volume neutral marker on the OSD if softvol is disabled.
|
||||
- Don't select a subtitle track when executing the sub_add input command in
|
||||
"auto" mode.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Fix busy loop when seeking while paused (this fixes a problem with pulseaudio
|
||||
that caused mpv and the pulseaudio daemon to use 100% CPU).
|
||||
- Fix Lua function utils.subprocess() in Windows versions older than Vista.
|
||||
- Avoid creating a window bigger than the screen on Windows.
|
||||
- Don't ignore the last line in m3u playlists.
|
||||
- Don't crash if a codec could not be opened.
|
||||
- Dynamically allocate audio channel map entries (this should fix a crash in
|
||||
the alsa and coreaudio AOs with audio devices that support more than 20
|
||||
channel maps).
|
||||
- Ignore the "srgb" option in the opengl VO if hardware decoding is enabled.
|
||||
- Linearize non-RGB sRGB files correctly (eg. JPEG).
|
||||
- Fix opening reference URLs (.file/id=) on OS X.
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 8d8b36d..6583ad6`` in
|
||||
the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/8d8b36d...6583ad6.
|
||||
|
||||
Release 0.7.0
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Buffer partial log messages in the client API (the client API will now only
|
||||
pass full log messages to clients).
|
||||
- Remove ncurses/terminfo/termcap support (it was disabled by default and
|
||||
replaced by new code since v0.6.0).
|
||||
- Enable cdda:// support by default again (it was disabled since v0.6.0).
|
||||
- Cascade-load input.conf (if there are several input.confs in the set of valid
|
||||
config paths, load them all).
|
||||
- Draw the OSD twice in 3D mode (this fixes subtitles display in 3D mode).
|
||||
- Make wasapi the default AO on Windows again since many of its problems have
|
||||
been solved.
|
||||
- Use "site-functions" subdir to install the zsh completion script instead of
|
||||
the Debian-specific "vendor-completions" (also provide the --zshdir waf
|
||||
configure option for changing this value).
|
||||
- Improve synchronization between the Cocoa GUI and the player (this fixes some
|
||||
long standing deadlock issues on Mac OS X).
|
||||
- Remove --fs-missioncontrol option (only relevant to Mac OS X).
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- Enable pitch correction by default when playing at higher speeds (this can be
|
||||
controlled with the --audio-pitch-correction option).
|
||||
- Open stream and demuxer asynchronously (this should avoid having the player
|
||||
get blocked on network streams).
|
||||
- Add cache-buffering-state property for querying the cache fill status until
|
||||
the player unpauses.
|
||||
- Add support for listing and selecting the audio device (note that it is not
|
||||
implemented for all AOs, see the --audio-device option for more information).
|
||||
- Add support for a JSON-based IPC mechanism (note that this is not currently
|
||||
supported on Windows, see the JSON IPC section in the manpage for more
|
||||
information).
|
||||
- Add Lua utility function for starting processes (see utils.subprocess() in the
|
||||
manpage).
|
||||
- Add Lua utility function for parsing JSON (see utils.parse_json() in the
|
||||
manpage).
|
||||
- Add field-dominance property (see --field-dominance option).
|
||||
- Add video-rotate property (see --video-rotate option).
|
||||
- Add playback-abort property for querying whether playback is stopped or is to
|
||||
be stopped.
|
||||
- Add cursor-autohide property (see --cursor-autohide option).
|
||||
- Add vo-configured property for querying whether a window is created.
|
||||
- Add support for dxva2 hardware acceleration on Windows.
|
||||
- Drop libquvi support (this has been replaced by a built-in Lua script that
|
||||
invokes the youtube-dl tool, which needs to be installed, see the --ytdl
|
||||
option).
|
||||
- Add support for loading chapters from an external file (see the
|
||||
--chapters-file option).
|
||||
- Add window-minimized property for querying whether the window is minimized
|
||||
(works for X11 only).
|
||||
- Make it possible to configure the OSC seekbar style (see the "seekbarstyle"
|
||||
OSC option).
|
||||
- Add support for libmpv on Mac OS X (it used to be broken, now it's fully
|
||||
functional and also provides support for embedding the mpv window inside a
|
||||
Cocoa/Qt application).
|
||||
- Try to use the audio channel map reported by ALSA in the alsa AO.
|
||||
- Add option to disable text OSD rendering completely (useful for working around
|
||||
certain fontconfig issues, see the --use-text-osd option).
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 7759c18..9479daa`` in
|
||||
the git repository or by visiting
|
||||
https://github.com/mpv-player/mpv/compare/7759c18...9479daa (only 250 commits).
|
||||
@@ -102,7 +102,7 @@ function judge()
|
||||
|
||||
mp.msg.verbose("progressive = "..num_progressive)
|
||||
mp.msg.verbose("interlaced-tff = "..num_tff)
|
||||
mp.msg.verbose("interlaced-bff = "..num_bff)
|
||||
mp.msg.verbose("interlaced-bff = "..num_bff)
|
||||
mp.msg.verbose("undetermined = "..num_undetermined)
|
||||
|
||||
if num_determined < num_undetermined then
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
-- parameters. See the entry for "drc" under the "AUDIO FILTERS"
|
||||
-- section of the man page for a complete description of the filter.
|
||||
--
|
||||
-- This script registers the key-binding "\" to toggle the filter between
|
||||
-- This script registers the key-binding "\" to toggle the filter between
|
||||
--
|
||||
-- * off
|
||||
-- * method=1 (single-sample smoothing)
|
||||
@@ -92,7 +92,7 @@ function drc_quieter_handler()
|
||||
drc_scale_target(0.5)
|
||||
end
|
||||
|
||||
-- toggle between off, method 1 and method 2
|
||||
-- toggle between off, method 1 and method 2
|
||||
mp.add_key_binding("\\", "drc_toggle_method", drc_toggle_method_handler)
|
||||
-- increase or decrease target volume
|
||||
mp.add_key_binding("ctrl+9", "drc_quieter", drc_quieter_handler)
|
||||
|
||||
+2
-1
@@ -118,6 +118,7 @@ void mp_audio_set_null_data(struct mp_audio *mpa)
|
||||
mpa->allocated[n] = NULL;
|
||||
}
|
||||
mpa->samples = 0;
|
||||
mpa->readonly = false;
|
||||
}
|
||||
|
||||
static int get_plane_size(const struct mp_audio *mpa, int samples)
|
||||
@@ -250,7 +251,7 @@ void mp_audio_skip_samples(struct mp_audio *data, int samples)
|
||||
// Will return true for non-refcounted frames.
|
||||
bool mp_audio_is_writeable(struct mp_audio *data)
|
||||
{
|
||||
bool ok = true;
|
||||
bool ok = !data->readonly;
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
|
||||
if (data->allocated[n])
|
||||
ok &= av_buffer_is_writable(data->allocated[n]);
|
||||
|
||||
@@ -38,6 +38,7 @@ struct mp_audio {
|
||||
int bps; // size of sub-samples (af_fmt2bps(format))
|
||||
|
||||
// private
|
||||
bool readonly;
|
||||
struct AVBufferRef *allocated[MP_NUM_CHANNELS];
|
||||
};
|
||||
|
||||
|
||||
+63
-52
@@ -50,6 +50,7 @@ static const char *const speaker_names[MP_SPEAKER_ID_COUNT][2] = {
|
||||
[MP_SPEAKER_ID_SDL] = {"sdl", "surround direct left"},
|
||||
[MP_SPEAKER_ID_SDR] = {"sdr", "surround direct right"},
|
||||
[MP_SPEAKER_ID_LFE2] = {"lfe2", "low frequency 2"},
|
||||
[MP_SPEAKER_ID_NA] = {"na", "not available"},
|
||||
};
|
||||
|
||||
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
|
||||
@@ -127,7 +128,8 @@ bool mp_chmap_is_valid(const struct mp_chmap *src)
|
||||
int sp = src->speaker[n];
|
||||
if (sp >= MP_SPEAKER_ID_COUNT || mapped[sp])
|
||||
return false;
|
||||
mapped[sp] = true;
|
||||
if (sp != MP_SPEAKER_ID_NA)
|
||||
mapped[sp] = true;
|
||||
}
|
||||
return src->num > 0;
|
||||
}
|
||||
@@ -142,12 +144,10 @@ bool mp_chmap_is_empty(const struct mp_chmap *src)
|
||||
bool mp_chmap_is_unknown(const struct mp_chmap *src)
|
||||
{
|
||||
for (int n = 0; n < src->num; n++) {
|
||||
int speaker = src->speaker[n];
|
||||
if (speaker >= MP_SPEAKER_ID_UNKNOWN0 &&
|
||||
speaker <= MP_SPEAKER_ID_UNKNOWN_LAST)
|
||||
return true;
|
||||
if (src->speaker[n] != MP_SPEAKER_ID_NA)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
return mp_chmap_is_valid(src);
|
||||
}
|
||||
|
||||
// Note: empty channel maps compare as equal. Invalid ones can equal too.
|
||||
@@ -171,15 +171,6 @@ bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *
|
||||
return mp_chmap_equals(&t1, &t2);
|
||||
}
|
||||
|
||||
bool mp_chmap_is_compatible(const struct mp_chmap *a, const struct mp_chmap *b)
|
||||
{
|
||||
if (mp_chmap_equals(a, b))
|
||||
return true;
|
||||
if (a->num == b->num && (mp_chmap_is_unknown(a) || mp_chmap_is_unknown(b)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mp_chmap_is_stereo(const struct mp_chmap *src)
|
||||
{
|
||||
static const struct mp_chmap stereo = MP_CHMAP_INIT_STEREO;
|
||||
@@ -199,6 +190,26 @@ void mp_chmap_reorder_norm(struct mp_chmap *map)
|
||||
qsort(arr, map->num, 1, comp_uint8);
|
||||
}
|
||||
|
||||
// Remove silent (NA) channels, if any.
|
||||
void mp_chmap_remove_na(struct mp_chmap *map)
|
||||
{
|
||||
struct mp_chmap new = {0};
|
||||
for (int n = 0; n < map->num; n++) {
|
||||
int sp = map->speaker[n];
|
||||
if (sp != MP_SPEAKER_ID_NA)
|
||||
new.speaker[new.num++] = map->speaker[n];
|
||||
}
|
||||
*map = new;
|
||||
}
|
||||
|
||||
// Add silent (NA) channels to map until map->num >= num.
|
||||
void mp_chmap_fill_na(struct mp_chmap *map, int num)
|
||||
{
|
||||
assert(num <= MP_NUM_CHANNELS);
|
||||
while (map->num < num)
|
||||
map->speaker[map->num++] = MP_SPEAKER_ID_NA;
|
||||
}
|
||||
|
||||
// Set *dst to a standard layout with the given number of channels.
|
||||
// If the number of channels is invalid, an invalid map is set, and
|
||||
// mp_chmap_is_valid(dst) will return false.
|
||||
@@ -227,6 +238,8 @@ void mp_chmap_from_channels_alsa(struct mp_chmap *dst, int num_channels)
|
||||
// Set *dst to an unknown layout for the given numbers of channels.
|
||||
// If the number of channels is invalid, an invalid map is set, and
|
||||
// mp_chmap_is_valid(dst) will return false.
|
||||
// A mp_chmap with all entries set to NA is treated specially in some
|
||||
// contexts (watch out for mp_chmap_is_unknown()).
|
||||
void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels)
|
||||
{
|
||||
if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
|
||||
@@ -234,7 +247,7 @@ void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels)
|
||||
} else {
|
||||
dst->num = num_channels;
|
||||
for (int n = 0; n < dst->num; n++)
|
||||
dst->speaker[n] = MP_SPEAKER_ID_UNKNOWN0 + n;
|
||||
dst->speaker[n] = MP_SPEAKER_ID_NA;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +290,7 @@ void mp_chmap_remove_useless_channels(struct mp_chmap *map,
|
||||
}
|
||||
|
||||
// Return the ffmpeg/libav channel layout as in <libavutil/channel_layout.h>.
|
||||
// Speakers not representable by ffmpeg/libav are dropped.
|
||||
// Warning: this ignores the order of the channels, and will return a channel
|
||||
// mask even if the order is different from libavcodec's.
|
||||
uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src)
|
||||
@@ -286,8 +300,10 @@ uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src)
|
||||
if (mp_chmap_is_unknown(&t))
|
||||
mp_chmap_from_channels(&t, t.num);
|
||||
uint64_t mask = 0;
|
||||
for (int n = 0; n < t.num; n++)
|
||||
mask |= 1ULL << t.speaker[n];
|
||||
for (int n = 0; n < t.num; n++) {
|
||||
if (t.speaker[n] < 64) // ignore MP_SPEAKER_ID_NA etc.
|
||||
mask |= 1ULL << t.speaker[n];
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
@@ -348,56 +364,51 @@ void mp_chmap_reorder_to_lavc(struct mp_chmap *map)
|
||||
|
||||
// Get reordering array for from->to reordering. from->to must have the same set
|
||||
// of speakers (i.e. same number and speaker IDs, just different order). Then,
|
||||
// for each speaker n, dst[n] will be set such that:
|
||||
// to->speaker[dst[n]] = from->speaker[n]
|
||||
// (dst[n] gives the source channel for destination channel n)
|
||||
void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
|
||||
// for each speaker n, src[n] will be set such that:
|
||||
// to->speaker[n] = from->speaker[src[n]]
|
||||
// (src[n] gives the source channel for destination channel n)
|
||||
// If *from and *to don't contain the same set of speakers, then the above
|
||||
// invariant is not guaranteed. Instead, src[n] can be set to -1 if the channel
|
||||
// at to->speaker[n] is unmapped.
|
||||
void mp_chmap_get_reorder(int src[MP_NUM_CHANNELS], const struct mp_chmap *from,
|
||||
const struct mp_chmap *to)
|
||||
{
|
||||
assert(from->num == to->num);
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++)
|
||||
src[n] = -1;
|
||||
|
||||
if (mp_chmap_is_unknown(from) || mp_chmap_is_unknown(to)) {
|
||||
for (int n = 0; n < from->num; n++)
|
||||
dst[n] = n;
|
||||
for (int n = 0; n < to->num; n++)
|
||||
src[n] = n < from->num ? n : -1;
|
||||
return;
|
||||
}
|
||||
// Same set of speakers required
|
||||
assert(mp_chmap_equals_reordered(from, to));
|
||||
|
||||
for (int n = 0; n < from->num; n++) {
|
||||
int src = from->speaker[n];
|
||||
dst[n] = -1;
|
||||
for (int i = 0; i < to->num; i++) {
|
||||
if (src == to->speaker[i]) {
|
||||
dst[n] = i;
|
||||
if (to->speaker[n] == from->speaker[i]) {
|
||||
src[n] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(dst[n] != -1);
|
||||
}
|
||||
for (int n = 0; n < from->num; n++)
|
||||
assert(to->speaker[dst[n]] == from->speaker[n]);
|
||||
|
||||
for (int n = 0; n < to->num; n++)
|
||||
assert(src[n] < 0 || (to->speaker[n] == from->speaker[src[n]]));
|
||||
}
|
||||
|
||||
// Performs the difference between a and b, and store it in diff. If b has
|
||||
// channels that do not appear in a, those will not appear in the difference.
|
||||
// To get to those the argument ordering in the function call has to be
|
||||
// inverted. For the same reason, the diff with a superset will return no
|
||||
// speakers.
|
||||
void mp_chmap_diff(const struct mp_chmap *a, const struct mp_chmap *b,
|
||||
struct mp_chmap *diff)
|
||||
static int popcount64(uint64_t bits)
|
||||
{
|
||||
int r = 0;
|
||||
for (int n = 0; n < 64; n++)
|
||||
r += !!(bits & (1ULL << n));
|
||||
return r;
|
||||
}
|
||||
|
||||
// Return the number of channels only in a.
|
||||
int mp_chmap_diffn(const struct mp_chmap *a, const struct mp_chmap *b)
|
||||
{
|
||||
uint64_t a_mask = mp_chmap_to_lavc_unchecked(a);
|
||||
uint64_t b_mask = mp_chmap_to_lavc_unchecked(b);
|
||||
mp_chmap_from_lavc(diff, (a_mask ^ b_mask) & a_mask);
|
||||
}
|
||||
|
||||
// Checks whether a contains all the speakers in b
|
||||
bool mp_chmap_contains(const struct mp_chmap *a, const struct mp_chmap *b)
|
||||
{
|
||||
struct mp_chmap d1;
|
||||
struct mp_chmap d2;
|
||||
mp_chmap_diff(a, b, &d1);
|
||||
mp_chmap_diff(b, a, &d2);
|
||||
return a->num >= b->num && d1.num >= 0 && d2.num == 0;
|
||||
return popcount64((a_mask ^ b_mask) & a_mask);
|
||||
}
|
||||
|
||||
// Returns something like "fl-fr-fc". If there's a standard layout in lavc
|
||||
|
||||
+10
-10
@@ -56,10 +56,12 @@ enum mp_speaker_id {
|
||||
MP_SPEAKER_ID_SDR, // SURROUND_DIRECT_RIGHT
|
||||
MP_SPEAKER_ID_LFE2, // LOW_FREQUENCY_2
|
||||
|
||||
// Special mpv-specific speaker entries reserved for channels which have no
|
||||
// known meaning.
|
||||
MP_SPEAKER_ID_UNKNOWN0 = 64,
|
||||
MP_SPEAKER_ID_UNKNOWN_LAST = MP_SPEAKER_ID_UNKNOWN0 + MP_NUM_CHANNELS - 1,
|
||||
// Speaker IDs >= 64 are not representable in WAVEFORMATEXTENSIBLE or libav*.
|
||||
|
||||
// "Silent" channels. These are sometimes used to insert padding for
|
||||
// unused channels. Unlike other speaker types, multiple of these can
|
||||
// occur in a single mp_chmap.
|
||||
MP_SPEAKER_ID_NA = 64,
|
||||
|
||||
// Including the unassigned IDs in between. This is not a valid ID anymore,
|
||||
// but is still within uint8_t.
|
||||
@@ -98,10 +100,11 @@ bool mp_chmap_is_empty(const struct mp_chmap *src);
|
||||
bool mp_chmap_is_unknown(const struct mp_chmap *src);
|
||||
bool mp_chmap_equals(const struct mp_chmap *a, const struct mp_chmap *b);
|
||||
bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *b);
|
||||
bool mp_chmap_is_compatible(const struct mp_chmap *a, const struct mp_chmap *b);
|
||||
bool mp_chmap_is_stereo(const struct mp_chmap *src);
|
||||
|
||||
void mp_chmap_reorder_norm(struct mp_chmap *map);
|
||||
void mp_chmap_remove_na(struct mp_chmap *map);
|
||||
void mp_chmap_fill_na(struct mp_chmap *map, int num);
|
||||
|
||||
void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels);
|
||||
void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels);
|
||||
@@ -117,13 +120,10 @@ void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src);
|
||||
bool mp_chmap_is_lavc(const struct mp_chmap *src);
|
||||
void mp_chmap_reorder_to_lavc(struct mp_chmap *map);
|
||||
|
||||
void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
|
||||
void mp_chmap_get_reorder(int src[MP_NUM_CHANNELS], const struct mp_chmap *from,
|
||||
const struct mp_chmap *to);
|
||||
|
||||
void mp_chmap_diff(const struct mp_chmap *a, const struct mp_chmap *b,
|
||||
struct mp_chmap *diff);
|
||||
|
||||
bool mp_chmap_contains(const struct mp_chmap *a, const struct mp_chmap *b);
|
||||
int mp_chmap_diffn(const struct mp_chmap *a, const struct mp_chmap *b);
|
||||
|
||||
char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src);
|
||||
#define mp_chmap_to_str(m) mp_chmap_to_str_buf((char[64]){0}, 64, (m))
|
||||
|
||||
+27
-53
@@ -199,51 +199,29 @@ bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
return false;
|
||||
}
|
||||
|
||||
#define UPMIX_IDX 0
|
||||
#define DOWNMIX_IDX 1
|
||||
#define FALLBACK_IDX 2
|
||||
|
||||
static bool test_fallbacks(struct mp_chmap *a, struct mp_chmap *b,
|
||||
int best_diffs[2], struct mp_chmap best[2])
|
||||
// Decide whether we should prefer old or new for the requested layout.
|
||||
// Return true if new should be used, false if old should be used.
|
||||
// If old is empty, always return new (initial case).
|
||||
static bool mp_chmap_is_better(struct mp_chmap *req, struct mp_chmap *old,
|
||||
struct mp_chmap *new)
|
||||
{
|
||||
struct mp_chmap diff1, diff2;
|
||||
int old_lost = mp_chmap_diffn(req, old); // num. channels only in req
|
||||
int new_lost = mp_chmap_diffn(req, new);
|
||||
|
||||
mp_chmap_diff(a, b, &diff1);
|
||||
if (mp_chmap_contains(a, b) && best_diffs[UPMIX_IDX] > diff1.num) {
|
||||
best[UPMIX_IDX] = *a;
|
||||
best_diffs[UPMIX_IDX] = diff1.num;
|
||||
// Initial case
|
||||
if (!old->num)
|
||||
return true;
|
||||
}
|
||||
|
||||
mp_chmap_diff(b, a, &diff2);
|
||||
if (mp_chmap_contains(b, a) && best_diffs[DOWNMIX_IDX] > diff2.num) {
|
||||
best[DOWNMIX_IDX] = *a;
|
||||
best_diffs[DOWNMIX_IDX] = diff2.num;
|
||||
return true;
|
||||
}
|
||||
// Imperfect upmix (no real superset) - minimize lost channels
|
||||
if (new_lost != old_lost)
|
||||
return new_lost < old_lost;
|
||||
|
||||
if (diff1.num > 0 && best_diffs[FALLBACK_IDX] > diff1.num) {
|
||||
best[FALLBACK_IDX] = *a;
|
||||
best_diffs[FALLBACK_IDX] = diff1.num;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (diff2.num > 0 && best_diffs[FALLBACK_IDX] > diff2.num) {
|
||||
best[FALLBACK_IDX] = *a;
|
||||
best_diffs[FALLBACK_IDX] = diff2.num;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Some kind of upmix. If it's perfect, prefer the smaller one. Even if not,
|
||||
// both have equal loss, so also prefer the smaller one.
|
||||
return new->num < old->num;
|
||||
}
|
||||
|
||||
// Determine which channel map to fallback to given a source channel map. It
|
||||
// uses the following heuristic:
|
||||
// 1) If mono is required always prefer stereo to a multichannel upmix.
|
||||
// 2) Search for an upmix that is an exact superset of the required chmap.
|
||||
// 3) Search for a downmix that is the exact subset of the required chmap.
|
||||
// 4) Search for either an upmix or downmix that is the closest (minimum
|
||||
// difference of speakers) to the required chmap.
|
||||
// Determine which channel map to fallback to given a source channel map.
|
||||
bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
{
|
||||
// special case: if possible always fallback mono to stereo (instead of
|
||||
@@ -255,8 +233,7 @@ bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
return true;
|
||||
}
|
||||
|
||||
int best_diffs[] = { INT_MAX, INT_MAX, INT_MAX };
|
||||
struct mp_chmap best[] = { {0}, {0}, {0} };
|
||||
struct mp_chmap best = {0};
|
||||
|
||||
for (int n = 0; n < s->num_chmaps; n++) {
|
||||
struct mp_chmap e = s->chmaps[n];
|
||||
@@ -264,25 +241,22 @@ bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
if (mp_chmap_is_unknown(&e))
|
||||
continue;
|
||||
|
||||
if (test_fallbacks(&e, map, best_diffs, best))
|
||||
continue;
|
||||
|
||||
// in case we didn't match any fallback retry after replacing speakers
|
||||
for (int i = 0; i < MP_ARRAY_SIZE(speaker_replacements); i++) {
|
||||
struct mp_chmap t = e;
|
||||
struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i];
|
||||
if (replace_speakers(&t, r)) {
|
||||
if (test_fallbacks(&t, map, best_diffs, best))
|
||||
for (int i = -1; i < (int)MP_ARRAY_SIZE(speaker_replacements); i++) {
|
||||
struct mp_chmap t = *map;
|
||||
if (i >= 0) {
|
||||
struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i];
|
||||
if (!replace_speakers(&t, r))
|
||||
continue;
|
||||
}
|
||||
if (mp_chmap_is_better(&t, &best, &e))
|
||||
best = e;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = UPMIX_IDX; i < MP_ARRAY_SIZE(best); i++) {
|
||||
if (best_diffs[i] < INT_MAX) {
|
||||
*map = best[i];
|
||||
return true;
|
||||
}
|
||||
if (best.num) {
|
||||
*map = best;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
+1
-1
@@ -577,7 +577,7 @@ static int af_reinit(struct af_stream *s)
|
||||
if (af_fmt_is_valid(fmt_in1) && af_fmt_is_valid(fmt_in2)) {
|
||||
bool spd1 = AF_FORMAT_IS_IEC61937(fmt_in1);
|
||||
bool spd2 = AF_FORMAT_IS_IEC61937(fmt_in2);
|
||||
if (spd1 != spd2) {
|
||||
if (spd1 != spd2 && af->next) {
|
||||
MP_WARN(af, "Filter %s apparently cannot be used due to "
|
||||
"spdif passthrough - removing it.\n",
|
||||
af->info->name);
|
||||
|
||||
@@ -81,6 +81,7 @@ struct af_resample {
|
||||
struct mp_audio *pending;
|
||||
bool avrctx_ok;
|
||||
struct AVAudioResampleContext *avrctx;
|
||||
struct mp_audio avrctx_fmt; // output format of avrctx
|
||||
struct AVAudioResampleContext *avrctx_out; // for output channel reordering
|
||||
struct af_resample_opts ctx; // opts in the context
|
||||
struct af_resample_opts opts; // opts requested by the user
|
||||
@@ -163,6 +164,22 @@ static bool test_conversion(int src_format, int dst_format)
|
||||
af_to_avformat(dst_format) != AV_SAMPLE_FMT_NONE;
|
||||
}
|
||||
|
||||
// mp_chmap_get_reorder() performs:
|
||||
// to->speaker[n] = from->speaker[src[n]]
|
||||
// but libavresample does:
|
||||
// to->speaker[dst[n]] = from->speaker[n]
|
||||
static void transpose_order(int *map, int num)
|
||||
{
|
||||
int nmap[MP_NUM_CHANNELS] = {0};
|
||||
for (int n = 0; n < num; n++) {
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (map[n] == i)
|
||||
nmap[i] = n;
|
||||
}
|
||||
}
|
||||
memcpy(map, nmap, sizeof(nmap));
|
||||
}
|
||||
|
||||
static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
|
||||
struct mp_audio *out)
|
||||
{
|
||||
@@ -172,8 +189,12 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
|
||||
|
||||
enum AVSampleFormat in_samplefmt = af_to_avformat(in->format);
|
||||
enum AVSampleFormat out_samplefmt = af_to_avformat(out->format);
|
||||
enum AVSampleFormat out_samplefmtp =
|
||||
af_to_avformat(af_fmt_to_planar(out->format));
|
||||
|
||||
if (in_samplefmt == AV_SAMPLE_FMT_NONE || out_samplefmt == AV_SAMPLE_FMT_NONE)
|
||||
if (in_samplefmt == AV_SAMPLE_FMT_NONE ||
|
||||
out_samplefmt == AV_SAMPLE_FMT_NONE ||
|
||||
out_samplefmtp == AV_SAMPLE_FMT_NONE)
|
||||
return AF_ERROR;
|
||||
|
||||
avresample_close(s->avrctx);
|
||||
@@ -220,27 +241,52 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
|
||||
uint64_t in_ch_layout = mp_chmap_to_lavc_unchecked(&map_in);
|
||||
uint64_t out_ch_layout = mp_chmap_to_lavc_unchecked(&map_out);
|
||||
|
||||
av_opt_set_int(s->avrctx, "in_channel_layout", in_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx, "out_channel_layout", out_ch_layout, 0);
|
||||
|
||||
av_opt_set_int(s->avrctx, "in_sample_rate", s->ctx.in_rate, 0);
|
||||
av_opt_set_int(s->avrctx, "out_sample_rate", s->ctx.out_rate, 0);
|
||||
|
||||
av_opt_set_int(s->avrctx, "in_sample_fmt", in_samplefmt, 0);
|
||||
av_opt_set_int(s->avrctx, "out_sample_fmt", out_samplefmt, 0);
|
||||
|
||||
struct mp_chmap in_lavc;
|
||||
mp_chmap_from_lavc(&in_lavc, in_ch_layout);
|
||||
if (in_lavc.num != map_in.num) {
|
||||
// For handling NA channels, we would have to add a planarization step.
|
||||
MP_FATAL(af, "Unsupported channel remapping.\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
mp_chmap_get_reorder(s->reorder_in, &map_in, &in_lavc);
|
||||
transpose_order(s->reorder_in, map_in.num);
|
||||
|
||||
struct mp_chmap out_lavc;
|
||||
mp_chmap_from_lavc(&out_lavc, out_ch_layout);
|
||||
mp_chmap_get_reorder(s->reorder_out, &out_lavc, &map_out);
|
||||
if (mp_chmap_equals(&out_lavc, &map_out)) {
|
||||
// No intermediate step required - output new format directly.
|
||||
out_samplefmtp = out_samplefmt;
|
||||
} else {
|
||||
// Verify that we really just reorder and/or insert NA channels.
|
||||
struct mp_chmap withna = out_lavc;
|
||||
mp_chmap_fill_na(&withna, map_out.num);
|
||||
if (withna.num != map_out.num)
|
||||
return AF_ERROR;
|
||||
mp_chmap_get_reorder(s->reorder_out, &out_lavc, &map_out);
|
||||
}
|
||||
|
||||
// Same configuration; we just reorder.
|
||||
av_opt_set_int(s->avrctx_out, "in_channel_layout", out_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx_out, "out_channel_layout", out_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx_out, "in_sample_fmt", out_samplefmt, 0);
|
||||
s->avrctx_fmt = *out;
|
||||
mp_audio_set_channels(&s->avrctx_fmt, &out_lavc);
|
||||
mp_audio_set_format(&s->avrctx_fmt, af_from_avformat(out_samplefmtp));
|
||||
|
||||
// Real conversion; output is input to avrctx_out.
|
||||
av_opt_set_int(s->avrctx, "in_channel_layout", in_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx, "out_channel_layout", out_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx, "in_sample_rate", s->ctx.in_rate, 0);
|
||||
av_opt_set_int(s->avrctx, "out_sample_rate", s->ctx.out_rate, 0);
|
||||
av_opt_set_int(s->avrctx, "in_sample_fmt", in_samplefmt, 0);
|
||||
av_opt_set_int(s->avrctx, "out_sample_fmt", out_samplefmtp, 0);
|
||||
|
||||
// Just needs the correct number of channels.
|
||||
int fake_out_ch_layout = av_get_default_channel_layout(map_out.num);
|
||||
if (!fake_out_ch_layout)
|
||||
return AF_ERROR;
|
||||
|
||||
// Deplanarize if needed.
|
||||
av_opt_set_int(s->avrctx_out, "in_channel_layout", fake_out_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx_out, "out_channel_layout", fake_out_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx_out, "in_sample_fmt", out_samplefmtp, 0);
|
||||
av_opt_set_int(s->avrctx_out, "out_sample_fmt", out_samplefmt, 0);
|
||||
av_opt_set_int(s->avrctx_out, "in_sample_rate", s->ctx.out_rate, 0);
|
||||
av_opt_set_int(s->avrctx_out, "out_sample_rate", s->ctx.out_rate, 0);
|
||||
@@ -249,7 +295,6 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
|
||||
// * This function can only be called when the allocated context is not open.
|
||||
// * Also, the input channel layout must have already been set.
|
||||
avresample_set_channel_mapping(s->avrctx, s->reorder_in);
|
||||
avresample_set_channel_mapping(s->avrctx_out, s->reorder_out);
|
||||
|
||||
if (avresample_open(s->avrctx) < 0 ||
|
||||
avresample_open(s->avrctx_out) < 0)
|
||||
@@ -354,22 +399,22 @@ static void uninit(struct af_instance *af)
|
||||
talloc_free(s->pending);
|
||||
}
|
||||
|
||||
static bool needs_reorder(int *reorder, int num_ch)
|
||||
{
|
||||
for (int n = 0; n < num_ch; n++) {
|
||||
if (reorder[n] != n)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void reorder_planes(struct mp_audio *mpa, int *reorder)
|
||||
static void reorder_planes(struct mp_audio *mpa, int *reorder,
|
||||
struct mp_chmap *newmap)
|
||||
{
|
||||
struct mp_audio prev = *mpa;
|
||||
for (int n = 0; n < mpa->num_planes; n++) {
|
||||
assert(reorder[n] >= 0 && reorder[n] < mpa->num_planes);
|
||||
mpa->planes[n] = prev.planes[reorder[n]];
|
||||
|
||||
for (int n = 0; n < newmap->num; n++) {
|
||||
int src = reorder[n];
|
||||
if (src < 0) {
|
||||
src = 0; // Use the first plane for padding channels.
|
||||
mpa->readonly = true;
|
||||
}
|
||||
assert(src < mpa->num_planes);
|
||||
mpa->planes[n] = prev.planes[src];
|
||||
}
|
||||
|
||||
mp_audio_set_channels(mpa, newmap);
|
||||
}
|
||||
|
||||
static int filter(struct af_instance *af, struct mp_audio *in)
|
||||
@@ -388,14 +433,14 @@ static int filter(struct af_instance *af, struct mp_audio *in)
|
||||
int samples = avresample_available(s->avrctx) +
|
||||
av_rescale_rnd(get_delay(s) + (in ? in->samples : 0),
|
||||
s->ctx.out_rate, s->ctx.in_rate, AV_ROUND_UP);
|
||||
struct mp_audio *out = mp_audio_pool_get(af->out_pool, af->data, samples);
|
||||
|
||||
struct mp_audio out_format = s->avrctx_fmt;
|
||||
struct mp_audio *out = mp_audio_pool_get(af->out_pool, &out_format, samples);
|
||||
if (!out)
|
||||
goto error;
|
||||
if (in)
|
||||
mp_audio_copy_attributes(out, in);
|
||||
|
||||
mp_audio_realloc_min(out, out->samples);
|
||||
|
||||
af->delay = get_delay(s) / (double)s->ctx.in_rate;
|
||||
|
||||
if (out->samples) {
|
||||
@@ -404,11 +449,11 @@ static int filter(struct af_instance *af, struct mp_audio *in)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (needs_reorder(s->reorder_out, out->nch)) {
|
||||
if (AF_FORMAT_IS_PLANAR(out->format)) {
|
||||
reorder_planes(out, s->reorder_out);
|
||||
} else if (out->samples) {
|
||||
struct mp_audio *new = mp_audio_pool_get(s->reorder_buffer, out,
|
||||
if (out->samples && !mp_audio_config_equals(out, af->data)) {
|
||||
assert(AF_FORMAT_IS_PLANAR(out->format));
|
||||
reorder_planes(out, s->reorder_out, &af->data->channels);
|
||||
if (!mp_audio_config_equals(out, af->data)) {
|
||||
struct mp_audio *new = mp_audio_pool_get(s->reorder_buffer, af->data,
|
||||
out->samples);
|
||||
if (!new)
|
||||
goto error;
|
||||
|
||||
+15
-15
@@ -64,24 +64,24 @@
|
||||
enum af_format {
|
||||
AF_FORMAT_UNKNOWN = 0,
|
||||
|
||||
AF_FORMAT_U8 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_S8 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_U16 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_S16 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_U24 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT),
|
||||
AF_FORMAT_S24 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT),
|
||||
AF_FORMAT_U32 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_S32 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_U8 = AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT,
|
||||
AF_FORMAT_S8 = AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT,
|
||||
AF_FORMAT_U16 = AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT,
|
||||
AF_FORMAT_S16 = AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT,
|
||||
AF_FORMAT_U24 = AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT,
|
||||
AF_FORMAT_S24 = AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT,
|
||||
AF_FORMAT_U32 = AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT,
|
||||
AF_FORMAT_S32 = AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT,
|
||||
|
||||
AF_FORMAT_FLOAT = (AF_FORMAT_F|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_DOUBLE = (AF_FORMAT_F|AF_FORMAT_64BIT),
|
||||
AF_FORMAT_FLOAT = AF_FORMAT_F|AF_FORMAT_32BIT,
|
||||
AF_FORMAT_DOUBLE = AF_FORMAT_F|AF_FORMAT_64BIT,
|
||||
|
||||
// Planar variants
|
||||
AF_FORMAT_U8P = (AF_INTP|AF_FORMAT_US|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_S16P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_S32P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_FLOATP = (AF_FLTP|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_DOUBLEP = (AF_FLTP|AF_FORMAT_64BIT),
|
||||
AF_FORMAT_U8P = AF_INTP|AF_FORMAT_US|AF_FORMAT_8BIT,
|
||||
AF_FORMAT_S16P = AF_INTP|AF_FORMAT_SI|AF_FORMAT_16BIT,
|
||||
AF_FORMAT_S32P = AF_INTP|AF_FORMAT_SI|AF_FORMAT_32BIT,
|
||||
AF_FORMAT_FLOATP = AF_FLTP|AF_FORMAT_32BIT,
|
||||
AF_FORMAT_DOUBLEP = AF_FLTP|AF_FORMAT_64BIT,
|
||||
|
||||
// All of these use IEC61937 framing, and otherwise pretend to be like PCM.
|
||||
AF_FORMAT_S_AAC = AF_FORMAT_S_(0),
|
||||
|
||||
+20
-16
@@ -385,28 +385,26 @@ bool ao_eof_reached(struct ao *ao)
|
||||
// Query the AO_EVENT_*s as requested by the events parameter, and return them.
|
||||
int ao_query_and_reset_events(struct ao *ao, int events)
|
||||
{
|
||||
int actual_events = 0;
|
||||
if (atomic_load(&ao->request_reload)) // don't need to reset it
|
||||
actual_events |= AO_EVENT_RELOAD;
|
||||
if (atomic_load(&ao->request_hotplug))
|
||||
actual_events |= AO_EVENT_HOTPLUG;
|
||||
return actual_events & events;
|
||||
return atomic_fetch_and(&ao->events_, ~(unsigned)events) & 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);
|
||||
}
|
||||
|
||||
// Request that the player core destroys and recreates the AO. Fully thread-safe.
|
||||
void ao_request_reload(struct ao *ao)
|
||||
{
|
||||
atomic_store(&ao->request_reload, true);
|
||||
if (ao->input_ctx)
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
ao_add_events(ao, AO_EVENT_RELOAD);
|
||||
}
|
||||
|
||||
// Notify the player that the device list changed. Fully thread-safe.
|
||||
void ao_hotplug_event(struct ao *ao)
|
||||
{
|
||||
atomic_store(&ao->request_hotplug, true);
|
||||
if (ao->input_ctx)
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
ao_add_events(ao, AO_EVENT_HOTPLUG);
|
||||
}
|
||||
|
||||
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
|
||||
@@ -415,12 +413,19 @@ bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
|
||||
if (mp_msg_test(ao->log, MSGL_DEBUG)) {
|
||||
for (int i = 0; i < s->num_chmaps; i++) {
|
||||
struct mp_chmap c = s->chmaps[i];
|
||||
MP_DBG(ao, "chmap_sel #%d: %s\n", i, mp_chmap_to_str(&c));
|
||||
struct mp_chmap cr = c;
|
||||
mp_chmap_reorder_norm(&cr);
|
||||
MP_DBG(ao, "chmap_sel #%d: %s (%s)\n", i, mp_chmap_to_str(&c),
|
||||
mp_chmap_to_str(&cr));
|
||||
}
|
||||
}
|
||||
bool r = mp_chmap_sel_adjust(s, map);
|
||||
if (r)
|
||||
MP_DBG(ao, "result: %s\n", mp_chmap_to_str(map));
|
||||
if (r) {
|
||||
struct mp_chmap mapr = *map;
|
||||
mp_chmap_reorder_norm(&mapr);
|
||||
MP_DBG(ao, "result: %s (%s)\n", mp_chmap_to_str(map),
|
||||
mp_chmap_to_str(&mapr));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -496,7 +501,6 @@ bool ao_hotplug_check_update(struct ao_hotplug *hp)
|
||||
{
|
||||
if (hp->ao && ao_query_and_reset_events(hp->ao, AO_EVENT_HOTPLUG)) {
|
||||
hp->needs_update = true;
|
||||
atomic_store(&hp->ao->request_hotplug, false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
+41
-33
@@ -252,6 +252,7 @@ static const int alsa_to_mp_channels[][2] = {
|
||||
{SND_CHMAP_TRR, MP_SP(TBR)},
|
||||
{SND_CHMAP_TRC, MP_SP(TBC)},
|
||||
{SND_CHMAP_MONO, MP_SP(FC)},
|
||||
{SND_CHMAP_NA, MP_SPEAKER_ID_NA},
|
||||
{SND_CHMAP_LAST, MP_SPEAKER_ID_COUNT}
|
||||
};
|
||||
|
||||
@@ -275,6 +276,20 @@ static int find_alsa_channel(int mp_channel)
|
||||
return SND_CHMAP_UNKNOWN;
|
||||
}
|
||||
|
||||
static int mp_chmap_from_alsa(struct mp_chmap *dst, snd_pcm_chmap_t *src)
|
||||
{
|
||||
*dst = (struct mp_chmap) {0};
|
||||
|
||||
if (src->channels > MP_NUM_CHANNELS)
|
||||
return -1;
|
||||
|
||||
dst->num = src->channels;
|
||||
for (int c = 0; c < dst->num; c++)
|
||||
dst->speaker[c] = find_mp_channel(src->pos[c]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
@@ -285,14 +300,8 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
|
||||
return false;
|
||||
|
||||
for (int i = 0; maps[i] != NULL; i++) {
|
||||
if (maps[i]->map.channels > MP_NUM_CHANNELS) {
|
||||
MP_VERBOSE(ao, "skipping ALSA channel map with too many channels.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
struct mp_chmap entry = {.num = maps[i]->map.channels};
|
||||
for (int c = 0; c < entry.num; c++)
|
||||
entry.speaker[c] = find_mp_channel(maps[i]->map.pos[c]);
|
||||
struct mp_chmap entry;
|
||||
mp_chmap_from_alsa(&entry, &maps[i]->map);
|
||||
|
||||
if (mp_chmap_is_valid(&entry)) {
|
||||
MP_VERBOSE(ao, "Got supported channel map: %s (type %s)\n",
|
||||
@@ -404,7 +413,7 @@ alsa_error: ;
|
||||
#define INIT_OK 0
|
||||
#define INIT_ERROR -1
|
||||
#define INIT_BRAINDEATH -2
|
||||
static int init_device(struct ao *ao)
|
||||
static int init_device(struct ao *ao, bool second_try)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int err;
|
||||
@@ -492,11 +501,12 @@ static int init_device(struct ao *ao)
|
||||
}
|
||||
|
||||
if (num_channels != ao->channels.num) {
|
||||
int req = ao->channels.num;
|
||||
mp_chmap_from_channels_alsa(&ao->channels, num_channels);
|
||||
if (!mp_chmap_is_valid(&ao->channels))
|
||||
mp_chmap_from_channels(&ao->channels, 2);
|
||||
MP_ERR(ao, "Couldn't get requested number of channels, fallback to %s.\n",
|
||||
mp_chmap_to_str(&ao->channels));
|
||||
MP_ERR(ao, "Couldn't get requested number of channels (%d), fallback "
|
||||
"to %s.\n", req, mp_chmap_to_str(&ao->channels));
|
||||
}
|
||||
|
||||
// Some ALSA drivers have broken delay reporting, so disable the ALSA
|
||||
@@ -564,9 +574,8 @@ static int init_device(struct ao *ao)
|
||||
if (snd_pcm_chmap_print(alsa_chmap, sizeof(tmp), tmp) > 0)
|
||||
MP_VERBOSE(ao, "channel map reported by ALSA: %s\n", tmp);
|
||||
|
||||
struct mp_chmap chmap = {.num = alsa_chmap->channels};
|
||||
for (int c = 0; c < chmap.num; c++)
|
||||
chmap.speaker[c] = find_mp_channel(alsa_chmap->pos[c]);
|
||||
struct mp_chmap chmap;
|
||||
mp_chmap_from_alsa(&chmap, alsa_chmap);
|
||||
|
||||
MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap));
|
||||
|
||||
@@ -575,24 +584,14 @@ static int init_device(struct ao *ao)
|
||||
} else if (AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
MP_VERBOSE(ao, "using spdif passthrough; ignoring the channel map.\n");
|
||||
} else if (mp_chmap_is_valid(&chmap)) {
|
||||
if (mp_chmap_equals(&chmap, &ao->channels)) {
|
||||
MP_VERBOSE(ao, "which is what we requested.\n");
|
||||
} else if (chmap.num == ao->channels.num) {
|
||||
MP_VERBOSE(ao, "using the ALSA channel map.\n");
|
||||
ao->channels = chmap;
|
||||
} else {
|
||||
MP_WARN(ao, "ALSA channel map conflicts with channel count!\n");
|
||||
}
|
||||
} else {
|
||||
// Is it one that contains NA channels?
|
||||
struct mp_chmap chmap2 = {0};
|
||||
for (int c = 0; c < alsa_chmap->channels; c++) {
|
||||
int alsa_ch = alsa_chmap->pos[c];
|
||||
if (alsa_ch != SND_CHMAP_NA)
|
||||
chmap2.speaker[chmap2.num++] = find_mp_channel(alsa_ch);
|
||||
}
|
||||
struct mp_chmap without_na = chmap;
|
||||
mp_chmap_remove_na(&without_na);
|
||||
|
||||
if (mp_chmap_is_valid(&chmap2)) {
|
||||
if (mp_chmap_is_valid(&without_na) &&
|
||||
!mp_chmap_equals(&without_na, &chmap) &&
|
||||
!second_try)
|
||||
{
|
||||
// Sometimes, ALSA will advertise certain chmaps, but it's not
|
||||
// possible to set them. This can happen with dmix: as of
|
||||
// alsa 1.0.28, dmix can do stereo only, but advertises the
|
||||
@@ -609,10 +608,19 @@ static int init_device(struct ao *ao)
|
||||
err = snd_pcm_close(p->alsa);
|
||||
p->alsa = NULL;
|
||||
CHECK_ALSA_ERROR("pcm close error");
|
||||
ao->channels = chmap2;
|
||||
ao->channels = without_na;
|
||||
return INIT_BRAINDEATH;
|
||||
}
|
||||
|
||||
if (mp_chmap_equals(&chmap, &ao->channels)) {
|
||||
MP_VERBOSE(ao, "which is what we requested.\n");
|
||||
} else if (chmap.num == ao->channels.num) {
|
||||
MP_VERBOSE(ao, "using the ALSA channel map.\n");
|
||||
ao->channels = chmap;
|
||||
} else {
|
||||
MP_WARN(ao, "ALSA channel map conflicts with channel count!\n");
|
||||
}
|
||||
} else {
|
||||
MP_WARN(ao, "Got unknown channel map from ALSA.\n");
|
||||
}
|
||||
|
||||
@@ -683,9 +691,9 @@ static int init(struct ao *ao)
|
||||
|
||||
MP_VERBOSE(ao, "using ALSA version: %s\n", snd_asoundlib_version());
|
||||
|
||||
int r = init_device(ao);
|
||||
int r = init_device(ao, false);
|
||||
if (r == INIT_BRAINDEATH)
|
||||
r = init_device(ao); // retry with normalized channel layout
|
||||
r = init_device(ao, true); // retry with normalized channel layout
|
||||
return r == INIT_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
|
||||
|
||||
int64_t end = mp_time_us();
|
||||
end += p->hw_latency_us + ca_get_latency(ts) + ca_frames_to_us(ao, frames);
|
||||
|
||||
ao_read_data(ao, &buf.mData, frames, end);
|
||||
return noErr;
|
||||
}
|
||||
@@ -381,8 +380,8 @@ static int hotplug_init(struct ao *ao)
|
||||
err = AudioObjectAddPropertyListener(
|
||||
kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao);
|
||||
if (err != noErr) {
|
||||
char *c1 = fourcc_repr(ao, hotplug_properties[i]);
|
||||
char *c2 = fourcc_repr(ao, err);
|
||||
char *c1 = fourcc_repr(hotplug_properties[i]);
|
||||
char *c2 = fourcc_repr(err);
|
||||
MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2);
|
||||
goto coreaudio_error;
|
||||
}
|
||||
@@ -406,8 +405,8 @@ static void hotplug_uninit(struct ao *ao)
|
||||
err = AudioObjectRemovePropertyListener(
|
||||
kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao);
|
||||
if (err != noErr) {
|
||||
char *c1 = fourcc_repr(ao, hotplug_properties[i]);
|
||||
char *c2 = fourcc_repr(ao, err);
|
||||
char *c1 = fourcc_repr(hotplug_properties[i]);
|
||||
char *c2 = fourcc_repr(err);
|
||||
MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2);
|
||||
}
|
||||
}
|
||||
@@ -445,7 +444,9 @@ static const int speaker_map[][2] = {
|
||||
{ kAudioChannelLabel_HeadphonesLeft, MP_SPEAKER_ID_DL },
|
||||
{ kAudioChannelLabel_HeadphonesRight, MP_SPEAKER_ID_DR },
|
||||
|
||||
{ kAudioChannelLabel_Unknown, MP_SPEAKER_ID_UNKNOWN0 },
|
||||
{ kAudioChannelLabel_Unknown, MP_SPEAKER_ID_NA },
|
||||
|
||||
{ 0, -1 },
|
||||
};
|
||||
|
||||
static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
|
||||
@@ -541,9 +542,10 @@ bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
|
||||
return false;
|
||||
}
|
||||
|
||||
int next_na = MP_SPEAKER_ID_NA;
|
||||
for (int n = 0; n < l->mNumberChannelDescriptions; n++) {
|
||||
AudioChannelLabel label = l->mChannelDescriptions[n].mChannelLabel;
|
||||
uint8_t speaker = ca_label_to_mp_speaker_id(label);
|
||||
int speaker = ca_label_to_mp_speaker_id(label);
|
||||
if (speaker < 0) {
|
||||
MP_VERBOSE(ao, "channel label=%u unusable to build channel "
|
||||
"bitmap, skipping layout\n", (unsigned) label);
|
||||
|
||||
@@ -41,12 +41,13 @@
|
||||
#include "internal.h"
|
||||
#include "audio/format.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "options/m_option.h"
|
||||
#include "common/msg.h"
|
||||
#include "audio/out/ao_coreaudio_properties.h"
|
||||
#include "audio/out/ao_coreaudio_utils.h"
|
||||
|
||||
static bool ca_format_is_digital(AudioStreamBasicDescription asbd)
|
||||
static bool ca_format_is_compressed(AudioStreamBasicDescription asbd)
|
||||
{
|
||||
switch (asbd.mFormatID)
|
||||
case 'IAC3':
|
||||
@@ -57,7 +58,7 @@ static bool ca_format_is_digital(AudioStreamBasicDescription asbd)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ca_stream_supports_digital(struct ao *ao, AudioStreamID stream)
|
||||
static bool ca_stream_supports_compressed(struct ao *ao, AudioStreamID stream)
|
||||
{
|
||||
AudioStreamRangedDescription *formats = NULL;
|
||||
size_t n_formats;
|
||||
@@ -71,7 +72,7 @@ static bool ca_stream_supports_digital(struct ao *ao, AudioStreamID stream)
|
||||
for (int i = 0; i < n_formats; i++) {
|
||||
AudioStreamBasicDescription asbd = formats[i].mFormat;
|
||||
ca_print_asbd(ao, "supported format:", &(asbd));
|
||||
if (ca_format_is_digital(asbd)) {
|
||||
if (ca_format_is_compressed(asbd)) {
|
||||
talloc_free(formats);
|
||||
return true;
|
||||
}
|
||||
@@ -82,7 +83,7 @@ coreaudio_error:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ca_device_supports_digital(struct ao *ao, AudioDeviceID device)
|
||||
static bool ca_device_supports_compressed(struct ao *ao, AudioDeviceID device)
|
||||
{
|
||||
AudioStreamID *streams = NULL;
|
||||
size_t n_streams;
|
||||
@@ -94,7 +95,7 @@ static bool ca_device_supports_digital(struct ao *ao, AudioDeviceID device)
|
||||
CHECK_CA_ERROR("could not get number of streams.");
|
||||
|
||||
for (int i = 0; i < n_streams; i++) {
|
||||
if (ca_stream_supports_digital(ao, streams[i])) {
|
||||
if (ca_stream_supports_compressed(ao, streams[i])) {
|
||||
talloc_free(streams);
|
||||
return true;
|
||||
}
|
||||
@@ -112,15 +113,13 @@ static OSStatus ca_property_listener(
|
||||
const AudioObjectPropertyAddress addresses[],
|
||||
void *data)
|
||||
{
|
||||
void *talloc_ctx = talloc_new(NULL);
|
||||
|
||||
for (int i = 0; i < n_addresses; i++) {
|
||||
if (addresses[i].mSelector == selector) {
|
||||
if (data) *(volatile int *)data = 1;
|
||||
if (data)
|
||||
atomic_store((atomic_bool *)data, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
talloc_free(talloc_ctx);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
@@ -242,7 +241,7 @@ static bool ca_change_format(struct ao *ao, AudioStreamID stream,
|
||||
{
|
||||
OSStatus err = noErr;
|
||||
AudioObjectPropertyAddress p_addr;
|
||||
volatile int stream_format_changed = 0;
|
||||
atomic_bool stream_format_changed = ATOMIC_VAR_INIT(false);
|
||||
|
||||
ca_print_asbd(ao, "setting stream format:", &change_format);
|
||||
|
||||
@@ -254,7 +253,7 @@ static bool ca_change_format(struct ao *ao, AudioStreamID stream,
|
||||
};
|
||||
|
||||
err = AudioObjectAddPropertyListener(stream, &p_addr, ca_stream_listener,
|
||||
(void *)&stream_format_changed);
|
||||
&stream_format_changed);
|
||||
if (!CHECK_CA_WARN("can't add property listener during format change")) {
|
||||
return false;
|
||||
}
|
||||
@@ -265,33 +264,32 @@ static bool ca_change_format(struct ao *ao, AudioStreamID stream,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The AudioStreamSetProperty is not only asynchronious,
|
||||
/* The AudioStreamSetProperty is not only asynchronous,
|
||||
* it is also not Atomic, in its behaviour.
|
||||
* Therefore we check 5 times before we really give up. */
|
||||
bool format_set = false;
|
||||
for (int i = 0; !format_set && i < 5; i++) {
|
||||
for (int j = 0; !stream_format_changed && j < 50; j++)
|
||||
AudioStreamBasicDescription actual_format = {0};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format);
|
||||
if (!CHECK_CA_WARN("could not retrieve physical format"))
|
||||
break;
|
||||
|
||||
format_set = ca_asbd_equals(&change_format, &actual_format);
|
||||
if (format_set)
|
||||
break;
|
||||
|
||||
for (int j = 0; !atomic_load(&stream_format_changed) && j < 50; j++)
|
||||
mp_sleep_us(10000);
|
||||
|
||||
if (stream_format_changed) {
|
||||
stream_format_changed = 0;
|
||||
} else {
|
||||
bool old = true;
|
||||
if (!atomic_compare_exchange_strong(&stream_format_changed, &old, false))
|
||||
MP_VERBOSE(ao, "reached timeout\n");
|
||||
}
|
||||
|
||||
AudioStreamBasicDescription actual_format;
|
||||
err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format);
|
||||
|
||||
ca_print_asbd(ao, "actual format in use:", &actual_format);
|
||||
if (actual_format.mSampleRate == change_format.mSampleRate &&
|
||||
actual_format.mFormatID == change_format.mFormatID &&
|
||||
actual_format.mFramesPerPacket == change_format.mFramesPerPacket) {
|
||||
format_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
ca_print_asbd(ao, "actual format in use:", &actual_format);
|
||||
|
||||
err = AudioObjectRemovePropertyListener(stream, &p_addr, ca_stream_listener,
|
||||
(void *)&stream_format_changed);
|
||||
&stream_format_changed);
|
||||
|
||||
if (!CHECK_CA_WARN("can't remove property listener")) {
|
||||
return false;
|
||||
@@ -306,32 +304,30 @@ struct priv {
|
||||
|
||||
bool paused;
|
||||
|
||||
// digital render callback
|
||||
// audio render callback
|
||||
AudioDeviceIOProcID render_cb;
|
||||
|
||||
// pid set for hog mode, (-1) means that hog mode on the device was
|
||||
// released. hog mode is exclusive access to a device
|
||||
pid_t hog_pid;
|
||||
|
||||
// stream selected for digital playback by the detection in init
|
||||
AudioStreamID stream;
|
||||
|
||||
// stream index in an AudioBufferList
|
||||
int stream_idx;
|
||||
|
||||
// format we changed the stream to: for the digital case each application
|
||||
// sets the stream format for a device to what it needs
|
||||
// format we changed the stream to, and the original format to restore
|
||||
AudioStreamBasicDescription stream_asbd;
|
||||
AudioStreamBasicDescription original_asbd;
|
||||
|
||||
bool changed_mixing;
|
||||
int stream_asbd_changed;
|
||||
atomic_bool stream_asbd_changed;
|
||||
bool reload_requested;
|
||||
|
||||
uint32_t hw_latency_us;
|
||||
};
|
||||
|
||||
static OSStatus render_cb_digital(
|
||||
static OSStatus render_cb_compressed(
|
||||
AudioDeviceID device, const AudioTimeStamp *ts,
|
||||
const void *in_data, const AudioTimeStamp *in_ts,
|
||||
AudioBufferList *out_data, const AudioTimeStamp *out_ts, void *ctx)
|
||||
@@ -355,58 +351,47 @@ static OSStatus render_cb_digital(
|
||||
|
||||
ao_read_data(ao, &buf.mData, pseudo_frames, end);
|
||||
|
||||
// Check whether we need to reset the digital output stream.
|
||||
if (p->stream_asbd_changed) {
|
||||
p->stream_asbd_changed = 0;
|
||||
if (!p->reload_requested && ca_stream_supports_digital(ao, p->stream)) {
|
||||
p->reload_requested = true;
|
||||
ao_request_reload(ao);
|
||||
MP_INFO(ao, "Stream format changed! Reloading.\n");
|
||||
}
|
||||
// Check whether we need to reset the compressed output stream.
|
||||
if (atomic_load(&p->stream_asbd_changed)) {
|
||||
AudioStreamBasicDescription f;
|
||||
OSErr err = CA_GET(p->stream, kAudioStreamPropertyPhysicalFormat, &f);
|
||||
CHECK_CA_WARN("could not get stream format");
|
||||
if (err == noErr && ca_asbd_equals(&p->stream_asbd, &f))
|
||||
atomic_store(&p->stream_asbd_changed, false);
|
||||
}
|
||||
|
||||
if (atomic_load(&p->stream_asbd_changed) && !p->reload_requested) {
|
||||
p->reload_requested = true;
|
||||
ao_request_reload(ao);
|
||||
MP_INFO(ao, "Stream format changed! Reloading.\n");
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd);
|
||||
|
||||
static int init(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
OSStatus err = ca_select_device(ao, ao->device, &p->device);
|
||||
CHECK_CA_ERROR("failed to select device");
|
||||
CHECK_CA_ERROR_L(coreaudio_error_nounlock, "failed to select device");
|
||||
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
bool supports_digital = false;
|
||||
/* Probe whether device support S/PDIF stream output if input is AC3 stream,
|
||||
* or anything else IEC61937-framed. */
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
if (ca_device_supports_digital(ao, p->device))
|
||||
supports_digital = true;
|
||||
if (!AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
MP_ERR(ao, "Only compressed formats are supported.\n");
|
||||
goto coreaudio_error_nounlock;
|
||||
}
|
||||
|
||||
if (!supports_digital) {
|
||||
MP_ERR(ao, "selected device doesn't support digital formats\n");
|
||||
goto coreaudio_error;
|
||||
} // closes if (!supports_digital)
|
||||
if (!ca_device_supports_compressed(ao, p->device)) {
|
||||
MP_ERR(ao, "selected device doesn't support compressed formats\n");
|
||||
goto coreaudio_error_nounlock;
|
||||
}
|
||||
|
||||
// Build ASBD for the input format
|
||||
AudioStreamBasicDescription asbd;
|
||||
ca_fill_asbd(ao, &asbd);
|
||||
|
||||
return init_digital(ao, asbd);
|
||||
|
||||
coreaudio_error:
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
|
||||
static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
OSStatus err = noErr;
|
||||
|
||||
uint32_t is_alive = 1;
|
||||
err = CA_GET(p->device, kAudioDevicePropertyDeviceIsAlive, &is_alive);
|
||||
CHECK_CA_WARN("could not check whether device is alive");
|
||||
@@ -430,15 +415,9 @@ static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
|
||||
CHECK_CA_ERROR("could not get number of streams");
|
||||
|
||||
for (int i = 0; i < n_streams && p->stream_idx < 0; i++) {
|
||||
bool digital = ca_stream_supports_digital(ao, streams[i]);
|
||||
|
||||
if (digital) {
|
||||
err = CA_GET(streams[i], kAudioStreamPropertyPhysicalFormat,
|
||||
&p->original_asbd);
|
||||
if (!CHECK_CA_WARN("could not get stream's physical format to "
|
||||
"revert to, getting the next one"))
|
||||
continue;
|
||||
bool compressed = ca_stream_supports_compressed(ao, streams[i]);
|
||||
|
||||
if (compressed) {
|
||||
AudioStreamRangedDescription *formats;
|
||||
size_t n_formats;
|
||||
|
||||
@@ -456,8 +435,8 @@ static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
|
||||
p->stream_idx = i;
|
||||
|
||||
for (int j = 0; j < n_formats; j++)
|
||||
if (ca_format_is_digital(formats[j].mFormat)) {
|
||||
// select the digital format that has exactly the same
|
||||
if (ca_format_is_compressed(formats[j].mFormat)) {
|
||||
// select the compressed format that has exactly the same
|
||||
// samplerate. If an exact match cannot be found, select
|
||||
// the format with highest samplerate as backup.
|
||||
if (formats[j].mFormat.mSampleRate == asbd.mSampleRate) {
|
||||
@@ -481,14 +460,18 @@ static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
|
||||
talloc_free(streams);
|
||||
|
||||
if (p->stream_idx < 0) {
|
||||
MP_WARN(ao , "can't find any digital output stream format\n");
|
||||
MP_WARN(ao , "can't find any compressed output stream format\n");
|
||||
goto coreaudio_error;
|
||||
}
|
||||
|
||||
err = CA_GET(p->stream, kAudioStreamPropertyPhysicalFormat,
|
||||
&p->original_asbd);
|
||||
CHECK_CA_ERROR("could not get stream's original physical format");
|
||||
|
||||
if (!ca_change_format(ao, p->stream, p->stream_asbd))
|
||||
goto coreaudio_error;
|
||||
|
||||
void *changed = (void *) &(p->stream_asbd_changed);
|
||||
void *changed = &p->stream_asbd_changed;
|
||||
err = ca_enable_device_listener(p->device, changed);
|
||||
CHECK_CA_ERROR("cannot install format change listener during init");
|
||||
|
||||
@@ -501,26 +484,34 @@ static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
|
||||
p->stream_asbd.mFramesPerPacket);
|
||||
|
||||
uint32_t latency_frames = 0;
|
||||
err = CA_GET_O(p->device, kAudioDevicePropertyLatency, &latency_frames);
|
||||
if (err != noErr) {
|
||||
uint32_t latency_properties[] = {
|
||||
kAudioDevicePropertyLatency,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioDevicePropertySafetyOffset,
|
||||
};
|
||||
for (int n = 0; n < MP_ARRAY_SIZE(latency_properties); n++) {
|
||||
uint32_t temp;
|
||||
err = CA_GET_O(p->device, latency_properties[n], &temp);
|
||||
CHECK_CA_WARN("cannot get device latency");
|
||||
latency_frames = 0;
|
||||
if (err == noErr)
|
||||
latency_frames += temp;
|
||||
}
|
||||
|
||||
p->hw_latency_us = ca_frames_to_us(ao, latency_frames);
|
||||
MP_VERBOSE(ao, "base latency: %d microseconds\n", (int)p->hw_latency_us);
|
||||
|
||||
err = AudioDeviceCreateIOProcID(p->device,
|
||||
(AudioDeviceIOProc)render_cb_digital,
|
||||
(AudioDeviceIOProc)render_cb_compressed,
|
||||
(void *)ao,
|
||||
&p->render_cb);
|
||||
CHECK_CA_ERROR("failed to register digital render callback");
|
||||
CHECK_CA_ERROR("failed to register audio render callback");
|
||||
|
||||
return CONTROL_TRUE;
|
||||
|
||||
coreaudio_error:
|
||||
err = ca_unlock_device(p->device, &p->hog_pid);
|
||||
CHECK_CA_WARN("can't release hog mode");
|
||||
coreaudio_error_nounlock:
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
|
||||
@@ -529,7 +520,7 @@ static void uninit(struct ao *ao)
|
||||
struct priv *p = ao->priv;
|
||||
OSStatus err = noErr;
|
||||
|
||||
void *changed = (void *) &(p->stream_asbd_changed);
|
||||
void *changed = &p->stream_asbd_changed;
|
||||
err = ca_disable_device_listener(p->device, changed);
|
||||
CHECK_CA_WARN("can't remove device listener, this may cause a crash");
|
||||
|
||||
@@ -554,7 +545,7 @@ static void audio_pause(struct ao *ao)
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
OSStatus err = AudioDeviceStop(p->device, p->render_cb);
|
||||
CHECK_CA_WARN("can't stop digital device");
|
||||
CHECK_CA_WARN("can't stop audio device");
|
||||
}
|
||||
|
||||
static void audio_resume(struct ao *ao)
|
||||
@@ -562,7 +553,7 @@ static void audio_resume(struct ao *ao)
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
OSStatus err = AudioDeviceStart(p->device, p->render_cb);
|
||||
CHECK_CA_WARN("can't start digital device");
|
||||
CHECK_CA_WARN("can't start audio device");
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
@@ -577,7 +568,7 @@ const struct ao_driver audio_out_coreaudio_exclusive = {
|
||||
.list_devs = ca_get_device_list,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.priv_defaults = &(const struct priv){
|
||||
.stream_asbd_changed = 0,
|
||||
.stream_asbd_changed = ATOMIC_VAR_INIT(false),
|
||||
.hog_pid = -1,
|
||||
.stream = 0,
|
||||
.stream_idx = -1,
|
||||
|
||||
+113
-28
@@ -88,7 +88,7 @@ OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device)
|
||||
OSStatus err = noErr;
|
||||
*device = kAudioObjectUnknown;
|
||||
|
||||
if (name) {
|
||||
if (name && name[0]) {
|
||||
CFStringRef uid = cfstr_from_cstr(name);
|
||||
AudioValueTranslation v = (AudioValueTranslation) {
|
||||
.mInputData = &uid,
|
||||
@@ -116,9 +116,8 @@ OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device)
|
||||
|
||||
if (mp_msg_test(ao->log, MSGL_V)) {
|
||||
char *desc;
|
||||
err = CA_GET_STR(*device, kAudioObjectPropertyName, &desc);
|
||||
CHECK_CA_WARN("could not get selected audio device name");
|
||||
if (err == noErr) {
|
||||
OSStatus err2 = CA_GET_STR(*device, kAudioObjectPropertyName, &desc);
|
||||
if (err2 == noErr) {
|
||||
MP_VERBOSE(ao, "selected audio output device: %s (%" PRIu32 ")\n",
|
||||
desc, *device);
|
||||
talloc_free(desc);
|
||||
@@ -129,7 +128,7 @@ coreaudio_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
char *fourcc_repr(void *talloc_ctx, uint32_t code)
|
||||
char *fourcc_repr_buf(char *buf, size_t buf_size, uint32_t code)
|
||||
{
|
||||
// Extract FourCC letters from the uint32_t and finde out if it's a valid
|
||||
// code that is made of letters.
|
||||
@@ -146,43 +145,40 @@ char *fourcc_repr(void *talloc_ctx, uint32_t code)
|
||||
valid_fourcc = false;
|
||||
}
|
||||
|
||||
char *repr;
|
||||
if (valid_fourcc)
|
||||
repr = talloc_asprintf(talloc_ctx, "'%c%c%c%c'",
|
||||
fcc[0], fcc[1], fcc[2], fcc[3]);
|
||||
snprintf(buf, buf_size, "'%c%c%c%c'", fcc[0], fcc[1], fcc[2], fcc[3]);
|
||||
else
|
||||
repr = talloc_asprintf(NULL, "%u", (unsigned int)code);
|
||||
snprintf(buf, buf_size, "%u", (unsigned int)code);
|
||||
|
||||
return repr;
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
|
||||
{
|
||||
if (code == noErr) return true;
|
||||
|
||||
char *error_string = fourcc_repr(NULL, code);
|
||||
mp_msg(ao->log, level, "%s (%s)\n", message, error_string);
|
||||
talloc_free(error_string);
|
||||
mp_msg(ao->log, level, "%s (%s)\n", message, fourcc_repr(code));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd)
|
||||
static void ca_fill_asbd_raw(AudioStreamBasicDescription *asbd, int mp_format,
|
||||
int samplerate, int num_channels)
|
||||
{
|
||||
asbd->mSampleRate = ao->samplerate;
|
||||
asbd->mSampleRate = samplerate;
|
||||
// Set "AC3" for other spdif formats too - unknown if that works.
|
||||
asbd->mFormatID = AF_FORMAT_IS_IEC61937(ao->format) ?
|
||||
asbd->mFormatID = AF_FORMAT_IS_IEC61937(mp_format) ?
|
||||
kAudioFormat60958AC3 :
|
||||
kAudioFormatLinearPCM;
|
||||
asbd->mChannelsPerFrame = ao->channels.num;
|
||||
asbd->mBitsPerChannel = af_fmt2bits(ao->format);
|
||||
asbd->mChannelsPerFrame = num_channels;
|
||||
asbd->mBitsPerChannel = af_fmt2bits(mp_format);
|
||||
asbd->mFormatFlags = kAudioFormatFlagIsPacked;
|
||||
|
||||
if ((ao->format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_F)
|
||||
if ((mp_format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_F) {
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsFloat;
|
||||
|
||||
if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI)
|
||||
} else if ((mp_format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI) {
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
|
||||
}
|
||||
|
||||
if (BYTE_ORDER == BIG_ENDIAN)
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsBigEndian;
|
||||
@@ -193,17 +189,66 @@ void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd)
|
||||
(asbd->mBitsPerChannel / 8);
|
||||
}
|
||||
|
||||
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd)
|
||||
{
|
||||
ca_fill_asbd_raw(asbd, ao->format, ao->samplerate, ao->channels.num);
|
||||
}
|
||||
|
||||
static bool ca_formatid_is_digital(uint32_t formatid)
|
||||
{
|
||||
switch (formatid)
|
||||
case 'IAC3':
|
||||
case 'iac3':
|
||||
case kAudioFormat60958AC3:
|
||||
case kAudioFormatAC3:
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// This might be wrong, but for now it's sufficient for us.
|
||||
static uint32_t ca_normalize_formatid(uint32_t formatID)
|
||||
{
|
||||
return ca_formatid_is_digital(formatID) ? kAudioFormat60958AC3 : formatID;
|
||||
}
|
||||
|
||||
bool ca_asbd_equals(const AudioStreamBasicDescription *a,
|
||||
const AudioStreamBasicDescription *b)
|
||||
{
|
||||
int flags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsFloat |
|
||||
kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian;
|
||||
|
||||
return (a->mFormatFlags & flags) == (b->mFormatFlags & flags) &&
|
||||
a->mBitsPerChannel == b->mBitsPerChannel &&
|
||||
ca_normalize_formatid(a->mFormatID) ==
|
||||
ca_normalize_formatid(b->mFormatID) &&
|
||||
a->mBytesPerPacket == b->mBytesPerPacket;
|
||||
}
|
||||
|
||||
// Return the AF_FORMAT_* (AF_FORMAT_S16 etc.) corresponding to the asbd.
|
||||
int ca_asbd_to_mp_format(const AudioStreamBasicDescription *asbd)
|
||||
{
|
||||
for (int n = 0; af_fmtstr_table[n].format; n++) {
|
||||
int mp_format = af_fmtstr_table[n].format;
|
||||
AudioStreamBasicDescription mp_asbd = {0};
|
||||
ca_fill_asbd_raw(&mp_asbd, mp_format, 0, asbd->mChannelsPerFrame);
|
||||
if (ca_asbd_equals(&mp_asbd, asbd))
|
||||
return mp_format;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ca_print_asbd(struct ao *ao, const char *description,
|
||||
const AudioStreamBasicDescription *asbd)
|
||||
{
|
||||
uint32_t flags = asbd->mFormatFlags;
|
||||
char *format = fourcc_repr(NULL, asbd->mFormatID);
|
||||
char *format = fourcc_repr(asbd->mFormatID);
|
||||
int mpfmt = ca_asbd_to_mp_format(asbd);
|
||||
|
||||
MP_VERBOSE(ao,
|
||||
"%s %7.1fHz %" PRIu32 "bit [%s]"
|
||||
"[%" PRIu32 "][%" PRIu32 "][%" PRIu32 "]"
|
||||
"[%" PRIu32 "][%" PRIu32 "] "
|
||||
"%s %s %s%s%s%s\n",
|
||||
"%s %7.1fHz %" PRIu32 "bit %s "
|
||||
"[%" PRIu32 "][%" PRIu32 "bpp][%" PRIu32 "fbp]"
|
||||
"[%" PRIu32 "bpf][%" PRIu32 "ch] "
|
||||
"%s %s %s%s%s%s (%s)\n",
|
||||
description, asbd->mSampleRate, asbd->mBitsPerChannel, format,
|
||||
asbd->mFormatFlags, asbd->mBytesPerPacket, asbd->mFramesPerPacket,
|
||||
asbd->mBytesPerFrame, asbd->mChannelsPerFrame,
|
||||
@@ -212,9 +257,49 @@ void ca_print_asbd(struct ao *ao, const char *description,
|
||||
(flags & kAudioFormatFlagIsSignedInteger) ? "S" : "U",
|
||||
(flags & kAudioFormatFlagIsPacked) ? " packed" : "",
|
||||
(flags & kAudioFormatFlagIsAlignedHigh) ? " aligned" : "",
|
||||
(flags & kAudioFormatFlagIsNonInterleaved) ? " P" : "");
|
||||
(flags & kAudioFormatFlagIsNonInterleaved) ? " P" : "",
|
||||
mpfmt ? af_fmt_to_str(mpfmt) : "-");
|
||||
}
|
||||
|
||||
talloc_free(format);
|
||||
// Return whether new is an improvement over old. Assume a higher value means
|
||||
// better quality, and we always prefer the value closest to the requested one,
|
||||
// which is still larger than the requested one.
|
||||
// Equal values prefer the new one (so ca_asbd_is_better() checks other params).
|
||||
static bool value_is_better(double req, double old, double new)
|
||||
{
|
||||
if (new >= req) {
|
||||
return old < req || new <= old;
|
||||
} else {
|
||||
return old < req && new >= old;
|
||||
}
|
||||
}
|
||||
|
||||
// Return whether new is an improvement over old (req is the requested format).
|
||||
bool ca_asbd_is_better(AudioStreamBasicDescription *req,
|
||||
AudioStreamBasicDescription *old,
|
||||
AudioStreamBasicDescription *new)
|
||||
{
|
||||
if (new->mChannelsPerFrame > MP_NUM_CHANNELS)
|
||||
return false;
|
||||
if (old->mChannelsPerFrame > MP_NUM_CHANNELS)
|
||||
return true;
|
||||
if (req->mFormatID != new->mFormatID)
|
||||
return false;
|
||||
if (req->mFormatID != old->mFormatID)
|
||||
return true;
|
||||
|
||||
if (!value_is_better(req->mBitsPerChannel, old->mBitsPerChannel,
|
||||
new->mBitsPerChannel))
|
||||
return false;
|
||||
|
||||
if (!value_is_better(req->mSampleRate, old->mSampleRate, new->mSampleRate))
|
||||
return false;
|
||||
|
||||
if (!value_is_better(req->mChannelsPerFrame, old->mChannelsPerFrame,
|
||||
new->mChannelsPerFrame))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t ca_frames_to_us(struct ao *ao, uint32_t frames)
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
CFStringRef cfstr_from_cstr(char *str);
|
||||
char *cfstr_get_cstr(CFStringRef cfstr);
|
||||
|
||||
char *fourcc_repr(void *talloc_ctx, uint32_t code);
|
||||
char *fourcc_repr_buf(char *buf, size_t buf_size, uint32_t code);
|
||||
#define fourcc_repr(code) fourcc_repr_buf((char[40]){0}, 40, code)
|
||||
|
||||
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message);
|
||||
|
||||
#define CHECK_CA_ERROR_L(label, message) \
|
||||
@@ -55,6 +57,12 @@ OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device);
|
||||
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd);
|
||||
void ca_print_asbd(struct ao *ao, const char *description,
|
||||
const AudioStreamBasicDescription *asbd);
|
||||
bool ca_asbd_equals(const AudioStreamBasicDescription *a,
|
||||
const AudioStreamBasicDescription *b);
|
||||
int ca_asbd_to_mp_format(const AudioStreamBasicDescription *asbd);
|
||||
bool ca_asbd_is_better(AudioStreamBasicDescription *req,
|
||||
AudioStreamBasicDescription *old,
|
||||
AudioStreamBasicDescription *new);
|
||||
|
||||
int64_t ca_frames_to_us(struct ao *ao, uint32_t frames);
|
||||
int64_t ca_get_latency(const AudioTimeStamp *ts);
|
||||
|
||||
@@ -98,7 +98,9 @@ static const struct mp_chmap sndio_layouts[MP_NUM_CHANNELS + 1] = {
|
||||
{0}, // 6.1
|
||||
MP_CHMAP8(FL, FR, BL, BR, FC, LFE, SL, SR), // 7.1
|
||||
/* above is the fixed channel assignment for sndio, since we need to fill
|
||||
all channels and cannot insert silence, not all layouts are supported. */
|
||||
all channels and cannot insert silence, not all layouts are supported.
|
||||
NOTE: MP_SPEAKER_ID_NA could be used to add padding channels. Keep
|
||||
in mind that they don't actually contain silence. */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -59,7 +59,7 @@ struct ao {
|
||||
char *redirect;
|
||||
|
||||
// Internal events (use ao_request_reload(), ao_hotplug_event())
|
||||
atomic_bool request_reload, request_hotplug;
|
||||
atomic_int events_;
|
||||
|
||||
int buffer;
|
||||
double def_buffer;
|
||||
|
||||
+6
-3
@@ -340,7 +340,8 @@ static void *playthread(void *arg)
|
||||
pthread_cond_signal(&p->wakeup); // for draining
|
||||
|
||||
if (p->still_playing && timeout > 0) {
|
||||
mpthread_cond_timedwait_rel(&p->wakeup, &p->lock, timeout);
|
||||
struct timespec ts = mp_rel_time_to_timespec(timeout);
|
||||
pthread_cond_timedwait(&p->wakeup, &p->lock, &ts);
|
||||
} else {
|
||||
pthread_cond_wait(&p->wakeup, &p->lock);
|
||||
}
|
||||
@@ -352,8 +353,10 @@ static void *playthread(void *arg)
|
||||
if (ao->driver->get_delay)
|
||||
timeout = ao->driver->get_delay(ao);
|
||||
timeout *= 0.25; // wake up if 25% played
|
||||
if (!p->need_wakeup)
|
||||
mpthread_cond_timedwait_rel(&p->wakeup, &p->lock, timeout);
|
||||
if (!p->need_wakeup) {
|
||||
struct timespec ts = mp_rel_time_to_timespec(timeout);
|
||||
pthread_cond_timedwait(&p->wakeup, &p->lock, &ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
MP_STATS(ao, "end audio wait");
|
||||
|
||||
@@ -95,6 +95,12 @@ void mp_rect_union(struct mp_rect *rc, const struct mp_rect *rc2)
|
||||
rc->y1 = FFMAX(rc->y1, rc2->y1);
|
||||
}
|
||||
|
||||
// Returns whether or not a point is contained by rc
|
||||
bool mp_rect_contains(struct mp_rect *rc, int x, int y)
|
||||
{
|
||||
return rc->x0 <= x && x < rc->x1 && rc->y0 <= y && y < rc->y1;
|
||||
}
|
||||
|
||||
// Set rc to the intersection of rc and src.
|
||||
// Return false if the result is empty.
|
||||
bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2)
|
||||
|
||||
@@ -75,6 +75,7 @@ struct mp_rect {
|
||||
|
||||
void mp_rect_union(struct mp_rect *rc, const struct mp_rect *src);
|
||||
bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2);
|
||||
bool mp_rect_contains(struct mp_rect *rc, int x, int y);
|
||||
|
||||
int mp_snprintf_cat(char *str, size_t size, const char *format, ...)
|
||||
PRINTF_ATTRIBUTE(3, 4);
|
||||
|
||||
+3
-1
@@ -702,7 +702,9 @@ void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y)
|
||||
input_lock(ictx);
|
||||
MP_DBG(ictx, "mouse move %d/%d\n", x, y);
|
||||
|
||||
if (!ictx->opts->enable_mouse_movements) {
|
||||
if ((ictx->mouse_vo_x == x && ictx->mouse_vo_y == y) ||
|
||||
!ictx->opts->enable_mouse_movements)
|
||||
{
|
||||
input_unlock(ictx);
|
||||
return;
|
||||
}
|
||||
|
||||
+2
-1
@@ -206,7 +206,8 @@ void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout)
|
||||
}
|
||||
} else {
|
||||
if (wait > 0) {
|
||||
mpthread_cond_timedwait(&queue->cond, &queue->lock, wait);
|
||||
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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -218,11 +218,11 @@ int json_parse(void *ta_parent, struct mpv_node *dst, char **src, int max_depth)
|
||||
|
||||
#define APPEND(b, s) bstr_xappend(NULL, (b), bstr0(s))
|
||||
|
||||
static void write_json_str(bstr *b, char *str)
|
||||
static void write_json_str(bstr *b, unsigned char *str)
|
||||
{
|
||||
APPEND(b, "\"");
|
||||
while (1) {
|
||||
char *cur = str;
|
||||
unsigned char *cur = str;
|
||||
while (cur[0] && cur[0] >= 32 && cur[0] != '"' && cur[0] != '\\')
|
||||
cur++;
|
||||
if (!cur[0])
|
||||
|
||||
+1
-1
@@ -709,7 +709,7 @@ check_pkg_config "OpenAL" $_openal OPENAL 'openal >= 1.13'
|
||||
|
||||
check_pkg_config "ALSA audio" $_alsa ALSA 'alsa >= 1.0.9'
|
||||
|
||||
check_pkg_config "Blu-ray support" $_bluray LIBBLURAY 'libbluray >= 0.2.1'
|
||||
check_pkg_config "Blu-ray support" $_bluray LIBBLURAY 'libbluray >= 0.3.0'
|
||||
|
||||
check_pkg_config "dvdread" $_dvdread DVDREAD 'dvdread >= 4.1.0'
|
||||
|
||||
|
||||
+2
-1
@@ -195,6 +195,7 @@ SOURCES = audio/audio.c \
|
||||
options/parse_configfile.c \
|
||||
options/path.c \
|
||||
osdep/io.c \
|
||||
osdep/path-unix.c \
|
||||
osdep/semaphore_osx.c \
|
||||
osdep/subprocess.c \
|
||||
osdep/subprocess-posix.c \
|
||||
@@ -334,7 +335,7 @@ all: $(ALL_TARGETS)
|
||||
%.o: %.c
|
||||
$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
mpv: $(OBJECTS) player/main-fn-unix.o
|
||||
mpv: $(OBJECTS) osdep/main-fn-unix.o
|
||||
$(CC) -o $@ $^ $(EXTRALIBS)
|
||||
|
||||
input/input.c: input/input_conf.h
|
||||
|
||||
+14
-9
@@ -102,12 +102,10 @@ static int parse_profile(struct m_config *config, const struct m_option *opt,
|
||||
if (!list || !list[0])
|
||||
return M_OPT_INVALID;
|
||||
for (int i = 0; list[i]; i++) {
|
||||
struct m_profile *p = m_config_get_profile0(config, list[i]);
|
||||
if (!p) {
|
||||
MP_WARN(config, "Unknown profile '%s'.\n", list[i]);
|
||||
r = M_OPT_INVALID;
|
||||
} else if (set)
|
||||
m_config_set_profile(config, p, flags);
|
||||
if (set)
|
||||
r = m_config_set_profile(config, list[i], flags);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
m_option_free(opt, &list);
|
||||
return r;
|
||||
@@ -897,12 +895,17 @@ int m_config_set_profile_option(struct m_config *config, struct m_profile *p,
|
||||
return 1;
|
||||
}
|
||||
|
||||
void m_config_set_profile(struct m_config *config, struct m_profile *p,
|
||||
int flags)
|
||||
int m_config_set_profile(struct m_config *config, char *name, int flags)
|
||||
{
|
||||
struct m_profile *p = m_config_get_profile0(config, name);
|
||||
if (!p) {
|
||||
MP_WARN(config, "Unknown profile '%s'.\n", name);
|
||||
return M_OPT_INVALID;
|
||||
}
|
||||
|
||||
if (config->profile_depth > MAX_PROFILE_DEPTH) {
|
||||
MP_WARN(config, "WARNING: Profile inclusion too deep.\n");
|
||||
return;
|
||||
return M_OPT_UNKNOWN;
|
||||
}
|
||||
config->profile_depth++;
|
||||
for (int i = 0; i < p->num_opts; i++) {
|
||||
@@ -912,6 +915,8 @@ void m_config_set_profile(struct m_config *config, struct m_profile *p,
|
||||
flags | M_SETOPT_FROM_CONFIG_FILE);
|
||||
}
|
||||
config->profile_depth--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *m_config_alloc_struct(void *talloc_ctx,
|
||||
|
||||
+2
-2
@@ -241,9 +241,9 @@ int m_config_set_profile_option(struct m_config *config, struct m_profile *p,
|
||||
* \param config The config object.
|
||||
* \param p The profile object.
|
||||
* \param flags M_SETOPT_* bits
|
||||
* Returns error code (<0) or 0 on success
|
||||
*/
|
||||
void m_config_set_profile(struct m_config *config, struct m_profile *p,
|
||||
int flags);
|
||||
int m_config_set_profile(struct m_config *config, char *name, int flags);
|
||||
|
||||
void *m_config_alloc_struct(void *talloc_ctx,
|
||||
const struct m_sub_options *subopts);
|
||||
|
||||
+1
-1
@@ -691,7 +691,7 @@ const struct MPOpts mp_default_opts = {
|
||||
#if HAVE_LUA
|
||||
.lua_load_osc = 1,
|
||||
.lua_load_ytdl = 1,
|
||||
.lua_ytdl_format = NULL,
|
||||
.lua_ytdl_format = "best",
|
||||
.lua_ytdl_raw_options = NULL,
|
||||
#endif
|
||||
.auto_load_scripts = 1,
|
||||
|
||||
+43
-46
@@ -43,41 +43,36 @@
|
||||
#include "osdep/io.h"
|
||||
#include "osdep/path.h"
|
||||
|
||||
#if !defined(_WIN32) || defined(__CYGWIN__)
|
||||
static int mp_add_xdg_config_dirs(struct mpv_global *global, char **dirs, int i)
|
||||
#define MAX_CONFIG_PATHS 32
|
||||
|
||||
static const char *mp_get_forced_home(void *talloc_ctx, const char *type)
|
||||
{
|
||||
void *talloc_ctx = dirs;
|
||||
|
||||
char *home = getenv("HOME");
|
||||
char *tmp = NULL;
|
||||
|
||||
char *xdg_home = NULL;
|
||||
tmp = getenv("XDG_CONFIG_HOME");
|
||||
if (tmp && *tmp)
|
||||
xdg_home = talloc_asprintf(talloc_ctx, "%s/mpv", tmp);
|
||||
else if (home && *home)
|
||||
xdg_home = talloc_asprintf(talloc_ctx, "%s/.config/mpv", home);
|
||||
|
||||
// Maintain compatibility with old ~/.mpv
|
||||
char *old_home = NULL;
|
||||
if (home && *home)
|
||||
old_home = talloc_asprintf(talloc_ctx, "%s/.mpv", home);
|
||||
|
||||
// If the old ~/.mpv exists, and the XDG config dir doesn't, use the old
|
||||
// config dir only.
|
||||
if (mp_path_exists(xdg_home) || !mp_path_exists(old_home))
|
||||
dirs[i++] = xdg_home;
|
||||
dirs[i++] = old_home;
|
||||
|
||||
#if HAVE_COCOA
|
||||
i = mp_add_macosx_bundle_dir(global, dirs, i);
|
||||
#endif
|
||||
|
||||
dirs[i++] = MPV_CONFDIR;
|
||||
|
||||
return i;
|
||||
return strcmp(type, "home") == 0 ? getenv("MPV_HOME") : NULL;
|
||||
}
|
||||
|
||||
// In order of increasing priority: the first hiz has highest priority.
|
||||
static const mp_get_platform_path_cb path_resolvers[] = {
|
||||
mp_get_forced_home,
|
||||
#if HAVE_COCOA
|
||||
mp_get_platform_path_osx,
|
||||
#endif
|
||||
#if !defined(_WIN32) || defined(__CYGWIN__)
|
||||
mp_get_platform_path_unix,
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
mp_get_platform_path_win,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const char *mp_get_platform_path(void *talloc_ctx, const char *type)
|
||||
{
|
||||
for (int n = 0; n < MP_ARRAY_SIZE(path_resolvers); n++) {
|
||||
const char *path = path_resolvers[n](talloc_ctx, type);
|
||||
if (path && path[0])
|
||||
return path;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return NULL-terminated array of config directories, from highest to lowest
|
||||
// priority
|
||||
@@ -85,7 +80,8 @@ static char **mp_config_dirs(void *talloc_ctx, struct mpv_global *global)
|
||||
{
|
||||
struct MPOpts *opts = global->opts;
|
||||
|
||||
char **ret = talloc_zero_array(talloc_ctx, char*, MAX_CONFIG_PATHS);
|
||||
char **ret = talloc_zero_array(talloc_ctx, char*, MAX_CONFIG_PATHS + 1);
|
||||
int num_ret = 0;
|
||||
|
||||
if (!opts->load_config)
|
||||
return ret;
|
||||
@@ -95,22 +91,23 @@ static char **mp_config_dirs(void *talloc_ctx, struct mpv_global *global)
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *tmp = NULL;
|
||||
int i = 0;
|
||||
// from highest (most preferred) to lowest priority
|
||||
static const char *const configdirs[] = {
|
||||
"home",
|
||||
"old_home",
|
||||
"osxbundle",
|
||||
"global",
|
||||
};
|
||||
|
||||
tmp = getenv("MPV_HOME");
|
||||
if (tmp && *tmp)
|
||||
ret[i++] = talloc_strdup(ret, tmp);
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
i = mp_add_win_config_dirs(global, ret, i);
|
||||
#else
|
||||
i = mp_add_xdg_config_dirs(global, ret, i);
|
||||
#endif
|
||||
for (int n = 0; n < MP_ARRAY_SIZE(configdirs); n++) {
|
||||
const char *path = mp_get_platform_path(ret, configdirs[n]);
|
||||
if (path && path[0] && num_ret < MAX_CONFIG_PATHS)
|
||||
ret[num_ret++] = (char *)path;
|
||||
}
|
||||
|
||||
MP_VERBOSE(global, "search dirs:");
|
||||
for (char **c = ret; *c; c++)
|
||||
MP_VERBOSE(global, " %s", *c);
|
||||
for (int n = 0; n < num_ret; n++)
|
||||
MP_VERBOSE(global, " %s", ret[n]);
|
||||
MP_VERBOSE(global, "\n");
|
||||
|
||||
return ret;
|
||||
|
||||
+22
-22
@@ -12,21 +12,21 @@
|
||||
//
|
||||
// Copyright (c) 2007-2011 IOSPIRIT GmbH (http://www.iospirit.com/)
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice, this list
|
||||
// of conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright notice, this
|
||||
// list of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
//
|
||||
// * Neither the name of IOSPIRIT GmbH nor the names of its contributors may be used to
|
||||
// endorse or promote products derived from this software without specific prior
|
||||
// written permission.
|
||||
//
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
@@ -166,7 +166,7 @@ typedef enum
|
||||
newID:(SInt32)newID
|
||||
forHardwareWithAttributes:(NSMutableDictionary *)attributes;
|
||||
|
||||
// Notification about hardware additions/removals
|
||||
// Notification about hardware additions/removals
|
||||
- (void)hidRemote:(HIDRemote *)hidRemote // Invoked when new hardware was found / added to HIDRemote's pool
|
||||
foundNewHardwareWithAttributes:(NSMutableDictionary *)attributes;
|
||||
|
||||
@@ -180,7 +180,7 @@ typedef enum
|
||||
|
||||
// Matching of newly found receiver hardware
|
||||
- (BOOL)hidRemote:(HIDRemote *)hidRemote // Invoked when new hardware is inspected
|
||||
inspectNewHardwareWithService:(io_service_t)service //
|
||||
inspectNewHardwareWithService:(io_service_t)service //
|
||||
prematchResult:(BOOL)prematchResult; // Return YES if HIDRemote should go on with this hardware and try
|
||||
// to use it, or NO if it should not be persued further.
|
||||
|
||||
@@ -194,7 +194,7 @@ typedef enum
|
||||
- (BOOL)hidRemote:(HIDRemote *)hidRemote
|
||||
shouldRetryExclusiveLockWithInfo:(NSDictionary *)applicationInfo;
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -- Actual header file for class --
|
||||
@@ -207,55 +207,55 @@ typedef enum
|
||||
// Notification ports
|
||||
IONotificationPortRef _notifyPort;
|
||||
CFRunLoopSourceRef _notifyRLSource;
|
||||
|
||||
|
||||
// Matching iterator
|
||||
io_iterator_t _matchingServicesIterator;
|
||||
|
||||
|
||||
// SecureInput notification
|
||||
io_object_t _secureInputNotification;
|
||||
|
||||
|
||||
// Service attributes
|
||||
NSMutableDictionary *_serviceAttribMap;
|
||||
|
||||
|
||||
// Mode
|
||||
HIDRemoteMode _mode;
|
||||
BOOL _autoRecover;
|
||||
NSTimer *_autoRecoveryTimer;
|
||||
|
||||
|
||||
// Delegate
|
||||
NSObject <HIDRemoteDelegate> *_delegate;
|
||||
|
||||
|
||||
// Last seen ID and remote model
|
||||
SInt32 _lastSeenRemoteID;
|
||||
HIDRemoteModel _lastSeenModel;
|
||||
SInt32 _lastSeenModelRemoteID;
|
||||
|
||||
|
||||
// Unused button codes
|
||||
NSArray *_unusedButtonCodes;
|
||||
|
||||
|
||||
// Simulate Plus/Minus Hold
|
||||
BOOL _simulateHoldEvents;
|
||||
|
||||
|
||||
// SecureEventInput workaround
|
||||
BOOL _secureEventInputWorkAround;
|
||||
UInt64 _lastSecureEventInputPIDSum;
|
||||
uid_t _lastFrontUserSession;
|
||||
|
||||
|
||||
// Exclusive lock lending
|
||||
BOOL _exclusiveLockLending;
|
||||
BOOL _sendExclusiveResourceReuseNotification;
|
||||
NSNumber *_waitForReturnByPID;
|
||||
NSNumber *_returnToPID;
|
||||
BOOL _isRestarting;
|
||||
|
||||
|
||||
// Status notifications
|
||||
BOOL _sendStatusNotifications;
|
||||
NSString *_pidString;
|
||||
|
||||
|
||||
// Status
|
||||
BOOL _applicationIsTerminating;
|
||||
BOOL _isStopping;
|
||||
|
||||
|
||||
// Thread safety
|
||||
#ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING /* #define HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING if you're running your HIDRemote instance on a background thread (requires OS X 10.5 or later) */
|
||||
NSThread *_runOnThread;
|
||||
@@ -271,7 +271,7 @@ typedef enum
|
||||
- (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel;
|
||||
|
||||
#pragma mark -- PUBLIC: Interface / API --
|
||||
- (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode;
|
||||
- (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode;
|
||||
- (void)stopRemoteControl;
|
||||
|
||||
- (BOOL)isStarted;
|
||||
|
||||
+178
-178
File diff suppressed because it is too large.
Load diff
+21
-7
@@ -30,13 +30,15 @@
|
||||
|
||||
// Emulate the parts of C11 stdatomic.h needed by mpv.
|
||||
// Still relies on gcc/clang atomic builtins.
|
||||
// The t field is a hack to make the non-atomic fallback macro mess work.
|
||||
|
||||
typedef struct { volatile unsigned long v; } atomic_ulong;
|
||||
typedef struct { volatile int v; } atomic_int;
|
||||
typedef struct { volatile _Bool v; } atomic_bool;
|
||||
typedef struct { volatile long long v; } atomic_llong;
|
||||
typedef struct { volatile uint_least32_t v; } atomic_uint_least32_t;
|
||||
typedef struct { volatile unsigned long long v; } atomic_ullong;
|
||||
typedef struct { volatile unsigned long v, t; } atomic_ulong;
|
||||
typedef struct { volatile int v, t; } atomic_int;
|
||||
typedef struct { volatile unsigned int v, t; } atomic_uint;
|
||||
typedef struct { volatile _Bool v, t; } atomic_bool;
|
||||
typedef struct { volatile long long v, t; } atomic_llong;
|
||||
typedef struct { volatile uint_least32_t v, t; } atomic_uint_least32_t;
|
||||
typedef struct { volatile unsigned long long v, t; } atomic_ullong;
|
||||
|
||||
#define ATOMIC_VAR_INIT(x) \
|
||||
{.v = (x)}
|
||||
@@ -54,6 +56,10 @@ typedef struct { volatile unsigned long long v; } atomic_ullong;
|
||||
__atomic_store_n(&(p)->v, val, __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_add(a, b) \
|
||||
__atomic_fetch_add(&(a)->v, b, __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_and(a, b) \
|
||||
__atomic_fetch_and(&(a)->v, b, __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_or(a, b) \
|
||||
__atomic_fetch_or(&(a)->v, b, __ATOMIC_SEQ_CST)
|
||||
#define atomic_compare_exchange_strong(a, b, c) \
|
||||
__atomic_compare_exchange_n(&(a)->v, b, c, 0, __ATOMIC_SEQ_CST, \
|
||||
__ATOMIC_SEQ_CST)
|
||||
@@ -66,6 +72,10 @@ typedef struct { volatile unsigned long long v; } atomic_ullong;
|
||||
(__sync_synchronize(), (p)->v = (val), __sync_synchronize())
|
||||
#define atomic_fetch_add(a, b) \
|
||||
__sync_fetch_and_add(&(a)->v, b)
|
||||
#define atomic_fetch_and(a, b) \
|
||||
__sync_fetch_and_and(&(a)->v, b)
|
||||
#define atomic_fetch_or(a, b) \
|
||||
__sync_fetch_and_or(&(a)->v, b)
|
||||
// Assumes __sync_val_compare_and_swap is "strong" (using the C11 meaning).
|
||||
#define atomic_compare_exchange_strong(p, old, new) \
|
||||
({ __typeof__((p)->v) val_ = __sync_val_compare_and_swap(&(p)->v, *(old), new); \
|
||||
@@ -79,7 +89,11 @@ typedef struct { volatile unsigned long long v; } atomic_ullong;
|
||||
// a serious dependency on working atomics, so this is barely ok.
|
||||
#define atomic_load(p) ((p)->v)
|
||||
#define atomic_store(p, val) ((p)->v = (val))
|
||||
#define atomic_fetch_add(a, b) (((a)->v += (b)) - (b))
|
||||
#define atomic_fetch_op_(a, b, op) \
|
||||
((a)->t = (a)->v, (a)->v = (a)->v op (b), (a)->t)
|
||||
#define atomic_fetch_add(a, b) atomic_fetch_op_(a, b, +)
|
||||
#define atomic_fetch_and(a, b) atomic_fetch_op_(a, b, &)
|
||||
#define atomic_fetch_or(a, b) atomic_fetch_op_(a, b, |)
|
||||
#define atomic_compare_exchange_strong(p, old, new) \
|
||||
((p)->v == *(old) ? ((p)->v = (new), 1) : (*(old) = (p)->v, 0))
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
#ifndef MPV_MACOSX_APPLICATION
|
||||
#define MPV_MACOSX_APPLICATION
|
||||
|
||||
typedef int (*mpv_main_fn)(int, char**);
|
||||
|
||||
// Menu Keys identifing menu items
|
||||
typedef enum {
|
||||
MPM_H_SIZE,
|
||||
@@ -30,8 +28,7 @@ typedef enum {
|
||||
} MPMenuKey;
|
||||
|
||||
// multithreaded wrapper for mpv_main
|
||||
int cocoa_main(mpv_main_fn mpv_main, int argc, char *argv[]);
|
||||
int cocoa_main(int argc, char *argv[]);
|
||||
void cocoa_register_menu_item_action(MPMenuKey key, void* action);
|
||||
void terminate_cocoa_application(void);
|
||||
|
||||
#endif /* MPV_MACOSX_APPLICATION */
|
||||
+18
-12
@@ -26,9 +26,14 @@
|
||||
#include "osdep/macosx_compat.h"
|
||||
#import "osdep/macosx_events_objc.h"
|
||||
#include "osdep/threads.h"
|
||||
#include "osdep/main-fn.h"
|
||||
|
||||
#define MPV_PROTOCOL @"mpv://"
|
||||
|
||||
// Whether the NSApplication singleton was created. If this is false, we are
|
||||
// running in libmpv mode, and cocoa_main() was never called.
|
||||
static bool application_instantiated;
|
||||
|
||||
static pthread_t playback_thread_id;
|
||||
|
||||
@interface Application ()
|
||||
@@ -53,11 +58,17 @@ static pthread_t playback_thread_id;
|
||||
- (void)setAppleMenu:(NSMenu *)aMenu;
|
||||
@end
|
||||
|
||||
Application *mpv_shared_app(void)
|
||||
static Application *mpv_shared_app(void)
|
||||
{
|
||||
return (Application *)[Application sharedApplication];
|
||||
}
|
||||
|
||||
static void terminate_cocoa_application(void)
|
||||
{
|
||||
[NSApp hide:NSApp];
|
||||
[NSApp terminate:NSApp];
|
||||
}
|
||||
|
||||
@implementation Application
|
||||
@synthesize menuItems = _menu_items;
|
||||
@synthesize openCount = _open_count;
|
||||
@@ -242,17 +253,10 @@ Application *mpv_shared_app(void)
|
||||
@end
|
||||
|
||||
struct playback_thread_ctx {
|
||||
mpv_main_fn mpv_main;
|
||||
int *argc;
|
||||
char ***argv;
|
||||
};
|
||||
|
||||
void terminate_cocoa_application(void)
|
||||
{
|
||||
[NSApp hide:NSApp];
|
||||
[NSApp terminate:NSApp];
|
||||
}
|
||||
|
||||
static void cocoa_run_runloop(void)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
@@ -265,7 +269,7 @@ static void *playback_thread(void *ctx_obj)
|
||||
mpthread_set_name("playback core (OSX)");
|
||||
@autoreleasepool {
|
||||
struct playback_thread_ctx *ctx = (struct playback_thread_ctx*) ctx_obj;
|
||||
int r = ctx->mpv_main(*ctx->argc, *ctx->argv);
|
||||
int r = mpv_main(*ctx->argc, *ctx->argv);
|
||||
terminate_cocoa_application();
|
||||
// normally never reached - unless the cocoa mainloop hasn't started yet
|
||||
exit(r);
|
||||
@@ -274,7 +278,8 @@ static void *playback_thread(void *ctx_obj)
|
||||
|
||||
void cocoa_register_menu_item_action(MPMenuKey key, void* action)
|
||||
{
|
||||
[NSApp registerSelector:(SEL)action forKey:key];
|
||||
if (application_instantiated)
|
||||
[NSApp registerSelector:(SEL)action forKey:key];
|
||||
}
|
||||
|
||||
static void init_cocoa_application(bool regular)
|
||||
@@ -356,11 +361,12 @@ static bool bundle_started_from_finder(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
int cocoa_main(mpv_main_fn mpv_main, int argc, char *argv[])
|
||||
int cocoa_main(int argc, char *argv[])
|
||||
{
|
||||
@autoreleasepool {
|
||||
application_instantiated = true;
|
||||
|
||||
struct playback_thread_ctx ctx = {0};
|
||||
ctx.mpv_main = mpv_main;
|
||||
ctx.argc = &argc;
|
||||
ctx.argv = &argv;
|
||||
|
||||
|
||||
@@ -27,5 +27,3 @@
|
||||
@property(nonatomic, retain) NSArray *files;
|
||||
@property(nonatomic, assign) size_t openCount;
|
||||
@end
|
||||
|
||||
Application *mpv_shared_app(void);
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "osdep/macosx_application.h"
|
||||
|
||||
// This is needed because Cocoa absolutely requires creating the NSApplication
|
||||
// singleton and running it in the "main" thread. It is apparently not
|
||||
// possible to do this on a separate thread at all. It is not known how
|
||||
// Apple managed this colossal fuckup.
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return cocoa_main(argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "main-fn.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return mpv_main(argc, argv);
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE
|
||||
#define BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE (0x0001)
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "common/common.h"
|
||||
#include "osdep/io.h"
|
||||
#include "osdep/terminal.h"
|
||||
|
||||
#include "core.h"
|
||||
#include "osdep/main-fn.h"
|
||||
|
||||
int wmain(int argc, wchar_t *argv[]);
|
||||
|
||||
@@ -25,8 +29,32 @@ static bool has_redirected_stdio(void)
|
||||
is_valid_handle(GetStdHandle(STD_ERROR_HANDLE));
|
||||
}
|
||||
|
||||
static void microsoft_nonsense(void)
|
||||
{
|
||||
// stop Windows from showing all kinds of annoying error dialogs
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
||||
|
||||
// Enable heap corruption detection
|
||||
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
|
||||
|
||||
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
|
||||
WINBOOL (WINAPI *pSetDllDirectory)(LPCWSTR lpPathName) =
|
||||
(WINBOOL (WINAPI *)(LPCWSTR))GetProcAddress(kernel32, "SetDllDirectoryW");
|
||||
WINBOOL (WINAPI *pSetSearchPathMode)(DWORD Flags) =
|
||||
(WINBOOL (WINAPI *)(DWORD))GetProcAddress(kernel32, "SetSearchPathMode");
|
||||
|
||||
// Always use safe search paths for DLLs and other files, ie. never use the
|
||||
// current directory
|
||||
if (pSetSearchPathMode)
|
||||
pSetDllDirectory(L"");
|
||||
if (pSetSearchPathMode)
|
||||
pSetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE);
|
||||
}
|
||||
|
||||
int wmain(int argc, wchar_t *argv[])
|
||||
{
|
||||
microsoft_nonsense();
|
||||
|
||||
// If started from the console wrapper (see osdep/win32-console-wrapper.c),
|
||||
// attach to the console and set up the standard IO handles
|
||||
bool has_console = terminal_try_attach();
|
||||
@@ -0,0 +1 @@
|
||||
int mpv_main(int argc, char *argv[]);
|
||||
+9
-7
@@ -19,12 +19,14 @@
|
||||
#include "options/path.h"
|
||||
#include "osdep/path.h"
|
||||
|
||||
int mp_add_macosx_bundle_dir(struct mpv_global *global, char **dirs, int i)
|
||||
const char *mp_get_platform_path_osx(void *talloc_ctx, const char *type)
|
||||
{
|
||||
void *talloc_ctx = dirs;
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString *path = [[NSBundle mainBundle] resourcePath];
|
||||
dirs[i++] = talloc_strdup(talloc_ctx, [path UTF8String]);
|
||||
[pool release];
|
||||
return i;
|
||||
if (strcmp(type, "osxbundle") == 0) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString *path = [[NSBundle mainBundle] resourcePath];
|
||||
char *res = talloc_strdup(talloc_ctx, [path UTF8String]);
|
||||
[pool release];
|
||||
return res;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "options/path.h"
|
||||
#include "path.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
static pthread_once_t path_init_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
static char mpv_home[512];
|
||||
static char old_home[512];
|
||||
|
||||
static void path_init(void)
|
||||
{
|
||||
char *home = getenv("HOME");
|
||||
char *xdg_dir = getenv("XDG_CONFIG_HOME");
|
||||
|
||||
if (xdg_dir && xdg_dir[0]) {
|
||||
snprintf(mpv_home, sizeof(mpv_home), "%s/mpv", xdg_dir);
|
||||
} else if (home && home[0]) {
|
||||
snprintf(mpv_home, sizeof(mpv_home), "%s/.config/mpv", home);
|
||||
}
|
||||
|
||||
// Maintain compatibility with old ~/.mpv
|
||||
if (home && home[0])
|
||||
snprintf(old_home, sizeof(old_home), "%s/.mpv", home);
|
||||
|
||||
// If the old ~/.mpv exists, and the XDG config dir doesn't, use the old
|
||||
// config dir only.
|
||||
if (mp_path_exists(old_home) && !mp_path_exists(mpv_home)) {
|
||||
snprintf(mpv_home, sizeof(mpv_home), "%s", old_home);
|
||||
old_home[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
const char *mp_get_platform_path_unix(void *talloc_ctx, const char *type)
|
||||
{
|
||||
pthread_once(&path_init_once, path_init);
|
||||
if (strcmp(type, "home") == 0)
|
||||
return mpv_home;
|
||||
if (strcmp(type, "old_home") == 0)
|
||||
return old_home;
|
||||
if (strcmp(type, "global") == 0)
|
||||
return MPV_CONFDIR;
|
||||
return NULL;
|
||||
}
|
||||
+10
-10
@@ -51,21 +51,21 @@ static char *mp_get_win_app_dir(void *talloc_ctx)
|
||||
{
|
||||
wchar_t w_appdir[MAX_PATH + 1] = {0};
|
||||
|
||||
if (SHGetFolderPathW(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL,
|
||||
if (SHGetFolderPathW(NULL, folder|CSIDL_FLAG_CREATE, NULL,
|
||||
SHGFP_TYPE_CURRENT, w_appdir) != S_OK)
|
||||
return NULL;
|
||||
|
||||
return talloc_asprintf(talloc_ctx, "%s/mpv", mp_to_utf8(talloc_ctx, w_appdir));
|
||||
}
|
||||
|
||||
int mp_add_win_config_dirs(struct mpv_global *global, char **dirs, int i)
|
||||
const char *mp_get_platform_path_win(void *talloc_ctx, const char *type)
|
||||
{
|
||||
void *talloc_ctx = dirs;
|
||||
if ((dirs[i] = mp_get_win_app_dir(talloc_ctx)))
|
||||
i++;
|
||||
if ((dirs[i] = mp_get_win_exe_dir(talloc_ctx)))
|
||||
i++;
|
||||
if ((dirs[i] = mp_get_win_exe_subdir(talloc_ctx)))
|
||||
i++;
|
||||
return i;
|
||||
if (strcmp(type, "home") == 0)
|
||||
return mp_get_win_app_dir(talloc_ctx);
|
||||
if (strcmp(type, "old_home") == 0)
|
||||
return mp_get_win_exe_dir(talloc_ctx);
|
||||
// Not really true, but serves as a way to return a lowest-priority dir.
|
||||
if (strcmp(type, "global") == 0)
|
||||
return mp_get_win_exe_subdir(talloc_ctx);
|
||||
return NULL;
|
||||
}
|
||||
+16
-9
@@ -1,15 +1,22 @@
|
||||
#ifndef OSDEP_PATH_H
|
||||
#define OSDEP_PATH_H
|
||||
|
||||
#define MAX_CONFIG_PATHS 32
|
||||
// Return a platform-specific path, identified by the type parameter. If the
|
||||
// return value is allocated, talloc_ctx is used as talloc parent context.
|
||||
//
|
||||
// The following type values are defined:
|
||||
// "home" the native mpv-specific user config dir
|
||||
// "old_home" same as "home", but lesser priority (compatibility)
|
||||
// "osxbundle" OSX bundle path
|
||||
// "global" the least priority, global config file location
|
||||
//
|
||||
// It is allowed to return a static string, so the caller must set talloc_ctx
|
||||
// to something other than NULL to avoid memory leaks.
|
||||
typedef const char *(*mp_get_platform_path_cb)(void *talloc_ctx, const char *type);
|
||||
|
||||
struct mpv_global;
|
||||
|
||||
// Append paths starting at dirs[i]. The dirs array has place only for at most
|
||||
// MAX_CONFIG_PATHS paths, but it's guaranteed that at least 4 paths can be
|
||||
// added without checking for i>=MAX_CONFIG_PATHS.
|
||||
// Return the new value of i.
|
||||
int mp_add_win_config_dirs(struct mpv_global *global, char **dirs, int i);
|
||||
int mp_add_macosx_bundle_dir(struct mpv_global *global, char **dirs, int i);
|
||||
// Conforming to mp_get_platform_path_cb.
|
||||
const char *mp_get_platform_path_win(void *talloc_ctx, const char *type);
|
||||
const char *mp_get_platform_path_osx(void *talloc_ctx, const char *type);
|
||||
const char *mp_get_platform_path_unix(void *talloc_ctx, const char *type);
|
||||
|
||||
#endif
|
||||
@@ -112,7 +112,7 @@ static int create_overlapped_pipe(HANDLE *read, HANDLE *write)
|
||||
unsigned long id = atomic_fetch_add(&counter, 1);
|
||||
unsigned pid = GetCurrentProcessId();
|
||||
wchar_t buf[36];
|
||||
swprintf(buf, sizeof(buf), L"\\\\?\\pipe\\mpv-anon-%08x-%08lx", pid, id);
|
||||
swprintf(buf, sizeof(buf), L"\\\\.\\pipe\\mpv-anon-%08x-%08lx", pid, id);
|
||||
|
||||
// The function for creating anonymous pipes (CreatePipe) can't create
|
||||
// overlapped pipes, so instead, use a named pipe with a unique name
|
||||
|
||||
@@ -27,19 +27,6 @@
|
||||
#include "threads.h"
|
||||
#include "timer.h"
|
||||
|
||||
int mpthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
int64_t abstime)
|
||||
{
|
||||
struct timespec ts = mp_time_us_to_timespec(abstime);
|
||||
return pthread_cond_timedwait(cond, mutex, &ts);
|
||||
}
|
||||
|
||||
int mpthread_cond_timedwait_rel(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
double s)
|
||||
{
|
||||
return mpthread_cond_timedwait(cond, mutex, mp_add_timeout(mp_time_us(), s));
|
||||
}
|
||||
|
||||
int mpthread_mutex_init_recursive(pthread_mutex_t *mutex)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
@@ -4,15 +4,6 @@
|
||||
#include <pthread.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
// Call pthread_cond_timedwait() with an absolute timeout using the same
|
||||
// time source/unit as mp_time_us() (microseconds).
|
||||
int mpthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
int64_t abstime);
|
||||
|
||||
// Wait by a relative amount of time in seconds.
|
||||
int mpthread_cond_timedwait_rel(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
double seconds);
|
||||
|
||||
// Helper to reduce boiler plate.
|
||||
int mpthread_mutex_init_recursive(pthread_mutex_t *mutex);
|
||||
|
||||
|
||||
+8
-2
@@ -28,7 +28,7 @@
|
||||
#include "timer.h"
|
||||
|
||||
static uint64_t raw_time_offset;
|
||||
pthread_once_t timer_init_once = PTHREAD_ONCE_INIT;
|
||||
static pthread_once_t timer_init_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
static void do_timer_init(void)
|
||||
{
|
||||
@@ -116,6 +116,11 @@ struct timespec mp_time_us_to_timespec(int64_t time_us)
|
||||
return ts;
|
||||
}
|
||||
|
||||
struct timespec mp_rel_time_to_timespec(double timeout_sec)
|
||||
{
|
||||
return mp_time_us_to_timespec(mp_add_timeout(mp_time_us(), timeout_sec));
|
||||
}
|
||||
|
||||
#if 0
|
||||
#include <stdio.h>
|
||||
#include "threads.h"
|
||||
@@ -138,7 +143,8 @@ int main(void) {
|
||||
#if TEST_SLEEP
|
||||
mp_sleep_us(delay);
|
||||
#else
|
||||
mpthread_cond_timedwait(&cnd, &mtx, r + delay);
|
||||
struct timespec ts = mp_time_us_to_timespec(r + delay);
|
||||
pthread_cond_timedwait(&cnd, &mtx, &ts);
|
||||
#endif
|
||||
j = (mp_time_us() - r) - delay;
|
||||
printf("sleep time: t=%"PRId64" sleep=%8i err=%5i\n", r, delay, (int)j);
|
||||
|
||||
@@ -52,4 +52,8 @@ int64_t mp_add_timeout(int64_t time_us, double timeout_sec);
|
||||
// Convert the mp time in microseconds to a timespec using CLOCK_REALTIME.
|
||||
struct timespec mp_time_us_to_timespec(int64_t time_us);
|
||||
|
||||
// Convert the relative timeout in seconds to a timespec.
|
||||
// The timespec is absolute, using CLOCK_REALTIME.
|
||||
struct timespec mp_rel_time_to_timespec(double timeout_sec);
|
||||
|
||||
#endif /* MPLAYER_TIMER_H */
|
||||
+4
-2
@@ -284,8 +284,10 @@ static int wait_wakeup(struct mpv_handle *ctx, int64_t end)
|
||||
int r = 0;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
pthread_mutex_lock(&ctx->wakeup_lock);
|
||||
if (!ctx->need_wakeup)
|
||||
r = mpthread_cond_timedwait(&ctx->wakeup, &ctx->wakeup_lock, end);
|
||||
if (!ctx->need_wakeup) {
|
||||
struct timespec ts = mp_time_us_to_timespec(end);
|
||||
r = pthread_cond_timedwait(&ctx->wakeup, &ctx->wakeup_lock, &ts);
|
||||
}
|
||||
if (r == 0)
|
||||
ctx->need_wakeup = false;
|
||||
pthread_mutex_unlock(&ctx->wakeup_lock);
|
||||
|
||||
@@ -4530,6 +4530,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
||||
char *lang = cmd->args[3].v.s;
|
||||
if (lang && lang[0])
|
||||
t->lang = talloc_strdup(t, lang);
|
||||
print_track_list(mpctx);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4540,6 +4541,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
||||
if (!t)
|
||||
return -1;
|
||||
mp_remove_track(mpctx, t);
|
||||
print_track_list(mpctx);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4553,6 +4555,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
||||
if (nt) {
|
||||
mp_remove_track(mpctx, t);
|
||||
mp_switch_track(mpctx, nt->type, nt);
|
||||
print_track_list(mpctx);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -4573,6 +4576,8 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
||||
opts->sub_id_ff, opts->sub_lang);
|
||||
if (s && s->is_external)
|
||||
mp_switch_track(mpctx, STREAM_SUB, s);
|
||||
|
||||
print_track_list(mpctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ void mp_parse_cfgfiles(struct MPContext *mpctx)
|
||||
load_all_cfgfiles(mpctx, section, "mpv.conf|config");
|
||||
|
||||
if (encoding)
|
||||
m_config_set_profile(conf, m_config_add_profile(conf, SECT_ENCODE), 0);
|
||||
m_config_set_profile(conf, SECT_ENCODE, 0);
|
||||
}
|
||||
|
||||
static int try_load_config(struct MPContext *mpctx, const char *file, int flags)
|
||||
@@ -143,7 +143,7 @@ static void mp_auto_load_profile(struct MPContext *mpctx, char *category,
|
||||
m_profile_t *p = m_config_get_profile0(mpctx->mconfig, t);
|
||||
if (p) {
|
||||
MP_INFO(mpctx, "Auto-loading profile '%s'\n", t);
|
||||
m_config_set_profile(mpctx->mconfig, p, FILE_LOCAL_FLAGS);
|
||||
m_config_set_profile(mpctx->mconfig, t, FILE_LOCAL_FLAGS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,10 +184,10 @@ static char *mp_get_playback_resume_config_filename(struct mpv_global *global,
|
||||
realpath = mp_path_join(tmp, bstr0(cwd), bstr0(fname));
|
||||
}
|
||||
}
|
||||
if (bstr_startswith0(bfname, "dvd://"))
|
||||
if (bstr_startswith0(bfname, "dvd://") && opts->dvd_device)
|
||||
realpath = talloc_asprintf(tmp, "%s - %s", realpath, opts->dvd_device);
|
||||
if (bstr_startswith0(bfname, "br://") || bstr_startswith0(bfname, "bd://") ||
|
||||
bstr_startswith0(bfname, "bluray://"))
|
||||
if ((bstr_startswith0(bfname, "br://") || bstr_startswith0(bfname, "bd://") ||
|
||||
bstr_startswith0(bfname, "bluray://")) && opts->bluray_device)
|
||||
realpath = talloc_asprintf(tmp, "%s - %s", realpath, opts->bluray_device);
|
||||
uint8_t md5[16];
|
||||
av_md5_sum(md5, realpath, strlen(realpath));
|
||||
|
||||
+1
-1
@@ -396,6 +396,7 @@ struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction,
|
||||
void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e);
|
||||
void mp_play_files(struct MPContext *mpctx);
|
||||
void update_demuxer_properties(struct MPContext *mpctx);
|
||||
void print_track_list(struct MPContext *mpctx);
|
||||
void reselect_demux_streams(struct MPContext *mpctx);
|
||||
void prepare_playlist(struct MPContext *mpctx, struct playlist *pl);
|
||||
void autoload_external_files(struct MPContext *mpctx);
|
||||
@@ -403,7 +404,6 @@ struct track *select_track(struct MPContext *mpctx, enum stream_type type,
|
||||
int tid, int ffid, char **langs);
|
||||
|
||||
// main.c
|
||||
int mpv_main(int argc, char *argv[]);
|
||||
int mp_initialize(struct MPContext *mpctx, char **argv);
|
||||
struct MPContext *mp_create(void);
|
||||
void mp_destroy(struct MPContext *mpctx);
|
||||
|
||||
+19
-6
@@ -143,6 +143,15 @@ static void print_stream(struct MPContext *mpctx, struct track *t)
|
||||
MP_INFO(mpctx, "%s\n", b);
|
||||
}
|
||||
|
||||
void print_track_list(struct MPContext *mpctx)
|
||||
{
|
||||
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
|
||||
for (int n = 0; n < mpctx->num_tracks; n++)
|
||||
if (mpctx->tracks[n]->type == t)
|
||||
print_stream(mpctx, mpctx->tracks[n]);
|
||||
}
|
||||
}
|
||||
|
||||
void update_demuxer_properties(struct MPContext *mpctx)
|
||||
{
|
||||
struct demuxer *demuxer = mpctx->master_demuxer;
|
||||
@@ -167,11 +176,7 @@ void update_demuxer_properties(struct MPContext *mpctx)
|
||||
struct demuxer *tracks = mpctx->track_layout;
|
||||
if (tracks->events & DEMUX_EVENT_STREAMS) {
|
||||
add_demuxer_tracks(mpctx, tracks);
|
||||
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
|
||||
for (int n = 0; n < mpctx->num_tracks; n++)
|
||||
if (mpctx->tracks[n]->type == t)
|
||||
print_stream(mpctx, mpctx->tracks[n]);
|
||||
}
|
||||
print_track_list(mpctx);
|
||||
tracks->events &= ~DEMUX_EVENT_STREAMS;
|
||||
}
|
||||
if (events & DEMUX_EVENT_METADATA) {
|
||||
@@ -653,8 +658,16 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
|
||||
for (int n = mpctx->num_tracks - 1; n >= 0 && !in_use; n--)
|
||||
in_use |= mpctx->tracks[n]->demuxer == d;
|
||||
|
||||
if (!in_use)
|
||||
if (!in_use) {
|
||||
for (int n = 0; n < mpctx->num_sources; n++) {
|
||||
if (mpctx->sources[n] == d) {
|
||||
MP_TARRAY_REMOVE_AT(mpctx->sources, mpctx->num_sources, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
uninit_stream_sub_decoders(d);
|
||||
free_demuxer_and_stream(d);
|
||||
}
|
||||
|
||||
mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL);
|
||||
|
||||
|
||||
+57
-35
@@ -141,16 +141,35 @@ function limit_range(min, max, val)
|
||||
return val
|
||||
end
|
||||
|
||||
-- translate value into element coordinates
|
||||
function get_slider_ele_pos_for(element, val)
|
||||
|
||||
local ele_pos = scale_value(
|
||||
element.slider.min.value, element.slider.max.value,
|
||||
element.slider.min.ele_pos, element.slider.max.ele_pos,
|
||||
val)
|
||||
|
||||
return limit_range(
|
||||
element.slider.min.ele_pos, element.slider.max.ele_pos,
|
||||
ele_pos)
|
||||
end
|
||||
|
||||
-- translates global (mouse) coordinates to value
|
||||
function get_slider_value_at(element, glob_pos)
|
||||
|
||||
local val = scale_value(
|
||||
element.slider.min.glob_pos, element.slider.max.glob_pos,
|
||||
element.slider.min.value, element.slider.max.value,
|
||||
glob_pos)
|
||||
|
||||
return limit_range(
|
||||
element.slider.min.value, element.slider.max.value,
|
||||
val)
|
||||
end
|
||||
|
||||
-- get value at current mouse position
|
||||
function get_slider_value(element)
|
||||
local foV = element.layout.slider.border
|
||||
local pH = (element.layout.geometry.h - (2*foV)) / 2
|
||||
|
||||
local b_x1, b_x2 = element.hitbox.x1 + pH, element.hitbox.x2 - pH
|
||||
local s_min, s_max = element.slider.min, element.slider.max
|
||||
|
||||
local pos = scale_value(b_x1, b_x2, s_min, s_max, mp.get_mouse_pos())
|
||||
|
||||
return limit_range(s_min, s_max, pos)
|
||||
return get_slider_value_at(element, mp.get_mouse_pos())
|
||||
end
|
||||
|
||||
function countone(val)
|
||||
@@ -303,6 +322,11 @@ function prepare_elements()
|
||||
for _,element in pairs(elements) do
|
||||
|
||||
local elem_geo = element.layout.geometry
|
||||
|
||||
-- Calculate the hitbox
|
||||
local bX1, bY1, bX2, bY2 = get_hitbox_coords_geo(elem_geo)
|
||||
element.hitbox = {x1 = bX1, y1 = bY1, x2 = bX2, y2 = bY2}
|
||||
|
||||
local style_ass = assdraw.ass_new()
|
||||
|
||||
-- prepare static elements
|
||||
@@ -331,13 +355,26 @@ function prepare_elements()
|
||||
local slider_lo = element.layout.slider
|
||||
-- offset between element outline and drag-area
|
||||
local foV = slider_lo.border + slider_lo.gap
|
||||
local foH = 0
|
||||
|
||||
-- calculate positions of min and max points
|
||||
if (slider_lo.stype == "slider") then
|
||||
foH = elem_geo.h / 2
|
||||
element.slider.min.ele_pos = elem_geo.h / 2
|
||||
element.slider.max.ele_pos = elem_geo.w - (elem_geo.h / 2)
|
||||
|
||||
elseif (slider_lo.stype == "bar") then
|
||||
foH = slider_lo.border + slider_lo.gap
|
||||
element.slider.min.ele_pos =
|
||||
slider_lo.border + slider_lo.gap
|
||||
element.slider.max.ele_pos =
|
||||
elem_geo.w - (slider_lo.border + slider_lo.gap)
|
||||
end
|
||||
|
||||
element.slider.min.glob_pos =
|
||||
element.hitbox.x1 + element.slider.min.ele_pos
|
||||
element.slider.max.glob_pos =
|
||||
element.hitbox.x1 + element.slider.max.ele_pos
|
||||
|
||||
-- -- --
|
||||
|
||||
static_ass:draw_start()
|
||||
|
||||
-- the box
|
||||
@@ -351,11 +388,10 @@ function prepare_elements()
|
||||
if not (element.slider.markerF == nil) and (slider_lo.gap > 0) then
|
||||
local markers = element.slider.markerF()
|
||||
for _,marker in pairs(markers) do
|
||||
if (marker > element.slider.min) and
|
||||
(marker < element.slider.max) then
|
||||
if (marker > element.slider.min.value) and
|
||||
(marker < element.slider.max.value) then
|
||||
|
||||
local s = scale_value(element.slider.min,
|
||||
element.slider.max, foH, (elem_geo.w - foH), marker)
|
||||
local s = get_slider_ele_pos_for(element, marker)
|
||||
|
||||
if (slider_lo.gap > 1) then -- draw triangles
|
||||
|
||||
@@ -408,18 +444,6 @@ function prepare_elements()
|
||||
element.layout.alpha[1] = 136
|
||||
element.eventresponder = nil
|
||||
end
|
||||
|
||||
-- Calculate the hitbox
|
||||
local bX1, bY1, bX2, bY2 = get_hitbox_coords_geo(elem_geo)
|
||||
if type == "slider" then
|
||||
-- if it's a slider, cut the border off,
|
||||
-- as it is not of interest for eventhandling
|
||||
element.hitbox = {
|
||||
x1 = bX1 + slider.border, y1 = bY1 + slider.border,
|
||||
x2 = bX2 - slider.border, y2 = bY2 - slider.border}
|
||||
else
|
||||
element.hitbox = {x1 = bX1, y1 = bY1, x2 = bX2, y2 = bY2}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -485,7 +509,8 @@ function render_elements(master_ass)
|
||||
|
||||
local slider_lo = element.layout.slider
|
||||
local elem_geo = element.layout.geometry
|
||||
local s_min, s_max = element.slider.min, element.slider.max
|
||||
local s_min, s_max = element.slider.min.value, element.slider.max.value
|
||||
|
||||
|
||||
-- draw pos marker
|
||||
local pos = element.slider.posF()
|
||||
@@ -500,12 +525,9 @@ function render_elements(master_ass)
|
||||
foH = slider_lo.border + slider_lo.gap
|
||||
end
|
||||
|
||||
pos = limit_range(s_min, s_max, pos)
|
||||
local xp = get_slider_ele_pos_for(element, pos)
|
||||
|
||||
local xp = scale_value(s_min, s_max,
|
||||
foH, (elem_geo.w - foH), pos)
|
||||
|
||||
-- the filling, draw it only if positive
|
||||
-- the filling
|
||||
local innerH = elem_geo.h - (2*foV)
|
||||
|
||||
if (slider_lo.stype == "bar") then
|
||||
@@ -657,7 +679,7 @@ function new_element(name, type)
|
||||
elements[name].state = {}
|
||||
|
||||
if (type == "slider") then
|
||||
elements[name].slider = {min = 0, max = 100}
|
||||
elements[name].slider = {min = {value = 0}, max = {value = 100}}
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -103,16 +103,17 @@ mp.add_hook("on_load", 10, function ()
|
||||
ytdl.path, "--no-warnings", "-J", "--flat-playlist", "--all-subs",
|
||||
"--sub-format", subformat, "--no-playlist"
|
||||
}
|
||||
|
||||
-- Checks if video option is "no", change options accordingly
|
||||
if (mp.get_property("options/vid") == "no") then
|
||||
format = "bestaudio/best"
|
||||
msg.verbose("Video disabled. Only using audio")
|
||||
end
|
||||
|
||||
if (format ~= "") then
|
||||
table.insert(command, "--format")
|
||||
table.insert(command, format)
|
||||
end
|
||||
|
||||
-- Checks if no-video option is set and disables video in ytdl if set
|
||||
if (mp.get_property("options/vid") == "no") then
|
||||
table.insert(command, "-x")
|
||||
msg.verbose("Video disabled. Only using audio")
|
||||
end
|
||||
|
||||
for param, arg in pairs(raw_options) do
|
||||
table.insert(command, "--" .. param)
|
||||
@@ -161,7 +162,9 @@ mp.add_hook("on_load", 10, function ()
|
||||
local playlist = "edl://"
|
||||
for i, entry in pairs(json.entries) do
|
||||
|
||||
playlist = playlist .. entry.url .. ";"
|
||||
local urllength = string.len(entry.url)
|
||||
playlist = playlist .. "%" .. urllength .. "%" .. entry.url .. ";"
|
||||
|
||||
end
|
||||
|
||||
msg.debug("EDL: " .. playlist)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#include "config.h"
|
||||
#include "core.h"
|
||||
|
||||
#if HAVE_COCOA_APPLICATION
|
||||
#include "osdep/macosx_application.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#if HAVE_COCOA_APPLICATION
|
||||
return cocoa_main(mpv_main, argc, argv);
|
||||
#else
|
||||
return mpv_main(argc, argv);
|
||||
#endif
|
||||
}
|
||||
+1
-25
@@ -30,6 +30,7 @@
|
||||
#include "osdep/io.h"
|
||||
#include "osdep/terminal.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/main-fn.h"
|
||||
|
||||
#include "common/av_log.h"
|
||||
#include "common/codecs.h"
|
||||
@@ -64,10 +65,6 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE
|
||||
#define BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE (0x0001)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAVE_COCOA
|
||||
@@ -282,27 +279,6 @@ static void osdep_preinit(int argc, char **argv)
|
||||
enable_talloc = "1";
|
||||
if (enable_talloc && strcmp(enable_talloc, "1") == 0)
|
||||
talloc_enable_leak_report();
|
||||
|
||||
#ifdef _WIN32
|
||||
// stop Windows from showing all kinds of annoying error dialogs
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
||||
|
||||
// Enable heap corruption detection
|
||||
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
|
||||
|
||||
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
|
||||
WINBOOL (WINAPI *pSetDllDirectory)(LPCWSTR lpPathName) =
|
||||
(WINBOOL (WINAPI *)(LPCWSTR))GetProcAddress(kernel32, "SetDllDirectoryW");
|
||||
WINBOOL (WINAPI *pSetSearchPathMode)(DWORD Flags) =
|
||||
(WINBOOL (WINAPI *)(DWORD))GetProcAddress(kernel32, "SetSearchPathMode");
|
||||
|
||||
// Always use safe search paths for DLLs and other files, ie. never use the
|
||||
// current directory
|
||||
if (pSetSearchPathMode)
|
||||
pSetDllDirectory(L"");
|
||||
if (pSetSearchPathMode)
|
||||
pSetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int cfg_include(void *ctx, char *filename, int flags)
|
||||
|
||||
@@ -382,6 +382,13 @@ double get_playback_time(struct MPContext *mpctx)
|
||||
{
|
||||
double cur = get_current_time(mpctx);
|
||||
double start = get_start_time(mpctx);
|
||||
// During seeking, the time corresponds to the last seek time - apply some
|
||||
// cosmetics to it.
|
||||
if (mpctx->playback_pts == MP_NOPTS_VALUE) {
|
||||
double length = get_time_length(mpctx);
|
||||
if (length >= 0)
|
||||
cur = MPCLAMP(cur, start, start + length);
|
||||
}
|
||||
return cur >= start ? cur - start : cur;
|
||||
}
|
||||
|
||||
@@ -1042,6 +1049,7 @@ void mp_idle(struct MPContext *mpctx)
|
||||
mp_wait_events(mpctx, mpctx->sleeptime);
|
||||
mpctx->sleeptime = 100.0;
|
||||
mp_process_input(mpctx);
|
||||
handle_command_updates(mpctx);
|
||||
handle_cursor_autohide(mpctx);
|
||||
handle_vo_events(mpctx);
|
||||
update_osd_msg(mpctx);
|
||||
|
||||
+4
-1
@@ -135,8 +135,11 @@ static void mp_load_script(struct MPContext *mpctx, const char *fname)
|
||||
MP_VERBOSE(arg, "Loading script %s...\n", fname);
|
||||
|
||||
pthread_t thread;
|
||||
if (pthread_create(&thread, NULL, script_thread, arg))
|
||||
if (pthread_create(&thread, NULL, script_thread, arg)) {
|
||||
mpv_detach_destroy(arg->client);
|
||||
talloc_free(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
wait_loaded(mpctx);
|
||||
MP_VERBOSE(mpctx, "Done loading %s.\n", fname);
|
||||
|
||||
+6
-3
@@ -134,7 +134,8 @@ static void cache_wakeup_and_wait(struct priv *s, double *retry_time)
|
||||
}
|
||||
|
||||
pthread_cond_signal(&s->wakeup);
|
||||
mpthread_cond_timedwait_rel(&s->wakeup, &s->mutex, CACHE_WAIT_TIME);
|
||||
struct timespec ts = mp_rel_time_to_timespec(CACHE_WAIT_TIME);
|
||||
pthread_cond_timedwait(&s->wakeup, &s->mutex, &ts);
|
||||
|
||||
if (*retry_time >= 0)
|
||||
*retry_time += mp_time_sec() - start;
|
||||
@@ -465,8 +466,10 @@ static void *cache_thread(void *arg)
|
||||
pthread_cond_signal(&s->wakeup);
|
||||
s->control = CACHE_CTRL_NONE;
|
||||
}
|
||||
if (s->idle && s->control == CACHE_CTRL_NONE)
|
||||
mpthread_cond_timedwait_rel(&s->wakeup, &s->mutex, CACHE_IDLE_SLEEP_TIME);
|
||||
if (s->idle && s->control == CACHE_CTRL_NONE) {
|
||||
struct timespec ts = mp_rel_time_to_timespec(CACHE_IDLE_SLEEP_TIME);
|
||||
pthread_cond_timedwait(&s->wakeup, &s->mutex, &ts);
|
||||
}
|
||||
}
|
||||
pthread_cond_signal(&s->wakeup);
|
||||
pthread_mutex_unlock(&s->mutex);
|
||||
|
||||
+5
-3
@@ -401,13 +401,15 @@ static bool stream_reconnect(stream_t *s)
|
||||
if (mp_cancel_wait(s->cancel, sleep_secs))
|
||||
break;
|
||||
|
||||
MP_WARN(s, "Connection lost! Attempting to reconnect (%d)...\n", retry + 1);
|
||||
|
||||
int r = stream_control(s, STREAM_CTRL_RECONNECT, NULL);
|
||||
if (r == STREAM_UNSUPPORTED)
|
||||
break;
|
||||
if (r == STREAM_OK && stream_seek_unbuffered(s, pos) && s->pos == pos)
|
||||
if (r == STREAM_OK && stream_seek_unbuffered(s, pos) && s->pos == pos) {
|
||||
MP_WARN(s, "Reconnected successfully.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
MP_WARN(s, "Connection lost! Attempting to reconnect (%d)...\n", retry + 1);
|
||||
|
||||
sleep_secs = MPMAX(sleep_secs, 0.1);
|
||||
sleep_secs = MPMIN(sleep_secs * 4, 10.0);
|
||||
|
||||
@@ -455,8 +455,8 @@ static void handle_nav_command(stream_t *s, struct mp_nav_cmd *ev)
|
||||
bd_vk_key_e key = translate_nav_menu_action(action);
|
||||
if (key != BD_VK_NONE) {
|
||||
if (key == BD_VK_MOUSE_ACTIVATE)
|
||||
ev->mouse_on_button = bd_mouse_select(priv->bd, pts,
|
||||
priv->mousex,
|
||||
ev->mouse_on_button = bd_mouse_select(priv->bd, pts,
|
||||
priv->mousex,
|
||||
priv->mousey);
|
||||
bd_user_input(priv->bd, pts, key);
|
||||
} else if (strcmp(action, "menu") == 0) {
|
||||
@@ -469,8 +469,8 @@ static void handle_nav_command(stream_t *s, struct mp_nav_cmd *ev)
|
||||
} case MP_NAV_CMD_MOUSE_POS:
|
||||
priv->mousex = ev->u.mouse_pos.x;
|
||||
priv->mousey = ev->u.mouse_pos.y;
|
||||
ev->mouse_on_button = bd_mouse_select(priv->bd, mp_time_us(),
|
||||
priv->mousex,
|
||||
ev->mouse_on_button = bd_mouse_select(priv->bd, mp_time_us(),
|
||||
priv->mousex,
|
||||
priv->mousey);
|
||||
break;
|
||||
case MP_NAV_CMD_SKIP_STILL:
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
static const char *const sub_exts[] = {"utf", "utf8", "utf-8", "idx", "sub", "srt",
|
||||
"smi", "rt", "txt", "ssa", "aqt", "jss",
|
||||
"js", "ass", "mks", "vtt", NULL};
|
||||
"js", "ass", "mks", "vtt", "sup", NULL};
|
||||
|
||||
static const char *const audio_exts[] = {"mp3", "aac", "mka", "dts", "flac",
|
||||
"ogg", "m4a", "ac3", NULL};
|
||||
|
||||
+3
-36
@@ -4,53 +4,20 @@
|
||||
static void test_mp_chmap_diff(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
struct mp_chmap diff;
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("3.1"));
|
||||
mp_chmap_from_str(&b, bstr0("2.1"));
|
||||
|
||||
mp_chmap_diff(&a, &b, &diff);
|
||||
assert_int_equal(diff.num, 1);
|
||||
assert_int_equal(diff.speaker[0], MP_SPEAKER_ID_FC);
|
||||
assert_int_equal(mp_chmap_diffn(&a, &b), 1);
|
||||
|
||||
mp_chmap_from_str(&b, bstr0("6.1(back)"));
|
||||
mp_chmap_diff(&a, &b, &diff);
|
||||
assert_int_equal(diff.num, 0);
|
||||
|
||||
mp_chmap_diff(&b, &a, &diff);
|
||||
assert_int_equal(diff.num, 3);
|
||||
assert_int_equal(diff.speaker[0], MP_SPEAKER_ID_BL);
|
||||
assert_int_equal(diff.speaker[1], MP_SPEAKER_ID_BR);
|
||||
assert_int_equal(diff.speaker[2], MP_SPEAKER_ID_BC);
|
||||
}
|
||||
|
||||
static void test_mp_chmap_contains_with_related_chmaps(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("3.1"));
|
||||
mp_chmap_from_str(&b, bstr0("2.1"));
|
||||
|
||||
assert_true(mp_chmap_contains(&a, &b));
|
||||
assert_false(mp_chmap_contains(&b, &a));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_contains_with_unrelated_chmaps(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("mono"));
|
||||
mp_chmap_from_str(&b, bstr0("stereo"));
|
||||
|
||||
assert_false(mp_chmap_contains(&a, &b));
|
||||
assert_false(mp_chmap_contains(&b, &a));
|
||||
assert_int_equal(mp_chmap_diffn(&a, &b), 0);
|
||||
assert_int_equal(mp_chmap_diffn(&b, &a), 3);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const UnitTest tests[] = {
|
||||
unit_test(test_mp_chmap_diff),
|
||||
unit_test(test_mp_chmap_contains_with_related_chmaps),
|
||||
unit_test(test_mp_chmap_contains_with_unrelated_chmaps),
|
||||
};
|
||||
return run_tests(tests);
|
||||
}
|
||||
+54
-110
@@ -1,87 +1,76 @@
|
||||
#include "test_helpers.h"
|
||||
#include "audio/chmap_sel.h"
|
||||
|
||||
static void test_mp_chmap_sel_fallback_upmix(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
#define LAYOUTS(...) (char*[]){__VA_ARGS__, NULL}
|
||||
|
||||
static void test_sel(const char *input, const char *expected_selection,
|
||||
char **layouts)
|
||||
{
|
||||
struct mp_chmap_sel s = {0};
|
||||
struct mp_chmap input_map;
|
||||
struct mp_chmap expected_map;
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("7.1"));
|
||||
mp_chmap_from_str(&b, bstr0("5.1"));
|
||||
assert_true(mp_chmap_from_str(&input_map, bstr0(input)));
|
||||
assert_true(mp_chmap_from_str(&expected_map, bstr0(expected_selection)));
|
||||
|
||||
mp_chmap_sel_add_map(&s, &a);
|
||||
assert_true(mp_chmap_sel_fallback(&s, &b));
|
||||
assert_string_equal(mp_chmap_to_str(&b), "7.1");
|
||||
for (int n = 0; layouts[n]; n++) {
|
||||
struct mp_chmap tmp;
|
||||
assert_true(mp_chmap_from_str(&tmp, bstr0(layouts[n])));
|
||||
int count = s.num_chmaps;
|
||||
mp_chmap_sel_add_map(&s, &tmp);
|
||||
assert_true(s.num_chmaps > count); // assure validity and max. count
|
||||
}
|
||||
|
||||
assert_true(mp_chmap_sel_fallback(&s, &input_map));
|
||||
// We convert expected_map to a chmap and then back to a string to avoid
|
||||
// problems with ambiguous layouts.
|
||||
assert_string_equal(mp_chmap_to_str(&input_map),
|
||||
mp_chmap_to_str(&expected_map));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_upmix(void **state) {
|
||||
test_sel("5.1", "7.1", LAYOUTS("7.1"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_downmix(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
struct mp_chmap_sel s = {0};
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("5.1"));
|
||||
mp_chmap_from_str(&b, bstr0("7.1"));
|
||||
|
||||
mp_chmap_sel_add_map(&s, &a);
|
||||
assert_true(mp_chmap_sel_fallback(&s, &b));
|
||||
assert_string_equal(mp_chmap_to_str(&b), "5.1");
|
||||
test_sel("7.1", "5.1", LAYOUTS("5.1"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_incompatible(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
struct mp_chmap_sel s = {0};
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("7.1"));
|
||||
mp_chmap_from_str(&b, bstr0("7.1(wide-side)"));
|
||||
|
||||
mp_chmap_sel_add_map(&s, &a);
|
||||
assert_true(mp_chmap_sel_fallback(&s, &b));
|
||||
assert_string_equal(mp_chmap_to_str(&b), "7.1");
|
||||
test_sel("7.1(wide-side)", "7.1", LAYOUTS("7.1"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_prefer_compatible(void **state) {
|
||||
struct mp_chmap a, b, c;
|
||||
struct mp_chmap_sel s = {0};
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("7.1"));
|
||||
mp_chmap_from_str(&b, bstr0("5.1(side)"));
|
||||
mp_chmap_from_str(&c, bstr0("7.1(wide-side)"));
|
||||
|
||||
mp_chmap_sel_add_map(&s, &a);
|
||||
mp_chmap_sel_add_map(&s, &b);
|
||||
|
||||
assert_true(mp_chmap_sel_fallback(&s, &c));
|
||||
assert_string_equal(mp_chmap_to_str(&b), "5.1(side)");
|
||||
test_sel("7.1(wide-side)", "5.1(side)", LAYOUTS("7.1", "5.1(side)"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_prefer_closest_upmix(void **state) {
|
||||
struct mp_chmap_sel s = {0};
|
||||
|
||||
char *maps[] = { "7.1", "5.1", "2.1", "stereo", "mono", NULL };
|
||||
for (int i = 0; maps[i]; i++) {
|
||||
struct mp_chmap m;
|
||||
mp_chmap_from_str(&m, bstr0(maps[i]));
|
||||
mp_chmap_sel_add_map(&s, &m);
|
||||
}
|
||||
|
||||
struct mp_chmap c;
|
||||
mp_chmap_from_str(&c, bstr0("3.1"));
|
||||
assert_true(mp_chmap_sel_fallback(&s, &c));
|
||||
assert_string_equal(mp_chmap_to_str(&c), "5.1");
|
||||
test_sel("3.1", "5.1", LAYOUTS("7.1", "5.1", "2.1", "stereo", "mono"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_use_replacements(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
struct mp_chmap_sel s = {0};
|
||||
test_sel("5.1", "7.1(rear)", LAYOUTS("7.1(rear)"));
|
||||
}
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("7.1(rear)"));
|
||||
mp_chmap_from_str(&b, bstr0("5.1"));
|
||||
static void test_mp_chmap_sel_fallback_works_on_alsa_chmaps(void **state) {
|
||||
test_sel("5.1", "7.1(alsa)", LAYOUTS("7.1(alsa)"));
|
||||
}
|
||||
|
||||
mp_chmap_sel_add_map(&s, &a);
|
||||
assert_true(mp_chmap_sel_fallback(&s, &b));
|
||||
assert_string_equal(mp_chmap_to_str(&b), "7.1(rear)");
|
||||
static void test_mp_chmap_sel_fallback_mono_to_stereo(void **state) {
|
||||
test_sel("mono", "stereo", LAYOUTS("stereo", "5.1"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_stereo_to_stereo(void **state) {
|
||||
test_sel("stereo", "stereo", LAYOUTS("stereo", "5.1"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_no_downmix(void **state) {
|
||||
test_sel("5.1(side)", "7.1(rear)", LAYOUTS("stereo", "7.1(rear)"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_minimal_downmix(void **state) {
|
||||
test_sel("7.1", "fl-fr-lfe-fc-bl-br-flc-frc",
|
||||
LAYOUTS("fl-fr-lfe-fc-bl-br-flc-frc", "3.0(back)"));
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_reject_unknown(void **state) {
|
||||
@@ -89,9 +78,7 @@ static void test_mp_chmap_sel_fallback_reject_unknown(void **state) {
|
||||
struct mp_chmap b;
|
||||
struct mp_chmap_sel s = {0};
|
||||
|
||||
a.num = 2;
|
||||
a.speaker[0] = MP_SPEAKER_ID_UNKNOWN0;
|
||||
a.speaker[1] = MP_SPEAKER_ID_UNKNOWN0 + 1;
|
||||
mp_chmap_set_unknown(&a, 2);
|
||||
|
||||
mp_chmap_from_str(&b, bstr0("5.1"));
|
||||
|
||||
@@ -100,51 +87,6 @@ static void test_mp_chmap_sel_fallback_reject_unknown(void **state) {
|
||||
assert_string_equal(mp_chmap_to_str(&b), "5.1");
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_works_on_alsa_chmaps(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
struct mp_chmap_sel s = {0};
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("7.1(alsa)"));
|
||||
mp_chmap_from_str(&b, bstr0("5.1"));
|
||||
|
||||
mp_chmap_sel_add_map(&s, &a);
|
||||
assert_true(mp_chmap_sel_fallback(&s, &b));
|
||||
assert_string_equal(mp_chmap_to_str(&b), "7.1(alsa)");
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_mono_to_stereo(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
struct mp_chmap c;
|
||||
struct mp_chmap_sel s = {0};
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("stereo"));
|
||||
mp_chmap_from_str(&b, bstr0("5.1"));
|
||||
mp_chmap_from_str(&c, bstr0("mono"));
|
||||
|
||||
mp_chmap_sel_add_map(&s, &a);
|
||||
mp_chmap_sel_add_map(&s, &b);
|
||||
assert_true(mp_chmap_sel_fallback(&s, &c));
|
||||
assert_string_equal(mp_chmap_to_str(&c), "stereo");
|
||||
}
|
||||
|
||||
static void test_mp_chmap_sel_fallback_stereo_to_stereo(void **state) {
|
||||
struct mp_chmap a;
|
||||
struct mp_chmap b;
|
||||
struct mp_chmap c;
|
||||
struct mp_chmap_sel s = {0};
|
||||
|
||||
mp_chmap_from_str(&a, bstr0("stereo"));
|
||||
mp_chmap_from_str(&b, bstr0("5.1"));
|
||||
mp_chmap_from_str(&c, bstr0("stereo"));
|
||||
|
||||
mp_chmap_sel_add_map(&s, &a);
|
||||
mp_chmap_sel_add_map(&s, &b);
|
||||
assert_true(mp_chmap_sel_fallback(&s, &c));
|
||||
assert_string_equal(mp_chmap_to_str(&c), "stereo");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const UnitTest tests[] = {
|
||||
unit_test(test_mp_chmap_sel_fallback_upmix),
|
||||
@@ -153,10 +95,12 @@ int main(void) {
|
||||
unit_test(test_mp_chmap_sel_fallback_prefer_compatible),
|
||||
unit_test(test_mp_chmap_sel_fallback_prefer_closest_upmix),
|
||||
unit_test(test_mp_chmap_sel_fallback_use_replacements),
|
||||
unit_test(test_mp_chmap_sel_fallback_reject_unknown),
|
||||
unit_test(test_mp_chmap_sel_fallback_works_on_alsa_chmaps),
|
||||
unit_test(test_mp_chmap_sel_fallback_mono_to_stereo),
|
||||
unit_test(test_mp_chmap_sel_fallback_stereo_to_stereo),
|
||||
unit_test(test_mp_chmap_sel_fallback_no_downmix),
|
||||
unit_test(test_mp_chmap_sel_fallback_minimal_downmix),
|
||||
unit_test(test_mp_chmap_sel_fallback_reject_unknown),
|
||||
};
|
||||
return run_tests(tests);
|
||||
}
|
||||
+9
-2
@@ -673,13 +673,20 @@ void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, struct mp_cmat *m)
|
||||
|
||||
double ymul = (rgblev.max - rgblev.min) / (yuvlev.ymax - yuvlev.ymin);
|
||||
double cmul = (rgblev.max - rgblev.min) / (yuvlev.cmid - yuvlev.cmin) / 2;
|
||||
|
||||
// Contrast scales the output value range (gain)
|
||||
ymul *= params->contrast;
|
||||
cmul *= params->contrast;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
m->m[i][0] *= ymul;
|
||||
m->m[i][1] *= cmul;
|
||||
m->m[i][2] *= cmul;
|
||||
// Set c so that Y=umin,UV=cmid maps to RGB=min (black to black)
|
||||
// Set c so that Y=umin,UV=cmid maps to RGB=min (black to black),
|
||||
// also add brightness offset (black lift)
|
||||
m->c[i] = rgblev.min - m->m[i][0] * yuvlev.ymin
|
||||
-(m->m[i][1] + m->m[i][2]) * yuvlev.cmid;
|
||||
- (m->m[i][1] + m->m[i][2]) * yuvlev.cmid
|
||||
+ params->brightness;
|
||||
}
|
||||
|
||||
// Brightness adds a constant to output R,G,B.
|
||||
|
||||
@@ -673,6 +673,13 @@ static int config(struct vf_instance *vf, int width, int height,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
|
||||
if (width % desc.align_x || height % desc.align_y) {
|
||||
MP_FATAL(vf, "VapourSynth does not allow unaligned/cropped video sizes.\n");
|
||||
destroy_vs(vf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
vf_rescale_dsize(&d_width, &d_height, width, height, vi->width, vi->height);
|
||||
|
||||
return vf_next_config(vf, vi->width, vi->height, d_width, d_height, flags, fmt);
|
||||
|
||||
@@ -163,14 +163,14 @@ static struct mp_image *render(struct vf_instance *vf, struct mp_image *in,
|
||||
status = vaMapBuffer(p->display, buffer, (void**)¶m);
|
||||
if (!check_error(vf, status, "vaMapBuffer()"))
|
||||
break;
|
||||
|
||||
|
||||
VAProcFilterParameterBufferDeinterlacing *filter_params;
|
||||
status = vaMapBuffer(p->display, *(p->pipe.filters), (void**)&filter_params);
|
||||
if (!check_error(vf, status, "vaMapBuffer()"))
|
||||
break;
|
||||
filter_params->flags = flags & VA_TOP_FIELD ? 0 : VA_DEINTERLACING_BOTTOM_FIELD;
|
||||
vaUnmapBuffer(p->display, *(p->pipe.filters));
|
||||
|
||||
|
||||
param->surface = in_id;
|
||||
param->surface_region = NULL;
|
||||
param->output_region = NULL;
|
||||
|
||||
@@ -27,9 +27,10 @@ struct vo_cocoa_state;
|
||||
int vo_cocoa_init(struct vo *vo);
|
||||
void vo_cocoa_uninit(struct vo *vo);
|
||||
|
||||
int vo_cocoa_config_window(struct vo *vo, uint32_t flags, void *gl_ctx);
|
||||
int vo_cocoa_config_window(struct vo *vo, uint32_t flags);
|
||||
|
||||
void vo_cocoa_set_current_context(struct vo *vo, bool current);
|
||||
bool vo_cocoa_start_frame(struct vo *vo);
|
||||
void vo_cocoa_swap_buffers(struct vo *vo);
|
||||
int vo_cocoa_check_events(struct vo *vo);
|
||||
int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg);
|
||||
|
||||
@@ -35,10 +35,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_COCOA_APPLICATION
|
||||
# include "osdep/macosx_application.h"
|
||||
# include "osdep/macosx_application_objc.h"
|
||||
#endif
|
||||
#include "osdep/macosx_application.h"
|
||||
#include "osdep/macosx_application_objc.h"
|
||||
|
||||
#include "options/options.h"
|
||||
#include "video/out/vo.h"
|
||||
@@ -109,6 +107,14 @@ static void with_cocoa_lock_on_main_thread(struct vo *vo, void(^block)(void))
|
||||
});
|
||||
}
|
||||
|
||||
static void with_cocoa_lock_on_main_thread_sync(struct vo *vo, void(^block)(void))
|
||||
{
|
||||
struct vo_cocoa_state *s = vo->cocoa;
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
with_cocoa_lock(s, block);
|
||||
});
|
||||
}
|
||||
|
||||
static void queue_new_video_size(struct vo *vo, int w, int h)
|
||||
{
|
||||
struct vo_cocoa_state *s = vo->cocoa;
|
||||
@@ -279,7 +285,7 @@ void vo_cocoa_uninit(struct vo *vo)
|
||||
{
|
||||
struct vo_cocoa_state *s = vo->cocoa;
|
||||
|
||||
with_cocoa_lock_on_main_thread(vo, ^{
|
||||
with_cocoa_lock_on_main_thread_sync(vo, ^{
|
||||
enable_power_management(s);
|
||||
cocoa_uninit_light_sensor(s);
|
||||
cocoa_rm_fs_screen_profile_observer(s);
|
||||
@@ -462,13 +468,11 @@ static void create_ui(struct vo *vo, struct mp_rect *win, int geo_flags)
|
||||
[view signalMousePosition];
|
||||
s->adapter = adapter;
|
||||
|
||||
#if HAVE_COCOA_APPLICATION
|
||||
cocoa_register_menu_item_action(MPM_H_SIZE, @selector(halfSize));
|
||||
cocoa_register_menu_item_action(MPM_N_SIZE, @selector(normalSize));
|
||||
cocoa_register_menu_item_action(MPM_D_SIZE, @selector(doubleSize));
|
||||
cocoa_register_menu_item_action(MPM_MINIMIZE, @selector(performMiniaturize:));
|
||||
cocoa_register_menu_item_action(MPM_ZOOM, @selector(performZoom:));
|
||||
#endif
|
||||
|
||||
s->video = [[MpvVideoView alloc] initWithFrame:[s->view bounds]];
|
||||
[s->video setWantsBestResolutionOpenGLSurface:YES];
|
||||
@@ -546,7 +550,7 @@ void vo_cocoa_release_nsgl_ctx(struct vo *vo)
|
||||
s->gl_ctx = nil;
|
||||
}
|
||||
|
||||
int vo_cocoa_config_window(struct vo *vo, uint32_t flags, void *gl_ctx)
|
||||
int vo_cocoa_config_window(struct vo *vo, uint32_t flags)
|
||||
{
|
||||
struct vo_cocoa_state *s = vo->cocoa;
|
||||
with_cocoa_lock_on_main_thread(vo, ^{
|
||||
@@ -632,13 +636,21 @@ static void draw_changes_after_next_frame(struct vo *vo)
|
||||
}
|
||||
}
|
||||
|
||||
bool vo_cocoa_start_frame(struct vo *vo)
|
||||
{
|
||||
struct vo_cocoa_state *s = vo->cocoa;
|
||||
|
||||
s->skip_swap_buffer = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void vo_cocoa_swap_buffers(struct vo *vo)
|
||||
{
|
||||
struct vo_cocoa_state *s = vo->cocoa;
|
||||
|
||||
if (s->skip_swap_buffer && !s->waiting_frame) {
|
||||
s->skip_swap_buffer = false;
|
||||
return;
|
||||
s->pending_events |= VO_EVENT_EXPOSE;
|
||||
} else {
|
||||
[s->gl_ctx flushBuffer];
|
||||
}
|
||||
@@ -771,11 +783,13 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
|
||||
*(double *)arg = vo->cocoa->screen_fps;
|
||||
return VO_TRUE;
|
||||
}
|
||||
break;
|
||||
case VOCTRL_GET_AMBIENT_LUX:
|
||||
if (vo->cocoa->light_sensor != IO_OBJECT_NULL) {
|
||||
*(int *)arg = vo->cocoa->last_lux;
|
||||
return VO_TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return VO_NOTIMPL;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ static bool config_window_cocoa(struct MPGLContext *ctx, int flags)
|
||||
if (!ctx->gl->SwapInterval)
|
||||
ctx->gl->SwapInterval = set_swap_interval;
|
||||
|
||||
vo_cocoa_config_window(ctx->vo, flags, p->ctx);
|
||||
vo_cocoa_config_window(ctx->vo, flags);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -153,6 +153,11 @@ static void releaseGlContext_cocoa(MPGLContext *ctx)
|
||||
CGLReleaseContext(p->ctx);
|
||||
}
|
||||
|
||||
static bool start_frame_cocoa(MPGLContext *ctx)
|
||||
{
|
||||
return vo_cocoa_start_frame(ctx->vo);
|
||||
}
|
||||
|
||||
static void swapGlBuffers_cocoa(MPGLContext *ctx)
|
||||
{
|
||||
vo_cocoa_swap_buffers(ctx->vo);
|
||||
@@ -169,6 +174,7 @@ void mpgl_set_backend_cocoa(MPGLContext *ctx)
|
||||
ctx->config_window = config_window_cocoa;
|
||||
ctx->releaseGlContext = releaseGlContext_cocoa;
|
||||
ctx->swapGlBuffers = swapGlBuffers_cocoa;
|
||||
ctx->start_frame = start_frame_cocoa;
|
||||
ctx->vo_init = vo_cocoa_init;
|
||||
ctx->register_resize_callback = vo_cocoa_register_resize_callback;
|
||||
ctx->vo_uninit = vo_cocoa_uninit;
|
||||
|
||||
@@ -114,9 +114,10 @@ typedef struct MPGLContext {
|
||||
void (*register_resize_callback)(struct vo *vo,
|
||||
void (*cb)(struct vo *vo, int w, int h));
|
||||
|
||||
// Optional activity state of context.
|
||||
// If false, OpenGL renderers should not draw anything.
|
||||
bool (*is_active)(struct MPGLContext *);
|
||||
// Optional callback on the beginning of a frame. The frame will be finished
|
||||
// with swapGlBuffers(). This returns false if use of the OpenGL context
|
||||
// should be avoided.
|
||||
bool (*start_frame)(struct MPGLContext *);
|
||||
|
||||
// For free use by the backend.
|
||||
void *priv;
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include "gl_video.h"
|
||||
#include "gl_lcms.h"
|
||||
|
||||
#include "osdep/io.h"
|
||||
|
||||
#if HAVE_LCMS2
|
||||
|
||||
#include <lcms2.h>
|
||||
|
||||
+16
-9
@@ -107,6 +107,7 @@ struct texplane {
|
||||
struct video_image {
|
||||
struct texplane planes[4];
|
||||
bool image_flipped;
|
||||
bool needs_upload;
|
||||
struct mp_image *mpi; // original input image
|
||||
};
|
||||
|
||||
@@ -320,7 +321,7 @@ const struct gl_video_opts gl_video_opts_def = {
|
||||
.npot = 1,
|
||||
.dither_depth = -1,
|
||||
.dither_size = 6,
|
||||
.fbo_format = GL_RGBA,
|
||||
.fbo_format = GL_RGBA16,
|
||||
.sigmoid_center = 0.75,
|
||||
.sigmoid_slope = 6.5,
|
||||
.scaler = {
|
||||
@@ -469,6 +470,7 @@ static void uninit_rendering(struct gl_video *p);
|
||||
static void uninit_scaler(struct gl_video *p, struct scaler *scaler);
|
||||
static void check_gl_features(struct gl_video *p);
|
||||
static bool init_format(int fmt, struct gl_video *init);
|
||||
static void gl_video_upload_image(struct gl_video *p);
|
||||
|
||||
#define GLSL(x) gl_sc_add(p->sc, #x "\n");
|
||||
#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__)
|
||||
@@ -2048,6 +2050,8 @@ void gl_video_render_frame(struct gl_video *p, int fbo, struct frame_timing *t)
|
||||
}
|
||||
|
||||
if (vimg->mpi) {
|
||||
gl_video_upload_image(p);
|
||||
|
||||
gl_sc_set_vao(p->sc, &p->vao);
|
||||
|
||||
if (p->opts.interpolation) {
|
||||
@@ -2124,27 +2128,30 @@ static bool get_image(struct gl_video *p, struct mp_image *mpi)
|
||||
return true;
|
||||
}
|
||||
|
||||
void gl_video_skip_image(struct gl_video *p, struct mp_image *mpi)
|
||||
void gl_video_set_image(struct gl_video *p, struct mp_image *mpi)
|
||||
{
|
||||
assert(mpi);
|
||||
|
||||
struct video_image *vimg = &p->image;
|
||||
talloc_free(vimg->mpi);
|
||||
vimg->mpi = mpi;
|
||||
vimg->needs_upload = true;
|
||||
|
||||
p->osd_pts = mpi->pts;
|
||||
}
|
||||
|
||||
void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
|
||||
static void gl_video_upload_image(struct gl_video *p)
|
||||
{
|
||||
GL *gl = p->gl;
|
||||
|
||||
struct video_image *vimg = &p->image;
|
||||
struct mp_image *mpi = vimg->mpi;
|
||||
|
||||
p->osd_pts = mpi->pts;
|
||||
|
||||
talloc_free(vimg->mpi);
|
||||
vimg->mpi = mpi;
|
||||
|
||||
if (p->hwdec_active)
|
||||
if (p->hwdec_active || !mpi || !vimg->needs_upload)
|
||||
return;
|
||||
|
||||
vimg->needs_upload = false;
|
||||
|
||||
assert(mpi->num_planes == p->plane_count);
|
||||
|
||||
mp_image_t mpi2 = *mpi;
|
||||
|
||||
@@ -83,8 +83,7 @@ bool gl_video_check_format(struct gl_video *p, int mp_format);
|
||||
void gl_video_config(struct gl_video *p, struct mp_image_params *params);
|
||||
void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b);
|
||||
void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d);
|
||||
void gl_video_skip_image(struct gl_video *p, struct mp_image *mpi);
|
||||
void gl_video_upload_image(struct gl_video *p, struct mp_image *img);
|
||||
void gl_video_set_image(struct gl_video *p, struct mp_image *img);
|
||||
void gl_video_render_frame(struct gl_video *p, int fbo, struct frame_timing *t);
|
||||
void gl_video_resize(struct gl_video *p, int vp_w, int vp_h,
|
||||
struct mp_rect *src, struct mp_rect *dst,
|
||||
|
||||
@@ -215,7 +215,7 @@ static int control(struct vo *vo, int *events, int request, void *data)
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool is_active(struct MPGLContext *ctx)
|
||||
static bool start_frame(struct MPGLContext *ctx)
|
||||
{
|
||||
struct vo_wayland_state *wl = ctx->vo->wayland;
|
||||
return wl->frame.pending;
|
||||
@@ -229,5 +229,5 @@ void mpgl_set_backend_wayland(MPGLContext *ctx)
|
||||
ctx->vo_control = control;
|
||||
ctx->vo_init = vo_wayland_init;
|
||||
ctx->vo_uninit = vo_wayland_uninit;
|
||||
ctx->is_active = is_active;
|
||||
ctx->start_frame = start_frame;
|
||||
}
|
||||
+19
-7
@@ -471,8 +471,10 @@ static void wait_vo(struct vo *vo, int64_t until_time)
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
} else {
|
||||
pthread_mutex_lock(&in->lock);
|
||||
if (!in->need_wakeup)
|
||||
mpthread_cond_timedwait(&in->wakeup, &in->lock, until_time);
|
||||
if (!in->need_wakeup) {
|
||||
struct timespec ts = mp_time_us_to_timespec(until_time);
|
||||
pthread_cond_timedwait(&in->wakeup, &in->lock, &ts);
|
||||
}
|
||||
in->need_wakeup = false;
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
}
|
||||
@@ -586,7 +588,7 @@ static bool render_frame(struct vo *vo)
|
||||
int64_t duration = in->frame_duration;
|
||||
struct mp_image *img = in->frame_queued;
|
||||
|
||||
if (!img && (!in->vsync_timed || in->paused || pts <= 0))
|
||||
if (!img && (!in->vsync_timed || in->paused))
|
||||
goto nothing_done;
|
||||
|
||||
if (in->vsync_timed && !in->hasframe)
|
||||
@@ -608,14 +610,22 @@ static bool render_frame(struct vo *vo)
|
||||
if (!in->hasframe_rendered)
|
||||
duration = -1; // disable framedrop
|
||||
|
||||
// if the clip and display have similar/identical fps, it's possible that
|
||||
// If the clip and display have similar/identical fps, it's possible that
|
||||
// we'll be very slightly late frequently due to timing jitter, or if the
|
||||
// clip/container timestamps are not very accurate.
|
||||
// so if we dropped the previous frame, keep dropping until we're aligned
|
||||
// So if we dropped the previous frame, keep dropping until we're aligned
|
||||
// perfectly, else, allow some slack (1 vsync) to let it settle into a rhythm.
|
||||
// On low clip fps, we don't drop anyway and the slack logic doesn't matter.
|
||||
// If the clip fps is more than ~5% above screen fps, we remove this slack
|
||||
// and use "normal" logic to allow more regular drops of 1 frame at a time.
|
||||
bool use_slack = duration > (0.95 * in->vsync_interval);
|
||||
in->dropped_frame = duration >= 0 &&
|
||||
use_slack ?
|
||||
((in->dropped_frame && end_time < next_vsync) ||
|
||||
(end_time < prev_vsync)); // hard threshold - 1 vsync late
|
||||
(end_time < prev_vsync)) // hard threshold - 1 vsync late
|
||||
:
|
||||
end_time < next_vsync; // normal frequent drops
|
||||
|
||||
|
||||
in->dropped_frame &= !(vo->driver->caps & VO_CAP_FRAMEDROP);
|
||||
in->dropped_frame &= (vo->global->opts->frame_dropping & 1);
|
||||
@@ -651,7 +661,7 @@ static bool render_frame(struct vo *vo)
|
||||
|
||||
MP_STATS(vo, "start video");
|
||||
|
||||
if (in->vsync_timed) {
|
||||
if (vo->driver->draw_image_timed) {
|
||||
struct frame_timing t = (struct frame_timing) {
|
||||
.pts = pts,
|
||||
.next_vsync = next_vsync,
|
||||
@@ -961,6 +971,8 @@ void vo_event(struct vo *vo, int event)
|
||||
pthread_mutex_lock(&in->lock);
|
||||
if ((in->queued_events & event & VO_EVENTS_USER) != (event & VO_EVENTS_USER))
|
||||
mp_input_wakeup(vo->input_ctx);
|
||||
if (event)
|
||||
wakeup_locked(vo);
|
||||
in->queued_events |= event;
|
||||
in->internal_events |= event;
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
|
||||
@@ -128,7 +128,6 @@ struct voctrl_get_equalizer_args {
|
||||
#define VO_NOTAVAIL -2
|
||||
#define VO_NOTIMPL -3
|
||||
|
||||
#define VOFLAG_FLIPPING 0x08
|
||||
#define VOFLAG_HIDDEN 0x10 //< Use to create a hidden window
|
||||
#define VOFLAG_GL_DEBUG 0x40 // Hint to request debug OpenGL context
|
||||
#define VOFLAG_ALPHA 0x80 // Hint to request alpha framebuffer
|
||||
|
||||
+17
-3
@@ -475,6 +475,10 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
|
||||
mp_image_params_guess_csp(&p->sws->dst);
|
||||
mp_image_set_params(p->cur_frame, &p->sws->dst);
|
||||
|
||||
struct modeset_buf *buf = p->dev->bufs;
|
||||
memset(buf[0].map, 0, buf[0].size);
|
||||
memset(buf[1].map, 0, buf[1].size);
|
||||
|
||||
if (mp_sws_reinit(p->sws) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -487,12 +491,13 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
if (p->active) {
|
||||
struct mp_image src = *mpi;
|
||||
struct mp_rect src_rc = p->src;
|
||||
src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
|
||||
src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
|
||||
mp_image_crop_rc(mpi, src_rc);
|
||||
mp_sws_scale(p->sws, p->cur_frame, mpi);
|
||||
osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->cur_frame);
|
||||
mp_image_crop_rc(&src, src_rc);
|
||||
mp_sws_scale(p->sws, p->cur_frame, &src);
|
||||
osd_draw_on_image(vo->osd, p->osd, src.pts, 0, p->cur_frame);
|
||||
|
||||
struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
|
||||
int32_t shift = (p->device_w * p->y + p->x) * 4;
|
||||
@@ -607,9 +612,18 @@ static int control(struct vo *vo, uint32_t request, void *data)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
switch (request) {
|
||||
case VOCTRL_SCREENSHOT_WIN:
|
||||
*(struct mp_image**)data = mp_image_new_copy(p->cur_frame);
|
||||
return VO_TRUE;
|
||||
case VOCTRL_REDRAW_FRAME:
|
||||
draw_image(vo, p->last_input);
|
||||
return VO_TRUE;
|
||||
case VOCTRL_GET_PANSCAN:
|
||||
return VO_TRUE;
|
||||
case VOCTRL_SET_PANSCAN:
|
||||
if (vo->config_ok)
|
||||
reconfig(vo, vo->params, 0);
|
||||
return VO_TRUE;
|
||||
}
|
||||
return VO_NOTIMPL;
|
||||
}
|
||||
|
||||
+17
-18
@@ -76,7 +76,7 @@ struct gl_priv {
|
||||
|
||||
char *backend;
|
||||
|
||||
int vo_flipped;
|
||||
bool frame_started;
|
||||
|
||||
int frames_rendered;
|
||||
unsigned int prev_sgi_sync_count;
|
||||
@@ -124,8 +124,11 @@ static void flip_page(struct vo *vo)
|
||||
struct gl_priv *p = vo->priv;
|
||||
GL *gl = p->gl;
|
||||
|
||||
if (p->glctx->is_active && !p->glctx->is_active(p->glctx))
|
||||
if (!p->frame_started) {
|
||||
vo_increment_drop_count(vo, 1);
|
||||
return;
|
||||
}
|
||||
p->frame_started = false;
|
||||
|
||||
mpgl_lock(p->glctx);
|
||||
|
||||
@@ -171,19 +174,17 @@ static void draw_image_timed(struct vo *vo, mp_image_t *mpi,
|
||||
struct gl_priv *p = vo->priv;
|
||||
GL *gl = p->gl;
|
||||
|
||||
if (p->glctx->is_active && !p->glctx->is_active(p->glctx)) {
|
||||
if (mpi)
|
||||
gl_video_skip_image(p->renderer, mpi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->vo_flipped)
|
||||
mp_image_vflip(mpi);
|
||||
|
||||
mpgl_lock(p->glctx);
|
||||
|
||||
if (mpi)
|
||||
gl_video_upload_image(p->renderer, mpi);
|
||||
gl_video_set_image(p->renderer, mpi);
|
||||
|
||||
if (p->glctx->start_frame && !p->glctx->start_frame(p->glctx)) {
|
||||
mpgl_unlock(p->glctx);
|
||||
return;
|
||||
}
|
||||
|
||||
p->frame_started = true;
|
||||
gl_video_render_frame(p->renderer, 0, t);
|
||||
|
||||
// The playloop calls this last before waiting some time until it decides
|
||||
@@ -236,8 +237,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
|
||||
|
||||
gl_video_config(p->renderer, params);
|
||||
|
||||
p->vo_flipped = !!(flags & VOFLAG_FLIPPING);
|
||||
|
||||
mpgl_unlock(p->glctx);
|
||||
|
||||
return 0;
|
||||
@@ -388,11 +387,11 @@ static int control(struct vo *vo, uint32_t request, void *data)
|
||||
request_hwdec_api(p, data);
|
||||
return true;
|
||||
case VOCTRL_REDRAW_FRAME:
|
||||
if (p->glctx->is_active && !p->glctx->is_active(p->glctx))
|
||||
return true;
|
||||
|
||||
mpgl_lock(p->glctx);
|
||||
gl_video_render_frame(p->renderer, 0, NULL);
|
||||
if (!(p->glctx->start_frame && !p->glctx->start_frame(p->glctx))) {
|
||||
p->frame_started = true;
|
||||
gl_video_render_frame(p->renderer, 0, NULL);
|
||||
}
|
||||
mpgl_unlock(p->glctx);
|
||||
return true;
|
||||
case VOCTRL_SET_COMMAND_LINE: {
|
||||
|
||||
@@ -328,7 +328,7 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (mpi)
|
||||
gl_video_upload_image(ctx->renderer, mpi);
|
||||
gl_video_set_image(ctx->renderer, mpi);
|
||||
|
||||
gl_video_render_frame(ctx->renderer, fbo, NULL);
|
||||
|
||||
@@ -542,6 +542,7 @@ static int preinit(struct vo *vo)
|
||||
}
|
||||
p->ctx->active = vo;
|
||||
p->ctx->reconfigured = true;
|
||||
p->ctx->update_new_opts = true;
|
||||
copy_vo_opts(vo);
|
||||
pthread_mutex_unlock(&p->ctx->lock);
|
||||
|
||||
|
||||
+89
-22
@@ -30,6 +30,8 @@
|
||||
|
||||
#include <libavutil/rational.h>
|
||||
|
||||
#include "osdep/atomics.h"
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/msg.h"
|
||||
#include "options/m_config.h"
|
||||
@@ -57,6 +59,7 @@ struct osd_part {
|
||||
struct priv {
|
||||
DISPMANX_DISPLAY_HANDLE_T display;
|
||||
DISPMANX_ELEMENT_HANDLE_T window;
|
||||
DISPMANX_RESOURCE_HANDLE_T window_back;
|
||||
DISPMANX_UPDATE_HANDLE_T update;
|
||||
uint32_t w, h;
|
||||
|
||||
@@ -72,6 +75,8 @@ struct priv {
|
||||
// for RAM input
|
||||
MMAL_POOL_T *swpool;
|
||||
|
||||
atomic_bool update_display;
|
||||
|
||||
int background_layer;
|
||||
int video_layer;
|
||||
int osd_layer;
|
||||
@@ -239,6 +244,64 @@ static void resize(struct vo *vo)
|
||||
MP_WARN(vo, "could not set video rectangle\n");
|
||||
}
|
||||
|
||||
static int update_display_size(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
uint32_t n_w = 0, n_h = 0;
|
||||
if (graphics_get_display_size(0, &n_w, &n_h) < 0) {
|
||||
MP_FATAL(vo, "Could not get display size.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p->w == n_w && p->h == n_h)
|
||||
return 0;
|
||||
|
||||
p->w = n_w;
|
||||
p->h = n_h;
|
||||
|
||||
MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h);
|
||||
|
||||
if (p->window)
|
||||
vc_dispmanx_element_remove(p->update, p->window);
|
||||
if (p->window_back)
|
||||
vc_dispmanx_resource_delete(p->window_back);
|
||||
p->window = 0;
|
||||
p->window_back = 0;
|
||||
|
||||
VC_IMAGE_TYPE_T format = VC_IMAGE_ARGB8888; // assuming RPI is always LE
|
||||
p->window_back = vc_dispmanx_resource_create(format, 1 | (4 << 16), 1,
|
||||
&(int32_t){0});
|
||||
if (!p->window_back) {
|
||||
MP_ERR(vo, "Could not create background bitmap.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t px = 0;
|
||||
VC_RECT_T rc = {.width = 1, .height = 1};
|
||||
vc_dispmanx_resource_write_data(p->window_back, format, 4, &px, &rc);
|
||||
|
||||
// Use the whole screen.
|
||||
VC_RECT_T dst = {.width = p->w, .height = p->h};
|
||||
VC_RECT_T src = {.width = 1 << 16, .height = 1 << 16};
|
||||
VC_DISPMANX_ALPHA_T alpha = {
|
||||
.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
|
||||
.opacity = 0xFF,
|
||||
};
|
||||
p->window = vc_dispmanx_element_add(p->update, p->display, p->background_layer,
|
||||
&dst, p->window_back, &src,
|
||||
DISPMANX_PROTECTION_NONE, &alpha, 0, 0);
|
||||
if (!p->window) {
|
||||
MP_FATAL(vo, "Could not add DISPMANX element.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vc_dispmanx_update_submit_sync(p->update);
|
||||
p->update = vc_dispmanx_update_start(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flip_page(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
@@ -466,19 +529,43 @@ static int control(struct vo *vo, uint32_t request, void *data)
|
||||
case VOCTRL_SCREENSHOT_WIN:
|
||||
*(struct mp_image **)data = take_screenshot(vo);
|
||||
return VO_TRUE;
|
||||
case VOCTRL_CHECK_EVENTS:
|
||||
if (atomic_load(&p->update_display)) {
|
||||
atomic_store(&p->update_display, false);
|
||||
update_display_size(vo);
|
||||
if (p->renderer_enabled)
|
||||
resize(vo);
|
||||
}
|
||||
return VO_TRUE;
|
||||
}
|
||||
|
||||
return VO_NOTIMPL;
|
||||
}
|
||||
|
||||
static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1,
|
||||
uint32_t param2)
|
||||
{
|
||||
struct vo *vo = callback_data;
|
||||
struct priv *p = vo->priv;
|
||||
atomic_store(&p->update_display, true);
|
||||
vo_wakeup(vo);
|
||||
}
|
||||
|
||||
static void uninit(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
vc_tv_unregister_callback_full(tv_callback, vo);
|
||||
|
||||
talloc_free(p->next_image);
|
||||
|
||||
wipe_osd(vo);
|
||||
|
||||
if (p->window)
|
||||
vc_dispmanx_element_remove(p->update, p->window);
|
||||
if (p->window_back)
|
||||
vc_dispmanx_resource_delete(p->window_back);
|
||||
|
||||
if (p->update)
|
||||
vc_dispmanx_update_submit_sync(p->update);
|
||||
|
||||
@@ -521,30 +608,10 @@ static int preinit(struct vo *vo)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (graphics_get_display_size(0, &p->w, &p->h) < 0) {
|
||||
MP_FATAL(vo, "Could not get display size.\n");
|
||||
if (update_display_size(vo) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h);
|
||||
|
||||
// Just use the whole screen.
|
||||
VC_RECT_T dst = {.width = p->w, .height = p->h};
|
||||
VC_RECT_T src = {.width = p->w << 16, .height = p->h << 16};
|
||||
VC_DISPMANX_ALPHA_T alpha = {
|
||||
.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
|
||||
.opacity = 0xFF,
|
||||
};
|
||||
p->window = vc_dispmanx_element_add(p->update, p->display, p->background_layer,
|
||||
&dst, 0, &src, DISPMANX_PROTECTION_NONE,
|
||||
&alpha, 0, 0);
|
||||
if (!p->window) {
|
||||
MP_FATAL(vo, "Could not add DISPMANX element.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vc_dispmanx_update_submit_sync(p->update);
|
||||
p->update = vc_dispmanx_update_start(10);
|
||||
vc_tv_register_callback(tv_callback, vo);
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
Loaded 100 of 109 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user