summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDiogo Franco (Kovensky) <diogomfranco@gmail.com>2015-04-25 18:28:17 +0900
committerDiogo Franco (Kovensky) <diogomfranco@gmail.com>2015-04-25 18:28:17 +0900
commit5f1d6b6e5ae515abfb77c78c44d21ee21beb0fc3 (patch)
tree6b757d37d296709dfd5955ef9d4d41426cd47da6
parentd64ff3aa51b49451348383a6ff6de7697a52dc62 (diff)
parent983d24e3b9ce9c9c7755279138c14170e85fbf37 (diff)
downloadmpv-5f1d6b6e5ae515abfb77c78c44d21ee21beb0fc3.tar.bz2
mpv-5f1d6b6e5ae515abfb77c78c44d21ee21beb0fc3.tar.xz
Merge branch 'master' into release/0.9
* master: (87 commits) manpage: move --autosync description player: add --window-scale option player: flush decoder even if cover art is decoded player: don't show A/V desync message in non-sense situations w32_common: add more rounded-down frame rates w32_common: use the current monitor's refresh rate dxva2: fix broken build with gcc 5.1 terminal: printf() is not signal-safe man: fix PDF build DOCS/mplayer-changes: Eleborate on joystick input osc: add nil check for element.eventresponder mp_image: remove some unused interlacing flags vf_vapoursynth: update _FieldBased semantics options: remove unneeded hack from command line parser manpage: document ff-index sub-property demux_mkv: limit timestamp fixing to 1ms max demux_mkv: attempt to fix rounded timestamps demux_mkv: move global options to the demuxer demux_mkv: better seeking after video end lua: add utils.format_json() function ...
-rw-r--r--DOCS/client-api-changes.rst1
-rw-r--r--DOCS/contribute.md3
-rw-r--r--DOCS/man/af.rst31
-rw-r--r--DOCS/man/ao.rst6
-rw-r--r--DOCS/man/input.rst57
-rw-r--r--DOCS/man/lua.rst8
-rw-r--r--DOCS/man/mpv.rst37
-rw-r--r--DOCS/man/options.rst61
-rw-r--r--DOCS/man/vf.rst6
-rw-r--r--DOCS/man/vo.rst20
-rw-r--r--DOCS/mplayer-changes.rst2
-rw-r--r--TOOLS/lua/autoload.lua41
-rw-r--r--TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf3
-rwxr-xr-xTOOLS/umpv2
-rw-r--r--audio/decode/ad_lavc.c3
-rw-r--r--audio/filter/af_lavrresample.c16
-rw-r--r--audio/out/ao_alsa.c5
-rw-r--r--common/msg.c11
-rw-r--r--common/msg_control.h1
-rw-r--r--demux/demux.c11
-rw-r--r--demux/demux.h1
-rw-r--r--demux/demux_lavf.c9
-rw-r--r--demux/demux_mkv.c87
-rw-r--r--demux/demux_playlist.c54
-rw-r--r--demux/demux_raw.c1
-rw-r--r--demux/stheader.h1
-rw-r--r--etc/input.conf1
-rw-r--r--input/cmd_list.c5
-rw-r--r--input/cmd_list.h1
-rw-r--r--libmpv/client.h29
-rwxr-xr-xold-configure1
-rw-r--r--old-makefile2
-rw-r--r--options/m_option.c18
-rw-r--r--options/options.c14
-rw-r--r--options/options.h5
-rw-r--r--options/parse_commandline.c5
-rw-r--r--options/parse_configfile.c5
-rw-r--r--osdep/subprocess-posix.c13
-rw-r--r--osdep/subprocess.c57
-rw-r--r--osdep/subprocess.h3
-rw-r--r--osdep/terminal-unix.c3
-rw-r--r--player/client.c15
-rw-r--r--player/command.c184
-rw-r--r--player/command.h3
-rw-r--r--player/configfiles.c14
-rw-r--r--player/core.h2
-rw-r--r--player/discnav.c23
-rw-r--r--player/lua.c21
-rw-r--r--player/lua/osc.lua12
-rw-r--r--player/playloop.c4
-rw-r--r--player/screenshot.c22
-rw-r--r--player/screenshot.h3
-rw-r--r--player/video.c30
-rw-r--r--stream/cache.c41
-rw-r--r--stream/discnav.h1
-rw-r--r--stream/stream.h1
-rw-r--r--stream/stream_bluray.c8
-rw-r--r--stream/stream_dvdnav.c9
-rw-r--r--stream/stream_file.c27
-rw-r--r--video/decode/dec_video.c11
-rw-r--r--video/decode/dec_video.h1
-rw-r--r--video/decode/dxva2.c5
-rw-r--r--video/decode/vd_lavc.c4
-rw-r--r--video/filter/vf.c5
-rw-r--r--video/filter/vf.h1
-rw-r--r--video/filter/vf_crop.c7
-rw-r--r--video/filter/vf_expand.c13
-rw-r--r--video/filter/vf_mirror.c89
-rw-r--r--video/filter/vf_screenshot.c74
-rw-r--r--video/filter/vf_vapoursynth.c14
-rw-r--r--video/filter/vf_vdpaupp.c4
-rw-r--r--video/image_writer.c72
-rw-r--r--video/image_writer.h10
-rw-r--r--video/mp_image.c3
-rw-r--r--video/mp_image.h3
-rw-r--r--video/out/drm_common.c149
-rw-r--r--video/out/drm_common.h36
-rw-r--r--video/out/vo.c50
-rw-r--r--video/out/vo_drm.c641
-rw-r--r--video/out/vo_opengl.c3
-rw-r--r--video/out/vo_rpi.c8
-rw-r--r--video/out/w32_common.c26
-rw-r--r--video/out/win_state.c2
-rw-r--r--video/out/x11_common.c67
-rw-r--r--video/out/x11_common.h6
-rw-r--r--wscript10
-rw-r--r--wscript_build.py6
87 files changed, 1742 insertions, 638 deletions
diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst
index 62640889b6..9b3f21c7c8 100644
--- a/DOCS/client-api-changes.rst
+++ b/DOCS/client-api-changes.rst
@@ -25,6 +25,7 @@ API changes
::
+ 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()
diff --git a/DOCS/contribute.md b/DOCS/contribute.md
index 224c52494c..11b5e33d50 100644
--- a/DOCS/contribute.md
+++ b/DOCS/contribute.md
@@ -14,6 +14,9 @@ Sending patches
``git format-patch``. diffs posted as pastebins (especially if the http link
returns HTML) just cause extra work for everyone, because they lack commit
message and authorship information.
+- When creating pull requests, be sure to test your changes. If you didn't, please
+ say so in the pull request message.
+
- Write informative commit messages. Use present tense to describe the
situation with the patch applied, and past tense for the situation before
the change.
diff --git a/DOCS/man/af.rst b/DOCS/man/af.rst
index 1ae3c092df..96e50686ae 100644
--- a/DOCS/man/af.rst
+++ b/DOCS/man/af.rst
@@ -225,14 +225,21 @@ Available filters are:
automatically up- and downmix standard channel layouts.
``format=format:srate:channels:out-format:out-srate:out-channels``
- Force a specific audio format/configuration without actually changing the
- audio data. Keep in mind that the filter system might auto-insert actual
- conversion filters before or after this filter if needed.
+ Does not do any format conversion itself. Rather, it may cause the
+ filter system to insert necessary conversion filters before or after this
+ filter if needed. It is primarily useful for controlling the audio format
+ going into other filters. To specify the format for audio output, see
+ ``--audio-format``, ``--audio-samplerate``, and ``--audio-channels``. This
+ filter is able to force a particular format, whereas ``--audio-*``
+ may be overridden by the ao based on output compatibility.
All parameters are optional. The first 3 parameters restrict what the filter
- accepts as input. The ``out-`` parameters change the audio format, without
- actually doing a conversion. The data will be 'reinterpreted' by the
- filters or audio outputs following this filter.
+ accepts as input. They will therefore cause conversion filters to be
+ inserted before this one. The ``out-`` parameters tell the filters or audio
+ outputs following this filter how to interpret the data without actually
+ doing a conversion. Setting these will probably just break things unless you
+ really know you want this for some reason, such as testing or dealing with
+ broken media.
``<format>``
Force conversion to this format. Use ``--af=format=format=help`` to get
@@ -252,14 +259,9 @@ Available filters are:
``<out-channels>``
- See also ``--audio-format``, ``--audio-samplerate``, and
- ``--audio-channels`` for related options. Keep in mind that
- ``--audio-channels`` does not actually force the number of
- channels in most cases, while this filter can do this.
-
- *NOTE*: this filter used to be named ``force``. Also, unlike the old
- ``format`` filter, this does not do any actual conversion anymore.
- Conversion is done by other, automatically inserted filters.
+ *NOTE*: this filter used to be named ``force``. The old ``format`` filter
+ used to do conversion itself, unlike this one which lets the filter system
+ handle the conversion.
``convert24``
Filter for internal use only. Converts between 24-bit and 32-bit sample
@@ -629,4 +631,3 @@ Available filters are:
``o=<string>``
AVOptions.
-
diff --git a/DOCS/man/ao.rst b/DOCS/man/ao.rst
index 5ca960c4ea..534f62588c 100644
--- a/DOCS/man/ao.rst
+++ b/DOCS/man/ao.rst
@@ -107,8 +107,10 @@ Available audio output drivers are:
``--audio-device`` to select the device (use ``--audio-device=help``
to get a list of all devices and their mpv name).
- You can also try
- `Using the upmix plugin <https://github.com/mpv-player/mpv/wiki/ALSA:-Surround-Sound-and-Upmixing>`_.
+ You can also try `using the upmix plugin <http://git.io/vfuAy>`_.
+ This setup enables multichannel audio on the ``default`` device
+ with automatic upmixing with shared access, so playing stereo
+ and multichannel audio at the same time will work as expected.
``oss``
OSS audio output driver
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index a3bad3706b..633f6a8308 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -187,8 +187,7 @@ List of Input Commands
The second argument is like the first argument to ``screenshot``.
- This command tries to never overwrite files. If the file already exists,
- it fails.
+ If the file already exists, it's overwritten.
Like all input command parameters, the filename is subject to property
expansion as described in `Property Expansion`_.
@@ -632,6 +631,15 @@ Input Commands that are Possibly Subject to Change
unseekable streams that are going out of sync.
This command might be changed or removed in the future.
+``screenshot_raw [subtitles|video|window]``
+ Return a screenshot in memory. This can be used only through the client
+ API. The MPV_FORMAT_NODE_MAP returned by this command has the ``w``, ``h``,
+ ``stride`` fields set to obvious contents. A ``format`` field is set to
+ ``bgr0`` by default. This format is organized as ``B8G8R8X8`` (where ``B``
+ is the LSB). The contents of the padding ``X`` is undefined. The ``data``
+ field is of type MPV_FORMAT_BYTE_ARRAY with the actual image data. The image
+ is freed as soon as the result node is freed.
+
Undocumented commands: ``tv_last_channel`` (TV/DVB only),
``get_property`` (deprecated), ``ao_reload`` (experimental/internal).
@@ -920,6 +928,10 @@ Property list
a BD or DVD. Use the ``discnav menu`` command to actually enter or leave
menu mode.
+``disc-mouse-on-button``
+ Return ``yes`` when the mouse cursor is located on a button, or ``no``
+ when cursor is outside of any button for disc navigation.
+
``chapters``
Number of chapters.
@@ -1078,6 +1090,11 @@ Property list
guess is very unreliable, and often the property will not be available
at all, even if data is buffered.
+``demuxer-cache-time``
+ Approximate time of video buffered in the demuxer, in seconds. Same as
+ ``demuxer-cache-duration`` but returns the last timestamp of bufferred
+ data in demuxer.
+
``demuxer-cache-idle``
Returns ``yes`` if the demuxer is idle, which means the demuxer cache is
filled to the requested amount, and is currently not reading more data.
@@ -1123,9 +1140,6 @@ Property list
``audio-codec``
Audio codec selected for decoding.
-``audio-bitrate``
- Audio bitrate. This is probably a very bad guess in most cases.
-
``audio-samplerate``
Audio samplerate.
@@ -1222,9 +1236,6 @@ Property list
``video-codec``
Video codec selected for decoding.
-``video-bitrate``
- Video bitrate (a bad guess).
-
``width``, ``height``
Video size. This uses the size of the video as decoded, or if no video
frame has been decoded yet, the (possibly incorrect) container indicated
@@ -1513,6 +1524,13 @@ Property list
``track-list/N/selected``
``yes`` if the track is currently decoded, ``no`` otherwise.
+ ``track-list/N/ff-index``
+ The stream index as usually used by the FFmpeg utilities. Note that
+ this can be potentially wrong if a demuxer other than libavformat
+ (``--demuxer=lavf``) is used. For mkv files, the index will usually
+ match even if the default (builtin) demuxer is used, but there is
+ no hard guarantee.
+
When querying the property with the client API using ``MPV_FORMAT_NODE``,
or with Lua ``mp.get_property_native``, this will return a mpv_node with
the following contents:
@@ -1634,7 +1652,7 @@ Property list
whether the video window is visible. If the ``--force-window`` option is
used, this is usually always returns ``yes``.
-``packet-video-bitrate``, ``packet-audio-bitrate``, ``packet-sub-bitrate``
+``video-bitrate``, ``audio-bitrate``, ``sub-bitrate``
Bitrate values calculated on the packet level. This works by dividing the
bit size of all packets between two keyframes by their presentation
timestamp distance. (This uses the timestamps are stored in the file, so
@@ -1643,8 +1661,29 @@ Property list
bitrate. To make the property more UI friendly, updates to these properties
are throttled in a certain way.
+ The unit is bits per second. OSD formatting turns these values in kilobits
+ (or megabits, if appropriate), which can be prevented by using the
+ raw property value, e.g. with ``${=video-bitrate}``.
+
+ Note that the accuracy of these properties is influenced by a few factors.
+ If the underlying demuxer rewrites the packets on demuxing (done for some
+ file formats), the bitrate might be slightly off. If timestamps are bad
+ or jittery (like in Matroska), even constant bitrate streams might show
+ fluctuating bitrate.
+
How exactly these values are calculated might change in the future.
+ In earlier versions of mpv, these properties returned a static (but bad)
+ guess using a completely different method.
+
+``packet-video-bitrate``, ``packet-audio-bitrate``, ``packet-sub-bitrate``
+ Old and deprecated properties for ``video-bitrate``, ``audio-bitrate``,
+ ``sub-bitrate``. They behave exactly the same, but return a value in
+ kilobits. Also, they don't have any OSD formatting, though the same can be
+ achieved with e.g. ``${=video-bitrate}``.
+
+ These properties shouldn't be used anymore.
+
``audio-device-list``
Return the list of discovered audio devices. This is mostly for use with
the client API, and reflects what ``--audio-device=help`` with the command
diff --git a/DOCS/man/lua.rst b/DOCS/man/lua.rst
index 4700b90cb2..ff70600a1d 100644
--- a/DOCS/man/lua.rst
+++ b/DOCS/man/lua.rst
@@ -639,6 +639,14 @@ strictly part of the guaranteed API.
trailing text is returned as 3rd return value. (The 3rd return value is
always there, but with ``trail`` set, no error is raised.)
+``utils.format_json(v)``
+ Format the given Lua table (or value) as a JSON string and return it. On
+ error, returns ``nil, error``. (Errors usually only happen on value types
+ incompatible with JSON.)
+
+ The argument value uses similar conventions as ``mp.set_property_native()``
+ to distinguish empty objects and arrays.
+
``utils.to_string(v)``
Turn the given value into a string. Formats tables and their contents. This
doesn't do anything special; it is only needed because Lua is terrible.
diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst
index ca1e87105e..751b2b2b59 100644
--- a/DOCS/man/mpv.rst
+++ b/DOCS/man/mpv.rst
@@ -276,14 +276,16 @@ Shells may actually strip some quotes from the string passed to the commandline,
so the example quotes the string twice, ensuring that mpv recieves the ``"``
quotes.
-The ``[...]`` from of quotes wraps everything between ``[`` and ``]``. It's
+The ``[...]`` form of quotes wraps everything between ``[`` and ``]``. It's
useful with shells that don't interpret these characters in the middle of
-an argument (like bash).
+an argument (like bash). These quotes are balanced (since mpv 0.9.0): the ``[``
+and ``]`` nest, and the quote terminates on the last ``]`` that has no matching
+``[`` within the string. (For example, ``[a[b]c]`` results in ``a[b]c``.)
-A special kind of string-escaping intended for use with external scripts and
-programs is started with ``%``.
+The fixed-length quoting syntax is intended for use with external
+scripts and programs.
-It has the following format::
+It is started with ``%`` and has the following format::
%n%string_of_length_n
@@ -319,7 +321,8 @@ console controls. (Which makes it suitable for playing data piped to stdin.)
For paths passed to suboptions, the situation is further complicated by the
need to escape special characters. To work this around, the path can be
-additionally wrapped in the ``%n%string_of_length_n`` syntax (see above).
+additionally wrapped in the fixed-length syntax, e.g. ``%n%string_of_length_n``
+(see above).
Some mpv options interpret paths starting with ``~``. Currently, the prefix
``~~/`` expands to the mpv configuration directory (usually ``~/.config/mpv/``).
@@ -397,10 +400,10 @@ Escaping spaces and special characters
This is done like with command line options. The shell is not involved here,
but option values still need to be quoted as a whole if it contains certain
characters like spaces. A config entry can be quoted with ``"`` and ``'``,
-as well as with the ``%n%`` syntax mentioned before. This is like passing
-the exact contents of the quoted string as command line option. C-style escapes
-are currently _not_ interpreted on this level, although some options to this
-manually. (This is a mess and should probably be changed at some point.)
+as well as with the fixed-length syntax (``%n%``) mentioned before. This is like
+passing the exact contents of the quoted string as command line option. C-style
+escapes are currently _not_ interpreted on this level, although some options to
+this manually. (This is a mess and should probably be changed at some point.)
Putting Command Line Options into the Configuration File
--------------------------------------------------------
@@ -475,11 +478,8 @@ A screenshot will usually contain the unscaled video contents at the end of the
video filter chain and subtitles. By default, ``S`` takes screenshots without
subtitles, while ``s`` includes subtitles.
-The ``screenshot`` video filter is not required when using a recommended GUI
-video output driver. It should normally not be added to the config file, as
-taking screenshots is handled by the VOs, and adding the screenshot filter will
-break hardware decoding. (The filter may still be useful for taking screenshots
-at a certain point within the video chain when using multiple video filters.)
+Unlike with MPlayer, the ``screenshot`` video filter is not required. This
+filter was never required in mpv, and has been removed.
TERMINAL STATUS LINE
====================
@@ -517,10 +517,9 @@ listed.
rendering is too slow. Also can be incremented on "hiccups" and when the video
frame couldn't be displayed on time. (``vo-drop-frame-count`` property.)
If the decoder drops frames, the number of decoder-dropped frames is appended
- to the display as well, e.g.: ``Dropped: 4/34``. This should almost never
- happen, unless decoder-framedropping is enabled with one of the
- ``--framedrop`` options, the stream contains errors, or a weird codec is in
- use. (``drop-frame-count`` property.)
+ to the display as well, e.g.: ``Dropped: 4/34``. This happens only if
+ decoder-framedropping is enabled with the ``--framedrop`` options.
+ (``drop-frame-count`` property.)
- Cache state, e.g. ``Cache: 2s+134KB``. Visible if the stream cache is enabled.
The first value shows the amount of video buffered in the demuxer in seconds,
the second value shows *additional* data buffered in the stream cache in
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index b380db9ffb..dd85fbfd63 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -181,6 +181,7 @@ Playback Control
file, such as a chapter seek, but not for relative seeks like
the default behavior of arrow keys (default).
:yes: Use precise seeks whenever possible.
+ :always: Same as ``yes`` (for compatibility).
``--hr-seek-demuxer-offset=<seconds>``
This option exists to work around failures to do precise seeks (as in
@@ -566,8 +567,7 @@ Video
The ``vaapi-copy`` mode allows you to use vaapi with any VO. Because
this copies the decoded video back to system RAM, it's likely less efficient
- than the ``vaapi`` mode. But there are reports that this is actually faster
- as well, and avoids many issues with ``vaapi``.
+ than the ``vaapi`` mode.
.. note::
@@ -983,8 +983,9 @@ Audio
``--audio-display=<no|attachment>``
Setting this option to ``attachment`` (default) will display image
- attachments when playing audio files. It will display the first image
- found, and additional images are available as video tracks.
+ attachments (e.g. album cover art) when playing audio files. It will
+ display the first image found, and additional images are available as
+ video tracks.
Setting this option to ``no`` disables display of video entirely when
playing audio files.
@@ -1723,19 +1724,13 @@ Window
(depending on the video aspect ratio, the width or height will be
larger than 500 in order to keep the aspect ratio the same).
-``--autosync=<factor>``
- Gradually adjusts the A/V sync based on audio delay measurements.
- Specifying ``--autosync=0``, the default, will cause frame timing to be
- based entirely on audio delay measurements. Specifying ``--autosync=1``
- will do the same, but will subtly change the A/V correction algorithm. An
- uneven video framerate in a video which plays fine with ``--no-audio`` can
- often be helped by setting this to an integer value greater than 1. The
- higher the value, the closer the timing will be to ``--no-audio``. Try
- ``--autosync=30`` to smooth out problems with sound drivers which do not
- implement a perfect audio delay measurement. With this value, if large A/V
- sync offsets occur, they will only take about 1 or 2 seconds to settle
- out. This delay in reaction time to sudden A/V offsets should be the only
- side-effect of turning this option on, for all sound drivers.
+``--window-scale=<factor>``
+ Resize the video window to a multiple (or fraction) of the video size. This
+ option is applied before ``--autofit`` and other options are applied (so
+ they override this option).
+
+ For example, ``--window-scale=0.5`` would show the window at half the
+ video size.
``--cursor-autohide=<number|no|always>``
Make mouse cursor automatically hide after given number of milliseconds.
@@ -2021,6 +2016,12 @@ Demuxer
``--demuxer-lavf-format=<name>``
Force a specific libavformat demuxer.
+``--demuxer-lavf-hacks=<yes|no>``
+ By default, some formats will be handled differently from other formats
+ by explicitly checking for them. Most of these compensate for weird or
+ imperfect behavior from libavformat demuxers. Passing ``no`` disables
+ these. For debugging and testing only.
+
``--demuxer-lavf-genpts-mode=<no|lavf>``
Mode for deriving missing packet PTS values from packet DTS. ``lavf``
enables libavformat's ``genpts`` option. ``no`` disables it. This used
@@ -2105,6 +2106,18 @@ Demuxer
will be slower (especially when playing over http), or that behavior with
broken files is much worse. So don't use this option.
+``--demuxer-mkv-fix-timestamps=<yes|no>``
+ Fix rounded Matroska timestamps (enabled by default). Matroska usually
+ stores timestamps rounded to milliseconds. This means timestamps jitter
+ by some amount around the intended timestamp. mpv can correct the timestamps
+ based on the framerate value stored in the file: the timestamp is rounded
+ to the next frame (according to the framerate), unless the new timestamp
+ would deviate more than 1ms from the old one. This should undo the rounding
+ done by the muxer.
+
+ (The allowed deviation can be less than 1ms if the file uses a non-standard
+ timecode scale.)
+
``--demuxer-rawaudio-channels=<value>``
Number of channels (or channel layout) if ``--demuxer=rawaudio`` is used
(default: stereo).
@@ -3231,6 +3244,20 @@ Miscellaneous
``--mc=<seconds/frame>``
Maximum A-V sync correction per frame (in seconds)
+``--autosync=<factor>``
+ Gradually adjusts the A/V sync based on audio delay measurements.
+ Specifying ``--autosync=0``, the default, will cause frame timing to be
+ based entirely on audio delay measurements. Specifying ``--autosync=1``
+ will do the same, but will subtly change the A/V correction algorithm. An
+ uneven video framerate in a video which plays fine with ``--no-audio`` can
+ often be helped by setting this to an integer value greater than 1. The
+ higher the value, the closer the timing will be to ``--no-audio``. Try
+ ``--autosync=30`` to smooth out problems with sound drivers which do not
+ implement a perfect audio delay measurement. With this value, if large A/V
+ sync offsets occur, they will only take about 1 or 2 seconds to settle
+ out. This delay in reaction time to sudden A/V offsets should be the only
+ side-effect of turning this option on, for all sound drivers.
+
``--mf-fps=<value>``
Framerate used when decoding from multiple PNG or JPEG files with ``mf://``
(default: 1).
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index be7daee09e..3739716340 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -561,12 +561,6 @@ Available filters are:
``show``
Draw a rectangle showing the area defined by x/y/w/h.
-``screenshot``
- Optional filter for screenshot support. This is only needed if the video
- output does not provide working direct screenshot support. Note that it is
- not always safe to insert this filter by default. See `TAKING SCREENSHOTS`_
- for details.
-
``sub=[=bottom-margin:top-margin]``
Moves subtitle rendering to an arbitrary point in the filter chain, or force
subtitle rendering in the video filter as opposed to using video output OSD
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 2447a8c455..b398d4f57d 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -548,10 +548,11 @@ Available video output drivers are:
X11/GLX only.
- ``dwmflush``
- Calls ``DwmFlush`` after swapping buffers on Windows (default: 0).
+ ``dwmflush=<no|windowed|yes>``
+ Calls ``DwmFlush`` after swapping buffers on Windows (default: no).
It also sets ``SwapInterval(0)`` to ignore the OpenGL timing. Values
- are: 0 (disabled), 1 (only in windowed mode), 2 (also in full screen).
+ are: no (disabled), windowed (only in windowed mode), yes (also in
+ full screen).
This may help getting more consistent frame intervals, especially with
high-fps clips - which might also reduce dropped frames. Typically a
value of 1 should be enough since full screen may bypass the DWM.
@@ -919,3 +920,16 @@ Available video output drivers are:
(default: -10). Note that mpv will also use the 2 layers above the
selected layer, to handle the window background and OSD. Actual video
rendering will happen on the layer above the selected layer.
+
+``drm`` (Direct Rendering Manager)
+ Video output driver using Kernel Mode Setting / Direct Rendering Manager.
+ Does not support hardware acceleration. Should be used when one doesn't
+ want to install full-blown graphical environment (e.g. no X).
+
+ ``connector=<number>``
+ Select the connector to use (usually this is a monitor.) If set to -1,
+ mpv renders the output on the first available connector. (default: -1)
+
+ ``devpath=<filename>``
+ Path to graphic card device.
+ (default: /dev/dri/card0)
diff --git a/DOCS/mplayer-changes.rst b/DOCS/mplayer-changes.rst
index bd803e41f3..e98daf4a36 100644
--- a/DOCS/mplayer-changes.rst
+++ b/DOCS/mplayer-changes.rst
@@ -49,6 +49,8 @@ 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``.
+* 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).
Audio
~~~~~
diff --git a/TOOLS/lua/autoload.lua b/TOOLS/lua/autoload.lua
index 433b2138d7..0730397e75 100644
--- a/TOOLS/lua/autoload.lua
+++ b/TOOLS/lua/autoload.lua
@@ -7,6 +7,17 @@
-- Add at most 5 * 2 files when starting a file (before + after).
MAXENTRIES = 5
+function Set (t)
+ local set = {}
+ for _, v in pairs(t) do set[v] = true end
+ return set
+end
+
+EXTENSIONS = Set {
+ 'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp',
+ 'mp3', 'wav', 'ogv', 'flac', 'm4a', 'wma',
+}
+
mputils = require 'mp.utils'
function add_files_at(index, files)
@@ -18,20 +29,44 @@ function add_files_at(index, files)
end
end
+function get_extension(path)
+ return string.match(path, "%.([^%.]+)$" )
+end
+
+table.filter = function(t, iter)
+ for i = #t, 1, -1 do
+ if not iter(t[i]) then
+ table.remove(t, i)
+ end
+ end
+end
+
function find_and_add_entries()
local path = mp.get_property("path", "")
local dir, filename = mputils.split_path(path)
if #dir == 0 then
return
end
+
local files = mputils.readdir(dir, "files")
if files == nil then
return
end
- table.sort(files)
+ table.filter(files, function (v, k)
+ local ext = get_extension(v)
+ if ext == nil then
+ return false
+ end
+ return EXTENSIONS[string.lower(ext)]
+ end)
+ table.sort(files, function (a, b)
+ return string.lower(a) < string.lower(b)
+ end)
+
if dir == "." then
dir = ""
end
+
local pl = mp.get_property_native("playlist", {})
local pl_current = mp.get_property_number("playlist-pos", 0) + 1
-- Find the current pl entry (dir+"/"+filename) in the sorted dir list
@@ -45,6 +80,7 @@ function find_and_add_entries()
if current == nil then
return
end
+
local append = {[-1] = {}, [1] = {}}
for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1
for i = 1, MAXENTRIES do
@@ -53,6 +89,7 @@ function find_and_add_entries()
if file == nil or file[1] == "." then
break
end
+
local filepath = dir .. file
if pl_e then
-- If there's a playlist entry, and it's the same file, stop.
@@ -60,6 +97,7 @@ function find_and_add_entries()
break
end
end
+
if direction == -1 then
if pl_current == 1 then -- never add additional entries in the middle
mp.msg.info("Prepending " .. file)
@@ -71,6 +109,7 @@ function find_and_add_entries()
end
end
end
+
add_files_at(pl_current + 1, append[1])
add_files_at(pl_current, append[-1])
end
diff --git a/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf b/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf
index ff78dfacf6..7527de0d1b 100644
--- a/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf
+++ b/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf
@@ -1,3 +1,2 @@
screenshot-template=~/Desktop/shot%n
-quiet
-idle=once
+profile=pseudo-gui
diff --git a/TOOLS/umpv b/TOOLS/umpv
index 4757e4b370..0080b44ffb 100755
--- a/TOOLS/umpv
+++ b/TOOLS/umpv
@@ -21,7 +21,7 @@ script after that will start a new mpv instance.
Note that you can control the mpv instance by writing to the command fifo:
- echo "cycle fullscreen" > ~/.umpv-fifo
+ echo "cycle fullscreen" > ~/.umpv_fifo
Note: you can supply custom mpv path and options with the MPV environment
variable. The environment variable will be split on whitespace, and the
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index f02f9213ab..5a2392e05a 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -144,9 +144,6 @@ static int init(struct dec_audio *da, const char *decoder)
return 0;
}
- if (lavc_context->bit_rate != 0)
- da->bitrate = lavc_context->bit_rate;
-
return 1;
}
diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c
index 41d5a8bdf4..412846a78e 100644
--- a/audio/filter/af_lavrresample.c
+++ b/audio/filter/af_lavrresample.c
@@ -318,17 +318,17 @@ static int control(struct af_instance *af, int cmd, void *arg)
if (new_rate != s->ctx.in_rate && s->avrctx_ok && af->fmt_out.format) {
// Before reconfiguring, drain the audio that is still buffered
// in the resampler.
- talloc_free(s->pending);
- s->pending = talloc_zero(NULL, struct mp_audio);
- mp_audio_copy_config(s->pending, &af->fmt_out);
- s->pending->samples = get_drain_samples(s);
- if (s->pending->samples > 0) {
- mp_audio_realloc_min(s->pending, s->pending->samples);
- int r = resample_frame(s->avrctx, s->pending, NULL);
- s->pending->samples = MPMAX(r, 0);
+ struct mp_audio *pending = talloc_zero(NULL, struct mp_audio);
+ mp_audio_copy_config(pending, &af->fmt_out);
+ pending->samples = get_drain_samples(s);
+ if (pending->samples > 0) {
+ mp_audio_realloc_min(pending, pending->samples);
+ int r = resample_frame(s->avrctx, pending, NULL);
+ pending->samples = MPMAX(r, 0);
}
// Reinitialize resampler.
configure_lavrr(af, &af->fmt_in, &af->fmt_out);
+ s->pending = pending;
}
return AF_OK;
}
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index f26c4cedb0..f4565da0dc 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -492,8 +492,11 @@ static int init_device(struct ao *ao)
}
if (num_channels != ao->channels.num) {
- MP_ERR(ao, "Couldn't get requested number of channels.\n");
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));
}
// Some ALSA drivers have broken delay reporting, so disable the ALSA
diff --git a/common/msg.c b/common/msg.c
index 34ed677a22..3a441d0bcf 100644
--- a/common/msg.c
+++ b/common/msg.c
@@ -61,8 +61,6 @@ struct mp_log_root {
int num_buffers;
FILE *log_file;
FILE *stats_file;
- // --- semi-atomic access
- bool mute;
// --- must be accessed atomically
/* This is incremented every time the msglevels must be reloaded.
* (This is perhaps better than maintaining a globally accessible and
@@ -133,7 +131,7 @@ static void update_loglevel(struct mp_log *log)
bool mp_msg_test(struct mp_log *log, int lev)
{
struct mp_log_root *root = log->root;
- if (!root || root->mute)
+ if (!root)
return false;
if (atomic_load_explicit(&log->reload_counter, memory_order_relaxed) !=
atomic_load_explicit(&root->reload_counter, memory_order_relaxed))
@@ -486,13 +484,6 @@ void mp_msg_update_msglevels(struct mpv_global *global)
pthread_mutex_unlock(&mp_msg_lock);
}
-void mp_msg_mute(struct mpv_global *global, bool mute)
-{
- struct mp_log_root *root = global->log->root;
-
- root->mute = mute;
-}
-
void mp_msg_force_stderr(struct mpv_global *global, bool force_stderr)
{
struct mp_log_root *root = global->log->root;
diff --git a/common/msg_control.h b/common/msg_control.h
index 2e1a4ec6bd..c26a557c9e 100644
--- a/common/msg_control.h
+++ b/common/msg_control.h
@@ -7,7 +7,6 @@ struct mpv_global;
void mp_msg_init(struct mpv_global *global);
void mp_msg_uninit(struct mpv_global *global);
void mp_msg_update_msglevels(struct mpv_global *global);
-void mp_msg_mute(struct mpv_global *global, bool mute);
void mp_msg_force_stderr(struct mpv_global *global, bool force_stderr);
bool mp_msg_has_status_line(struct mpv_global *global);
diff --git a/demux/demux.c b/demux/demux.c
index c5fb6dc517..bb367055cb 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -144,6 +144,8 @@ struct demux_stream {
// all fields are protected by in->lock
bool selected; // user wants packets from this stream
bool active; // try to keep at least 1 packet queued
+ // if false, this stream is disabled, or passively
+ // read (like subtitles)
bool eof; // end of demuxed stream? (true if all buffer empty)
bool refreshing;
size_t packs; // number of packets in buffer
@@ -431,7 +433,6 @@ static bool read_packet(struct demux_internal *in)
for (int n = 0; n < in->d_buffer->num_streams; n++) {
struct demux_stream *ds = in->d_buffer->streams[n]->ds;
ds->eof = true;
- ds->active = false;
}
// If we had EOF previously, then don't wakeup (avoids wakeup loop)
if (!in->last_eof) {
@@ -498,7 +499,7 @@ static void start_refreshing(struct demux_internal *in)
// Seek back to player's current position, with a small offset added.
in->d_thread->desc->seek(in->d_thread, start_ts - 1.0,
- SEEK_ABSOLUTE | SEEK_BACKWARD | SEEK_SUBPREROLL);
+ SEEK_ABSOLUTE | SEEK_BACKWARD | SEEK_HR);
pthread_mutex_lock(&in->lock);
}
@@ -1382,10 +1383,8 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
struct demux_stream *ds = in->d_user->streams[n]->ds;
if (ds->active) {
r->underrun |= !ds->head && !ds->eof;
- if (!ds->eof) {
- r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
- r->ts_range[1] = MP_PTS_MIN(r->ts_range[1], ds->last_ts);
- }
+ r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
+ r->ts_range[1] = MP_PTS_MIN(r->ts_range[1], ds->last_ts);
num_packets += ds->packs;
}
}
diff --git a/demux/demux.h b/demux/demux.h
index b730724ed3..f3ed97c386 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -67,7 +67,6 @@ struct demux_ctrl_stream_ctrl {
#define SEEK_FACTOR (1 << 1) // argument is in range [0,1]
#define SEEK_FORWARD (1 << 2) // prefer later time if not exact
#define SEEK_BACKWARD (1 << 3) // prefer earlier time if not exact
-#define SEEK_SUBPREROLL (1 << 4) // try to get more subtitle packets
#define SEEK_HR (1 << 5) // hr-seek (this is a weak hint only)
// Strictness of the demuxer open format check.
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index dd122f7325..c14869555c 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -65,6 +65,7 @@ struct demux_lavf_opts {
char *format;
char *cryptokey;
char **avopts;
+ int hacks;
int genptsmode;
};
@@ -78,6 +79,7 @@ const struct m_sub_options demux_lavf_conf = {
OPT_FLAG("allow-mimetype", allow_mimetype, 0),
OPT_INTRANGE("probescore", probescore, 0, 1, AVPROBE_SCORE_MAX),
OPT_STRING("cryptokey", cryptokey, 0),
+ OPT_FLAG("hacks", hacks, 0),
OPT_CHOICE("genpts-mode", genptsmode, 0,
({"lavf", 1}, {"no", 0})),
OPT_KEYVALUELIST("o", avopts, 0),
@@ -86,6 +88,7 @@ const struct m_sub_options demux_lavf_conf = {
.size = sizeof(struct demux_lavf_opts),
.defaults = &(const struct demux_lavf_opts){
.allow_mimetype = 1,
+ .hacks = 1,
// AVPROBE_SCORE_MAX/4 + 1 is the "recommended" limit. Below that, the
// user is supposed to retry with larger probe sizes until a higher
// value is reached.
@@ -351,6 +354,8 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
for (int n = 0; format_hacks[n].ff_name; n++) {
const struct format_hack *entry = &format_hacks[n];
+ if (!lavfdopts->hacks)
+ continue;
if (!matches_avinputformat_name(priv, entry->ff_name))
continue;
if (entry->mime_type && strcasecmp(entry->mime_type, mime_type) != 0)
@@ -540,10 +545,6 @@ static void handle_stream(demuxer_t *demuxer, int i)
sh_video->aspect = codec->width * codec->sample_aspect_ratio.num
/ (float)(codec->height * codec->sample_aspect_ratio.den);
- sh_video->bitrate = codec->bit_rate;
- if (sh_video->bitrate == 0)
- sh_video->bitrate = avfc->bit_rate;
-
uint8_t *sd = av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if (sd)
sh_video->rotate = -av_display_rotation_get((uint32_t *)sd);
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index 4216a6ad71..b4ae042feb 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -24,6 +24,7 @@
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <math.h>
#include <assert.h>
#include <libavutil/common.h>
@@ -43,6 +44,7 @@
#include "talloc.h"
#include "common/av_common.h"
#include "options/options.h"
+#include "options/m_option.h"
#include "misc/bstr.h"
#include "stream/stream.h"
#include "video/csputils.h"
@@ -184,6 +186,30 @@ typedef struct mkv_demuxer {
bool eof_warning;
} mkv_demuxer_t;
+#define OPT_BASE_STRUCT struct demux_mkv_opts
+struct demux_mkv_opts {
+ int subtitle_preroll;
+ double subtitle_preroll_secs;
+ int probe_duration;
+ int fix_timestamps;
+};
+
+const struct m_sub_options demux_mkv_conf = {
+ .opts = (const m_option_t[]) {
+ OPT_FLAG("subtitle-preroll", subtitle_preroll, 0),
+ OPT_DOUBLE("subtitle-preroll-secs", subtitle_preroll_secs,
+ M_OPT_MIN, .min = 0),
+ OPT_FLAG("probe-video-duration", probe_duration, 0),
+ OPT_FLAG("fix-timestamps", fix_timestamps, 0),
+ {0}
+ },
+ .size = sizeof(struct demux_mkv_opts),
+ .defaults = &(const struct demux_mkv_opts){
+ .subtitle_preroll_secs = 1.0,
+ .fix_timestamps = 1,
+ },
+};
+
#define REALHEADER_SIZE 16
#define RVPROPERTIES_SIZE 34
#define RAPROPERTIES4_SIZE 56
@@ -1751,6 +1777,7 @@ static int read_mkv_segment_header(demuxer_t *demuxer, int64_t *segment_end)
static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
{
stream_t *s = demuxer->stream;
+ struct MPOpts *opts = demuxer->opts;
mkv_demuxer_t *mkv_d;
int64_t start_pos;
int64_t end_pos;
@@ -1836,7 +1863,7 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
add_coverart(demuxer);
demuxer->allow_refresh_seeks = true;
- if (demuxer->opts->mkv_probe_duration)
+ if (opts->demux_mkv->probe_duration)
probe_last_timestamp(demuxer);
return 0;
@@ -2304,6 +2331,19 @@ exit:
return res;
}
+static double fix_timestamp(demuxer_t *demuxer, mkv_track_t *track, double ts)
+{
+ mkv_demuxer_t *mkv_d = demuxer->priv;
+ if (demuxer->opts->demux_mkv->fix_timestamps && track->default_duration > 0) {
+ // Assume that timestamps have been rounded to the timecode scale.
+ double quant = MPMIN(mkv_d->tc_scale / 1e9, 0.001);
+ double rts = rint(ts / track->default_duration) * track->default_duration;
+ if (fabs(rts - ts) < quant)
+ ts = rts;
+ }
+ return ts;
+}
+
static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
@@ -2326,7 +2366,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
return 0;
}
- current_pts = tc / 1e9 - track->codec_delay;
+ current_pts = fix_timestamp(demuxer, track, tc / 1e9) - track->codec_delay;
if (track->type == MATROSKA_TRACK_AUDIO) {
if (mkv_d->a_skip_to_keyframe)
@@ -2619,35 +2659,30 @@ static int create_index_until(struct demuxer *demuxer, uint64_t timecode)
return 0;
}
+#define FLAG_BACKWARD 1
+#define FLAG_SUBPREROLL 2
static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
int64_t target_timecode, int flags)
{
+ struct MPOpts *opts = demuxer->opts;
struct mkv_demuxer *mkv_d = demuxer->priv;
struct mkv_index *index = NULL;
- /* Find the entry in the index closest to the target timecode in the
- * give direction. If there are no such entries - we're trying to seek
- * backward from a target time before the first entry or forward from a
- * target time after the last entry - then still seek to the first/last
- * entry if that's further in the direction wanted than mkv_d->last_pts.
- */
- int64_t min_diff = target_timecode - (int64_t)(mkv_d->last_pts * 1e9 + 0.5);
- if (flags & SEEK_BACKWARD)
- min_diff = -min_diff;
- min_diff = FFMAX(min_diff, 1);
-
+ int64_t min_diff = INT64_MIN;
for (size_t i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
int64_t diff =
target_timecode -
(int64_t) (mkv_d->indexes[i].timecode * mkv_d->tc_scale);
- if (flags & SEEK_BACKWARD)
+ if (flags & FLAG_BACKWARD)
diff = -diff;
- if (diff <= 0) {
- if (min_diff <= 0 && diff <= min_diff)
+ if (min_diff != INT64_MIN) {
+ if (diff <= 0) {
+ if (min_diff <= 0 && diff <= min_diff)
+ continue;
+ } else if (diff >= min_diff)
continue;
- } else if (diff >= min_diff)
- continue;
+ }
min_diff = diff;
index = mkv_d->indexes + i;
}
@@ -2655,10 +2690,10 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
if (index) { /* We've found an entry. */
uint64_t seek_pos = index->filepos;
- if (flags & SEEK_SUBPREROLL) {
+ if (flags & FLAG_SUBPREROLL) {
// Find the cluster with the highest filepos, that has a timestamp
// still lower than min_tc.
- double secs = demuxer->opts->mkv_subtitle_preroll_secs;
+ double secs = opts->demux_mkv->subtitle_preroll_secs;
uint64_t pre = MPMIN(INT64_MAX, secs * 1e9 / mkv_d->tc_scale);
uint64_t min_tc = pre < index->timecode ? index->timecode - pre : 0;
uint64_t prev_target = 0;
@@ -2717,9 +2752,13 @@ static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
a_tnum = track->tnum;
}
}
+
+ int cueflags = (flags & SEEK_BACKWARD) ? FLAG_BACKWARD : 0;
+
mkv_d->subtitle_preroll = NUM_SUB_PREROLL_PACKETS;
- if (!st_active[STREAM_SUB] || !st_active[STREAM_VIDEO])
- flags &= ~SEEK_SUBPREROLL;
+ if (((flags & SEEK_HR) || demuxer->opts->demux_mkv->subtitle_preroll) &&
+ st_active[STREAM_SUB] && st_active[STREAM_VIDEO])
+ cueflags |= FLAG_SUBPREROLL;
// Adjust the target a little bit to catch cases where the target position
// specifies a keyframe with high, but not perfect, precision.
@@ -2735,9 +2774,9 @@ static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
if (create_index_until(demuxer, target_timecode) >= 0) {
int seek_id = st_active[STREAM_VIDEO] ? v_tnum : a_tnum;
- index = seek_with_cues(demuxer, seek_id, target_timecode, flags);
+ index = seek_with_cues(demuxer, seek_id, target_timecode, cueflags);
if (!index)
- index = seek_with_cues(demuxer, -1, target_timecode, flags);
+ index = seek_with_cues(demuxer, -1, target_timecode, cueflags);
}
if (!index)
diff --git a/demux/demux_playlist.c b/demux/demux_playlist.c
index 381b50f269..6f73bea5ba 100644
--- a/demux/demux_playlist.c
+++ b/demux/demux_playlist.c
@@ -15,7 +15,10 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <stdlib.h>
+#include <string.h>
#include <strings.h>
+#include <dirent.h>
#include "common/common.h"
#include "options/options.h"
@@ -23,6 +26,7 @@
#include "common/playlist.h"
#include "options/path.h"
#include "stream/stream.h"
+#include "osdep/io.h"
#include "demux.h"
#define PROBE_SIZE (8 * 1024)
@@ -47,6 +51,7 @@ struct pl_parser {
bool error;
bool probing;
bool force;
+ bool add_base;
enum demux_check check_level;
struct stream *real_stream;
};
@@ -204,6 +209,51 @@ static int parse_txt(struct pl_parser *p)
return 0;
}
+static int cmp_filename(const void *a, const void *b)
+{
+ return strcmp(*(char **)a, *(char **)b);
+}
+
+static int parse_dir(struct pl_parser *p)
+{
+ if (p->real_stream->type != STREAMTYPE_DIR)
+ return -1;
+ if (p->probing)
+ return 0;
+
+ char *path = mp_file_get_path(p, bstr0(p->real_stream->url));
+ if (strlen(path) >= 8192)
+ return -1; // things like mount bind loops
+
+ DIR *dp = opendir(path);
+ if (!dp) {
+ MP_ERR(p, "Could not read directory.\n");
+ return -1;
+ }
+
+ char **files = NULL;
+ int num_files = 0;
+
+ struct dirent *ep;
+ while ((ep = readdir(dp))) {
+ if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
+ continue;
+ MP_TARRAY_APPEND(p, files, num_files, talloc_strdup(p, ep->d_name));
+ }
+
+ if (files)
+ qsort(files, num_files, sizeof(files[0]), cmp_filename);
+
+ for (int n = 0; n < num_files; n++)
+ playlist_add_file(p->pl, mp_path_join(p, bstr0(path), bstr0(files[n])));
+
+ closedir(dp);
+
+ p->add_base = false;
+
+ return num_files > 0 ? 0 : -1;
+}
+
#define MIME_TYPES(...) \
.mime_types = (const char*const[]){__VA_ARGS__, NULL}
@@ -214,6 +264,7 @@ struct pl_format {
};
static const struct pl_format formats[] = {
+ {"directory", parse_dir},
{"m3u", parse_m3u,
MIME_TYPES("audio/mpegurl", "audio/x-mpegurl", "application/x-mpegurl")},
{"ini", parse_ref_init},
@@ -248,6 +299,7 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
p->log = demuxer->log;
p->pl = talloc_zero(p, struct playlist);
p->real_stream = demuxer->stream;
+ p->add_base = true;
bstr probe_buf = stream_peek(demuxer->stream, PROBE_SIZE);
p->s = open_memory_stream(probe_buf.start, probe_buf.len);
@@ -269,7 +321,7 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
p->s = demuxer->stream;
p->utf16 = stream_skip_bom(p->s);
bool ok = fmt->parse(p) >= 0 && !p->error;
- if (ok)
+ if (p->add_base)
playlist_add_base_path(p->pl, mp_dirname(demuxer->filename));
demuxer->playlist = talloc_steal(demuxer, p->pl);
demuxer->filetype = fmt->name;
diff --git a/demux/demux_raw.c b/demux/demux_raw.c
index f12b774984..65d6b754be 100644
--- a/demux/demux_raw.c
+++ b/demux/demux_raw.c
@@ -226,7 +226,6 @@ static int demux_rawvideo_open(demuxer_t *demuxer, enum demux_check check)
sh_video->fps = opts->fps;
sh_video->disp_w = width;
sh_video->disp_h = height;
- sh_video->bitrate = sh_video->fps * imgsize * 8;
struct priv *p = talloc_ptrtype(demuxer, p);
demuxer->priv = p;
diff --git a/demux/stheader.h b/demux/stheader.h
index 3f44a5c57c..444f2f3e34 100644
--- a/demux/stheader.h
+++ b/demux/stheader.h
@@ -82,7 +82,6 @@ typedef struct sh_video {
bool avi_dts; // use DTS timing; first frame and DTS is 0
float fps; // frames per second (set only if constant fps)
float aspect; // aspect ratio stored in the file (for prescaling)
- int bitrate; // compressed bits/sec
int bits_per_coded_sample;
unsigned char *extradata;
int extradata_len;
diff --git a/etc/input.conf b/etc/input.conf
index 02b396956f..73be521c74 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -125,7 +125,6 @@
# cycle video aspect ratios; "-1" is the container aspect
#A cycle_values video-aspect "16:9" "4:3" "2.35:1" "-1"
#POWER quit
-#MENU cycle osd
#PLAY cycle pause
#PAUSE cycle pause
#PLAYPAUSE cycle pause
diff --git a/input/cmd_list.c b/input/cmd_list.c
index 9005121985..acd8ae72a1 100644
--- a/input/cmd_list.c
+++ b/input/cmd_list.c
@@ -119,6 +119,11 @@ const struct mp_cmd_def mp_cmds[] = {
{"window", 1},
{"subtitles", 2})),
}},
+ { MP_CMD_SCREENSHOT_RAW, "screenshot_raw", {
+ OARG_CHOICE(2, ({"video", 0},
+ {"window", 1},
+ {"subtitles", 2})),
+ }},
{ MP_CMD_LOADFILE, "loadfile", {
ARG_STRING,
OARG_CHOICE(0, ({"replace", 0},
diff --git a/input/cmd_list.h b/input/cmd_list.h
index 52dc6426fa..e9418d4429 100644
--- a/input/cmd_list.h
+++ b/input/cmd_list.h
@@ -48,6 +48,7 @@ enum mp_command_type {
MP_CMD_OSD,
MP_CMD_SCREENSHOT,
MP_CMD_SCREENSHOT_TO_FILE,
+ MP_CMD_SCREENSHOT_RAW,
MP_CMD_LOADFILE,
MP_CMD_LOADLIST,
MP_CMD_PLAYLIST_CLEAR,
diff --git a/libmpv/client.h b/libmpv/client.h
index 511c4794b4..a800043359 100644
--- a/libmpv/client.h
+++ b/libmpv/client.h
@@ -24,6 +24,7 @@
#ifndef MPV_CLIENT_API_H_
#define MPV_CLIENT_API_H_
+#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
@@ -632,7 +633,13 @@ typedef enum mpv_format {
/**
* See MPV_FORMAT_NODE_ARRAY.
*/
- MPV_FORMAT_NODE_MAP = 8
+ MPV_FORMAT_NODE_MAP = 8,
+ /**
+ * A raw, untyped byte array. Only used only with mpv_node, and only in
+ * some very special situations. (Currently, only for the screenshot_raw
+ * command.)
+ */
+ MPV_FORMAT_BYTE_ARRAY = 9
} mpv_format;
/**
@@ -654,6 +661,10 @@ typedef struct mpv_node {
* or if format==MPV_FORMAT_NODE_MAP
*/
struct mpv_node_list *list;
+ /**
+ * valid if format==MPV_FORMAT_BYTE_ARRAY
+ */
+ struct mpv_byte_array *ba;
} u;
/**
* Type of the data stored in this struct. This value rules what members in
@@ -666,6 +677,7 @@ typedef struct mpv_node {
* MPV_FORMAT_DOUBLE (u.double_)
* MPV_FORMAT_NODE_ARRAY (u.list)
* MPV_FORMAT_NODE_MAP (u.list)
+ * MPV_FORMAT_BYTE_ARRAY (u.ba)
* MPV_FORMAT_NONE (no member)
*
* If you encounter a value you don't know, you must not make any
@@ -707,6 +719,21 @@ typedef struct mpv_node_list {
} mpv_node_list;
/**
+ * (see mpv_node)
+ */
+typedef struct mpv_byte_array {
+ /**
+ * Pointer to the data. In what format the data is stored is up to whatever
+ * uses MPV_FORMAT_BYTE_ARRAY.
+ */
+ void *data;
+ /**
+ * Size of the data pointed to by ptr.
+ */
+ size_t size;
+} mpv_byte_array;
+
+/**
* Frees any data referenced by the node. It doesn't free the node itself.
* Call this only if the mpv client API set the node. If you constructed the
* node yourself (manually), you have to free it yourself.
diff --git a/old-configure b/old-configure
index d4b4f31436..b41e7b5096 100755
--- a/old-configure
+++ b/old-configure
@@ -935,6 +935,7 @@ cat > $TMPC << EOF
#define HAVE_RPI 0
#define HAVE_RPI_GLES 0
#define HAVE_AV_PIX_FMT_MMAL 0
+#define HAVE_DRM 0
#ifdef __OpenBSD__
#define DEFAULT_CDROM_DEVICE "/dev/rcd0c"
diff --git a/old-makefile b/old-makefile
index 2ec33455d3..7adc45d712 100644
--- a/old-makefile
+++ b/old-makefile
@@ -196,6 +196,7 @@ SOURCES = audio/audio.c \
options/path.c \
osdep/io.c \
osdep/semaphore_osx.c \
+ osdep/subprocess.c \
osdep/subprocess-posix.c \
osdep/terminal-unix.c \
osdep/timer.c \
@@ -262,7 +263,6 @@ SOURCES = audio/audio.c \
video/filter/vf_mirror.c \
video/filter/vf_noformat.c \
video/filter/vf_scale.c \
- video/filter/vf_screenshot.c \
video/filter/vf_stereo3d.c \
video/filter/vf_sub.c \
video/out/bitmap_packer.c \
diff --git a/options/m_option.c b/options/m_option.c
index 165656500d..bd8e3357b9 100644
--- a/options/m_option.c
+++ b/options/m_option.c
@@ -1736,11 +1736,22 @@ static int read_subparam(struct mp_log *log, bstr optname,
}
p = bstr_cut(p, 1);
} else if (bstr_eatstart0(&p, "[")) {
- if (!bstr_split_tok(p, "]", &subparam, &p)) {
+ bstr s = p;
+ int balance = 1;
+ while (p.len && balance > 0) {
+ if (p.start[0] == '[') {
+ balance++;
+ } else if (p.start[0] == ']') {
+ balance--;
+ }
+ p = bstr_cut(p, 1);
+ }
+ if (balance != 0) {
mp_err(log, "Terminating ']' missing for '%.*s'\n",
BSTR_P(optname));
return M_OPT_INVALID;
}
+ subparam = bstr_splice(s, 0, s.len - p.len - 1);
} else if (bstr_eatstart0(&p, "%")) {
int optlen = bstrtoll(p, &p, 0);
if (!bstr_startswith0(p, "%") || (optlen > p.len - 1)) {
@@ -2934,6 +2945,11 @@ static int parse_obj_settings_list(struct mp_log *log, const m_option_t *opt,
}
if (op == OP_CLR) {
+ if (param.len) {
+ mp_err(log, "Option %.*s: -clr does not take an argument.\n",
+ BSTR_P(name));
+ return M_OPT_INVALID;
+ }
if (dst)
free_obj_settings_list(dst);
return 0;
diff --git a/options/options.c b/options/options.c
index 8dc5cab255..08ee420bd1 100644
--- a/options/options.c
+++ b/options/options.c
@@ -67,6 +67,7 @@ extern const struct m_sub_options sws_conf;
extern const struct m_sub_options demux_rawaudio_conf;
extern const struct m_sub_options demux_rawvideo_conf;
extern const struct m_sub_options demux_lavf_conf;
+extern const struct m_sub_options demux_mkv_conf;
extern const struct m_sub_options vd_lavc_conf;
extern const struct m_sub_options ad_lavc_conf;
extern const struct m_sub_options input_config;
@@ -311,11 +312,7 @@ const m_option_t mp_opts[] = {
OPT_SUBSTRUCT("demuxer-lavf", demux_lavf, demux_lavf_conf, 0),
OPT_SUBSTRUCT("demuxer-rawaudio", demux_rawaudio, demux_rawaudio_conf, 0),
OPT_SUBSTRUCT("demuxer-rawvideo", demux_rawvideo, demux_rawvideo_conf, 0),
-
- OPT_FLAG("demuxer-mkv-subtitle-preroll", mkv_subtitle_preroll, 0),
- OPT_DOUBLE("demuxer-mkv-subtitle-preroll-secs", mkv_subtitle_preroll_secs,
- M_OPT_MIN, .min = 0),
- OPT_FLAG("demuxer-mkv-probe-video-duration", mkv_probe_duration, 0),
+ OPT_SUBSTRUCT("demuxer-mkv", demux_mkv, demux_mkv_conf, 0),
// ------------------------- subtitles options --------------------
@@ -403,6 +400,7 @@ const m_option_t mp_opts[] = {
OPT_SIZE_BOX("autofit", vo.autofit, 0),
OPT_SIZE_BOX("autofit-larger", vo.autofit_larger, 0),
OPT_SIZE_BOX("autofit-smaller", vo.autofit_smaller, 0),
+ OPT_FLOATRANGE("window-scale", vo.window_scale, 0, 0.001, 100),
OPT_FLAG("force-window-position", vo.force_window_position, 0),
// vo name (X classname) and window title strings
OPT_STRING("x11-name", vo.winname, 0),
@@ -511,8 +509,8 @@ const m_option_t mp_opts[] = {
({"auto", 0}, {"decoder", 1}, {"sort", 2})),
OPT_FLAG("initial-audio-sync", initial_audio_sync, 0),
OPT_CHOICE("hr-seek", hr_seek, 0,
- ({"no", -1}, {"absolute", 0}, {"always", 1}, {"yes", 1})),
- OPT_FLOATRANGE("hr-seek-demuxer-offset", hr_seek_demuxer_offset, 0, -9, 99),
+ ({"no", -1}, {"absolute", 0}, {"yes", 1}, {"always", 1})),
+ OPT_FLOAT("hr-seek-demuxer-offset", hr_seek_demuxer_offset, 0),
OPT_FLAG("hr-seek-framedrop", hr_seek_framedrop, 0),
OPT_CHOICE_OR_INT("autosync", autosync, 0, 0, 10000,
({"no", -1})),
@@ -665,6 +663,7 @@ const struct MPOpts mp_default_opts = {
.keepaspect_window = 1,
.border = 1,
.WinID = -1,
+ .window_scale = 1.0,
},
.allow_win_drag = 1,
.wintitle = "mpv - ${?media-title:${media-title}}${!media-title:No file.}",
@@ -764,7 +763,6 @@ const struct MPOpts mp_default_opts = {
.use_embedded_fonts = 1,
.sub_fix_timing = 1,
.sub_cp = "auto",
- .mkv_subtitle_preroll_secs = 1.0,
.screenshot_template = "shot%n",
.hwdec_codecs = "h264,vc1,wmv3",
diff --git a/options/options.h b/options/options.h
index 24173168e6..ced518453f 100644
--- a/options/options.h
+++ b/options/options.h
@@ -30,6 +30,7 @@ typedef struct mp_vo_opts {
struct m_geometry autofit;
struct m_geometry autofit_larger;
struct m_geometry autofit_smaller;
+ float window_scale;
int keepaspect;
int keepaspect_window;
@@ -199,9 +200,6 @@ typedef struct MPOpts {
double demuxer_min_secs;
char *audio_demuxer_name;
char *sub_demuxer_name;
- int mkv_subtitle_preroll;
- double mkv_subtitle_preroll_secs;
- int mkv_probe_duration;
double demuxer_min_secs_cache;
int cache_pausing;
@@ -294,6 +292,7 @@ typedef struct MPOpts {
struct demux_rawaudio_opts *demux_rawaudio;
struct demux_rawvideo_opts *demux_rawvideo;
struct demux_lavf_opts *demux_lavf;
+ struct demux_mkv_opts *demux_mkv;
struct vd_lavc_params *vd_lavc_params;
struct ad_lavc_params *ad_lavc_params;
diff --git a/options/parse_commandline.c b/options/parse_commandline.c
index 5145a8c03b..ab1f28f72b 100644
--- a/options/parse_commandline.c
+++ b/options/parse_commandline.c
@@ -285,9 +285,6 @@ void m_config_preparse_command_line(m_config_t *config, struct mpv_global *globa
{
struct MPOpts *opts = global->opts;
- // Hack to shut up parser error messages
- mp_msg_mute(global, true);
-
struct parse_state p = {config, argv};
while (split_opt_silent(&p) == 0) {
if (p.is_opt) {
@@ -300,8 +297,6 @@ void m_config_preparse_command_line(m_config_t *config, struct mpv_global *globa
}
}
- mp_msg_mute(global, false);
-
for (int n = 0; n < config->num_opts; n++)
config->opts[n].warning_was_printed = false;
}
diff --git a/options/parse_configfile.c b/options/parse_configfile.c
index 2b2e8a864f..8ccf6579ba 100644
--- a/options/parse_configfile.c
+++ b/options/parse_configfile.c
@@ -106,7 +106,10 @@ int m_config_parse(m_config_t *config, const char *location, bstr data,
if (rest.len == line.len || !bstr_eatstart0(&rest, "%") ||
len > rest.len)
{
- MP_ERR(config, "%s broken escaping with '%%'\n", loc);
+ MP_ERR(config, "%s fixed-length quoting expected - put "
+ "\"quotes\" around the option value if you did not "
+ "intend to use this, but your option value starts "
+ "with '%%'\n", loc);
goto error;
}
value = bstr_splice(rest, 0, len);
diff --git a/osdep/subprocess-posix.c b/osdep/subprocess-posix.c
index c2120b2aa8..9d565f411e 100644
--- a/osdep/subprocess-posix.c
+++ b/osdep/subprocess-posix.c
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <fcntl.h>
#include <errno.h>
#include <signal.h>
@@ -63,6 +64,7 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
int status = -1;
int p_stdout[2] = {-1, -1};
int p_stderr[2] = {-1, -1};
+ int devnull = -1;
pid_t pid = -1;
if (on_stdout && mp_make_cloexec_pipe(p_stdout) < 0)
@@ -70,10 +72,16 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
if (on_stderr && mp_make_cloexec_pipe(p_stderr) < 0)
goto done;
+ devnull = open("/dev/null", O_RDONLY | O_CLOEXEC);
+ if (devnull < 0)
+ goto done;
+
if (posix_spawn_file_actions_init(&fa))
goto done;
fa_destroy = true;
- // redirect stdout and stderr
+ // redirect stdin/stdout/stderr
+ if (posix_spawn_file_actions_adddup2(&fa, devnull, 0))
+ goto done;
if (p_stdout[1] >= 0 && posix_spawn_file_actions_adddup2(&fa, p_stdout[1], 1))
goto done;
if (p_stderr[1] >= 0 && posix_spawn_file_actions_adddup2(&fa, p_stderr[1], 2))
@@ -88,6 +96,8 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
p_stdout[1] = -1;
close(p_stderr[1]);
p_stderr[1] = -1;
+ close(devnull);
+ devnull = -1;
int *read_fds[2] = {&p_stdout[0], &p_stderr[0]};
subprocess_read_cb read_cbs[2] = {on_stdout, on_stderr};
@@ -133,6 +143,7 @@ done:
close(p_stdout[1]);
close(p_stderr[0]);
close(p_stderr[1]);
+ close(devnull);
if (WIFEXITED(status) && WEXITSTATUS(status) != 127) {
*error = NULL;
diff --git a/osdep/subprocess.c b/osdep/subprocess.c
new file mode 100644
index 0000000000..84a1b52fe6
--- /dev/null
+++ b/osdep/subprocess.c
@@ -0,0 +1,57 @@
+/*
+ * 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 <pthread.h>
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "common/msg_control.h"
+
+#include "subprocess.h"
+
+struct subprocess_args {
+ struct mp_log *log;
+ char **args;
+};
+
+static void *run_subprocess(void *ptr)
+{
+ struct subprocess_args *p = ptr;
+ pthread_detach(pthread_self());
+
+ mp_msg_flush_status_line(p->log);
+
+ char *err = NULL;
+ if (mp_subprocess(p->args, NULL, NULL, NULL, NULL, &err) < 0)
+ mp_err(p->log, "Running subprocess failed: %s\n", err);
+
+ talloc_free(p);
+ return NULL;
+}
+
+void mp_subprocess_detached(struct mp_log *log, char **args)
+{
+ struct subprocess_args *p = talloc_zero(NULL, struct subprocess_args);
+ p->log = mp_log_new(p, log, NULL);
+ int num_args = 0;
+ for (int n = 0; args[n]; n++)
+ MP_TARRAY_APPEND(p, p->args, num_args, talloc_strdup(p, args[n]));
+ MP_TARRAY_APPEND(p, p->args, num_args, NULL);
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, run_subprocess, p))
+ talloc_free(p);
+}
diff --git a/osdep/subprocess.h b/osdep/subprocess.h
index 09c07da981..1bd5afe1f8 100644
--- a/osdep/subprocess.h
+++ b/osdep/subprocess.h
@@ -29,4 +29,7 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
subprocess_read_cb on_stdout, subprocess_read_cb on_stderr,
char **error);
+struct mp_log;
+void mp_subprocess_detached(struct mp_log *log, char **args);
+
#endif
diff --git a/osdep/terminal-unix.c b/osdep/terminal-unix.c
index d0f40f3a4c..f43376b088 100644
--- a/osdep/terminal-unix.c
+++ b/osdep/terminal-unix.c
@@ -275,8 +275,7 @@ static void enable_kx(bool enable)
// shouldn't be relied on here either.
if (isatty(STDOUT_FILENO)) {
char *cmd = enable ? "\033=" : "\033>";
- printf("%s", cmd);
- fflush(stdout);
+ write(STDOUT_FILENO, cmd, strlen(cmd));
}
}
diff --git a/player/client.c b/player/client.c
index 614d64205f..43c7c07c60 100644
--- a/player/client.c
+++ b/player/client.c
@@ -954,6 +954,7 @@ static int run_async(mpv_handle *ctx, void (*fn)(void *fn_data), void *fn_data)
struct cmd_request {
struct MPContext *mpctx;
struct mp_cmd *cmd;
+ struct mpv_node *res;
int status;
struct mpv_handle *reply_ctx;
uint64_t userdata;
@@ -962,7 +963,7 @@ struct cmd_request {
static void cmd_fn(void *data)
{
struct cmd_request *req = data;
- int r = run_command(req->mpctx, req->cmd);
+ int r = run_command(req->mpctx, req->cmd, req->res);
req->status = r >= 0 ? 0 : MPV_ERROR_COMMAND;
talloc_free(req->cmd);
if (req->reply_ctx) {
@@ -971,7 +972,7 @@ static void cmd_fn(void *data)
}
}
-static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd)
+static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res)
{
if (!ctx->mpctx->initialized)
return MPV_ERROR_UNINITIALIZED;
@@ -986,6 +987,7 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd)
struct cmd_request req = {
.mpctx = ctx->mpctx,
.cmd = cmd,
+ .res = res,
};
run_locked(ctx, cmd_fn, &req);
return req.status;
@@ -993,21 +995,22 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd)
int mpv_command(mpv_handle *ctx, const char **args)
{
- return run_client_command(ctx, mp_input_parse_cmd_strv(ctx->log, args));
+ return run_client_command(ctx, mp_input_parse_cmd_strv(ctx->log, args), NULL);
}
int mpv_command_node(mpv_handle *ctx, mpv_node *args, mpv_node *result)
{
- int r = run_client_command(ctx, mp_input_parse_cmd_node(ctx->log, args));
+ struct mpv_node rn = {.format = MPV_FORMAT_NONE};
+ int r = run_client_command(ctx, mp_input_parse_cmd_node(ctx->log, args), &rn);
if (result && r >= 0)
- *result = (mpv_node){.format = MPV_FORMAT_NONE};
+ *result = rn;
return r;
}
int mpv_command_string(mpv_handle *ctx, const char *args)
{
return run_client_command(ctx,
- mp_input_parse_cmd(ctx->mpctx->input, bstr0((char*)args), ctx->name));
+ mp_input_parse_cmd(ctx->mpctx->input, bstr0((char*)args), ctx->name), NULL);
}
static int run_cmd_async(mpv_handle *ctx, uint64_t ud, struct mp_cmd *cmd)
diff --git a/player/command.c b/player/command.c
index 88b3164436..3556986473 100644
--- a/player/command.c
+++ b/player/command.c
@@ -216,14 +216,6 @@ static void mark_seek(struct MPContext *mpctx)
cmd->last_seek_time = now;
}
-static char *format_bitrate(int rate)
-{
- if (rate < 1024 * 1024)
- return talloc_asprintf(NULL, "%.3f kbps", rate / 1000.0);
-
- return talloc_asprintf(NULL, "%.3f mbps", rate / 1000000.0);
-}
-
static char *format_file_size(int64_t size)
{
double s = size;
@@ -702,6 +694,14 @@ static int mp_property_disc_menu(void *ctx, struct m_property *prop,
return m_property_flag_ro(action, arg, !!state);
}
+static int mp_property_mouse_on_button(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ bool on = mp_nav_mouse_on_button(mpctx);
+ return m_property_flag_ro(action, arg, on);
+}
+
/// Current chapter (RW)
static int mp_property_chapter(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -1383,6 +1383,24 @@ static int mp_property_demuxer_cache_duration(void *ctx, struct m_property *prop
return m_property_double_ro(action, arg, s.ts_duration);
}
+static int mp_property_demuxer_cache_time(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ if (!mpctx->demuxer)
+ return M_PROPERTY_UNAVAILABLE;
+
+ struct demux_ctrl_reader_state s;
+ if (demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_READER_STATE, &s) < 1)
+ return M_PROPERTY_UNAVAILABLE;
+
+ double ts = s.ts_range[1];
+ if (ts == MP_NOPTS_VALUE)
+ return M_PROPERTY_UNAVAILABLE;
+
+ return m_property_double_ro(action, arg, ts);
+}
+
static int mp_property_demuxer_cache_idle(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -1636,20 +1654,6 @@ static int mp_property_audio_codec(void *ctx, struct m_property *prop,
return m_property_strdup_ro(action, arg, c);
}
-/// Audio bitrate (RO)
-static int mp_property_audio_bitrate(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (!mpctx->d_audio)
- return M_PROPERTY_UNAVAILABLE;
- if (action == M_PROPERTY_PRINT) {
- *(char **)arg = format_bitrate(mpctx->d_audio->bitrate);
- return M_PROPERTY_OK;
- }
- return m_property_int_ro(action, arg, mpctx->d_audio->bitrate);
-}
-
/// Samplerate (RO)
static int mp_property_samplerate(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -2329,21 +2333,6 @@ static int mp_property_video_codec(void *ctx, struct m_property *prop,
return m_property_strdup_ro(action, arg, c);
}
-
-/// Video bitrate (RO)
-static int mp_property_video_bitrate(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (!mpctx->d_video)
- return M_PROPERTY_UNAVAILABLE;
- if (action == M_PROPERTY_PRINT) {
- *(char **)arg = format_bitrate(mpctx->d_video->bitrate);
- return M_PROPERTY_OK;
- }
- return m_property_int_ro(action, arg, mpctx->d_video->bitrate);
-}
-
static int property_imgparams(struct mp_image_params p, int action, void *arg)
{
if (!p.imgfmt)
@@ -3022,7 +3011,8 @@ static int mp_property_packet_bitrate(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- int type = (intptr_t)prop->priv;
+ int type = (uintptr_t)prop->priv & ~0x100;
+ bool old = (uintptr_t)prop->priv & 0x100;
if (!mpctx->demuxer)
return M_PROPERTY_UNAVAILABLE;
@@ -3031,8 +3021,23 @@ static int mp_property_packet_bitrate(void *ctx, struct m_property *prop,
if (demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_BITRATE_STATS, &r) < 1)
return M_PROPERTY_UNAVAILABLE;
- // r[type] is in bytes/second -> kilobits
- return m_property_int64_ro(action, arg, r[type] * 8 / 1000.0 + 0.5);
+ // r[type] is in bytes/second -> bits
+ double rate = r[type] * 8;
+
+ // Same story, but used kilobits for some reason.
+ if (old)
+ return m_property_int64_ro(action, arg, rate / 1000.0 + 0.5);
+
+ if (action == M_PROPERTY_PRINT) {
+ rate /= 1000;
+ if (rate < 1000) {
+ *(char **)arg = talloc_asprintf(NULL, "%d kbps", (int)rate);
+ } else {
+ *(char **)arg = talloc_asprintf(NULL, "%.3f mbps", rate / 1000.0);
+ }
+ return M_PROPERTY_OK;
+ }
+ return m_property_int64_ro(action, arg, rate);
}
static int mp_property_cwd(void *ctx, struct m_property *prop,
@@ -3262,6 +3267,7 @@ static const struct m_property mp_properties[] = {
{"playback-time", mp_property_playback_time},
{"disc-title", mp_property_disc_title},
{"disc-menu-active", mp_property_disc_menu},
+ {"disc-mouse-on-button", mp_property_mouse_on_button},
{"chapter", mp_property_chapter},
{"edition", mp_property_edition},
{"disc-titles", mp_property_disc_titles},
@@ -3283,6 +3289,7 @@ static const struct m_property mp_properties[] = {
{"cache-size", mp_property_cache_size},
{"cache-idle", mp_property_cache_idle},
{"demuxer-cache-duration", mp_property_demuxer_cache_duration},
+ {"demuxer-cache-time", mp_property_demuxer_cache_time},
{"demuxer-cache-idle", mp_property_demuxer_cache_idle},
{"cache-buffering-state", mp_property_cache_buffering},
{"paused-for-cache", mp_property_paused_for_cache},
@@ -3308,7 +3315,6 @@ static const struct m_property mp_properties[] = {
{"audio-delay", mp_property_audio_delay},
{"audio-format", mp_property_audio_format},
{"audio-codec", mp_property_audio_codec},
- {"audio-bitrate", mp_property_audio_bitrate},
{"audio-samplerate", mp_property_samplerate},
{"audio-channels", mp_property_channels},
{"aid", mp_property_audio},
@@ -3343,7 +3349,6 @@ static const struct m_property mp_properties[] = {
{"video-params", mp_property_vd_imgparams},
{"video-format", mp_property_video_format},
{"video-codec", mp_property_video_codec},
- {"video-bitrate", mp_property_video_bitrate},
M_PROPERTY_ALIAS("dwidth", "video-out-params/dw"),
M_PROPERTY_ALIAS("dheight", "video-out-params/dh"),
M_PROPERTY_ALIAS("width", "video-params/w"),
@@ -3390,11 +3395,15 @@ static const struct m_property mp_properties[] = {
{"ab-loop-a", mp_property_ab_loop},
{"ab-loop-b", mp_property_ab_loop},
-#define PROPERTY_BITRATE(name, type) \
- {name, mp_property_packet_bitrate, (void *)(intptr_t)type}
- PROPERTY_BITRATE("packet-video-bitrate", STREAM_VIDEO),
- PROPERTY_BITRATE("packet-audio-bitrate", STREAM_AUDIO),
- PROPERTY_BITRATE("packet-sub-bitrate", STREAM_SUB),
+#define PROPERTY_BITRATE(name, old, type) \
+ {name, mp_property_packet_bitrate, (void *)(uintptr_t)((type)|(old?0x100:0))}
+ PROPERTY_BITRATE("packet-video-bitrate", true, STREAM_VIDEO),
+ PROPERTY_BITRATE("packet-audio-bitrate", true, STREAM_AUDIO),
+ PROPERTY_BITRATE("packet-sub-bitrate", true, STREAM_SUB),
+
+ PROPERTY_BITRATE("video-bitrate", false, STREAM_VIDEO),
+ PROPERTY_BITRATE("audio-bitrate", false, STREAM_AUDIO),
+ PROPERTY_BITRATE("sub-bitrate", false, STREAM_SUB),
#define PROPERTY_TV_COLOR(name, type) \
{name, mp_property_tv_color, (void *)(intptr_t)type}
@@ -3473,7 +3482,8 @@ static const char *const *const mp_event_property_change[] = {
E(MPV_EVENT_METADATA_UPDATE, "metadata", "filtered-metadata", "media-title"),
E(MPV_EVENT_CHAPTER_CHANGE, "chapter", "chapter-metadata"),
E(MP_EVENT_CACHE_UPDATE, "cache", "cache-free", "cache-used", "cache-idle",
- "demuxer-cache-duration", "demuxer-cache-idle", "paused-for-cache"),
+ "demuxer-cache-duration", "demuxer-cache-idle", "paused-for-cache",
+ "demuxer-cache-time"),
E(MP_EVENT_WIN_RESIZE, "window-scale"),
E(MP_EVENT_WIN_STATE, "window-minimized", "display-names", "display-fps"),
E(MP_EVENT_AUDIO_DEVICES, "audio-device-list"),
@@ -3960,39 +3970,6 @@ static void overlay_uninit(struct MPContext *mpctx)
osd_set_external2(mpctx->osd, NULL);
}
-struct subprocess_args {
- struct mp_log *log;
- char **args;
-};
-
-static void *run_subprocess(void *ptr)
-{
- struct subprocess_args *p = ptr;
- pthread_detach(pthread_self());
-
- mp_msg_flush_status_line(p->log);
-
- char *err = NULL;
- if (mp_subprocess(p->args, NULL, NULL, NULL, NULL, &err) < 0)
- mp_err(p->log, "Running subprocess failed: %s\n", err);
-
- talloc_free(p);
- return NULL;
-}
-
-static void subprocess_detached(struct mp_log *log, char **args)
-{
- struct subprocess_args *p = talloc_zero(NULL, struct subprocess_args);
- p->log = mp_log_new(p, log, NULL);
- int num_args = 0;
- for (int n = 0; args[n]; n++)
- MP_TARRAY_APPEND(p, p->args, num_args, talloc_strdup(p, args[n]));
- MP_TARRAY_APPEND(p, p->args, num_args, NULL);
- pthread_t thread;
- if (pthread_create(&thread, NULL, run_subprocess, p))
- talloc_free(p);
-}
-
struct cycle_counter {
char **args;
int counter;
@@ -4083,7 +4060,23 @@ static bool check_property_autorepeat(char *property, struct MPContext *mpctx)
return true;
}
-int run_command(MPContext *mpctx, mp_cmd_t *cmd)
+static struct mpv_node *add_map_entry(struct mpv_node *dst, const char *key)
+{
+ struct mpv_node_list *list = dst->u.list;
+ assert(dst->format == MPV_FORMAT_NODE_MAP && dst->u.list);
+ MP_TARRAY_GROW(list, list->values, list->num);
+ MP_TARRAY_GROW(list, list->keys, list->num);
+ list->keys[list->num] = talloc_strdup(list, key);
+ return &list->values[list->num++];
+}
+
+#define ADD_MAP_INT(dst, name, i) (*add_map_entry(dst, name) = \
+ (struct mpv_node){ .format = MPV_FORMAT_INT64, .u.int64 = (i) });
+
+#define ADD_MAP_CSTR(dst, name, s) (*add_map_entry(dst, name) = \
+ (struct mpv_node){ .format = MPV_FORMAT_STRING, .u.string = (s) });
+
+int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *res)
{
struct command_ctx *cmdctx = mpctx->command_ctx;
struct MPOpts *opts = mpctx->opts;
@@ -4595,11 +4588,34 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd);
break;
+ case MP_CMD_SCREENSHOT_RAW: {
+ if (!res)
+ return -1;
+ struct mp_image *img = screenshot_get_rgb(mpctx, cmd->args[0].v.i);
+ if (!img)
+ return -1;
+ struct mpv_node_list *info = talloc_zero(NULL, struct mpv_node_list);
+ talloc_steal(info, img);
+ *res = (mpv_node){ .format = MPV_FORMAT_NODE_MAP, .u.list = info };
+ ADD_MAP_INT(res, "w", img->w);
+ ADD_MAP_INT(res, "h", img->h);
+ ADD_MAP_INT(res, "stride", img->stride[0]);
+ ADD_MAP_CSTR(res, "format", "bgr0");
+ struct mpv_byte_array *ba = talloc_ptrtype(info, ba);
+ *ba = (struct mpv_byte_array){
+ .data = img->planes[0],
+ .size = img->stride[0] * img->h,
+ };
+ *add_map_entry(res, "data") =
+ (struct mpv_node){.format = MPV_FORMAT_BYTE_ARRAY, .u.ba = ba,};
+ break;
+ }
+
case MP_CMD_RUN: {
char *args[MP_CMD_MAX_ARGS + 1] = {0};
for (int n = 0; n < cmd->nargs; n++)
args[n] = cmd->args[n].v.s;
- subprocess_detached(mpctx->log, args);
+ mp_subprocess_detached(mpctx->log, args);
break;
}
@@ -4735,7 +4751,7 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
case MP_CMD_COMMAND_LIST: {
for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next)
- run_command(mpctx, sub);
+ run_command(mpctx, sub, NULL);
break;
}
diff --git a/player/command.h b/player/command.h
index 1a36c79570..447e01c011 100644
--- a/player/command.h
+++ b/player/command.h
@@ -21,11 +21,12 @@
struct MPContext;
struct mp_cmd;
struct mp_log;
+struct mpv_node;
void command_init(struct MPContext *mpctx);
void command_uninit(struct MPContext *mpctx);
-int run_command(struct MPContext *mpctx, struct mp_cmd *cmd);
+int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *res);
char *mp_property_expand_string(struct MPContext *mpctx, const char *str);
char *mp_property_expand_escaped_string(struct MPContext *mpctx, const char *str);
void property_print_help(struct mp_log *log);
diff --git a/player/configfiles.c b/player/configfiles.c
index e056b43e8e..b7d3795f60 100644
--- a/player/configfiles.c
+++ b/player/configfiles.c
@@ -72,16 +72,14 @@ void mp_parse_cfgfiles(struct MPContext *mpctx)
// So we "divert" normal options into a separate section, and the diverted
// section is never used - unless maybe it's explicitly referenced from an
// encoding profile.
- if (encoding)
+ if (encoding) {
section = "playback-default";
- // The #if is a stupid hack to avoid errors if libavfilter is not available.
-#if HAVE_LIBAVFILTER && HAVE_ENCODING
- char *cf = mp_find_config_file(NULL, mpctx->global, "encoding-profiles.conf");
- if (cf)
- m_config_parse_config_file(mpctx->mconfig, cf, SECT_ENCODE, 0);
- talloc_free(cf);
-#endif
+ char *cf = mp_find_config_file(NULL, mpctx->global, "encoding-profiles.conf");
+ if (cf)
+ m_config_parse_config_file(mpctx->mconfig, cf, SECT_ENCODE, 0);
+ talloc_free(cf);
+ }
load_all_cfgfiles(mpctx, section, "mpv.conf|config");
diff --git a/player/core.h b/player/core.h
index 23547ac630..714fd5d4bd 100644
--- a/player/core.h
+++ b/player/core.h
@@ -373,6 +373,7 @@ void mp_nav_destroy(struct MPContext *mpctx);
void mp_nav_user_input(struct MPContext *mpctx, char *command);
void mp_handle_nav(struct MPContext *mpctx);
int mp_nav_in_menu(struct MPContext *mpctx);
+bool mp_nav_mouse_on_button(struct MPContext *mpctx);
// loadfile.c
void uninit_player(struct MPContext *mpctx, unsigned int mask);
@@ -490,7 +491,6 @@ int reinit_video_chain(struct MPContext *mpctx);
int reinit_video_filters(struct MPContext *mpctx);
void write_video(struct MPContext *mpctx, double endpts);
void mp_force_video_refresh(struct MPContext *mpctx);
-void update_fps(struct MPContext *mpctx);
void uninit_video_out(struct MPContext *mpctx);
void uninit_video_chain(struct MPContext *mpctx);
diff --git a/player/discnav.c b/player/discnav.c
index d66b569d4b..719e88ac8f 100644
--- a/player/discnav.c
+++ b/player/discnav.c
@@ -42,6 +42,7 @@ struct mp_nav_state {
bool nav_eof;
bool nav_menu;
bool nav_draining;
+ bool nav_mouse_on_button;
// Accessed by OSD (possibly separate thread)
// Protected by the given lock
@@ -91,6 +92,25 @@ int mp_nav_in_menu(struct MPContext *mpctx)
return mpctx->nav_state ? mpctx->nav_state->nav_menu : -1;
}
+static void update_mouse_on_button(struct MPContext *mpctx)
+{
+ mp_notify_property(mpctx, "disc-mouse-on-button");
+}
+
+static void set_mouse_on_button(struct MPContext *mpctx, bool in)
+{
+ struct mp_nav_state *nav = mpctx->nav_state;
+ if (nav->nav_mouse_on_button != in) {
+ nav->nav_mouse_on_button = in;
+ update_mouse_on_button(mpctx);
+ }
+}
+
+bool mp_nav_mouse_on_button(struct MPContext *mpctx)
+{
+ return mpctx->nav_state ? mpctx->nav_state->nav_mouse_on_button : false;
+}
+
// If a demuxer is accessing the stream, we have to use demux_stream_control()
// to avoid synchronization issues; otherwise access it directly.
static int run_stream_control(struct MPContext *mpctx, int cmd, void *arg)
@@ -129,6 +149,7 @@ void mp_nav_init(struct MPContext *mpctx)
MP_INPUT_ALLOW_VO_DRAGGING | MP_INPUT_ALLOW_HIDE_CURSOR);
update_state(mpctx);
+ update_mouse_on_button(mpctx);
}
void mp_nav_reset(struct MPContext *mpctx)
@@ -159,6 +180,7 @@ void mp_nav_destroy(struct MPContext *mpctx)
talloc_free(mpctx->nav_state);
mpctx->nav_state = NULL;
update_state(mpctx);
+ update_mouse_on_button(mpctx);
}
void mp_nav_user_input(struct MPContext *mpctx, char *command)
@@ -182,6 +204,7 @@ void mp_nav_user_input(struct MPContext *mpctx, char *command)
inp.u.mouse_pos.x = x;
inp.u.mouse_pos.y = y;
run_stream_control(mpctx, STREAM_CTRL_NAV_CMD, &inp);
+ set_mouse_on_button(mpctx, inp.mouse_on_button);
} else {
struct mp_nav_cmd inp = {MP_NAV_CMD_MENU};
inp.u.menu.action = command;
diff --git a/player/lua.c b/player/lua.c
index 43f549b4ed..4ef772fd84 100644
--- a/player/lua.c
+++ b/player/lua.c
@@ -860,6 +860,9 @@ static void pushnode(lua_State *L, mpv_node *node)
lua_rawset(L, -3);
}
break;
+ case MPV_FORMAT_BYTE_ARRAY:
+ lua_pushlstring(L, node->u.ba->data, node->u.ba->size);
+ break;
default:
// unknown value - what do we do?
// for now, set a unique dummy value
@@ -1252,6 +1255,23 @@ static int script_parse_json(lua_State *L)
return 3;
}
+static int script_format_json(lua_State *L)
+{
+ void *tmp = mp_lua_PITA(L);
+ struct mpv_node node;
+ makenode(tmp, &node, L, 1);
+ char *dst = talloc_strdup(tmp, "");
+ if (json_write(&dst, &node) >= 0) {
+ lua_pushstring(L, dst);
+ lua_pushnil(L);
+ } else {
+ lua_pushnil(L);
+ lua_pushstring(L, "error");
+ }
+ talloc_free_children(tmp);
+ return 2;
+}
+
#define FN_ENTRY(name) {#name, script_ ## name}
struct fn_entry {
const char *name;
@@ -1300,6 +1320,7 @@ static const struct fn_entry utils_fns[] = {
FN_ENTRY(join_path),
FN_ENTRY(subprocess),
FN_ENTRY(parse_json),
+ FN_ENTRY(format_json),
{0}
};
diff --git a/player/lua/osc.lua b/player/lua/osc.lua
index 26e2f89c5f..7f602b8689 100644
--- a/player/lua/osc.lua
+++ b/player/lua/osc.lua
@@ -266,11 +266,13 @@ end
-- get the currently selected track of <type>, OSC-style counted
function get_track(type)
local track = mp.get_property(type)
- if (track == "no" or track == nil) then
- return 0
- else
- return tracks_mpv[type][tonumber(track)].osc_id
+ if track ~= "no" and track ~= nil then
+ local tr = tracks_mpv[type][tonumber(track)]
+ if tr then
+ return tr.osc_id
+ end
end
+ return 0
end
@@ -446,7 +448,7 @@ function render_elements(master_ass)
style_ass:append(string.format("{\\1a&H%X&\\2a&H%X&\\3a&H%X&\\4a&H%X&}",
ar[1], ar[2], ar[3], ar[4]))
- if (state.active_element == n) then
+ if element.eventresponder and (state.active_element == n) then
-- run render event functions
if not (element.eventresponder.render == nil) then
diff --git a/player/playloop.c b/player/playloop.c
index 1f480cf3ba..c34ae7e6da 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -67,7 +67,7 @@ void mp_process_input(struct MPContext *mpctx)
mp_cmd_t *cmd = mp_input_read_cmd(mpctx->input);
if (!cmd)
break;
- run_command(mpctx, cmd);
+ run_command(mpctx, cmd, NULL);
mp_cmd_free(cmd);
mp_dispatch_queue_process(mpctx->dispatch, 0);
}
@@ -240,8 +240,6 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
}
if (hr_seek)
demuxer_style |= SEEK_HR;
- if (hr_seek || opts->mkv_subtitle_preroll)
- demuxer_style |= SEEK_SUBPREROLL;
if (hr_seek)
demuxer_amount -= hr_seek_offset;
diff --git a/player/screenshot.c b/player/screenshot.c
index ec01adffaa..a47de292d5 100644
--- a/player/screenshot.c
+++ b/player/screenshot.c
@@ -32,7 +32,6 @@
#include "options/path.h"
#include "video/mp_image.h"
#include "video/decode/dec_video.h"
-#include "video/filter/vf.h"
#include "video/out/vo.h"
#include "video/image_writer.h"
#include "sub/osd.h"
@@ -330,11 +329,7 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
if (mode == MODE_SUBTITLES && osd_get_render_subs_in_filter(mpctx->osd))
mode = 0;
- // vf_screenshot
- if (mpctx->d_video && mpctx->d_video->vfilter)
- vf_control_any(mpctx->d_video->vfilter, VFCTRL_SCREENSHOT, &image);
-
- if (!image && mpctx->video_out && mpctx->video_out->config_ok) {
+ if (mpctx->video_out && mpctx->video_out->config_ok) {
vo_wait_frame(mpctx->video_out); // important for each-frame mode
if (mode != MODE_FULL_WINDOW)
@@ -362,6 +357,16 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
return image;
}
+struct mp_image *screenshot_get_rgb(struct MPContext *mpctx, int mode)
+{
+ struct mp_image *mpi = screenshot_get(mpctx, mode);
+ if (!mpi)
+ return NULL;
+ struct mp_image *res = convert_image(mpi, IMGFMT_BGR0, mpctx->log);
+ talloc_free(mpi);
+ return res;
+}
+
void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
bool osd)
{
@@ -370,11 +375,6 @@ void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
bool old_osd = ctx->osd;
ctx->osd = osd;
- if (mp_path_exists(filename)) {
- screenshot_msg(ctx, SMSG_ERR, "Screenshot: file '%s' already exists.",
- filename);
- goto end;
- }
char *ext = mp_splitext(filename, NULL);
if (ext)
opts.format = ext;
diff --git a/player/screenshot.h b/player/screenshot.h
index 84b7ef4f58..9ebe9ef76e 100644
--- a/player/screenshot.h
+++ b/player/screenshot.h
@@ -39,6 +39,9 @@ void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
bool osd);
+// mode is the same as in screenshot_request()
+struct mp_image *screenshot_get_rgb(struct MPContext *mpctx, int mode);
+
// Called by the playback core code when a new frame is displayed.
void screenshot_flip(struct MPContext *mpctx);
diff --git a/player/video.c b/player/video.c
index 6c41593c72..d35a9de011 100644
--- a/player/video.c
+++ b/player/video.c
@@ -76,15 +76,6 @@ static const char av_desync_help_text[] =
" with --no-video, --no-audio, or --no-sub.\n"
"If none of this helps you, file a bug report.\n\n";
-void update_fps(struct MPContext *mpctx)
-{
-#if HAVE_ENCODING
- struct dec_video *d_video = mpctx->d_video;
- if (mpctx->encode_lavc_ctx && d_video)
- encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, d_video->fps);
-#endif
-}
-
static void set_allowed_vo_formats(struct vf_chain *c, struct vo *vo)
{
vo_query_formats(vo, c->allowed_output_formats);
@@ -296,12 +287,18 @@ int reinit_video_chain(struct MPContext *mpctx)
d_video->fps = sh->video->fps;
d_video->vo = mpctx->video_out;
+ MP_VERBOSE(d_video, "Container reported FPS: %f\n", sh->video->fps);
+
if (opts->force_fps) {
d_video->fps = opts->force_fps;
MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
}
- update_fps(mpctx);
+
+#if HAVE_ENCODING
+ if (mpctx->encode_lavc_ctx && d_video)
+ encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, d_video->fps);
+#endif
vo_control(mpctx->video_out, VOCTRL_GET_HWDEC_INFO, &d_video->hwdec_info);
@@ -380,7 +377,7 @@ static int decode_image(struct MPContext *mpctx)
if (d_video->header->attached_picture) {
d_video->waiting_decoded_mpi =
video_decode(d_video, d_video->header->attached_picture, 0);
- return VD_EOF;
+ return d_video->waiting_decoded_mpi ? VD_EOF : VD_PROGRESS;
}
struct demux_packet *pkt;
@@ -405,7 +402,8 @@ static int decode_image(struct MPContext *mpctx)
talloc_free(pkt);
if (had_packet && !d_video->waiting_decoded_mpi &&
- mpctx->video_status == STATUS_PLAYING)
+ mpctx->video_status == STATUS_PLAYING &&
+ (mpctx->opts->frame_dropping & 2))
{
mpctx->dropped_frames_total++;
mpctx->dropped_frames++;
@@ -726,9 +724,9 @@ static void update_avsync_after_frame(struct MPContext *mpctx)
mpctx->last_av_difference = a_pos - mpctx->video_pts + opts->audio_delay;
if (mpctx->time_frame > 0)
mpctx->last_av_difference += mpctx->time_frame * opts->playback_speed;
- if (a_pos == MP_NOPTS_VALUE || mpctx->video_pts == MP_NOPTS_VALUE)
+ if (a_pos == MP_NOPTS_VALUE || mpctx->video_pts == MP_NOPTS_VALUE) {
mpctx->last_av_difference = MP_NOPTS_VALUE;
- if (fabs(mpctx->last_av_difference) > 0.5 && !mpctx->drop_message_shown) {
+ } else if (fabs(mpctx->last_av_difference) > 0.5 && !mpctx->drop_message_shown) {
MP_WARN(mpctx, "%s", av_desync_help_text);
mpctx->drop_message_shown = true;
}
@@ -768,8 +766,6 @@ void write_video(struct MPContext *mpctx, double endpts)
if (mpctx->paused && mpctx->video_status >= STATUS_READY)
return;
- update_fps(mpctx);
-
int r = video_output_image(mpctx, endpts);
MP_TRACE(mpctx, "video_output_image: %d\n", r);
@@ -784,7 +780,7 @@ void write_video(struct MPContext *mpctx, double endpts)
vo_still_displaying(vo) ? STATUS_DRAINING : STATUS_EOF;
mpctx->delay = 0;
mpctx->last_av_difference = 0;
- MP_VERBOSE(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
+ MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
return;
}
diff --git a/stream/cache.c b/stream/cache.c
index 51d228a08c..0ffa3927be 100644
--- a/stream/cache.c
+++ b/stream/cache.c
@@ -18,7 +18,7 @@
// Time in seconds the main thread waits for the cache thread. On wakeups, the
// code checks for user requested aborts and also prints warnings that the
// cache is being slow.
-#define CACHE_WAIT_TIME 0.5
+#define CACHE_WAIT_TIME 1.0
// The time the cache sleeps in idle mode. This controls how often the cache
// retries reading from the stream after EOF has reached (in case the stream is
@@ -32,9 +32,6 @@
// the cache is active.
#define CACHE_UPDATE_CONTROLS_TIME 2.0
-// Time in seconds the cache prints a new message at all.
-#define CACHE_NO_SPAM 5.0
-
#include <stdio.h>
#include <stdlib.h>
@@ -81,7 +78,6 @@ struct priv {
// Owned by the main thread
stream_t *cache; // wrapper stream, used by demuxer etc.
- double last_warn_time;
// Owned by the cache thread
stream_t *stream; // "real" stream, used to read from the source media
@@ -118,8 +114,6 @@ struct priv {
};
enum {
- CACHE_INTERRUPTED = -1,
-
CACHE_CTRL_NONE = 0,
CACHE_CTRL_QUIT = -1,
CACHE_CTRL_PING = -2,
@@ -131,31 +125,19 @@ enum {
// Used by the main thread to wakeup the cache thread, and to wait for the
// cache thread. The cache mutex has to be locked when calling this function.
// *retry_time should be set to 0 on the first call.
-// Returns CACHE_INTERRUPTED if the caller is supposed to abort.
-static int cache_wakeup_and_wait(struct priv *s, double *retry_time)
+static void cache_wakeup_and_wait(struct priv *s, double *retry_time)
{
- if (mp_cancel_test(s->cache->cancel))
- return CACHE_INTERRUPTED;
-
double start = mp_time_sec();
-
- if (!s->last_warn_time || start - s->last_warn_time >= CACHE_NO_SPAM) {
- // Print a "more severe" warning after waiting 1 second and no new data
- if ((*retry_time) >= 1.0) {
- MP_ERR(s, "Cache keeps not responding.\n");
- s->last_warn_time = start;
- } else if (*retry_time > 0.1) {
- MP_WARN(s, "Cache is not responding - slow/stuck network connection?\n");
- s->last_warn_time = start;
- }
+ if (*retry_time >= CACHE_WAIT_TIME) {
+ MP_WARN(s, "Cache is not responding - slow/stuck network connection?\n");
+ *retry_time = -1; // do not warn again for this call
}
pthread_cond_signal(&s->wakeup);
mpthread_cond_timedwait_rel(&s->wakeup, &s->mutex, CACHE_WAIT_TIME);
- *retry_time += mp_time_sec() - start;
-
- return 0;
+ if (*retry_time >= 0)
+ *retry_time += mp_time_sec() - start;
}
// Runs in the cache thread
@@ -226,6 +208,9 @@ static bool cache_fill(struct priv *s)
goto done;
}
+ if (mp_cancel_test(s->cache->cancel))
+ goto done;
+
// number of buffer bytes which should be preserved in backwards direction
int64_t back = MPCLAMP(read - s->min_filepos, 0, s->back_size);
@@ -511,8 +496,9 @@ static int cache_fill_buffer(struct stream *cache, char *buffer, int max_len)
if (s->eof && s->read_filepos >= s->max_filepos && s->reads >= retry)
break;
s->idle = false;
- if (cache_wakeup_and_wait(s, &retry_time) == CACHE_INTERRUPTED)
+ if (mp_cancel_test(s->cache->cancel))
break;
+ cache_wakeup_and_wait(s, &retry_time);
}
}
@@ -570,11 +556,12 @@ static int cache_control(stream_t *cache, int cmd, void *arg)
s->control_arg = arg;
double retry = 0;
while (s->control != CACHE_CTRL_NONE) {
- if (cache_wakeup_and_wait(s, &retry) == CACHE_INTERRUPTED) {
+ if (mp_cancel_test(s->cache->cancel)) {
s->eof = 1;
r = STREAM_UNSUPPORTED;
goto done;
}
+ cache_wakeup_and_wait(s, &retry);
}
r = s->control_res;
if (s->control_flush) {
diff --git a/stream/discnav.h b/stream/discnav.h
index e6a063f8e1..b40998d3cb 100644
--- a/stream/discnav.h
+++ b/stream/discnav.h
@@ -80,6 +80,7 @@ struct mp_nav_cmd {
int x, y;
} mouse_pos;
} u;
+ bool mouse_on_button;
};
#endif /* MPLAYER_STREAM_DVDNAV_H */
diff --git a/stream/stream.h b/stream/stream.h
index 387006cb8f..7b4751450c 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -31,6 +31,7 @@
enum streamtype {
STREAMTYPE_GENERIC = 0,
STREAMTYPE_FILE,
+ STREAMTYPE_DIR,
STREAMTYPE_DVB,
STREAMTYPE_DVD,
STREAMTYPE_BLURAY,
diff --git a/stream/stream_bluray.c b/stream/stream_bluray.c
index 1718531ecc..adc2e5828b 100644
--- a/stream/stream_bluray.c
+++ b/stream/stream_bluray.c
@@ -455,7 +455,9 @@ 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)
- bd_mouse_select(priv->bd, pts, priv->mousex, priv->mousey);
+ 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) {
if (priv->popup_enabled)
@@ -467,7 +469,9 @@ 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;
- bd_mouse_select(priv->bd, mp_time_us(), priv->mousex, priv->mousey);
+ ev->mouse_on_button = bd_mouse_select(priv->bd, mp_time_us(),
+ priv->mousex,
+ priv->mousey);
break;
case MP_NAV_CMD_SKIP_STILL:
bd_read_skip_still(priv->bd);
diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c
index b94187b3ff..095ba98ddf 100644
--- a/stream/stream_dvdnav.c
+++ b/stream/stream_dvdnav.c
@@ -202,18 +202,19 @@ static void handle_menu_input(stream_t *stream, const char *cmd)
}
}
-static void handle_mouse_pos(stream_t *stream, int x, int y)
+static dvdnav_status_t handle_mouse_pos(stream_t *stream, int x, int y)
{
struct priv *priv = stream->priv;
dvdnav_t *nav = priv->dvdnav;
pci_t *pci = dvdnav_get_current_nav_pci(nav);
if (!pci)
- return;
+ return DVDNAV_STATUS_ERR;
- dvdnav_mouse_select(nav, pci, x, y);
+ dvdnav_status_t status = dvdnav_mouse_select(nav, pci, x, y);
priv->mousex = x;
priv->mousey = y;
+ return status;
}
/**
@@ -305,7 +306,7 @@ static void handle_cmd(stream_t *s, struct mp_nav_cmd *ev)
handle_menu_input(s, ev->u.menu.action);
break;
case MP_NAV_CMD_MOUSE_POS:
- handle_mouse_pos(s, ev->u.mouse_pos.x, ev->u.mouse_pos.y);
+ ev->mouse_on_button = handle_mouse_pos(s, ev->u.mouse_pos.x, ev->u.mouse_pos.y);
break;
}
diff --git a/stream/stream_file.c b/stream/stream_file.c
index 01e983fa31..d0da8629bc 100644
--- a/stream/stream_file.c
+++ b/stream/stream_file.c
@@ -155,12 +155,11 @@ char *mp_file_get_path(void *talloc_ctx, bstr url)
}
#if HAVE_BSD_FSTATFS
-static bool check_stream_network(stream_t *stream)
+static bool check_stream_network(int fd)
{
struct statfs fs;
const char *stypes[] = { "afpfs", "nfs", "smbfs", "webdav", NULL };
- struct priv *priv = stream->priv;
- if (fstatfs(priv->fd, &fs) == 0)
+ if (fstatfs(fd, &fs) == 0)
for (int i=0; stypes[i]; i++)
if (strcmp(stypes[i], fs.f_fstypename) == 0)
return true;
@@ -168,7 +167,7 @@ static bool check_stream_network(stream_t *stream)
}
#elif HAVE_LINUX_FSTATFS
-static bool check_stream_network(stream_t *stream)
+static bool check_stream_network(int fd)
{
struct statfs fs;
const uint32_t stypes[] = {
@@ -181,8 +180,7 @@ static bool check_stream_network(stream_t *stream)
0xBEEFDEAD /*SNFS*/, 0xBACBACBC /*VMHGFS*/, 0x7461636f /*OCFS2*/,
0
};
- struct priv *priv = stream->priv;
- if (fstatfs(priv->fd, &fs) == 0) {
+ if (fstatfs(fd, &fs) == 0) {
for (int i=0; stypes[i]; i++) {
if (stypes[i] == fs.f_type)
return true;
@@ -192,7 +190,7 @@ static bool check_stream_network(stream_t *stream)
}
#elif defined(_WIN32)
-static bool check_stream_network(stream_t *stream)
+static bool check_stream_network(int fd)
{
NTSTATUS (NTAPI *pNtQueryVolumeInformationFile)(HANDLE,
PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS) = NULL;
@@ -208,8 +206,7 @@ static bool check_stream_network(stream_t *stream)
if (!pNtQueryVolumeInformationFile)
return false;
- struct priv *priv = stream->priv;
- HANDLE h = (HANDLE)_get_osfhandle(priv->fd);
+ HANDLE h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE)
return false;
@@ -224,7 +221,7 @@ static bool check_stream_network(stream_t *stream)
(info.Characteristics & FILE_REMOTE_DEVICE);
}
#else
-static bool check_stream_network(stream_t *stream)
+static bool check_stream_network(int fd)
{
return false;
}
@@ -238,6 +235,7 @@ static int open_f(stream_t *stream)
.fd = -1
};
stream->priv = priv;
+ stream->type = STREAMTYPE_FILE;
bool write = stream->mode == STREAM_WRITE;
int m = O_CLOEXEC | (write ? O_RDWR | O_CREAT | O_TRUNC : O_RDONLY);
@@ -278,9 +276,9 @@ static int open_f(stream_t *stream)
struct stat st;
if (fstat(fd, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
- MP_ERR(stream, "File is a directory: '%s'\n", filename);
- close(fd);
- return STREAM_ERROR;
+ stream->type = STREAMTYPE_DIR;
+ stream->allow_caching = false;
+ MP_INFO(stream, "This is a directory - adding to playlist.\n");
}
#ifndef __MINGW32__
if (S_ISREG(st.st_mode)) {
@@ -302,7 +300,6 @@ static int open_f(stream_t *stream)
stream->seekable = true;
}
- stream->type = STREAMTYPE_FILE;
stream->fast_skip = true;
stream->fill_buffer = fill_buffer;
stream->write_buffer = write_buffer;
@@ -310,7 +307,7 @@ static int open_f(stream_t *stream)
stream->read_chunk = 64 * 1024;
stream->close = s_close;
- if (check_stream_network(stream))
+ if (check_stream_network(fd))
stream->streaming = true;
return STREAM_OK;
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
index afbda5142f..0c733b0aa6 100644
--- a/video/decode/dec_video.c
+++ b/video/decode/dec_video.c
@@ -385,13 +385,6 @@ int video_reconfig_filters(struct dec_video *d_video,
struct mp_image_params p = *params;
struct sh_video *sh = d_video->header->video;
- MP_VERBOSE(d_video, "VIDEO: %dx%d %5.3f fps %5.1f kbps (%4.1f kB/s)\n",
- p.w, p.h, sh->fps, sh->bitrate / 1000.0,
- sh->bitrate / 8000.0);
-
- MP_VERBOSE(d_video, "VDec: vo config request - %d x %d (%s)\n",
- p.w, p.h, vo_format_name(p.imgfmt));
-
float decoder_aspect = p.d_w / (float)p.d_h;
if (d_video->initial_decoder_aspect == 0)
d_video->initial_decoder_aspect = decoder_aspect;
@@ -414,10 +407,6 @@ int video_reconfig_filters(struct dec_video *d_video,
// Detect colorspace from resolution.
mp_image_params_guess_csp(&p);
- // Time to config libvo!
- MP_VERBOSE(d_video, "VO Config (%dx%d->%dx%d,0x%X)\n",
- p.w, p.h, p.d_w, p.d_h, p.imgfmt);
-
if (vf_reconfig(d_video->vfilter, params, &p) < 0) {
MP_FATAL(d_video, "Cannot initialize video filters.\n");
return -1;
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h
index ce24d5a58e..5ab7213ee7 100644
--- a/video/decode/dec_video.h
+++ b/video/decode/dec_video.h
@@ -71,7 +71,6 @@ struct dec_video {
// Final PTS of previously decoded image
double decoded_pts;
- int bitrate; // compressed bits/sec
float fps; // FPS from demuxer or from user override
float initial_decoder_aspect;
diff --git a/video/decode/dxva2.c b/video/decode/dxva2.c
index cbd08a7e52..ef7756178d 100644
--- a/video/decode/dxva2.c
+++ b/video/decode/dxva2.c
@@ -256,6 +256,9 @@ static void copy_nv12_fallback(struct mp_image *dest, uint8_t *src_bits,
mp_image_copy(dest, &buf);
}
+#pragma GCC push_options
+#pragma GCC target("sse4.1")
+
static void copy_nv12_gpu_sse4(struct mp_image *dest, uint8_t *src_bits,
unsigned src_pitch, unsigned surf_height)
{
@@ -291,6 +294,8 @@ static void copy_nv12_gpu_sse4(struct mp_image *dest, uint8_t *src_bits,
}
}
+#pragma GCC pop_options
+
static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s,
struct mp_image *img)
{
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 1a0d504461..ca9ab9e5eb 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -328,9 +328,6 @@ static int init(struct dec_video *vd, const char *decoder)
}
}
- if (ctx->avctx->bit_rate != 0)
- vd->bitrate = ctx->avctx->bit_rate;
-
return 1;
}
@@ -362,7 +359,6 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
AVCodecContext *avctx = ctx->avctx;
if (!ctx->avctx)
goto error;
- avctx->bit_rate = 0;
avctx->opaque = vd;
avctx->codec_type = AVMEDIA_TYPE_VIDEO;
avctx->codec_id = lavc_codec->id;
diff --git a/video/filter/vf.c b/video/filter/vf.c
index 888e937fa5..4f9f43f2e0 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -54,7 +54,6 @@ extern const vf_info_t vf_info_hqdn3d;
extern const vf_info_t vf_info_dsize;
extern const vf_info_t vf_info_pullup;
extern const vf_info_t vf_info_delogo;
-extern const vf_info_t vf_info_screenshot;
extern const vf_info_t vf_info_sub;
extern const vf_info_t vf_info_yadif;
extern const vf_info_t vf_info_stereo3d;
@@ -74,9 +73,9 @@ static const vf_info_t *const filter_list[] = {
&vf_info_format,
&vf_info_noformat,
&vf_info_flip,
- &vf_info_mirror,
#if HAVE_LIBAVFILTER
+ &vf_info_mirror,
&vf_info_lavfi,
&vf_info_rotate,
&vf_info_noise,
@@ -88,8 +87,6 @@ static const vf_info_t *const filter_list[] = {
&vf_info_yadif,
#endif
- &vf_info_screenshot,
-
&vf_info_eq,
&vf_info_dsize,
&vf_info_sub,
diff --git a/video/filter/vf.h b/video/filter/vf.h
index f7ee9d011b..76835528b5 100644
--- a/video/filter/vf.h
+++ b/video/filter/vf.h
@@ -144,7 +144,6 @@ enum vf_ctrl {
VFCTRL_SEEK_RESET = 1, // reset on picture and PTS discontinuities
VFCTRL_SET_EQUALIZER, // set color options (brightness,contrast etc)
VFCTRL_GET_EQUALIZER, // get color options (brightness,contrast etc)
- VFCTRL_SCREENSHOT, // Take screenshot, arg is mp_image**
VFCTRL_INIT_OSD, // Filter OSD renderer present?
VFCTRL_SET_DEINTERLACE, // Set deinterlacing status
VFCTRL_GET_DEINTERLACE, // Get deinterlacing status
diff --git a/video/filter/vf_crop.c b/video/filter/vf_crop.c
index 498b2a9037..02787b8f22 100644
--- a/video/filter/vf_crop.c
+++ b/video/filter/vf_crop.c
@@ -58,7 +58,7 @@ static int config(struct vf_instance *vf,
// check:
if(vf->priv->crop_w+vf->priv->crop_x>width ||
vf->priv->crop_h+vf->priv->crop_y>height){
- MP_WARN(vf, "[CROP] Bad position/width/height - cropped area outside of the original!\n");
+ MP_WARN(vf, "Bad position/width/height - cropped area outside of the original!\n");
return 0;
}
vf_rescale_dsize(&d_width, &d_height, width, height,
@@ -85,11 +85,6 @@ static int vf_open(vf_instance_t *vf){
vf->config=config;
vf->filter=filter;
vf->query_format=query_format;
- MP_INFO(vf, "Crop: %d x %d, %d ; %d\n",
- vf->priv->crop_w,
- vf->priv->crop_h,
- vf->priv->crop_x,
- vf->priv->crop_y);
return 1;
}
diff --git a/video/filter/vf_expand.c b/video/filter/vf_expand.c
index 44a8ec5767..6d5b694613 100644
--- a/video/filter/vf_expand.c
+++ b/video/filter/vf_expand.c
@@ -63,18 +63,12 @@ static int config(struct vf_instance *vf,
vf->priv->exp_y = vf->priv->cfg_exp_y;
vf->priv->exp_w = vf->priv->cfg_exp_w;
vf->priv->exp_h = vf->priv->cfg_exp_h;
- // calculate the missing parameters:
-#if 0
- if(vf->priv->exp_w<width) vf->priv->exp_w=width;
- if(vf->priv->exp_h<height) vf->priv->exp_h=height;
-#else
if ( vf->priv->exp_w == -1 ) vf->priv->exp_w=width;
else if (vf->priv->exp_w < -1 ) vf->priv->exp_w=width - vf->priv->exp_w;
else if ( vf->priv->exp_w<width ) vf->priv->exp_w=width;
if ( vf->priv->exp_h == -1 ) vf->priv->exp_h=height;
else if ( vf->priv->exp_h < -1 ) vf->priv->exp_h=height - vf->priv->exp_h;
else if( vf->priv->exp_h<height ) vf->priv->exp_h=height;
-#endif
if (vf->priv->aspect) {
float adjusted_aspect = vf->priv->aspect;
adjusted_aspect *= ((double)width/height) / ((double)d_width/d_height);
@@ -149,13 +143,6 @@ static int vf_open(vf_instance_t *vf){
vf->config=config;
vf->query_format=query_format;
vf->filter=filter;
- MP_INFO(vf, "Expand: %d x %d, %d ; %d, aspect: %f, round: %d\n",
- vf->priv->cfg_exp_w,
- vf->priv->cfg_exp_h,
- vf->priv->cfg_exp_x,
- vf->priv->cfg_exp_y,
- vf->priv->aspect,
- vf->priv->round);
return 1;
}
diff --git a/video/filter/vf_mirror.c b/video/filter/vf_mirror.c
index d92f320235..342c6abd2a 100644
--- a/video/filter/vf_mirror.c
+++ b/video/filter/vf_mirror.c
@@ -15,93 +15,14 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
+#include <stddef.h>
-#include "config.h"
-#include "common/msg.h"
-
-#include "video/img_format.h"
-#include "video/mp_image.h"
#include "vf.h"
+#include "vf_lavfi.h"
-static int config(struct vf_instance *vf, int width, int height,
- int d_width, int d_height,
- unsigned int flags, unsigned int fmt)
-{
- struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
- int a_w = MP_ALIGN_DOWN(width, desc.align_x);
- vf_rescale_dsize(&d_width, &d_height, width, height, a_w, height);
- return vf_next_config(vf, a_w, height, d_width, d_height, flags, fmt);
-}
-
-static inline void mirror_4_m(uint8_t *dst, uint8_t *src, int p,
- int c0, int c1, int c2, int c3)
-{
- for (int x = 0; x < p; x++) {
- dst[x * 4 + 0] = src[(p - x - 1) * 4 + c0];
- dst[x * 4 + 1] = src[(p - x - 1) * 4 + c1];
- dst[x * 4 + 2] = src[(p - x - 1) * 4 + c2];
- dst[x * 4 + 3] = src[(p - x - 1) * 4 + c3];
- }
-}
-
-static inline void mirror(uint8_t *dst, uint8_t *src, int bp, int w)
-{
- for (int x = 0; x < w; x++)
- memcpy(dst + x * bp, src + (w - x - 1) * bp, bp);
-}
-
-static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
+static int vf_open(vf_instance_t *vf)
{
- mp_image_t *dmpi = vf_alloc_out_image(vf);
- if (!dmpi)
- return NULL;
- mp_image_copy_attributes(dmpi, mpi);
-
- for (int p = 0; p < mpi->num_planes; p++) {
- int plane_h = mp_image_plane_h(mpi, p);
- for (int y = 0; y < plane_h; y++) {
- void *p_src = mpi->planes[p] + mpi->stride[p] * y;
- void *p_dst = dmpi->planes[p] + dmpi->stride[p] * y;
- int w = mp_image_plane_w(dmpi, p);
- if (mpi->imgfmt == IMGFMT_YUYV) {
- mirror_4_m(p_dst, p_src, w / 2, 2, 1, 0, 3);
- } else if (mpi->imgfmt == IMGFMT_UYVY) {
- mirror_4_m(p_dst, p_src, w / 2, 0, 3, 2, 1);
- } else {
- // make the compiler unroll the memcpy in mirror()
- switch (mpi->fmt.bytes[p]) {
- case 1: mirror(p_dst, p_src, 1, w); break;
- case 2: mirror(p_dst, p_src, 2, w); break;
- case 3: mirror(p_dst, p_src, 3, w); break;
- case 4: mirror(p_dst, p_src, 4, w); break;
- default:
- mirror(p_dst, p_src, mpi->fmt.bytes[p], w);
- }
- }
- }
- }
-
- talloc_free(mpi);
- return dmpi;
-}
-
-static int query_format(struct vf_instance *vf, unsigned int fmt)
-{
- struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
- if (!(desc.flags & MP_IMGFLAG_BYTE_ALIGNED))
- return 0;
- return vf_next_query_format(vf, fmt);
-}
-
-static int vf_open(vf_instance_t *vf){
- vf->config=config;
- vf->filter=filter;
- vf->query_format=query_format;
- return 1;
+ return vf_lw_set_graph(vf, NULL, NULL, "hflip") >= 0;
}
const vf_info_t vf_info_mirror = {
@@ -109,5 +30,3 @@ const vf_info_t vf_info_mirror = {
.name = "mirror",
.open = vf_open,
};
-
-//===========================================================================//
diff --git a/video/filter/vf_screenshot.c b/video/filter/vf_screenshot.c
deleted file mode 100644
index 57cd9fbb0b..0000000000
--- a/video/filter/vf_screenshot.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "talloc.h"
-
-#include "video/img_format.h"
-#include "video/mp_image.h"
-#include "video/sws_utils.h"
-#include "video/out/vo.h"
-
-#include "vf.h"
-
-struct vf_priv_s {
- struct mp_image *current;
-};
-
-static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
-{
- mp_image_unrefp(&vf->priv->current);
- vf->priv->current = talloc_steal(vf, mp_image_new_ref(mpi));
- return mpi;
-}
-
-static int control (vf_instance_t *vf, int request, void *data)
-{
- if (request == VFCTRL_SCREENSHOT && vf->priv->current) {
- *(struct mp_image **)data = mp_image_new_ref(vf->priv->current);
- return CONTROL_TRUE;
- }
- return CONTROL_UNKNOWN;
-}
-
-static int query_format(struct vf_instance *vf, unsigned int fmt)
-{
- if (mp_sws_supported_format(fmt))
- return vf_next_query_format(vf, fmt);
- return 0;
-}
-
-static int vf_open(vf_instance_t *vf)
-{
- vf->control = control;
- vf->filter = filter;
- vf->query_format = query_format;
- vf->priv = talloc_zero(vf, struct vf_priv_s);
- return 1;
-}
-
-const vf_info_t vf_info_screenshot = {
- .description = "screenshot to file",
- .name = "screenshot",
- .open = vf_open,
-};
diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c
index db0bce3210..01cacbf742 100644
--- a/video/filter/vf_vapoursynth.c
+++ b/video/filter/vf_vapoursynth.c
@@ -20,6 +20,7 @@
#include <string.h>
#include <inttypes.h>
#include <pthread.h>
+#include <limits.h>
#include <assert.h>
#include <VapourSynth.h>
@@ -167,10 +168,10 @@ static void copy_mp_to_vs_frame_props_map(struct vf_priv_s *p, VSMap *map,
}
if (pict_type)
p->vsapi->propSetData(map, "_PictType", &pict_type, 1, 0);
- p->vsapi->propSetInt(map, "_FieldBased",
- !!(img->fields & MP_IMGFIELD_INTERLACED), 0);
- p->vsapi->propSetInt(map, "_Field",
- !!(img->fields & MP_IMGFIELD_TOP_FIRST), 0);
+ int field = 0;
+ if (img->fields & MP_IMGFIELD_INTERLACED)
+ field = img->fields & MP_IMGFIELD_TOP_FIRST ? 2 : 1;
+ p->vsapi->propSetInt(map, "_FieldBased", field, 0);
}
static int set_vs_frame_props(struct vf_priv_s *p, VSFrameRef *frame,
@@ -407,6 +408,10 @@ static void VS_CC infiltInit(VSMap *in, VSMap *out, void **instanceData,
{
struct vf_instance *vf = *instanceData;
struct vf_priv_s *p = vf->priv;
+ // The number of frames of our input node is obviously unknown. The user
+ // could for example seek any time, randomly "ending" the clip.
+ // This specific value was suggested by the VapourSynth developer.
+ int enough_for_everyone = INT_MAX / 16;
// Note: this is called from createFilter, so no need for locking.
@@ -414,6 +419,7 @@ static void VS_CC infiltInit(VSMap *in, VSMap *out, void **instanceData,
.format = p->vsapi->getFormatPreset(mp_to_vs(p->fmt_in.imgfmt), p->vscore),
.width = p->fmt_in.w,
.height = p->fmt_in.h,
+ .numFrames = enough_for_everyone,
};
if (!fmt.format) {
p->vsapi->setError(out, "Unsupported input format.\n");
diff --git a/video/filter/vf_vdpaupp.c b/video/filter/vf_vdpaupp.c
index 4db6ab9186..26c1eef5ca 100644
--- a/video/filter/vf_vdpaupp.c
+++ b/video/filter/vf_vdpaupp.c
@@ -92,9 +92,7 @@ static bool output_field(struct vf_instance *vf, int pos)
frame->field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
if (p->opts.deint) {
- int top_field_first = 1;
- if (mpi->fields & MP_IMGFIELD_ORDERED)
- top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
+ int top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
frame->field = top_field_first ^ (pos & 1) ?
VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
diff --git a/video/image_writer.c b/video/image_writer.c
index 491828aa22..bcb71f4863 100644
--- a/video/image_writer.c
+++ b/video/image_writer.c
@@ -78,14 +78,14 @@ struct image_writer_ctx {
struct img_writer {
const char *file_ext;
- int (*write)(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp);
+ bool (*write)(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp);
const int *pixfmts;
int lavc_codec;
};
-static int write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
+static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
{
- int success = 0;
+ bool success = 0;
AVFrame *pic = NULL;
AVPacket pkt = {0};
int got_output = 0;
@@ -161,7 +161,7 @@ static void write_jpeg_error_exit(j_common_ptr cinfo)
longjmp(*(jmp_buf*)cinfo->client_data, 1);
}
-static int write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
+static bool write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
@@ -173,7 +173,7 @@ static int write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
cinfo.client_data = &error_return_jmpbuf;
if (setjmp(cinfo.client_data)) {
jpeg_destroy_compress(&cinfo);
- return 0;
+ return false;
}
jpeg_create_compress(&cinfo);
@@ -209,7 +209,7 @@ static int write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
jpeg_destroy_compress(&cinfo);
- return 1;
+ return true;
}
#endif
@@ -279,15 +279,38 @@ const char *image_writer_file_ext(const struct image_writer_opts *opts)
return get_writer(opts)->file_ext;
}
-int write_image(struct mp_image *image, const struct image_writer_opts *opts,
- const char *filename, struct mp_log *log)
+struct mp_image *convert_image(struct mp_image *image, int destfmt,
+ struct mp_log *log)
{
- struct mp_image *allocated_image = NULL;
- struct image_writer_opts defs = image_writer_opts_defaults;
int d_w = image->params.d_w;
int d_h = image->params.d_h;
bool is_anamorphic = image->w != d_w || image->h != d_h;
+ // Caveat: no colorspace/levels conversion done if pixel formats equal
+ // it's unclear what colorspace/levels the target wants
+ if (image->imgfmt == destfmt && !is_anamorphic)
+ return mp_image_new_ref(image);
+
+ struct mp_image *dst = mp_image_alloc(destfmt, d_w, d_h);
+ if (!dst) {
+ mp_err(log, "Out of memory.\n");
+ return NULL;
+ }
+ mp_image_copy_attributes(dst, image);
+
+ if (mp_image_swscale(dst, image, mp_sws_hq_flags) < 0) {
+ mp_err(log, "Error when converting image.\n");
+ talloc_free(dst);
+ return NULL;
+ }
+
+ return dst;
+}
+
+bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
+ const char *filename, struct mp_log *log)
+{
+ struct image_writer_opts defs = image_writer_opts_defaults;
if (!opts)
opts = &defs;
@@ -295,39 +318,22 @@ int write_image(struct mp_image *image, const struct image_writer_opts *opts,
struct image_writer_ctx ctx = { log, opts, writer, image->fmt };
int destfmt = get_target_format(&ctx, image->imgfmt);
- // Caveat: no colorspace/levels conversion done if pixel formats equal
- // it's unclear what colorspace/levels the target wants
- if (image->imgfmt != destfmt || is_anamorphic) {
- struct mp_image *dst = mp_image_alloc(destfmt, d_w, d_h);
- if (!dst) {
- mp_err(log, "Out of memory.\n");
- return 0;
- }
- mp_image_copy_attributes(dst, image);
-
- if (mp_image_swscale(dst, image, mp_sws_hq_flags) < 0) {
- mp_err(log, "Error when converting image.\n");
- talloc_free(dst);
- return 0;
- }
-
- allocated_image = dst;
- image = dst;
- }
+ struct mp_image *dst = convert_image(image, destfmt, log);
+ if (!dst)
+ return false;
FILE *fp = fopen(filename, "wb");
- int success = 0;
+ bool success = false;
if (fp == NULL) {
mp_err(log, "Error opening '%s' for writing!\n", filename);
} else {
- success = writer->write(&ctx, image, fp);
+ success = writer->write(&ctx, dst, fp);
success = !fclose(fp) && success;
if (!success)
mp_err(log, "Error writing file '%s'!\n", filename);
}
- talloc_free(allocated_image);
-
+ talloc_free(dst);
return success;
}
diff --git a/video/image_writer.h b/video/image_writer.h
index 7db4293712..b27db3981a 100644
--- a/video/image_writer.h
+++ b/video/image_writer.h
@@ -48,8 +48,14 @@ const char *image_writer_file_ext(const struct image_writer_opts *opts);
* accordingly. Setting w and width or h and height to different values
* can be used to store snapshots of anamorphic video.
*/
-int write_image(struct mp_image *image, const struct image_writer_opts *opts,
- const char *filename, struct mp_log *log);
+bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
+ const char *filename, struct mp_log *log);
+
+/* Return the image converted to the given format. If the pixel aspect ratio is
+ * not 1:1, the image is scaled as well. Returns NULL on failure.
+ */
+struct mp_image *convert_image(struct mp_image *image, int destfmt,
+ struct mp_log *log);
// Debugging helper.
void dump_png(struct mp_image *image, const char *filename, struct mp_log *log);
diff --git a/video/mp_image.c b/video/mp_image.c
index e97ca1564b..dc305a4b63 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -625,14 +625,13 @@ void mp_image_copy_fields_from_av_frame(struct mp_image *dst,
dst->pict_type = src->pict_type;
- dst->fields = MP_IMGFIELD_ORDERED;
+ dst->fields = 0;
if (src->interlaced_frame)
dst->fields |= MP_IMGFIELD_INTERLACED;
if (src->top_field_first)
dst->fields |= MP_IMGFIELD_TOP_FIRST;
if (src->repeat_pict == 1)
dst->fields |= MP_IMGFIELD_REPEAT_FIRST;
-
}
// Copy properties and data of the mp_image into the AVFrame, without taking
diff --git a/video/mp_image.h b/video/mp_image.h
index f2028c33e6..b0110c1376 100644
--- a/video/mp_image.h
+++ b/video/mp_image.h
@@ -30,11 +30,8 @@
#define MP_PALETTE_SIZE (256 * 4)
-#define MP_IMGFIELD_ORDERED 0x01
#define MP_IMGFIELD_TOP_FIRST 0x02
#define MP_IMGFIELD_REPEAT_FIRST 0x04
-#define MP_IMGFIELD_TOP 0x08
-#define MP_IMGFIELD_BOTTOM 0x10
#define MP_IMGFIELD_INTERLACED 0x20
// Describes image parameters that usually stay constant.
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
new file mode 100644
index 0000000000..c61ad69eba
--- /dev/null
+++ b/video/out/drm_common.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of mpv.
+ *
+ * by rr- <rr-@sakuya.pl>
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/vt.h>
+#include <unistd.h>
+
+#include "drm_common.h"
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "osdep/io.h"
+
+#define EVT_RELEASE 1
+#define EVT_ACQUIRE 2
+#define EVT_INTERRUPT 255
+#define HANDLER_ACQUIRE 0
+#define HANDLER_RELEASE 1
+
+static int vt_switcher_pipe[2];
+
+static void vt_switcher_sighandler(int sig)
+{
+ unsigned char event = sig == SIGUSR1 ? EVT_RELEASE : EVT_ACQUIRE;
+ write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+int vt_switcher_init(struct vt_switcher *s, struct mp_log *log)
+{
+ s->log = log;
+ s->tty_fd = -1;
+ vt_switcher_pipe[0] = -1;
+ vt_switcher_pipe[1] = -1;
+
+ if (mp_make_cloexec_pipe(vt_switcher_pipe)) {
+ MP_ERR(s, "Creating pipe failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC);
+ if (s->tty_fd < 0) {
+ MP_ERR(s, "Can't open TTY for VT control: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ struct sigaction act;
+ act.sa_handler = vt_switcher_sighandler;
+ act.sa_flags = SA_RESTART;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGUSR1, &act, 0);
+ sigaction(SIGUSR2, &act, 0);
+
+ struct vt_mode vt_mode;
+ if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) {
+ MP_ERR(s, "VT_GETMODE failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ vt_mode.mode = VT_PROCESS;
+ vt_mode.relsig = SIGUSR1;
+ vt_mode.acqsig = SIGUSR2;
+ if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) {
+ MP_ERR(s, "VT_SETMODE failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+void vt_switcher_acquire(struct vt_switcher *s,
+ void (*handler)(void*), void *user_data)
+{
+ s->handlers[HANDLER_ACQUIRE] = handler;
+ s->handler_data[HANDLER_ACQUIRE] = user_data;
+}
+
+void vt_switcher_release(struct vt_switcher *s,
+ void (*handler)(void*), void *user_data)
+{
+ s->handlers[HANDLER_RELEASE] = handler;
+ s->handler_data[HANDLER_RELEASE] = user_data;
+}
+
+void vt_switcher_interrupt_poll(struct vt_switcher *s)
+{
+ unsigned char event = EVT_INTERRUPT;
+ write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+void vt_switcher_destroy(struct vt_switcher *s)
+{
+ close(s->tty_fd);
+ close(vt_switcher_pipe[0]);
+ close(vt_switcher_pipe[1]);
+}
+
+void vt_switcher_poll(struct vt_switcher *s, int timeout_ms)
+{
+ struct pollfd fds[1] = {
+ { .events = POLLIN, .fd = vt_switcher_pipe[0] },
+ };
+ poll(fds, 1, timeout_ms);
+ if (!fds[0].revents)
+ return;
+
+ unsigned char event;
+ if (read(fds[0].fd, &event, sizeof(event)) != sizeof(event))
+ return;
+
+ switch (event) {
+ case EVT_RELEASE:
+ s->handlers[HANDLER_RELEASE](s->handler_data[HANDLER_RELEASE]);
+
+ if (ioctl(s->tty_fd, VT_RELDISP, 1) < 0) {
+ MP_ERR(s, "Failed to release virtual terminal\n");
+ }
+ break;
+
+ case EVT_ACQUIRE:
+ s->handlers[HANDLER_ACQUIRE](s->handler_data[HANDLER_ACQUIRE]);
+
+ if (ioctl(s->tty_fd, VT_RELDISP, VT_ACKACQ) < 0) {
+ MP_ERR(s, "Failed to acquire virtual terminal\n");
+ }
+ break;
+
+ case EVT_INTERRUPT:
+ break;
+ }
+}
diff --git a/video/out/drm_common.h b/video/out/drm_common.h
new file mode 100644
index 0000000000..5e6c1915ba
--- /dev/null
+++ b/video/out/drm_common.h
@@ -0,0 +1,36 @@
+/*
+ * 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/>.
+ */
+
+#ifndef MP_VT_SWITCHER_H
+#define MP_VT_SWITCHER_H
+
+struct vt_switcher {
+ int tty_fd;
+ struct mp_log *log;
+ void (*handlers[2])(void*);
+ void *handler_data[2];
+};
+
+int vt_switcher_init(struct vt_switcher *s, struct mp_log *log);
+void vt_switcher_destroy(struct vt_switcher *s);
+void vt_switcher_poll(struct vt_switcher *s, int timeout_ms);
+void vt_switcher_interrupt_poll(struct vt_switcher *s);
+
+void vt_switcher_acquire(struct vt_switcher *s, void (*handler)(void*), void *user_data);
+void vt_switcher_release(struct vt_switcher *s, void (*handler)(void*), void *user_data);
+
+#endif
diff --git a/video/out/vo.c b/video/out/vo.c
index 4600205cb7..70f682263e 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -57,6 +57,7 @@ extern const struct vo_driver video_out_null;
extern const struct vo_driver video_out_image;
extern const struct vo_driver video_out_lavc;
extern const struct vo_driver video_out_caca;
+extern const struct vo_driver video_out_drm;
extern const struct vo_driver video_out_direct3d;
extern const struct vo_driver video_out_direct3d_shaders;
extern const struct vo_driver video_out_sdl;
@@ -67,47 +68,50 @@ extern const struct vo_driver video_out_rpi;
const struct vo_driver *const video_out_drivers[] =
{
#if HAVE_RPI
- &video_out_rpi,
+ &video_out_rpi,
#endif
#if HAVE_GL
- &video_out_opengl,
+ &video_out_opengl,
#endif
#if HAVE_VDPAU
- &video_out_vdpau,
+ &video_out_vdpau,
#endif
#if HAVE_DIRECT3D
- &video_out_direct3d_shaders,
- &video_out_direct3d,
+ &video_out_direct3d_shaders,
+ &video_out_direct3d,
#endif
#if HAVE_XV
- &video_out_xv,
+ &video_out_xv,
#endif
#if HAVE_SDL2
- &video_out_sdl,
+ &video_out_sdl,
#endif
#if HAVE_VAAPI
- &video_out_vaapi,
+ &video_out_vaapi,
#endif
#if HAVE_X11
- &video_out_x11,
+ &video_out_x11,
#endif
- &video_out_null,
- // should not be auto-selected
- &video_out_image,
+ &video_out_null,
+ // should not be auto-selected
+ &video_out_image,
#if HAVE_CACA
- &video_out_caca,
+ &video_out_caca,
+#endif
+#if HAVE_DRM
+ &video_out_drm,
#endif
#if HAVE_ENCODING
- &video_out_lavc,
+ &video_out_lavc,
#endif
#if HAVE_GL
- &video_out_opengl_hq,
- &video_out_opengl_cb,
+ &video_out_opengl_hq,
+ &video_out_opengl_cb,
#endif
#if HAVE_WAYLAND
- &video_out_wayland,
+ &video_out_wayland,
#endif
- NULL
+ NULL
};
struct vo_internal {
@@ -751,7 +755,7 @@ static void *vo_thread(void *ptr)
return NULL;
update_display_fps(vo);
- vo_event(vo, VO_WIN_STATE_MINIMIZED);
+ vo_event(vo, VO_EVENT_WIN_STATE);
while (1) {
mp_dispatch_queue_process(vo->in->dispatch, 0);
@@ -983,10 +987,10 @@ struct mp_image *vo_get_current_frame(struct vo *vo)
return r;
}
-/**
- * \brief lookup an integer in a table, table must have 0 as the last key
- * \param key key to search for
- * \result translation corresponding to key or "to" value of last mapping
+/*
+ * lookup an integer in a table, table must have 0 as the last key
+ * param: key key to search for
+ * returns translation corresponding to key or "to" value of last mapping
* if not found.
*/
int lookup_keymap_table(const struct mp_keymap *map, int key)
diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c
new file mode 100644
index 0000000000..64fdadb271
--- /dev/null
+++ b/video/out/vo_drm.c
@@ -0,0 +1,641 @@
+/*
+ * video output driver for libdrm
+ *
+ * by rr- <rr-@sakuya.pl>
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <unistd.h>
+
+#include <libswscale/swscale.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "drm_common.h"
+
+#include "common/msg.h"
+#include "osdep/timer.h"
+#include "sub/osd.h"
+#include "video/fmt-conversion.h"
+#include "video/mp_image.h"
+#include "video/sws_utils.h"
+#include "vo.h"
+
+#define USE_MASTER 0
+#define BUF_COUNT 2
+
+struct modeset_buf {
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t size;
+ uint32_t handle;
+ uint8_t *map;
+ uint32_t fb;
+};
+
+struct modeset_dev {
+ struct modeset_buf bufs[BUF_COUNT];
+ drmModeModeInfo mode;
+ drmModeEncoder *enc;
+ uint32_t conn;
+ uint32_t crtc;
+ int front_buf;
+};
+
+struct priv {
+ char *device_path;
+ int connector_id;
+
+ int fd;
+ struct vt_switcher vt_switcher;
+ struct modeset_dev *dev;
+ drmModeCrtc *old_crtc;
+ drmEventContext ev;
+
+ bool active;
+ bool pflip_happening;
+
+ int32_t device_w;
+ int32_t device_h;
+ int32_t x, y;
+ struct mp_image *last_input;
+ struct mp_image *cur_frame;
+ struct mp_rect src;
+ struct mp_rect dst;
+ struct mp_osd_res osd;
+ struct mp_sws_context *sws;
+};
+
+static int modeset_open(struct vo *vo, int *out, const char *node)
+{
+ *out = -1;
+
+ int fd = open(node, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ MP_ERR(vo, "Cannot open \"%s\": %s.\n", node, mp_strerror(errno));
+ return -errno;
+ }
+
+ uint64_t has_dumb;
+ if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0) {
+ MP_ERR(vo, "Device \"%s\" does not support dumb buffers.\n", node);
+ return -EOPNOTSUPP;
+ }
+
+ *out = fd;
+ return 0;
+}
+
+static void modeset_destroy_fb(int fd, struct modeset_buf *buf)
+{
+ if (buf->map) {
+ munmap(buf->map, buf->size);
+ }
+ if (buf->fb) {
+ drmModeRmFB(fd, buf->fb);
+ }
+ if (buf->handle) {
+ struct drm_mode_destroy_dumb dreq = {
+ .handle = buf->handle,
+ };
+ drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+ }
+}
+
+static int modeset_create_fb(struct vo *vo, int fd, struct modeset_buf *buf)
+{
+ int ret = 0;
+
+ buf->handle = 0;
+
+ // create dumb buffer
+ struct drm_mode_create_dumb creq = {
+ .width = buf->width,
+ .height = buf->height,
+ .bpp = 32,
+ };
+ ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
+ if (ret < 0) {
+ MP_ERR(vo, "Cannot create dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+ buf->stride = creq.pitch;
+ buf->size = creq.size;
+ buf->handle = creq.handle;
+
+ // create framebuffer object for the dumb-buffer
+ ret = drmModeAddFB(fd, buf->width, buf->height, 24, 32, buf->stride,
+ buf->handle, &buf->fb);
+ if (ret) {
+ MP_ERR(vo, "Cannot create framebuffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ // prepare buffer for memory mapping
+ struct drm_mode_map_dumb mreq = {
+ .handle = buf->handle,
+ };
+ ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
+ if (ret) {
+ MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ // perform actual memory mapping
+ buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, mreq.offset);
+ if (buf->map == MAP_FAILED) {
+ MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ memset(buf->map, 0, buf->size);
+
+end:
+ if (ret == 0) {
+ return 0;
+ }
+
+ modeset_destroy_fb(fd, buf);
+ return ret;
+}
+
+static int modeset_find_crtc(struct vo *vo, int fd, drmModeRes *res,
+ drmModeConnector *conn, struct modeset_dev *dev)
+{
+ for (unsigned int i = 0; i < conn->count_encoders; ++i) {
+ drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]);
+ if (!enc) {
+ MP_WARN(vo, "Cannot retrieve encoder %u:%u: %s\n",
+ i, conn->encoders[i], mp_strerror(errno));
+ continue;
+ }
+
+ // iterate all global CRTCs
+ for (unsigned int j = 0; j < res->count_crtcs; ++j) {
+ // check whether this CRTC works with the encoder
+ if (!(enc->possible_crtcs & (1 << j)))
+ continue;
+
+ dev->enc = enc;
+ dev->crtc = enc->crtc_id;
+ return 0;
+ }
+
+ drmModeFreeEncoder(enc);
+ }
+
+ MP_ERR(vo, "Connector %u has no suitable CRTC\n", conn->connector_id);
+ return -ENOENT;
+}
+
+static bool is_connector_valid(struct vo *vo, int conn_id,
+ drmModeConnector *conn, bool silent)
+{
+ if (!conn) {
+ if (!silent) {
+ MP_ERR(vo, "Cannot get connector %d: %s\n", conn_id,
+ mp_strerror(errno));
+ }
+ return false;
+ }
+
+ if (conn->connection != DRM_MODE_CONNECTED) {
+ if (!silent) {
+ MP_ERR(vo, "Connector %d is disconnected\n", conn_id);
+ }
+ return false;
+ }
+
+ if (conn->count_modes == 0) {
+ if (!silent) {
+ MP_ERR(vo, "Connector %d has no valid modes\n", conn_id);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static int modeset_prepare_dev(struct vo *vo, int fd, int conn_id,
+ struct modeset_dev **out)
+{
+ struct modeset_dev *dev = NULL;
+ drmModeConnector *conn = NULL;
+
+ int ret = 0;
+ *out = NULL;
+
+ drmModeRes *res = drmModeGetResources(fd);
+ if (!res) {
+ MP_ERR(vo, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ if (conn_id == -1) {
+ // get the first connected connector
+ for (int i = 0; i < res->count_connectors; i++) {
+ conn = drmModeGetConnector(fd, res->connectors[i]);
+ if (is_connector_valid(vo, i, conn, true)) {
+ conn_id = i;
+ break;
+ }
+ if (conn) {
+ drmModeFreeConnector(conn);
+ conn = NULL;
+ }
+ }
+ if (conn_id == -1) {
+ MP_ERR(vo, "No connected connectors found\n");
+ ret = -ENODEV;
+ goto end;
+ }
+ }
+
+ if (conn_id < 0 || conn_id >= res->count_connectors) {
+ MP_ERR(vo, "Bad connector ID. Max valid connector ID = %u\n",
+ res->count_connectors);
+ ret = -ENODEV;
+ goto end;
+ }
+
+ conn = drmModeGetConnector(fd, res->connectors[conn_id]);
+ if (!is_connector_valid(vo, conn_id, conn, false)) {
+ ret = -ENODEV;
+ goto end;
+ }
+
+ dev = talloc_zero(vo->priv, struct modeset_dev);
+ dev->conn = conn->connector_id;
+ dev->front_buf = 0;
+ dev->mode = conn->modes[0];
+ dev->bufs[0].width = conn->modes[0].hdisplay;
+ dev->bufs[0].height = conn->modes[0].vdisplay;
+ dev->bufs[1].width = conn->modes[0].hdisplay;
+ dev->bufs[1].height = conn->modes[0].vdisplay;
+
+ MP_INFO(vo, "Connector using mode %ux%u\n",
+ dev->bufs[0].width, dev->bufs[0].height);
+
+ ret = modeset_find_crtc(vo, fd, res, conn, dev);
+ if (ret) {
+ MP_ERR(vo, "Connector %d has no valid CRTC\n", conn_id);
+ goto end;
+ }
+
+ for (unsigned int i = 0; i < BUF_COUNT; i++) {
+ ret = modeset_create_fb(vo, fd, &dev->bufs[i]);
+ if (ret) {
+ MP_ERR(vo, "Cannot create framebuffer for connector %d\n",
+ conn_id);
+ for (unsigned int j = 0; j < i; j++) {
+ modeset_destroy_fb(fd, &dev->bufs[j]);
+ }
+ goto end;
+ }
+ }
+
+end:
+ if (conn) {
+ drmModeFreeConnector(conn);
+ conn = NULL;
+ }
+ if (res) {
+ drmModeFreeResources(res);
+ res = NULL;
+ }
+ if (ret == 0) {
+ *out = dev;
+ } else {
+ talloc_free(dev);
+ }
+ return ret;
+}
+
+static void modeset_page_flipped(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, void *data)
+{
+ struct priv *p = data;
+ p->pflip_happening = false;
+}
+
+
+
+static int setup_vo_crtc(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ if (p->active)
+ return 0;
+ p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc);
+ int ret = drmModeSetCrtc(p->fd, p->dev->crtc,
+ p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb,
+ 0, 0, &p->dev->conn, 1, &p->dev->mode);
+ p->active = true;
+ return ret;
+}
+
+static void release_vo_crtc(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (!p->active)
+ return;
+ p->active = false;
+
+ // wait for current page flip
+ while (p->pflip_happening) {
+ int ret = drmHandleEvent(p->fd, &p->ev);
+ if (ret) {
+ MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
+ break;
+ }
+ }
+
+ if (p->old_crtc) {
+ drmModeSetCrtc(p->fd,
+ p->old_crtc->crtc_id,
+ p->old_crtc->buffer_id,
+ p->old_crtc->x,
+ p->old_crtc->y,
+ &p->dev->conn,
+ 1,
+ &p->dev->mode);
+ drmModeFreeCrtc(p->old_crtc);
+ p->old_crtc = NULL;
+ }
+}
+
+static void release_vt(void *data)
+{
+ struct vo *vo = data;
+ release_vo_crtc(vo);
+ if (USE_MASTER) {
+ //this function enables support for switching to x, weston etc.
+ //however, for whatever reason, it can be called only by root users.
+ //until things change, this is commented.
+ struct priv *p = vo->priv;
+ if (drmDropMaster(p->fd)) {
+ MP_WARN(vo, "Failed to drop DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+}
+
+static void acquire_vt(void *data)
+{
+ struct vo *vo = data;
+ if (USE_MASTER) {
+ struct priv *p = vo->priv;
+ if (drmSetMaster(p->fd)) {
+ MP_WARN(vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+
+ setup_vo_crtc(vo);
+}
+
+
+
+static int wait_events(struct vo *vo, int64_t until_time_us)
+{
+ struct priv *p = vo->priv;
+ int64_t wait_us = until_time_us - mp_time_us();
+ int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000);
+ vt_switcher_poll(&p->vt_switcher, timeout_ms);
+ return 0;
+}
+
+static void wakeup(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ vt_switcher_interrupt_poll(&p->vt_switcher);
+}
+
+static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
+{
+ struct priv *p = vo->priv;
+
+ vo->dwidth = p->device_w;
+ vo->dheight = p->device_h;
+ vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
+
+ int32_t w = p->dst.x1 - p->dst.x0;
+ int32_t h = p->dst.y1 - p->dst.y0;
+
+ // p->osd contains the parameters assuming OSD rendering in window
+ // coordinates, but OSD can only be rendered in the intersection
+ // between window and video rectangle (i.e. not into panscan borders).
+ p->osd.w = w;
+ p->osd.h = h;
+ p->osd.mt = MPMIN(0, p->osd.mt);
+ p->osd.mb = MPMIN(0, p->osd.mb);
+ p->osd.mr = MPMIN(0, p->osd.mr);
+ p->osd.ml = MPMIN(0, p->osd.ml);
+
+ p->x = (p->device_w - w) >> 1;
+ p->y = (p->device_h - h) >> 1;
+
+ mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts);
+ p->sws->src = *params;
+ p->sws->dst = (struct mp_image_params) {
+ .imgfmt = IMGFMT_BGR0,
+ .w = w,
+ .h = h,
+ .d_w = w,
+ .d_h = h,
+ };
+
+ talloc_free(p->cur_frame);
+ p->cur_frame = mp_image_alloc(IMGFMT_BGR0, p->device_w, p->device_h);
+ mp_image_params_guess_csp(&p->sws->dst);
+ mp_image_set_params(p->cur_frame, &p->sws->dst);
+
+ if (mp_sws_reinit(p->sws) < 0)
+ return -1;
+
+ vo->want_redraw = true;
+ return 0;
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct priv *p = vo->priv;
+
+ if (p->active) {
+ 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);
+
+ struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
+ int32_t shift = (p->device_w * p->y + p->x) * 4;
+ memcpy_pic(front_buf->map + shift,
+ p->cur_frame->planes[0],
+ (p->dst.x1 - p->dst.x0) * 4,
+ p->dst.y1 - p->dst.y0,
+ p->device_w * 4,
+ p->cur_frame->stride[0]);
+ }
+
+ if (mpi != p->last_input) {
+ talloc_free(p->last_input);
+ p->last_input = mpi;
+ }
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ if (!p->active || p->pflip_happening)
+ return;
+
+ int ret = drmModePageFlip(p->fd, p->dev->crtc,
+ p->dev->bufs[p->dev->front_buf].fb,
+ DRM_MODE_PAGE_FLIP_EVENT, p);
+ if (ret) {
+ MP_WARN(vo, "Cannot flip page for connector\n");
+ } else {
+ p->dev->front_buf++;
+ p->dev->front_buf %= BUF_COUNT;
+ p->pflip_happening = true;
+ }
+
+ // poll page flip finish event
+ const int timeout_ms = 3000;
+ struct pollfd fds[1] = {
+ { .events = POLLIN, .fd = p->fd },
+ };
+ poll(fds, 1, timeout_ms);
+ if (fds[0].revents & POLLIN) {
+ ret = drmHandleEvent(p->fd, &p->ev);
+ if (ret != 0) {
+ MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
+ return;
+ }
+ }
+}
+
+static void uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (p->dev) {
+ release_vo_crtc(vo);
+
+ modeset_destroy_fb(p->fd, &p->dev->bufs[1]);
+ modeset_destroy_fb(p->fd, &p->dev->bufs[0]);
+ drmModeFreeEncoder(p->dev->enc);
+ }
+
+ vt_switcher_destroy(&p->vt_switcher);
+ talloc_free(p->last_input);
+ talloc_free(p->cur_frame);
+ talloc_free(p->dev);
+ close(p->fd);
+}
+
+static int preinit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ p->sws = mp_sws_alloc(vo);
+ p->fd = -1;
+ p->ev.version = DRM_EVENT_CONTEXT_VERSION;
+ p->ev.page_flip_handler = modeset_page_flipped;
+
+ if (vt_switcher_init(&p->vt_switcher, vo->log))
+ goto err;
+
+ vt_switcher_acquire(&p->vt_switcher, acquire_vt, vo);
+ vt_switcher_release(&p->vt_switcher, release_vt, vo);
+
+ if (modeset_open(vo, &p->fd, p->device_path))
+ goto err;
+
+ if (modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev))
+ goto err;
+
+ assert(p->dev);
+ p->device_w = p->dev->bufs[0].width;
+ p->device_h = p->dev->bufs[0].height;
+
+ if (setup_vo_crtc(vo)) {
+ MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id,
+ mp_strerror(errno));
+ goto err;
+ }
+
+ return 0;
+
+err:
+ uninit(vo);
+ return -1;
+}
+
+static int query_format(struct vo *vo, int format)
+{
+ return sws_isSupportedInput(imgfmt2pixfmt(format));
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+ switch (request) {
+ case VOCTRL_REDRAW_FRAME:
+ draw_image(vo, p->last_input);
+ return VO_TRUE;
+ }
+ return VO_NOTIMPL;
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct vo_driver video_out_drm = {
+ .name = "drm",
+ .description = "Direct Rendering Manager",
+ .preinit = preinit,
+ .query_format = query_format,
+ .reconfig = reconfig,
+ .control = control,
+ .draw_image = draw_image,
+ .flip_page = flip_page,
+ .uninit = uninit,
+ .wait_events = wait_events,
+ .wakeup = wakeup,
+ .priv_size = sizeof(struct priv),
+ .options = (const struct m_option[]) {
+ OPT_STRING("devpath", device_path, 0),
+ OPT_INT("connector", connector_id, 0),
+ {0},
+ },
+ .priv_defaults = &(const struct priv) {
+ .device_path = "/dev/dri/card0",
+ .connector_id = -1,
+ },
+};
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index 136ad03c9f..e409eaa18d 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -507,7 +507,8 @@ static const struct m_option options[] = {
OPT_FLAG("glfinish", use_glFinish, 0),
OPT_FLAG("waitvsync", waitvsync, 0),
OPT_INT("swapinterval", swap_interval, 0, OPTDEF_INT(1)),
- OPT_INT("dwmflush", dwm_flush, 0, OPTDEF_INT(0)),
+ OPT_CHOICE("dwmflush", dwm_flush, 0,
+ ({"no", 0}, {"windowed", 1}, {"yes", 2})),
OPT_FLAG("debug", use_gl_debug, 0),
OPT_STRING_VALIDATE("backend", backend, 0, mpgl_validate_backend_opt),
OPT_FLAG("sw", allow_sw, 0),
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
index 48827b8a67..c60218d575 100644
--- a/video/out/vo_rpi.c
+++ b/video/out/vo_rpi.c
@@ -26,6 +26,7 @@
#include <interface/mmal/mmal.h>
#include <interface/mmal/util/mmal_util.h>
#include <interface/mmal/util/mmal_default_components.h>
+#include <interface/mmal/vc/mmal_vc_api.h>
#include <libavutil/rational.h>
@@ -488,6 +489,8 @@ static void uninit(struct vo *vo)
if (p->display)
vc_dispmanx_display_close(p->display);
+
+ mmal_vc_deinit();
}
static int preinit(struct vo *vo)
@@ -500,6 +503,11 @@ static int preinit(struct vo *vo)
bcm_host_init();
+ if (mmal_vc_init()) {
+ MP_FATAL(vo, "Could not initialize MMAL.\n");
+ return -1;
+ }
+
p->display = vc_dispmanx_display_open(p->display_nr);
p->update = vc_dispmanx_update_start(0);
if (!p->display || !p->update) {
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index e1ec3ad8e9..075bda586e 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -565,12 +565,15 @@ static void wakeup_gui_thread(void *ctx)
PostMessage(w32->window, WM_USER, 0, 0);
}
-static double vo_w32_get_display_fps(void)
+static double vo_w32_get_display_fps(struct vo_w32_state *w32)
{
- DEVMODE dm;
- dm.dmSize = sizeof(DEVMODE);
- dm.dmDriverExtra = 0;
- if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
+ // Get the device name of the monitor containing the window
+ HMONITOR mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFOEXW mi = { .cbSize = sizeof mi };
+ GetMonitorInfoW(mon, (MONITORINFO*)&mi);
+
+ DEVMODE dm = { .dmSize = sizeof dm };
+ if (!EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm))
return -1;
// May return 0 or 1 which "represent the display hardware's default refresh rate"
@@ -586,20 +589,22 @@ static double vo_w32_get_display_fps(void)
switch (dm.dmDisplayFrequency) {
case 23:
case 29:
+ case 47:
case 59:
case 71:
+ case 89:
+ case 95:
case 119:
+ case 143:
rv = (rv + 1) / 1.001;
}
return rv;
}
-static void update_display_fps(void *ctx)
+static void update_display_fps(struct vo_w32_state *w32)
{
- struct vo_w32_state *w32 = ctx;
-
- double fps = vo_w32_get_display_fps();
+ double fps = vo_w32_get_display_fps(w32);
if (fps != w32->display_fps) {
w32->display_fps = fps;
signal_events(w32, VO_EVENT_WIN_STATE);
@@ -1316,7 +1321,8 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
return VO_TRUE;
case VOCTRL_KILL_SCREENSAVER:
w32->disable_screensaver = true;
- SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
+ SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED |
+ ES_DISPLAY_REQUIRED);
return VO_TRUE;
case VOCTRL_RESTORE_SCREENSAVER:
w32->disable_screensaver = false;
diff --git a/video/out/win_state.c b/video/out/win_state.c
index af54f8215f..f48f628173 100644
--- a/video/out/win_state.c
+++ b/video/out/win_state.c
@@ -90,6 +90,8 @@ void vo_calc_window_geometry(struct vo *vo, const struct mp_rect *screen,
int d_h = params.d_h;
if ((vo->driver->caps & VO_CAP_ROTATE90) && params.rotate % 180 == 90)
MPSWAP(int, d_w, d_h);
+ d_w = MPCLAMP(d_w * opts->window_scale, 1, 16000);
+ d_h = MPCLAMP(d_h * opts->window_scale, 1, 16000);
int scr_w = screen->x1 - screen->x0;
int scr_h = screen->y1 - screen->y0;
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index 392da3cee0..36a497055a 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -38,6 +38,10 @@
#include "vo.h"
#include "win_state.h"
#include "osdep/timer.h"
+#include "osdep/subprocess.h"
+
+// Specifically for mp_cancel
+#include "stream/stream.h"
#include <X11/Xmd.h>
#include <X11/Xlib.h>
@@ -470,6 +474,30 @@ static void vo_x11_get_bounding_monitors(struct vo_x11_state *x11, long b[4])
#endif
}
+static void *screensaver_thread(void *arg)
+{
+ struct vo_x11_state *x11 = arg;
+
+ for (;;) {
+ sem_wait(&x11->screensaver_sem);
+ // don't queue multiple wakeups
+ while (!sem_trywait(&x11->screensaver_sem)) {}
+
+ if (mp_cancel_test(x11->screensaver_terminate))
+ break;
+
+ char *args[] = {"xdg-screensaver", "reset", NULL};
+ if (mp_subprocess(args, x11->screensaver_terminate, NULL, NULL,
+ NULL, &(char*){0}))
+ {
+ MP_WARN(x11, "Disabling screensaver failed.\n");
+ break;
+ }
+ }
+
+ return NULL;
+}
+
int vo_x11_init(struct vo *vo)
{
struct mp_vo_opts *opts = vo->opts;
@@ -487,6 +515,14 @@ int vo_x11_init(struct vo *vo)
};
vo->x11 = x11;
+ x11->screensaver_terminate = mp_cancel_new(x11);
+ sem_init(&x11->screensaver_sem, 0, 0);
+ if (pthread_create(&x11->screensaver_thread, NULL, screensaver_thread, x11)) {
+ x11->screensaver_terminate = NULL;
+ sem_destroy(&x11->screensaver_sem);
+ goto error;
+ }
+
x11_error_output = x11->log;
XSetErrorHandler(x11_errorhandler);
@@ -498,13 +534,7 @@ int vo_x11_init(struct vo *vo)
if (!x11->display) {
MP_MSG(x11, vo->probing ? MSGL_V : MSGL_ERR,
"couldn't open the X11 display (%s)!\n", dispName);
-
- x11_error_output = NULL;
- XSetErrorHandler(NULL);
-
- talloc_free(x11);
- vo->x11 = NULL;
- return 0;
+ goto error;
}
x11->screen = DefaultScreen(x11->display); // screen ID
x11->rootwin = RootWindow(x11->display, x11->screen); // root window ID
@@ -543,6 +573,10 @@ int vo_x11_init(struct vo *vo)
vo_x11_update_geometry(vo);
return 1;
+
+error:
+ vo_x11_uninit(vo);
+ return 0;
}
static const struct mp_keymap keymap[] = {
@@ -691,9 +725,18 @@ void vo_x11_uninit(struct vo *vo)
MP_VERBOSE(x11, "uninit ...\n");
if (x11->xim)
XCloseIM(x11->xim);
- x11_error_output = NULL;
- XSetErrorHandler(NULL);
- XCloseDisplay(x11->display);
+ if (x11->display) {
+ x11_error_output = NULL;
+ XSetErrorHandler(NULL);
+ XCloseDisplay(x11->display);
+ }
+
+ if (x11->screensaver_terminate) {
+ mp_cancel_trigger(x11->screensaver_terminate);
+ sem_post(&x11->screensaver_sem);
+ pthread_join(x11->screensaver_thread, NULL);
+ sem_destroy(&x11->screensaver_sem);
+ }
talloc_free(x11);
vo->x11 = NULL;
@@ -1766,7 +1809,7 @@ static void xscreensaver_heartbeat(struct vo_x11_state *x11)
(time - x11->screensaver_time_last) >= 10)
{
x11->screensaver_time_last = time;
-
+ sem_post(&x11->screensaver_sem);
XResetScreenSaver(x11->display);
}
}
@@ -1790,7 +1833,7 @@ static int xss_suspend(Display *mDisplay, Bool suspend)
static void set_screensaver(struct vo_x11_state *x11, bool enabled)
{
Display *mDisplay = x11->display;
- if (x11->screensaver_enabled == enabled)
+ if (!mDisplay || x11->screensaver_enabled == enabled)
return;
MP_VERBOSE(x11, "%s screensaver.\n", enabled ? "Enabling" : "Disabling");
x11->screensaver_enabled = enabled;
diff --git a/video/out/x11_common.h b/video/out/x11_common.h
index f6d73dae00..eeff773bef 100644
--- a/video/out/x11_common.h
+++ b/video/out/x11_common.h
@@ -20,9 +20,12 @@
#include <stdint.h>
#include <stdbool.h>
+#include <pthread.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include "osdep/semaphore.h"
+
#include "common/common.h"
struct vo;
@@ -57,6 +60,9 @@ struct vo_x11_state {
bool screensaver_enabled;
bool dpms_touched;
double screensaver_time_last;
+ pthread_t screensaver_thread;
+ sem_t screensaver_sem;
+ struct mp_cancel *screensaver_terminate;
XIM xim;
XIC xic;
diff --git a/wscript b/wscript
index 4cf92d977d..56f03202c1 100644
--- a/wscript
+++ b/wscript
@@ -235,6 +235,11 @@ iconv support use --disable-iconv.",
'desc': 'fchmod()',
'func': check_statement('sys/stat.h', 'fchmod(0, 0)'),
}, {
+ 'name': 'vt.h',
+ 'desc': 'vt.h',
+ 'func': check_statement(['sys/vt.h', 'sys/ioctl.h'],
+ 'int m; ioctl(0, VT_GETMODE, &m)'),
+ }, {
'name': 'glibc-thread-name',
'desc': 'GLIBC API for setting thread name',
'func': check_statement('pthread.h',
@@ -632,6 +637,11 @@ video_output_features = [
'desc': 'CACA',
'func': check_pkg_config('caca', '>= 0.99.beta18'),
}, {
+ 'name': '--drm',
+ 'desc': 'DRM',
+ 'deps': [ 'vt.h' ],
+ 'func': check_pkg_config('libdrm'),
+ }, {
'name': '--jpeg',
'desc': 'JPEG support',
'func': check_cc(header_name=['stdio.h', 'jpeglib.h'],
diff --git a/wscript_build.py b/wscript_build.py
index 4478725596..7328be13b8 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -305,13 +305,12 @@ def build(ctx):
( "video/filter/vf_gradfun.c", "libavfilter"),
( "video/filter/vf_hqdn3d.c", "libavfilter"),
( "video/filter/vf_lavfi.c", "libavfilter"),
- ( "video/filter/vf_mirror.c" ),
+ ( "video/filter/vf_mirror.c", "libavfilter"),
( "video/filter/vf_noformat.c" ),
( "video/filter/vf_noise.c", "libavfilter"),
( "video/filter/vf_pullup.c", "libavfilter"),
( "video/filter/vf_rotate.c", "libavfilter"),
( "video/filter/vf_scale.c" ),
- ( "video/filter/vf_screenshot.c" ),
( "video/filter/vf_stereo3d.c" ),
( "video/filter/vf_sub.c" ),
( "video/filter/vf_unsharp.c", "libavfilter"),
@@ -344,6 +343,7 @@ def build(ctx):
( "video/out/gl_x11egl.c", "egl-x11" ),
( "video/out/vo.c" ),
( "video/out/vo_caca.c", "caca" ),
+ ( "video/out/vo_drm.c", "drm" ),
( "video/out/vo_direct3d.c", "direct3d" ),
( "video/out/vo_image.c" ),
( "video/out/vo_lavc.c", "encoding" ),
@@ -363,6 +363,7 @@ def build(ctx):
( "video/out/wayland/memfile.c", "wayland" ),
( "video/out/win_state.c"),
( "video/out/x11_common.c", "x11" ),
+ ( "video/out/drm_common.c", "drm" ),
## osdep
( getch2_c ),
@@ -375,6 +376,7 @@ def build(ctx):
( "osdep/macosx_application.m", "cocoa-application" ),
( "osdep/macosx_events.m", "cocoa" ),
( "osdep/semaphore_osx.c" ),
+ ( "osdep/subprocess.c" ),
( "osdep/subprocess-posix.c", "posix-spawn" ),
( "osdep/subprocess-win.c", "os-win32" ),
( "osdep/path-macosx.m", "cocoa" ),